From 88558f4ccfb5226e11c593b49407f3862f0d9063 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Sat, 16 Nov 2019 13:21:39 +0300 Subject: [PATCH 01/17] [7.x] Move @kbn/es-query into data plugin - es-query folder (#50182) (#50824) * Move @kbn/es-query into data plugin - es-query folder (#50182) * Move @kbn/es-query into data plugin - es-query * fix eslint issues * Fix PR comments * fix CI * fix Ci * remove extra ts-ignore * fix imports * fix imports * Test importing from data/public and casting to ES Field Types. * Test importing from data/public and casting to ES Field Types. # Conflicts: # src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_pattern.tsx # x-pack/legacy/plugins/siem/public/components/timeline/helpers.test.tsx # x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx # x-pack/legacy/plugins/siem/public/lib/keury/index.ts # x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx # x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx # x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx * fix merge conflicts --- .../src/es_query/__tests__/_migrate_filter.js | 64 -------- .../src/es_query/__tests__/from_filters.js | 151 ------------------ .../src/es_query/__tests__/from_lucene.js | 87 ---------- packages/kbn-es-query/src/es_query/index.d.ts | 39 ----- .../src/es_query/migrate_filter.js | 49 ------ packages/kbn-es-query/src/index.d.ts | 1 - packages/kbn-es-query/src/index.js | 1 - packages/kbn-es-query/src/kuery/ast/ast.d.ts | 14 +- src/fixtures/logstash_fields.js | 7 +- .../public/filter/filter_bar/filter_bar.tsx | 3 +- .../public/index_patterns/fields/field.ts | 40 ++--- .../index_patterns/index_pattern.tsx | 16 +- .../components/create_search_bar.tsx | 3 +- .../search_bar/components/search_bar.tsx | 8 +- .../public/np_ready/public/types.ts | 3 +- .../kibana/public/dashboard/dashboard_app.tsx | 3 +- .../dashboard/dashboard_state_manager.ts | 3 +- .../discover/embeddable/search_embeddable.ts | 3 +- .../field_formats/__tests__/_conformance.js | 2 +- .../kibana/server/field_formats/register.js | 2 +- .../public/vis/timelion_request_handler.ts | 8 +- .../public/components/aggs/filter_ratio.js | 2 +- .../public/components/aggs/percentile.js | 2 +- .../aggs/percentile_rank/percentile_rank.js | 2 +- .../public/components/aggs/std_agg.js | 2 +- .../public/components/aggs/std_deviation.js | 2 +- .../public/components/aggs/top_hit.js | 2 +- .../request_processors/annotations/query.js | 8 +- .../request_processors/series/query.js | 8 +- .../series/split_by_filter.js | 14 +- .../series/split_by_filters.js | 10 +- .../request_processors/table/query.js | 6 +- .../table/split_by_everything.js | 11 +- .../table/split_by_terms.js | 14 +- .../public/vega_request_handler.ts | 8 +- src/legacy/ui/public/agg_types/agg_config.ts | 2 +- .../agg_types/buckets/_bucket_agg_type.ts | 2 +- .../buckets/_terms_other_bucket_helper.js | 5 +- .../buckets/create_filter/date_range.test.ts | 2 +- .../buckets/create_filter/histogram.test.ts | 2 +- .../buckets/create_filter/ip_range.test.ts | 2 +- .../buckets/create_filter/range.test.ts | 2 +- .../agg_types/buckets/date_histogram.ts | 2 +- .../ui/public/agg_types/buckets/date_range.ts | 3 +- .../ui/public/agg_types/buckets/filters.ts | 5 +- .../ui/public/agg_types/buckets/geo_hash.ts | 2 +- .../ui/public/agg_types/buckets/geo_tile.ts | 2 +- .../ui/public/agg_types/buckets/histogram.ts | 2 +- .../ui/public/agg_types/buckets/ip_range.ts | 3 +- .../ui/public/agg_types/buckets/range.test.ts | 2 +- .../ui/public/agg_types/buckets/range.ts | 3 +- .../agg_types/buckets/significant_terms.ts | 2 +- .../ui/public/agg_types/buckets/terms.ts | 3 +- src/legacy/ui/public/agg_types/metrics/avg.ts | 2 +- .../ui/public/agg_types/metrics/geo_bounds.ts | 2 +- .../public/agg_types/metrics/geo_centroid.ts | 2 +- src/legacy/ui/public/agg_types/metrics/max.ts | 2 +- .../ui/public/agg_types/metrics/median.ts | 2 +- .../agg_types/metrics/metric_agg_type.ts | 2 +- src/legacy/ui/public/agg_types/metrics/min.ts | 2 +- .../agg_types/metrics/percentile_ranks.ts | 2 +- .../public/agg_types/metrics/percentiles.ts | 2 +- .../public/agg_types/metrics/std_deviation.ts | 2 +- src/legacy/ui/public/agg_types/metrics/sum.ts | 2 +- .../public/agg_types/metrics/top_hit.test.ts | 2 +- .../ui/public/agg_types/metrics/top_hit.ts | 2 +- .../agg_types/param_types/field.test.ts | 2 +- src/legacy/ui/public/agg_types/utils.ts | 2 +- .../courier/search_source/search_source.js | 7 +- .../loader/pipeline_helpers/utilities.ts | 2 +- .../es_query/es_query/build_es_query.test.ts | 90 ++++++----- .../es_query/es_query/build_es_query.ts | 53 ++++-- .../es_query/es_query/decorate_query.test.ts | 31 ++-- .../es_query/es_query/decorate_query.ts | 19 ++- .../common/es_query/es_query/es_query_dsl.ts | 65 ++++++++ .../es_query/filter_matches_index.test.ts | 35 ++-- .../es_query/es_query/filter_matches_index.ts | 15 +- .../es_query/es_query/from_filters.test.ts | 148 +++++++++++++++++ .../common/es_query/es_query/from_filters.ts | 44 +++-- .../es_query/es_query/from_kuery.test.ts | 59 +++---- .../common/es_query/es_query/from_kuery.ts | 33 ++-- .../es_query/es_query/from_lucene.test.ts | 74 +++++++++ .../common/es_query/es_query/from_lucene.ts | 14 +- .../es_query/get_es_query_config.test.ts | 32 ++-- .../es_query/es_query/get_es_query_config.ts | 16 +- .../data/common/es_query/es_query/index.ts | 2 +- .../es_query/lucene_string_to_dsl.test.ts | 33 ++-- .../es_query/es_query/lucene_string_to_dsl.ts | 33 ++++ .../es_query/es_query/migrate_filter.test.ts | 65 ++++++++ .../es_query/es_query/migrate_filter.ts | 65 ++++++++ .../common/es_query/filters/exists_filter.ts | 4 +- .../data/common/es_query/filters/index.ts | 12 ++ .../es_query/filters/phrase_filter.test.ts | 8 +- .../common/es_query/filters/phrase_filter.ts | 12 +- .../common/es_query/filters/phrases_filter.ts | 11 +- .../filters/query_string_filter.test.ts | 13 +- .../es_query/filters/query_string_filter.ts | 7 +- .../es_query/filters/range_filter.test.ts | 12 +- .../common/es_query/filters/range_filter.ts | 12 +- src/plugins/data/common/es_query/index.ts | 4 +- .../utils/get_time_zone_from_settings.ts | 14 +- .../data/common/es_query/utils/index.ts | 20 +++ .../field_formats/converters/color.test.ts | 2 +- .../field_formats/converters/url.test.ts | 2 +- src/plugins/data/common/index.ts | 1 + .../fields/fields.mocks.ts.ts} | 5 +- .../common/index_patterns/fields/index.ts | 20 +++ .../common/index_patterns/fields/types.ts | 44 +++++ .../data/common/index_patterns/index.ts | 21 +++ .../data/common/index_patterns/mocks.ts | 20 +++ .../data/common/index_patterns/types.ts | 36 +++++ src/plugins/data/common/types.ts | 8 +- .../public/autocomplete_provider/types.ts | 6 +- src/plugins/data/public/index.ts | 3 +- .../data/public/index_patterns/field.stub.ts | 4 +- .../index_patterns/index_pattern.stub.ts | 4 +- .../filter_manager/filter_manager.test.ts | 2 +- .../query/filter_manager/filter_manager.ts | 2 +- .../lib/compare_filters.test.ts | 2 +- .../filter_manager/lib/compare_filters.ts | 2 +- .../filter_manager/lib/dedup_filters.test.ts | 52 +++++- .../query/filter_manager/lib/dedup_filters.ts | 2 +- .../lib/generate_filter.test.ts | 7 +- .../filter_manager/lib/generate_filters.ts | 12 +- .../lib/generate_mapping_chain.test.ts | 2 +- .../lib/generate_mapping_chain.ts | 2 +- .../lib/map_and_flatten_filters.test.ts | 2 +- .../lib/map_and_flatten_filters.ts | 2 +- .../filter_manager/lib/map_filter.test.ts | 2 +- .../query/filter_manager/lib/map_filter.ts | 2 +- .../lib/mappers/map_default.test.ts | 2 +- .../filter_manager/lib/mappers/map_default.ts | 2 +- .../lib/mappers/map_exists.test.ts | 12 +- .../filter_manager/lib/mappers/map_exists.ts | 2 +- .../lib/mappers/map_geo_bounding_box.test.ts | 2 +- .../lib/mappers/map_geo_bounding_box.ts | 2 +- .../lib/mappers/map_geo_polygon.test.ts | 2 +- .../lib/mappers/map_geo_polygon.ts | 2 +- .../lib/mappers/map_match_all.test.ts | 2 +- .../lib/mappers/map_match_all.ts | 2 +- .../lib/mappers/map_missing.test.ts | 2 +- .../filter_manager/lib/mappers/map_missing.ts | 2 +- .../lib/mappers/map_phrase.test.ts | 2 +- .../filter_manager/lib/mappers/map_phrase.ts | 2 +- .../filter_manager/lib/mappers/map_phrases.ts | 2 +- .../lib/mappers/map_query_string.test.ts | 2 +- .../lib/mappers/map_query_string.ts | 2 +- .../lib/mappers/map_range.test.ts | 2 +- .../filter_manager/lib/mappers/map_range.ts | 2 +- .../filter_manager/lib/only_disabled.test.ts | 2 +- .../query/filter_manager/lib/only_disabled.ts | 2 +- .../filter_manager/lib/uniq_filters.test.ts | 2 +- .../query/filter_manager/lib/uniq_filters.ts | 2 +- .../test_helpers/get_filters_array.ts | 2 +- .../test_helpers/get_stub_filter.ts | 2 +- .../data/public/query/filter_manager/types.ts | 2 +- .../data/public/query/timefilter/get_time.ts | 2 +- .../timefilter/lib/change_time_filter.test.ts | 3 +- .../timefilter/lib/change_time_filter.ts | 4 +- .../timefilter/lib/diff_time_picker_vals.ts | 2 +- .../lib/extract_time_filter.test.ts | 23 ++- .../timefilter/lib/extract_time_filter.ts | 2 +- .../public/query/timefilter/time_history.ts | 2 +- .../query/timefilter/timefilter.test.ts | 2 +- .../public/query/timefilter/timefilter.ts | 3 +- .../data/public/query/timefilter/types.ts | 2 +- .../data/public/suggestions_provider/types.ts | 9 +- .../suggestions_provider/value_suggestions.ts | 8 +- src/plugins/data/server/index.ts | 1 + .../field_caps_response.test.js | 2 +- src/plugins/data/server/search/index.ts | 1 - .../field_mapping/mapping_setup.test.ts | 2 +- .../public/field_mapping/mapping_setup.ts | 21 ++- .../public/field_mapping/types.ts | 2 +- .../plugins/demo_search/common/index.ts | 2 +- .../server/lib/build_embeddable_filters.ts | 3 +- .../lens/public/app_plugin/app.test.tsx | 18 +-- .../editor_frame/data_panel_wrapper.tsx | 3 +- .../editor_frame/save.test.ts | 7 +- .../editor_frame/workspace_panel.test.tsx | 7 +- .../public/indexpattern_plugin/field_item.tsx | 16 +- .../public/persistence/saved_object_store.ts | 3 +- x-pack/legacy/plugins/lens/public/types.ts | 3 +- .../legacy/plugins/ml/common/types/fields.ts | 2 +- .../datavisualizer/index_based/page.tsx | 10 +- .../common/job_creator/job_creator.ts | 2 +- .../jobs/new_job_new/utils/new_job_utils.ts | 9 +- .../job_service/new_job_caps/field_service.ts | 2 +- .../job_validation/validate_time_range.js | 2 +- .../csv/server/__tests__/execute_job.js | 2 +- .../server/lib/__tests__/field_format_map.js | 2 +- .../server/lib/generate_csv_search.ts | 40 +++-- .../csv_from_savedobject/types.d.ts | 6 - .../components/embeddables/embedded_map.tsx | 3 +- .../embeddables/embedded_map_helpers.tsx | 3 +- .../public/components/embeddables/types.ts | 3 +- .../events_viewer/events_viewer.tsx | 6 +- .../public/components/events_viewer/index.tsx | 4 +- .../public/components/navigation/helpers.ts | 3 +- .../public/components/search_bar/index.tsx | 4 +- .../public/components/search_bar/selectors.ts | 2 +- .../components/timeline/helpers.test.tsx | 4 +- .../public/components/timeline/helpers.tsx | 6 +- .../public/components/timeline/timeline.tsx | 4 +- .../components/url_state/test_dependencies.ts | 3 +- .../plugins/siem/public/lib/keury/index.ts | 41 +++-- .../siem/public/pages/hosts/details/index.tsx | 4 +- .../plugins/siem/public/pages/hosts/hosts.tsx | 4 +- .../public/pages/network/ip_details/index.tsx | 4 +- .../siem/public/pages/network/network.tsx | 4 +- .../siem/public/pages/network/types.ts | 3 +- .../plugins/siem/public/store/inputs/model.ts | 3 +- .../lib/detection_engine/alerts/get_filter.ts | 22 +-- .../lib/detection_engine/alerts/types.ts | 2 +- .../transform/public/app/lib/kibana/common.ts | 9 +- 215 files changed, 1387 insertions(+), 1053 deletions(-) delete mode 100644 packages/kbn-es-query/src/es_query/__tests__/_migrate_filter.js delete mode 100644 packages/kbn-es-query/src/es_query/__tests__/from_filters.js delete mode 100644 packages/kbn-es-query/src/es_query/__tests__/from_lucene.js delete mode 100644 packages/kbn-es-query/src/es_query/index.d.ts delete mode 100644 packages/kbn-es-query/src/es_query/migrate_filter.js rename packages/kbn-es-query/src/es_query/__tests__/build_es_query.js => src/plugins/data/common/es_query/es_query/build_es_query.test.ts (61%) rename packages/kbn-es-query/src/es_query/build_es_query.js => src/plugins/data/common/es_query/es_query/build_es_query.ts (58%) rename packages/kbn-es-query/src/es_query/__tests__/decorate_query.js => src/plugins/data/common/es_query/es_query/decorate_query.test.ts (50%) rename packages/kbn-es-query/src/es_query/decorate_query.js => src/plugins/data/common/es_query/es_query/decorate_query.ts (69%) create mode 100644 src/plugins/data/common/es_query/es_query/es_query_dsl.ts rename packages/kbn-es-query/src/es_query/__tests__/filter_matches_index.js => src/plugins/data/common/es_query/es_query/filter_matches_index.test.ts (62%) rename packages/kbn-es-query/src/es_query/filter_matches_index.js => src/plugins/data/common/es_query/es_query/filter_matches_index.ts (68%) create mode 100644 src/plugins/data/common/es_query/es_query/from_filters.test.ts rename packages/kbn-es-query/src/es_query/from_filters.js => src/plugins/data/common/es_query/es_query/from_filters.ts (69%) rename packages/kbn-es-query/src/es_query/__tests__/from_kuery.js => src/plugins/data/common/es_query/es_query/from_kuery.test.ts (54%) rename packages/kbn-es-query/src/es_query/from_kuery.js => src/plugins/data/common/es_query/es_query/from_kuery.ts (59%) create mode 100644 src/plugins/data/common/es_query/es_query/from_lucene.test.ts rename packages/kbn-es-query/src/es_query/from_lucene.js => src/plugins/data/common/es_query/es_query/from_lucene.ts (81%) rename packages/kbn-es-query/src/es_query/__tests__/get_es_query_config.js => src/plugins/data/common/es_query/es_query/get_es_query_config.test.ts (69%) rename packages/kbn-es-query/src/es_query/get_es_query_config.js => src/plugins/data/common/es_query/es_query/get_es_query_config.ts (78%) rename packages/kbn-es-query/src/es_query/index.js => src/plugins/data/common/es_query/es_query/index.ts (94%) rename packages/kbn-es-query/src/es_query/__tests__/lucene_string_to_dsl.js => src/plugins/data/common/es_query/es_query/lucene_string_to_dsl.test.ts (58%) create mode 100644 src/plugins/data/common/es_query/es_query/lucene_string_to_dsl.ts create mode 100644 src/plugins/data/common/es_query/es_query/migrate_filter.test.ts create mode 100644 src/plugins/data/common/es_query/es_query/migrate_filter.ts rename packages/kbn-es-query/src/es_query/lucene_string_to_dsl.js => src/plugins/data/common/es_query/utils/get_time_zone_from_settings.ts (78%) create mode 100644 src/plugins/data/common/es_query/utils/index.ts rename src/plugins/data/common/{es_query/__tests__/fields_mock.ts => index_patterns/fields/fields.mocks.ts.ts} (98%) create mode 100644 src/plugins/data/common/index_patterns/fields/index.ts create mode 100644 src/plugins/data/common/index_patterns/fields/types.ts create mode 100644 src/plugins/data/common/index_patterns/index.ts create mode 100644 src/plugins/data/common/index_patterns/mocks.ts create mode 100644 src/plugins/data/common/index_patterns/types.ts diff --git a/packages/kbn-es-query/src/es_query/__tests__/_migrate_filter.js b/packages/kbn-es-query/src/es_query/__tests__/_migrate_filter.js deleted file mode 100644 index d9f559987f58b..0000000000000 --- a/packages/kbn-es-query/src/es_query/__tests__/_migrate_filter.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 expect from '@kbn/expect'; -import _ from 'lodash'; -import { migrateFilter } from '../migrate_filter'; - -describe('migrateFilter', function () { - - const oldMatchPhraseFilter = { - match: { - fieldFoo: { - query: 'foobar', - type: 'phrase' - } - } - }; - - const newMatchPhraseFilter = { - match_phrase: { - fieldFoo: { - query: 'foobar' - } - } - }; - - // https://github.com/elastic/elasticsearch/pull/17508 - it('should migrate match filters of type phrase', function () { - const migratedFilter = migrateFilter(oldMatchPhraseFilter); - expect(_.isEqual(migratedFilter, newMatchPhraseFilter)).to.be(true); - }); - - it('should not modify the original filter', function () { - const oldMatchPhraseFilterCopy = _.clone(oldMatchPhraseFilter, true); - migrateFilter(oldMatchPhraseFilter); - expect(_.isEqual(oldMatchPhraseFilter, oldMatchPhraseFilterCopy)).to.be(true); - }); - - it('should return the original filter if no migration is necessary', function () { - const originalFilter = { - match_all: {} - }; - const migratedFilter = migrateFilter(originalFilter); - expect(migratedFilter).to.be(originalFilter); - expect(_.isEqual(migratedFilter, originalFilter)).to.be(true); - }); - -}); diff --git a/packages/kbn-es-query/src/es_query/__tests__/from_filters.js b/packages/kbn-es-query/src/es_query/__tests__/from_filters.js deleted file mode 100644 index 676992e4dddc8..0000000000000 --- a/packages/kbn-es-query/src/es_query/__tests__/from_filters.js +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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 expect from '@kbn/expect'; -import { buildQueryFromFilters } from '../from_filters'; - -describe('build query', function () { - describe('buildQueryFromFilters', function () { - it('should return the parameters of an Elasticsearch bool query', function () { - const result = buildQueryFromFilters([]); - const expected = { - must: [], - filter: [], - should: [], - must_not: [], - }; - expect(result).to.eql(expected); - }); - - it('should transform an array of kibana filters into ES queries combined in the bool clauses', function () { - const filters = [ - { - match_all: {}, - meta: { type: 'match_all' }, - }, - { - exists: { field: 'foo' }, - meta: { type: 'exists' }, - }, - ]; - - const expectedESQueries = [ - { match_all: {} }, - { exists: { field: 'foo' } }, - ]; - - const result = buildQueryFromFilters(filters); - - expect(result.filter).to.eql(expectedESQueries); - }); - - it('should remove disabled filters', function () { - const filters = [ - { - match_all: {}, - meta: { type: 'match_all', negate: true, disabled: true }, - }, - ]; - - const expectedESQueries = []; - - const result = buildQueryFromFilters(filters); - - expect(result.must_not).to.eql(expectedESQueries); - }); - - it('should remove falsy filters', function () { - const filters = [null, undefined]; - - const expectedESQueries = []; - - const result = buildQueryFromFilters(filters); - - expect(result.must_not).to.eql(expectedESQueries); - expect(result.must).to.eql(expectedESQueries); - }); - - it('should place negated filters in the must_not clause', function () { - const filters = [ - { - match_all: {}, - meta: { type: 'match_all', negate: true }, - }, - ]; - - const expectedESQueries = [{ match_all: {} }]; - - const result = buildQueryFromFilters(filters); - - expect(result.must_not).to.eql(expectedESQueries); - }); - - it('should translate old ES filter syntax into ES 5+ query objects', function () { - const filters = [ - { - query: { exists: { field: 'foo' } }, - meta: { type: 'exists' }, - }, - ]; - - const expectedESQueries = [ - { - exists: { field: 'foo' }, - }, - ]; - - const result = buildQueryFromFilters(filters); - - expect(result.filter).to.eql(expectedESQueries); - }); - - it('should migrate deprecated match syntax', function () { - const filters = [ - { - query: { match: { extension: { query: 'foo', type: 'phrase' } } }, - meta: { type: 'phrase' }, - }, - ]; - - const expectedESQueries = [ - { - match_phrase: { extension: { query: 'foo' } }, - }, - ]; - - const result = buildQueryFromFilters(filters); - - expect(result.filter).to.eql(expectedESQueries); - }); - - it('should not add query:queryString:options to query_string filters', function () { - const filters = [ - { - query: { query_string: { query: 'foo' } }, - meta: { type: 'query_string' }, - }, - ]; - const expectedESQueries = [{ query_string: { query: 'foo' } }]; - - const result = buildQueryFromFilters(filters); - - expect(result.filter).to.eql(expectedESQueries); - }); - }); -}); diff --git a/packages/kbn-es-query/src/es_query/__tests__/from_lucene.js b/packages/kbn-es-query/src/es_query/__tests__/from_lucene.js deleted file mode 100644 index 4361659021bd5..0000000000000 --- a/packages/kbn-es-query/src/es_query/__tests__/from_lucene.js +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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 expect from '@kbn/expect'; -import { buildQueryFromLucene } from '../from_lucene'; -import { decorateQuery } from '../decorate_query'; -import { luceneStringToDsl } from '../lucene_string_to_dsl'; - -describe('build query', function () { - - describe('buildQueryFromLucene', function () { - - it('should return the parameters of an Elasticsearch bool query', function () { - const result = buildQueryFromLucene(); - const expected = { - must: [], - filter: [], - should: [], - must_not: [], - }; - expect(result).to.eql(expected); - }); - - it('should transform an array of lucene queries into ES queries combined in the bool\'s must clause', function () { - const queries = [ - { query: 'foo:bar', language: 'lucene' }, - { query: 'bar:baz', language: 'lucene' }, - ]; - - const expectedESQueries = queries.map( - (query) => { - return decorateQuery(luceneStringToDsl(query.query), {}); - } - ); - - const result = buildQueryFromLucene(queries, {}); - - expect(result.must).to.eql(expectedESQueries); - }); - - it('should also accept queries in ES query DSL format, simply passing them through', function () { - const queries = [ - { query: { match_all: {} }, language: 'lucene' }, - ]; - - const result = buildQueryFromLucene(queries, {}); - - expect(result.must).to.eql([queries[0].query]); - }); - - }); - - it('should accept a date format in the decorated queries and combine that into the bool\'s must clause', function () { - const queries = [ - { query: 'foo:bar', language: 'lucene' }, - { query: 'bar:baz', language: 'lucene' }, - ]; - const dateFormatTZ = 'America/Phoenix'; - - const expectedESQueries = queries.map( - (query) => { - return decorateQuery(luceneStringToDsl(query.query), {}, dateFormatTZ); - } - ); - - const result = buildQueryFromLucene(queries, {}, dateFormatTZ); - - expect(result.must).to.eql(expectedESQueries); - }); - -}); diff --git a/packages/kbn-es-query/src/es_query/index.d.ts b/packages/kbn-es-query/src/es_query/index.d.ts deleted file mode 100644 index 9510a18441e53..0000000000000 --- a/packages/kbn-es-query/src/es_query/index.d.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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. - */ - -export function buildQueryFromFilters(filters: unknown[], indexPattern: unknown): unknown; -export function buildEsQuery( - indexPattern: unknown, - queries: unknown, - filters: unknown, - config?: { - allowLeadingWildcards: boolean; - queryStringOptions: unknown; - ignoreFilterIfFieldNotInIndex: boolean; - dateFormatTZ?: string | null; - } -): unknown; -export function getEsQueryConfig(config: { - get: (name: string) => unknown; -}): { - allowLeadingWildcards: boolean; - queryStringOptions: unknown; - ignoreFilterIfFieldNotInIndex: boolean; - dateFormatTZ?: string | null; -}; diff --git a/packages/kbn-es-query/src/es_query/migrate_filter.js b/packages/kbn-es-query/src/es_query/migrate_filter.js deleted file mode 100644 index b74fc485a6184..0000000000000 --- a/packages/kbn-es-query/src/es_query/migrate_filter.js +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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 _ from 'lodash'; -import { getConvertedValueForField } from '../utils/filters'; - -export function migrateFilter(filter, indexPattern) { - if (filter.match) { - const fieldName = Object.keys(filter.match)[0]; - - - if (isMatchPhraseFilter(filter, fieldName)) { - const params = _.get(filter, ['match', fieldName]); - if (indexPattern) { - const field = indexPattern.fields.find(f => f.name === fieldName); - if (field) { - params.query = getConvertedValueForField(field, params.query); - } - } - return { - match_phrase: { - [fieldName]: _.omit(params, 'type'), - }, - }; - } - } - - return filter; -} - -function isMatchPhraseFilter(filter, fieldName) { - return _.get(filter, ['match', fieldName, 'type']) === 'phrase'; -} diff --git a/packages/kbn-es-query/src/index.d.ts b/packages/kbn-es-query/src/index.d.ts index c06cef6367fe7..79e6903b18644 100644 --- a/packages/kbn-es-query/src/index.d.ts +++ b/packages/kbn-es-query/src/index.d.ts @@ -17,5 +17,4 @@ * under the License. */ -export * from './es_query'; export * from './kuery'; diff --git a/packages/kbn-es-query/src/index.js b/packages/kbn-es-query/src/index.js index 963999bd0999b..79e6903b18644 100644 --- a/packages/kbn-es-query/src/index.js +++ b/packages/kbn-es-query/src/index.js @@ -18,4 +18,3 @@ */ export * from './kuery'; -export * from './es_query'; diff --git a/packages/kbn-es-query/src/kuery/ast/ast.d.ts b/packages/kbn-es-query/src/kuery/ast/ast.d.ts index 06f4940e8ed3b..ef3d0ee828874 100644 --- a/packages/kbn-es-query/src/kuery/ast/ast.d.ts +++ b/packages/kbn-es-query/src/kuery/ast/ast.d.ts @@ -25,18 +25,26 @@ import { JsonObject } from '..'; export type KueryNode = any; +export type DslQuery = any; + export interface KueryParseOptions { helpers: { [key: string]: any; }; startRule: string; + allowLeadingWildcards: boolean; } export function fromKueryExpression( - expression: string, - parseOptions?: KueryParseOptions + expression: string | DslQuery, + parseOptions?: Partial ): KueryNode; -export function toElasticsearchQuery(node: KueryNode, indexPattern?: any): JsonObject; +export function toElasticsearchQuery( + node: KueryNode, + indexPattern?: any, + config?: Record, + context?: Record +): JsonObject; export function doesKueryExpressionHaveLuceneSyntaxError(expression: string): boolean; diff --git a/src/fixtures/logstash_fields.js b/src/fixtures/logstash_fields.js index ab96b69851b71..f054c4d53fd8d 100644 --- a/src/fixtures/logstash_fields.js +++ b/src/fixtures/logstash_fields.js @@ -17,9 +17,10 @@ * under the License. */ -import { castEsToKbnFieldTypeName } from '../plugins/data/common'; -// eslint-disable-next-line max-len -import { shouldReadFieldFromDocValues } from '../plugins/data/server'; +import { + shouldReadFieldFromDocValues, + castEsToKbnFieldTypeName, +} from '../plugins/data/server'; function stubbedLogstashFields() { return [ diff --git a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx index 333e1e328651d..5b389f5b98aba 100644 --- a/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx +++ b/src/legacy/core_plugins/data/public/filter/filter_bar/filter_bar.tsx @@ -22,13 +22,12 @@ import { FormattedMessage, InjectedIntl, injectI18n } from '@kbn/i18n/react'; import classNames from 'classnames'; import React, { useState } from 'react'; import { CoreStart } from 'src/core/public'; -import { DataPublicPluginStart } from 'src/plugins/data/public'; import { IndexPattern } from '../../index_patterns'; import { FilterEditor } from './filter_editor'; import { FilterItem } from './filter_item'; import { FilterOptions } from './filter_options'; import { useKibana, KibanaContextProvider } from '../../../../../../plugins/kibana_react/public'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { DataPublicPluginStart, esFilters } from '../../../../../../plugins/data/public'; interface Props { filters: esFilters.Filter[]; diff --git a/src/legacy/core_plugins/data/public/index_patterns/fields/field.ts b/src/legacy/core_plugins/data/public/index_patterns/fields/field.ts index dc5023795bf19..6084b4c106452 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/fields/field.ts +++ b/src/legacy/core_plugins/data/public/index_patterns/fields/field.ts @@ -22,40 +22,24 @@ import { fieldFormats } from 'ui/registry/field_formats'; import { i18n } from '@kbn/i18n'; // @ts-ignore import { ObjDefine } from './obj_define'; -import { FieldFormat } from '../../../../../../plugins/data/common/field_formats'; // @ts-ignore import { shortenDottedString } from '../../../../../core_plugins/kibana/common/utils/shorten_dotted_string'; import { IndexPattern } from '../index_patterns'; import { getNotifications } from '../services'; -import { getKbnFieldType } from '../../../../../../plugins/data/public'; - -interface FieldSubType { - multi?: { parent: string }; - nested?: { path: string }; -} +import { + FieldFormat, + getKbnFieldType, + IFieldType, + IFieldSubType, +} from '../../../../../../plugins/data/public'; export type FieldSpec = Record; -export interface FieldType { - name: string; - type: string; - script?: string; - lang?: string; - count?: number; - // esTypes might be undefined on old index patterns that have not been refreshed since we added - // this prop. It is also undefined on scripted fields. - esTypes?: string[]; - aggregatable?: boolean; - filterable?: boolean; - searchable?: boolean; - sortable?: boolean; - visualizable?: boolean; - readFromDocValues?: boolean; - scripted?: boolean; - subType?: FieldSubType; - displayName?: string; - format?: any; -} + +/** @deprecated + * Please use IFieldType instead + * */ +export type FieldType = IFieldType; export class Field implements FieldType { name: string; @@ -72,7 +56,7 @@ export class Field implements FieldType { sortable?: boolean; visualizable?: boolean; scripted?: boolean; - subType?: FieldSubType; + subType?: IFieldSubType; displayName?: string; format: any; routes: Record = { diff --git a/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_pattern.tsx b/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_pattern.tsx index 453d726625a79..b1432e61baaeb 100644 --- a/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_pattern.tsx +++ b/src/legacy/core_plugins/data/public/index_patterns/index_patterns/index_pattern.tsx @@ -42,22 +42,18 @@ import { createFieldsFetcher } from './_fields_fetcher'; import { formatHitProvider } from './format_hit'; import { flattenHitWrapper } from './flatten_hit'; import { IIndexPatternsApiClient } from './index_patterns_api_client'; -import { ES_FIELD_TYPES } from '../../../../../../plugins/data/common'; +import { ES_FIELD_TYPES, IIndexPattern } from '../../../../../../plugins/data/public'; import { getNotifications } from '../services'; const MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS = 3; const type = 'index-pattern'; -export interface StaticIndexPattern { - fields: FieldType[]; - title: string; - id?: string; - type?: string; - timeFieldName?: string; - intervalName?: string | null; -} +/** @deprecated + * Please use IIndexPattern instead + * */ +export type StaticIndexPattern = IIndexPattern; -export class IndexPattern implements StaticIndexPattern { +export class IndexPattern implements IIndexPattern { [key: string]: any; public id?: string; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/create_search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/create_search_bar.tsx index 4485b74ca0901..125c6b8dad006 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/create_search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/create_search_bar.tsx @@ -20,12 +20,11 @@ import React, { useState, useEffect } from 'react'; import { Subscription } from 'rxjs'; import { CoreStart } from 'src/core/public'; -import { DataPublicPluginStart } from 'src/plugins/data/public'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public'; import { SearchBar } from '../../../'; import { SearchBarOwnProps } from '.'; -import { esFilters } from '../../../../../../../plugins/data/public'; +import { DataPublicPluginStart, esFilters } from '../../../../../../../plugins/data/public'; interface StatefulSearchBarDeps { core: CoreStart; diff --git a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx index ea0f6775e4831..08a7e89f24c38 100644 --- a/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx +++ b/src/legacy/core_plugins/data/public/search/search_bar/components/search_bar.tsx @@ -24,7 +24,6 @@ import React, { Component } from 'react'; import ResizeObserver from 'resize-observer-polyfill'; import { get, isEqual } from 'lodash'; -import { TimeRange, Query, TimeHistoryContract } from 'src/plugins/data/public'; import { IndexPattern, FilterBar } from '../../../../../data/public'; import { QueryBarTopRow } from '../../../query'; import { SavedQuery, SavedQueryAttributes } from '../index'; @@ -37,7 +36,12 @@ import { KibanaReactContextValue, } from '../../../../../../../plugins/kibana_react/public'; import { IDataPluginServices } from '../../../types'; -import { esFilters } from '../../../../../../../plugins/data/public'; +import { + TimeRange, + Query, + esFilters, + TimeHistoryContract, +} from '../../../../../../../plugins/data/public'; interface SearchBarInjectedDeps { kibana: KibanaReactContextValue; diff --git a/src/legacy/core_plugins/expressions/public/np_ready/public/types.ts b/src/legacy/core_plugins/expressions/public/np_ready/public/types.ts index 9d7b4fb6d0480..c8870b9f97957 100644 --- a/src/legacy/core_plugins/expressions/public/np_ready/public/types.ts +++ b/src/legacy/core_plugins/expressions/public/np_ready/public/types.ts @@ -17,9 +17,8 @@ * under the License. */ -import { TimeRange } from '../../../../../../plugins/data/public'; import { Adapters } from '../../../../../../plugins/inspector/public'; -import { Query } from '../../../../../../plugins/data/public'; +import { TimeRange, Query } from '../../../../../../plugins/data/public'; export { TimeRange, Adapters, Query }; export * from '../../../../../../plugins/expressions/public'; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx index 656b54040ad99..d5da4ba51e55b 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_app.tsx @@ -32,7 +32,6 @@ import { } from 'ui/state_management/app_state'; import { KbnUrl } from 'ui/url/kbn_url'; -import { TimeRange, Query } from 'src/plugins/data/public'; import { IndexPattern } from 'ui/index_patterns'; import { IPrivate } from 'ui/private'; import { StaticIndexPattern, SavedQuery } from 'plugins/data'; @@ -42,7 +41,7 @@ import { Subscription } from 'rxjs'; import { ViewMode } from '../../../embeddable_api/public/np_ready/public'; import { SavedObjectDashboard } from './saved_dashboard/saved_dashboard'; import { DashboardAppState, SavedDashboardPanel, ConfirmModalFn } from './types'; -import { esFilters } from '../../../../../../src/plugins/data/public'; +import { TimeRange, Query, esFilters } from '../../../../../../src/plugins/data/public'; import { DashboardAppController } from './dashboard_app_controller'; diff --git a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts index 1a42ed837a9de..d5af4c93d0e0c 100644 --- a/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts +++ b/src/legacy/core_plugins/kibana/public/dashboard/dashboard_state_manager.ts @@ -27,9 +27,8 @@ import { migrateLegacyQuery } from 'ui/utils/migrate_legacy_query'; import { Moment } from 'moment'; import { DashboardContainer } from 'src/legacy/core_plugins/dashboard_embeddable_container/public/np_ready/public'; -import { Query } from 'src/plugins/data/public'; import { ViewMode } from '../../../../../../src/plugins/embeddable/public'; -import { esFilters } from '../../../../../../src/plugins/data/public'; +import { Query, esFilters } from '../../../../../../src/plugins/data/public'; import { getAppStateDefaults, migrateAppState } from './lib'; import { convertPanelStateToSavedDashboardPanel } from './lib/embeddable_saved_object_converters'; diff --git a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts index c575465a377e2..ef79cda476e51 100644 --- a/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts +++ b/src/legacy/core_plugins/kibana/public/discover/embeddable/search_embeddable.ts @@ -30,6 +30,7 @@ import { generateFilters, getTime, Query, + IFieldType, } from '../../../../../../plugins/data/public'; import { APPLY_FILTER_TRIGGER, @@ -66,7 +67,7 @@ interface SearchScope extends ng.IScope { removeColumn?: (column: string) => void; addColumn?: (column: string) => void; moveColumn?: (column: string, index: number) => void; - filter?: (field: { name: string; scripted: boolean }, value: string[], operator: string) => void; + filter?: (field: IFieldType, value: string[], operator: string) => void; hits?: any[]; indexPattern?: IndexPattern; totalHitCount?: number; diff --git a/src/legacy/core_plugins/kibana/public/field_formats/__tests__/_conformance.js b/src/legacy/core_plugins/kibana/public/field_formats/__tests__/_conformance.js index bc27e1a0ac2a1..1c63d2efc7e0b 100644 --- a/src/legacy/core_plugins/kibana/public/field_formats/__tests__/_conformance.js +++ b/src/legacy/core_plugins/kibana/public/field_formats/__tests__/_conformance.js @@ -20,8 +20,8 @@ import _ from 'lodash'; import expect from '@kbn/expect'; import { fieldFormats } from 'ui/registry/field_formats'; -import { FieldFormat } from '../../../../../../plugins/data/common/field_formats'; import { npStart } from 'ui/new_platform'; +import { FieldFormat } from '../../../../../../plugins/data/public'; const config = npStart.core.uiSettings; diff --git a/src/legacy/core_plugins/kibana/server/field_formats/register.js b/src/legacy/core_plugins/kibana/server/field_formats/register.js index 95818f22b397e..bade66ce2c881 100644 --- a/src/legacy/core_plugins/kibana/server/field_formats/register.js +++ b/src/legacy/core_plugins/kibana/server/field_formats/register.js @@ -33,7 +33,7 @@ import { BoolFormat, SourceFormat, StaticLookupFormat -} from '../../../../../plugins/data/common'; +} from '../../../../../plugins/data/server'; export function registerFieldFormats(server) { server.registerFieldFormat(UrlFormat); diff --git a/src/legacy/core_plugins/timelion/public/vis/timelion_request_handler.ts b/src/legacy/core_plugins/timelion/public/vis/timelion_request_handler.ts index 35dea4a0deb9b..74111bf794877 100644 --- a/src/legacy/core_plugins/timelion/public/vis/timelion_request_handler.ts +++ b/src/legacy/core_plugins/timelion/public/vis/timelion_request_handler.ts @@ -17,15 +17,13 @@ * under the License. */ -// @ts-ignore -import { buildEsQuery, getEsQueryConfig } from '@kbn/es-query'; // @ts-ignore import { timezoneProvider } from 'ui/vis/lib/timezone'; import { KIBANA_CONTEXT_NAME } from 'src/plugins/expressions/public'; -import { Query, TimeRange, esFilters } from 'src/plugins/data/public'; import { VisParams } from 'ui/vis'; import { i18n } from '@kbn/i18n'; import { TimelionVisualizationDependencies } from '../plugin'; +import { TimeRange, esFilters, esQuery, Query } from '../../../../../plugins/data/public'; interface Stats { cacheCount: number; @@ -74,7 +72,7 @@ export function getTimelionRequestHandler(dependencies: TimelionVisualizationDep ); } - const esQueryConfigs = getEsQueryConfig(uiSettings); + const esQueryConfigs = esQuery.getEsQueryConfig(uiSettings); // parse the time range client side to make sure it behaves like other charts const timeRangeBounds = timefilter.calculateBounds(timeRange); @@ -85,7 +83,7 @@ export function getTimelionRequestHandler(dependencies: TimelionVisualizationDep sheet: [expression], extended: { es: { - filter: buildEsQuery(undefined, query, filters, esQueryConfigs), + filter: esQuery.buildEsQuery(null, query, filters, esQueryConfigs), }, }, time: { diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/filter_ratio.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/filter_ratio.js index 92558559845be..6974389897812 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/filter_ratio.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/filter_ratio.js @@ -35,7 +35,7 @@ import { EuiFormRow, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; import { METRIC_TYPES } from '../../../common/metric_types'; export const FilterRatioAgg = props => { diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/percentile.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/percentile.js index fafb2621aafe0..ec16a0f2eb3ee 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/percentile.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/percentile.js @@ -34,7 +34,7 @@ import { EuiFormRow, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; import { Percentiles, newPercentile } from './percentile_ui'; const RESTRICT_FIELDS = [KBN_FIELD_TYPES.NUMBER]; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/percentile_rank/percentile_rank.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/percentile_rank/percentile_rank.js index 85aded0ca248b..069ea9706e927 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/percentile_rank/percentile_rank.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/percentile_rank/percentile_rank.js @@ -36,7 +36,7 @@ import { EuiSpacer, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../../../plugins/data/public'; const RESTRICT_FIELDS = [KBN_FIELD_TYPES.NUMBER]; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/std_agg.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/std_agg.js index 4b8b356f2af48..67fe9403e402b 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/std_agg.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/std_agg.js @@ -33,7 +33,7 @@ import { EuiSpacer, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; import { METRIC_TYPES } from '../../../common/metric_types'; export function StandardAgg(props) { diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/std_deviation.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/std_deviation.js index c078bac8d7f9c..1f0347b210886 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/std_deviation.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/std_deviation.js @@ -36,7 +36,7 @@ import { EuiSpacer, } from '@elastic/eui'; import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; const RESTRICT_FIELDS = [KBN_FIELD_TYPES.NUMBER]; diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/top_hit.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/top_hit.js index 1439768b95643..fa92713046aca 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/top_hit.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/aggs/top_hit.js @@ -35,7 +35,7 @@ import { EuiFormRow, } from '@elastic/eui'; import { injectI18n, FormattedMessage } from '@kbn/i18n/react'; -import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../../plugins/data/public'; import { PANEL_TYPES } from '../../../common/panel_types'; const isFieldTypeEnabled = (fieldRestrictions, fieldType) => diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/query.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/query.js index e91c182ad1d5a..19bf807f1b315 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/query.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/annotations/query.js @@ -19,7 +19,7 @@ import { getBucketSize } from '../../helpers/get_bucket_size'; import { getTimerange } from '../../helpers/get_timerange'; -import { buildEsQuery } from '@kbn/es-query'; +import { esQuery } from '../../../../../../../../plugins/data/server'; export function query(req, panel, annotation, esQueryConfig, indexPattern, capabilities) { return next => doc => { @@ -30,7 +30,7 @@ export function query(req, panel, annotation, esQueryConfig, indexPattern, capab doc.size = 0; const queries = !annotation.ignore_global_filters ? req.payload.query : []; const filters = !annotation.ignore_global_filters ? req.payload.filters : []; - doc.query = buildEsQuery(indexPattern, queries, filters, esQueryConfig); + doc.query = esQuery.buildEsQuery(indexPattern, queries, filters, esQueryConfig); const timerange = { range: { [timeField]: { @@ -44,12 +44,12 @@ export function query(req, panel, annotation, esQueryConfig, indexPattern, capab if (annotation.query_string) { doc.query.bool.must.push( - buildEsQuery(indexPattern, [annotation.query_string], [], esQueryConfig) + esQuery.buildEsQuery(indexPattern, [annotation.query_string], [], esQueryConfig) ); } if (!annotation.ignore_panel_filters && panel.filter) { - doc.query.bool.must.push(buildEsQuery(indexPattern, [panel.filter], [], esQueryConfig)); + doc.query.bool.must.push(esQuery.buildEsQuery(indexPattern, [panel.filter], [], esQueryConfig)); } if (annotation.fields) { diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js index 75de84ae33462..a287b394422e1 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/query.js @@ -19,7 +19,7 @@ import { offsetTime } from '../../offset_time'; import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; -import { buildEsQuery } from '@kbn/es-query'; +import { esQuery } from '../../../../../../../../plugins/data/server'; export function query(req, panel, series, esQueryConfig, indexPatternObject) { return next => doc => { @@ -29,7 +29,7 @@ export function query(req, panel, series, esQueryConfig, indexPatternObject) { doc.size = 0; const queries = !panel.ignore_global_filter ? req.payload.query : []; const filters = !panel.ignore_global_filter ? req.payload.filters : []; - doc.query = buildEsQuery(indexPatternObject, queries, filters, esQueryConfig); + doc.query = esQuery.buildEsQuery(indexPatternObject, queries, filters, esQueryConfig); const timerange = { range: { @@ -43,12 +43,12 @@ export function query(req, panel, series, esQueryConfig, indexPatternObject) { doc.query.bool.must.push(timerange); if (panel.filter) { - doc.query.bool.must.push(buildEsQuery(indexPatternObject, [panel.filter], [], esQueryConfig)); + doc.query.bool.must.push(esQuery.buildEsQuery(indexPatternObject, [panel.filter], [], esQueryConfig)); } if (series.filter) { doc.query.bool.must.push( - buildEsQuery(indexPatternObject, [series.filter], [], esQueryConfig) + esQuery.buildEsQuery(indexPatternObject, [series.filter], [], esQueryConfig) ); } diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filter.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filter.js index 8b9ebf9319efe..1548c9e17c2e1 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filter.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filter.js @@ -17,17 +17,21 @@ * under the License. */ -import _ from 'lodash'; -import { buildEsQuery } from '@kbn/es-query'; +import { set } from 'lodash'; +import { esQuery } from '../../../../../../../../plugins/data/server'; export function splitByFilter(req, panel, series, esQueryConfig, indexPattern) { return next => doc => { - if (series.split_mode !== 'filter') return next(doc); - _.set( + if (series.split_mode !== 'filter') { + return next(doc); + } + + set( doc, `aggs.${series.id}.filter`, - buildEsQuery(indexPattern, [series.filter], [], esQueryConfig) + esQuery.buildEsQuery(indexPattern, [series.filter], [], esQueryConfig) ); + return next(doc); }; } diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filters.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filters.js index 60b29141aaa3e..4295bceed43cc 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filters.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/series/split_by_filters.js @@ -17,14 +17,16 @@ * under the License. */ -import _ from 'lodash'; -import { buildEsQuery } from '@kbn/es-query'; +import { set } from 'lodash'; +import { esQuery } from '../../../../../../../../plugins/data/server'; + export function splitByFilters(req, panel, series, esQueryConfig, indexPattern) { return next => doc => { if (series.split_mode === 'filters' && series.split_filters) { series.split_filters.forEach(filter => { - const builtEsQuery = buildEsQuery(indexPattern, [filter.filter], [], esQueryConfig); - _.set(doc, `aggs.${series.id}.filters.filters.${filter.id}`, builtEsQuery); + const builtEsQuery = esQuery.buildEsQuery(indexPattern, [filter.filter], [], esQueryConfig); + + set(doc, `aggs.${series.id}.filters.filters.${filter.id}`, builtEsQuery); }); } return next(doc); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.js index 212e7a615dcad..f3b5413c63d79 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/query.js @@ -16,9 +16,9 @@ * specific language governing permissions and limitations * under the License. */ -import { buildEsQuery } from '@kbn/es-query'; import { getTimerange } from '../../helpers/get_timerange'; import { getIntervalAndTimefield } from '../../get_interval_and_timefield'; +import { esQuery } from '../../../../../../../../plugins/data/server'; export function query(req, panel, esQueryConfig, indexPatternObject) { return next => doc => { @@ -29,7 +29,7 @@ export function query(req, panel, esQueryConfig, indexPatternObject) { const queries = !panel.ignore_global_filter ? req.payload.query : []; const filters = !panel.ignore_global_filter ? req.payload.filters : []; - doc.query = buildEsQuery(indexPatternObject, queries, filters, esQueryConfig); + doc.query = esQuery.buildEsQuery(indexPatternObject, queries, filters, esQueryConfig); const timerange = { range: { @@ -42,7 +42,7 @@ export function query(req, panel, esQueryConfig, indexPatternObject) { }; doc.query.bool.must.push(timerange); if (panel.filter) { - doc.query.bool.must.push(buildEsQuery(indexPatternObject, [panel.filter], [], esQueryConfig)); + doc.query.bool.must.push(esQuery.buildEsQuery(indexPatternObject, [panel.filter], [], esQueryConfig)); } return next(doc); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_everything.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_everything.js index 01d33ca86e6d1..17f99ea431fd3 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_everything.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_everything.js @@ -17,21 +17,22 @@ * under the License. */ -import _ from 'lodash'; -import { buildEsQuery } from '@kbn/es-query'; +import { set } from 'lodash'; +import { esQuery } from '../../../../../../../../plugins/data/server'; + export function splitByEverything(req, panel, esQueryConfig, indexPattern) { return next => doc => { panel.series .filter(c => !(c.aggregate_by && c.aggregate_function)) .forEach(column => { if (column.filter) { - _.set( + set( doc, `aggs.pivot.aggs.${column.id}.filter`, - buildEsQuery(indexPattern, [column.filter], [], esQueryConfig) + esQuery.buildEsQuery(indexPattern, [column.filter], [], esQueryConfig) ); } else { - _.set(doc, `aggs.pivot.aggs.${column.id}.filter.match_all`, {}); + set(doc, `aggs.pivot.aggs.${column.id}.filter.match_all`, {}); } }); return next(doc); diff --git a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_terms.js b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_terms.js index 829f7d8c5a0de..042e4d98e2767 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_terms.js +++ b/src/legacy/core_plugins/vis_type_timeseries/server/lib/vis_data/request_processors/table/split_by_terms.js @@ -17,20 +17,22 @@ * under the License. */ -import _ from 'lodash'; -import { buildEsQuery } from '@kbn/es-query'; +import { set } from 'lodash'; +import { esQuery } from '../../../../../../../../plugins/data/server'; + export function splitByTerms(req, panel, esQueryConfig, indexPattern) { return next => doc => { panel.series .filter(c => c.aggregate_by && c.aggregate_function) .forEach(column => { - _.set(doc, `aggs.pivot.aggs.${column.id}.terms.field`, column.aggregate_by); - _.set(doc, `aggs.pivot.aggs.${column.id}.terms.size`, 100); + set(doc, `aggs.pivot.aggs.${column.id}.terms.field`, column.aggregate_by); + set(doc, `aggs.pivot.aggs.${column.id}.terms.size`, 100); + if (column.filter) { - _.set( + set( doc, `aggs.pivot.aggs.${column.id}.column_filter.filter`, - buildEsQuery(indexPattern, [column.filter], [], esQueryConfig) + esQuery.buildEsQuery(indexPattern, [column.filter], [], esQueryConfig) ); } }); diff --git a/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts b/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts index accc52c1e5a14..83ae31bf87400 100644 --- a/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts +++ b/src/legacy/core_plugins/vis_type_vega/public/vega_request_handler.ts @@ -18,9 +18,7 @@ */ import { timefilter } from 'ui/timefilter'; - -import { buildEsQuery, getEsQueryConfig } from '@kbn/es-query'; -import { esFilters, TimeRange, Query } from '../../../../plugins/data/public'; +import { esFilters, esQuery, TimeRange, Query } from '../../../../plugins/data/public'; // @ts-ignore import { VegaParser } from './data_model/vega_parser'; @@ -50,8 +48,8 @@ export function createVegaRequestHandler({ return ({ timeRange, filters, query, visParams }: VegaRequestHandlerParams) => { timeCache.setTimeRange(timeRange); - const esQueryConfigs = getEsQueryConfig(uiSettings); - const filtersDsl = buildEsQuery(undefined, query, filters, esQueryConfigs); + const esQueryConfigs = esQuery.getEsQueryConfig(uiSettings); + const filtersDsl = esQuery.buildEsQuery(null, query, filters, esQueryConfigs); const vp = new VegaParser(visParams.spec, searchCache, timeCache, filtersDsl, serviceSettings); return vp.parseAsync(); diff --git a/src/legacy/ui/public/agg_types/agg_config.ts b/src/legacy/ui/public/agg_types/agg_config.ts index eedfc1cc05a84..becfaf8c89e27 100644 --- a/src/legacy/ui/public/agg_types/agg_config.ts +++ b/src/legacy/ui/public/agg_types/agg_config.ts @@ -32,7 +32,7 @@ import { AggGroupNames } from '../vis/editors/default/agg_groups'; import { writeParams } from './agg_params'; import { AggConfigs } from './agg_configs'; import { Schema } from '../vis/editors/default/schemas'; -import { ContentType } from '../../../../plugins/data/common'; +import { ContentType } from '../../../../plugins/data/public'; // @ts-ignore import { fieldFormats } from '../registry/field_formats'; diff --git a/src/legacy/ui/public/agg_types/buckets/_bucket_agg_type.ts b/src/legacy/ui/public/agg_types/buckets/_bucket_agg_type.ts index 74e4017b28728..c151f9101d090 100644 --- a/src/legacy/ui/public/agg_types/buckets/_bucket_agg_type.ts +++ b/src/legacy/ui/public/agg_types/buckets/_bucket_agg_type.ts @@ -19,7 +19,7 @@ import { AggParamType } from '../param_types/agg'; import { AggConfig } from '../../vis'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; import { AggType, AggTypeConfig } from '../agg_type'; export type IBucketAggConfig = AggConfig; diff --git a/src/legacy/ui/public/agg_types/buckets/_terms_other_bucket_helper.js b/src/legacy/ui/public/agg_types/buckets/_terms_other_bucket_helper.js index 70bca2e40ae3f..d0d712704964b 100644 --- a/src/legacy/ui/public/agg_types/buckets/_terms_other_bucket_helper.js +++ b/src/legacy/ui/public/agg_types/buckets/_terms_other_bucket_helper.js @@ -18,9 +18,8 @@ */ import _ from 'lodash'; -import { buildQueryFromFilters } from '@kbn/es-query'; import { AggGroupNames } from '../../vis/editors/default/agg_groups'; -import { esFilters } from '../../../../../plugins/data/public'; +import { esFilters, esQuery } from '../../../../../plugins/data/public'; /** * walks the aggregation DSL and returns DSL starting at aggregation with id of startFromAggId @@ -197,7 +196,7 @@ export const buildOtherBucketAgg = (aggConfigs, aggWithOtherBucket, response) => }); resultAgg.filters.filters[key] = { - bool: buildQueryFromFilters(filters, indexPattern), + bool: esQuery.buildQueryFromFilters(filters, indexPattern), }; }; walkBucketTree(0, response.aggregations, bucketAggs[0].id, [], ''); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/date_range.test.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/date_range.test.ts index 35b6c38bad799..0399e8d382320 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/date_range.test.ts +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/date_range.test.ts @@ -19,7 +19,7 @@ import moment from 'moment'; import { createFilterDateRange } from './date_range'; -import { DateFormat } from '../../../../../../plugins/data/common'; +import { DateFormat } from '../../../../../../plugins/data/public'; import { AggConfigs } from '../../agg_configs'; import { BUCKET_TYPES } from '../bucket_agg_types'; diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.test.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.test.ts index 0095df75b8914..ac8e55f096fb4 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.test.ts +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.test.ts @@ -19,7 +19,7 @@ import { createFilterHistogram } from './histogram'; import { AggConfigs } from '../../agg_configs'; import { BUCKET_TYPES } from '../bucket_agg_types'; -import { BytesFormat } from '../../../../../../plugins/data/common'; +import { BytesFormat } from '../../../../../../plugins/data/public'; jest.mock('ui/new_platform'); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.test.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.test.ts index 2e030d820b396..569735a60298d 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.test.ts +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/ip_range.test.ts @@ -19,7 +19,7 @@ import { createFilterIpRange } from './ip_range'; import { AggConfigs } from '../../agg_configs'; -import { IpFormat } from '../../../../../../plugins/data/common'; +import { IpFormat } from '../../../../../../plugins/data/public'; import { BUCKET_TYPES } from '../bucket_agg_types'; jest.mock('ui/new_platform'); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/range.test.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/range.test.ts index 04476ba62ccd5..e7344f16ba0b1 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/range.test.ts +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/range.test.ts @@ -18,7 +18,7 @@ */ import { createFilterRange } from './range'; -import { BytesFormat } from '../../../../../../plugins/data/common'; +import { BytesFormat } from '../../../../../../plugins/data/public'; import { AggConfigs } from '../../agg_configs'; import { BUCKET_TYPES } from '../bucket_agg_types'; diff --git a/src/legacy/ui/public/agg_types/buckets/date_histogram.ts b/src/legacy/ui/public/agg_types/buckets/date_histogram.ts index e86d561a1c79b..03e358af5f1f0 100644 --- a/src/legacy/ui/public/agg_types/buckets/date_histogram.ts +++ b/src/legacy/ui/public/agg_types/buckets/date_histogram.ts @@ -34,7 +34,7 @@ import { writeParams } from '../agg_params'; import { AggConfigs } from '../agg_configs'; import { isMetricAggType } from '../metrics/metric_agg_type'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; // @ts-ignore import { TimeBuckets } from '../../time_buckets'; diff --git a/src/legacy/ui/public/agg_types/buckets/date_range.ts b/src/legacy/ui/public/agg_types/buckets/date_range.ts index 4de6002e2e374..908d921d12313 100644 --- a/src/legacy/ui/public/agg_types/buckets/date_range.ts +++ b/src/legacy/ui/public/agg_types/buckets/date_range.ts @@ -23,14 +23,13 @@ import { npStart } from 'ui/new_platform'; import { BUCKET_TYPES } from './bucket_agg_types'; import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; import { createFilterDateRange } from './create_filter/date_range'; -import { FieldFormat } from '../../../../../plugins/data/common/field_formats'; +import { FieldFormat, KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; import { DateRangesParamEditor } from '../../vis/editors/default/controls/date_ranges'; // @ts-ignore import { fieldFormats } from '../../registry/field_formats'; // @ts-ignore import { dateRange } from '../../utils/date_range'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; const dateRangeTitle = i18n.translate('common.ui.aggTypes.buckets.dateRangeTitle', { defaultMessage: 'Date Range', diff --git a/src/legacy/ui/public/agg_types/buckets/filters.ts b/src/legacy/ui/public/agg_types/buckets/filters.ts index a8d509d507c6b..caebf2d7d974e 100644 --- a/src/legacy/ui/public/agg_types/buckets/filters.ts +++ b/src/legacy/ui/public/agg_types/buckets/filters.ts @@ -23,12 +23,11 @@ import angular from 'angular'; import { i18n } from '@kbn/i18n'; import chrome from 'ui/chrome'; -import { buildEsQuery } from '@kbn/es-query'; import { FiltersParamEditor, FilterValue } from '../../vis/editors/default/controls/filters'; import { createFilterFilters } from './create_filter/filters'; import { BucketAggType, IBucketAggConfig } from './_bucket_agg_type'; import { Storage } from '../../../../../plugins/kibana_utils/public'; -import { getQueryLog } from '../../../../../plugins/data/public'; +import { getQueryLog, esQuery } from '../../../../../plugins/data/public'; const config = chrome.getUiSettingsClient(); const storage = new Storage(window.localStorage); @@ -68,7 +67,7 @@ export const filtersBucketAgg = new BucketAggType({ return; } - const query = buildEsQuery(aggConfig.getIndexPattern(), [input], [], config); + const query = esQuery.buildEsQuery(aggConfig.getIndexPattern(), [input], [], config); if (!query) { console.log('malformed filter agg params, missing "query" on input'); // eslint-disable-line no-console diff --git a/src/legacy/ui/public/agg_types/buckets/geo_hash.ts b/src/legacy/ui/public/agg_types/buckets/geo_hash.ts index 1716891231b83..700f5a048fce2 100644 --- a/src/legacy/ui/public/agg_types/buckets/geo_hash.ts +++ b/src/legacy/ui/public/agg_types/buckets/geo_hash.ts @@ -26,7 +26,7 @@ import { IsFilteredByCollarParamEditor } from '../../vis/editors/default/control import { PrecisionParamEditor } from '../../vis/editors/default/controls/precision'; import { geohashColumns } from '../../utils/decode_geo_hash'; import { AggGroupNames } from '../../vis/editors/default/agg_groups'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; // @ts-ignore import { geoContains, scaleBounds } from '../../utils/geo_utils'; diff --git a/src/legacy/ui/public/agg_types/buckets/geo_tile.ts b/src/legacy/ui/public/agg_types/buckets/geo_tile.ts index 87373a064086f..3afb35a035690 100644 --- a/src/legacy/ui/public/agg_types/buckets/geo_tile.ts +++ b/src/legacy/ui/public/agg_types/buckets/geo_tile.ts @@ -24,7 +24,7 @@ import { AggConfigOptions } from 'ui/agg_types/agg_config'; import { BucketAggType } from './_bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; const geotileGridTitle = i18n.translate('common.ui.aggTypes.buckets.geotileGridTitle', { defaultMessage: 'Geotile', diff --git a/src/legacy/ui/public/agg_types/buckets/histogram.ts b/src/legacy/ui/public/agg_types/buckets/histogram.ts index fba2f47010c34..7bd3d565003be 100644 --- a/src/legacy/ui/public/agg_types/buckets/histogram.ts +++ b/src/legacy/ui/public/agg_types/buckets/histogram.ts @@ -28,7 +28,7 @@ import { NumberIntervalParamEditor } from '../../vis/editors/default/controls/nu import { MinDocCountParamEditor } from '../../vis/editors/default/controls/min_doc_count'; import { HasExtendedBoundsParamEditor } from '../../vis/editors/default/controls/has_extended_bounds'; import { ExtendedBoundsParamEditor } from '../../vis/editors/default/controls/extended_bounds'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; import { BUCKET_TYPES } from './bucket_agg_types'; export interface AutoBounds { diff --git a/src/legacy/ui/public/agg_types/buckets/ip_range.ts b/src/legacy/ui/public/agg_types/buckets/ip_range.ts index bbc91b0768f6b..7ef415ff8d0c4 100644 --- a/src/legacy/ui/public/agg_types/buckets/ip_range.ts +++ b/src/legacy/ui/public/agg_types/buckets/ip_range.ts @@ -24,13 +24,12 @@ import { IpRangeTypeParamEditor } from '../../vis/editors/default/controls/ip_ra import { IpRangesParamEditor } from '../../vis/editors/default/controls/ip_ranges'; // @ts-ignore import { fieldFormats } from '../../registry/field_formats'; -import { FieldFormat } from '../../../../../plugins/data/common/field_formats'; +import { FieldFormat, KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; import { ipRange } from '../../utils/ip_range'; import { BUCKET_TYPES } from './bucket_agg_types'; // @ts-ignore import { createFilterIpRange } from './create_filter/ip_range'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; const ipRangeTitle = i18n.translate('common.ui.aggTypes.buckets.ipRangeTitle', { defaultMessage: 'IPv4 Range', diff --git a/src/legacy/ui/public/agg_types/buckets/range.test.ts b/src/legacy/ui/public/agg_types/buckets/range.test.ts index 1b423e64c48ae..5db7eb3c2d8e9 100644 --- a/src/legacy/ui/public/agg_types/buckets/range.test.ts +++ b/src/legacy/ui/public/agg_types/buckets/range.test.ts @@ -19,7 +19,7 @@ import { AggConfigs } from '../agg_configs'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { NumberFormat } from '../../../../../plugins/data/common/'; +import { NumberFormat } from '../../../../../plugins/data/public'; jest.mock('ui/new_platform'); diff --git a/src/legacy/ui/public/agg_types/buckets/range.ts b/src/legacy/ui/public/agg_types/buckets/range.ts index 230675ab487ad..89529442b24a6 100644 --- a/src/legacy/ui/public/agg_types/buckets/range.ts +++ b/src/legacy/ui/public/agg_types/buckets/range.ts @@ -20,14 +20,13 @@ import { i18n } from '@kbn/i18n'; import { IBucketAggConfig } from './_bucket_agg_type'; import { BucketAggType } from './_bucket_agg_type'; -import { FieldFormat } from '../../../../../plugins/data/common/field_formats'; +import { FieldFormat, KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; import { RangeKey } from './range_key'; import { RangesEditor } from './range_editor'; // @ts-ignore import { createFilterRange } from './create_filter/range'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; const keyCaches = new WeakMap(); const formats = new WeakMap(); diff --git a/src/legacy/ui/public/agg_types/buckets/significant_terms.ts b/src/legacy/ui/public/agg_types/buckets/significant_terms.ts index 865ede2b1bd23..65c73e5f9b7dd 100644 --- a/src/legacy/ui/public/agg_types/buckets/significant_terms.ts +++ b/src/legacy/ui/public/agg_types/buckets/significant_terms.ts @@ -23,7 +23,7 @@ import { BucketAggType, BucketAggParam } from './_bucket_agg_type'; import { createFilterTerms } from './create_filter/terms'; import { isStringType, migrateIncludeExcludeFormat } from './migrate_include_exclude_format'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; const significantTermsTitle = i18n.translate('common.ui.aggTypes.buckets.significantTermsTitle', { defaultMessage: 'Significant Terms', diff --git a/src/legacy/ui/public/agg_types/buckets/terms.ts b/src/legacy/ui/public/agg_types/buckets/terms.ts index bc6dd4860561e..c0f870c27f10d 100644 --- a/src/legacy/ui/public/agg_types/buckets/terms.ts +++ b/src/legacy/ui/public/agg_types/buckets/terms.ts @@ -23,7 +23,6 @@ import { SearchSource } from 'ui/courier'; import { i18n } from '@kbn/i18n'; import { BucketAggType, BucketAggParam } from './_bucket_agg_type'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; import { AggConfigOptions } from '../agg_config'; import { IBucketAggConfig } from './_bucket_agg_type'; import { @@ -39,10 +38,10 @@ import { OrderByParamEditor, aggFilter } from '../../vis/editors/default/control import { SizeParamEditor } from '../../vis/editors/default/controls/size'; import { MissingBucketParamEditor } from '../../vis/editors/default/controls/missing_bucket'; import { OtherBucketParamEditor } from '../../vis/editors/default/controls/other_bucket'; -import { ContentType } from '../../../../../plugins/data/common'; import { AggConfigs } from '../agg_configs'; import { Adapters } from '../../../../../plugins/inspector/public'; +import { ContentType, KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; // @ts-ignore import { Schemas } from '../../vis/editors/default/schemas'; diff --git a/src/legacy/ui/public/agg_types/metrics/avg.ts b/src/legacy/ui/public/agg_types/metrics/avg.ts index cd069f3133af1..0222a8e543223 100644 --- a/src/legacy/ui/public/agg_types/metrics/avg.ts +++ b/src/legacy/ui/public/agg_types/metrics/avg.ts @@ -20,7 +20,7 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; const averageTitle = i18n.translate('common.ui.aggTypes.metrics.averageTitle', { defaultMessage: 'Average', diff --git a/src/legacy/ui/public/agg_types/metrics/geo_bounds.ts b/src/legacy/ui/public/agg_types/metrics/geo_bounds.ts index b5c479bde5cb0..b8ce03cdf11ec 100644 --- a/src/legacy/ui/public/agg_types/metrics/geo_bounds.ts +++ b/src/legacy/ui/public/agg_types/metrics/geo_bounds.ts @@ -20,7 +20,7 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; const geoBoundsTitle = i18n.translate('common.ui.aggTypes.metrics.geoBoundsTitle', { defaultMessage: 'Geo Bounds', diff --git a/src/legacy/ui/public/agg_types/metrics/geo_centroid.ts b/src/legacy/ui/public/agg_types/metrics/geo_centroid.ts index 25b45ff7d6b1c..5313e31796a5b 100644 --- a/src/legacy/ui/public/agg_types/metrics/geo_centroid.ts +++ b/src/legacy/ui/public/agg_types/metrics/geo_centroid.ts @@ -20,7 +20,7 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; const geoCentroidTitle = i18n.translate('common.ui.aggTypes.metrics.geoCentroidTitle', { defaultMessage: 'Geo Centroid', diff --git a/src/legacy/ui/public/agg_types/metrics/max.ts b/src/legacy/ui/public/agg_types/metrics/max.ts index f6e460be9e624..5c43511acee72 100644 --- a/src/legacy/ui/public/agg_types/metrics/max.ts +++ b/src/legacy/ui/public/agg_types/metrics/max.ts @@ -20,7 +20,7 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; const maxTitle = i18n.translate('common.ui.aggTypes.metrics.maxTitle', { defaultMessage: 'Max', diff --git a/src/legacy/ui/public/agg_types/metrics/median.ts b/src/legacy/ui/public/agg_types/metrics/median.ts index 1a3d6cf4b8d23..8797bed5105c5 100644 --- a/src/legacy/ui/public/agg_types/metrics/median.ts +++ b/src/legacy/ui/public/agg_types/metrics/median.ts @@ -22,7 +22,7 @@ import { METRIC_TYPES } from './metric_agg_types'; // @ts-ignore import { percentilesMetricAgg } from './percentiles'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; const medianTitle = i18n.translate('common.ui.aggTypes.metrics.medianTitle', { defaultMessage: 'Median', diff --git a/src/legacy/ui/public/agg_types/metrics/metric_agg_type.ts b/src/legacy/ui/public/agg_types/metrics/metric_agg_type.ts index c1f5528825bcc..7428bd6caa22d 100644 --- a/src/legacy/ui/public/agg_types/metrics/metric_agg_type.ts +++ b/src/legacy/ui/public/agg_types/metrics/metric_agg_type.ts @@ -25,7 +25,7 @@ import { METRIC_TYPES } from './metric_agg_types'; // @ts-ignore import { fieldFormats } from '../../registry/field_formats'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; export type IMetricAggConfig = AggConfig; diff --git a/src/legacy/ui/public/agg_types/metrics/min.ts b/src/legacy/ui/public/agg_types/metrics/min.ts index 4761985c75a43..5f8ca72954cc2 100644 --- a/src/legacy/ui/public/agg_types/metrics/min.ts +++ b/src/legacy/ui/public/agg_types/metrics/min.ts @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; const minTitle = i18n.translate('common.ui.aggTypes.metrics.minTitle', { defaultMessage: 'Min', diff --git a/src/legacy/ui/public/agg_types/metrics/percentile_ranks.ts b/src/legacy/ui/public/agg_types/metrics/percentile_ranks.ts index 8b923092772db..4fabe137f1bc8 100644 --- a/src/legacy/ui/public/agg_types/metrics/percentile_ranks.ts +++ b/src/legacy/ui/public/agg_types/metrics/percentile_ranks.ts @@ -26,7 +26,7 @@ import { getPercentileValue } from './percentiles_get_value'; import { METRIC_TYPES } from './metric_agg_types'; // @ts-ignore import { fieldFormats } from '../../registry/field_formats'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; // required by the values editor diff --git a/src/legacy/ui/public/agg_types/metrics/percentiles.ts b/src/legacy/ui/public/agg_types/metrics/percentiles.ts index 0ac0455468472..1a3606d677951 100644 --- a/src/legacy/ui/public/agg_types/metrics/percentiles.ts +++ b/src/legacy/ui/public/agg_types/metrics/percentiles.ts @@ -21,7 +21,7 @@ import { i18n } from '@kbn/i18n'; import { IMetricAggConfig, MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; import { getResponseAggConfigClass, IResponseAggConfig } from './lib/get_response_agg_config_class'; import { getPercentileValue } from './percentiles_get_value'; diff --git a/src/legacy/ui/public/agg_types/metrics/std_deviation.ts b/src/legacy/ui/public/agg_types/metrics/std_deviation.ts index ebd5fceb9c751..b2e6d3b3ca4d0 100644 --- a/src/legacy/ui/public/agg_types/metrics/std_deviation.ts +++ b/src/legacy/ui/public/agg_types/metrics/std_deviation.ts @@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; import { getResponseAggConfigClass, IResponseAggConfig } from './lib/get_response_agg_config_class'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; interface ValProp { valProp: string[]; diff --git a/src/legacy/ui/public/agg_types/metrics/sum.ts b/src/legacy/ui/public/agg_types/metrics/sum.ts index 4e428a35a5383..ce79c761ce799 100644 --- a/src/legacy/ui/public/agg_types/metrics/sum.ts +++ b/src/legacy/ui/public/agg_types/metrics/sum.ts @@ -20,7 +20,7 @@ import { i18n } from '@kbn/i18n'; import { MetricAggType } from './metric_agg_type'; import { METRIC_TYPES } from './metric_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; const sumTitle = i18n.translate('common.ui.aggTypes.metrics.sumTitle', { defaultMessage: 'Sum', diff --git a/src/legacy/ui/public/agg_types/metrics/top_hit.test.ts b/src/legacy/ui/public/agg_types/metrics/top_hit.test.ts index e9d1ebb93d3ba..051174c388c1b 100644 --- a/src/legacy/ui/public/agg_types/metrics/top_hit.test.ts +++ b/src/legacy/ui/public/agg_types/metrics/top_hit.test.ts @@ -21,7 +21,7 @@ import { dropRight, last } from 'lodash'; import { topHitMetricAgg } from './top_hit'; import { AggConfigs } from '../agg_configs'; import { IMetricAggConfig } from './metric_agg_type'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; jest.mock('ui/new_platform'); diff --git a/src/legacy/ui/public/agg_types/metrics/top_hit.ts b/src/legacy/ui/public/agg_types/metrics/top_hit.ts index 7ff83e326fc8f..49c4a7951fab9 100644 --- a/src/legacy/ui/public/agg_types/metrics/top_hit.ts +++ b/src/legacy/ui/public/agg_types/metrics/top_hit.ts @@ -27,7 +27,7 @@ import { TopSizeParamEditor } from '../../vis/editors/default/controls/top_size' import { TopAggregateParamEditor } from '../../vis/editors/default/controls/top_aggregate'; import { aggTypeFieldFilters } from '../param_types/filter'; import { METRIC_TYPES } from './metric_agg_types'; -import { KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; // @ts-ignore import { wrapWithInlineComp } from '../buckets/inline_comp_wrapper'; diff --git a/src/legacy/ui/public/agg_types/param_types/field.test.ts b/src/legacy/ui/public/agg_types/param_types/field.test.ts index 2434f95056b78..9cea2934d7459 100644 --- a/src/legacy/ui/public/agg_types/param_types/field.test.ts +++ b/src/legacy/ui/public/agg_types/param_types/field.test.ts @@ -19,7 +19,7 @@ import { BaseParamType } from './base'; import { FieldParamType } from './field'; -import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '../../../../../plugins/data/common'; +import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '../../../../../plugins/data/public'; jest.mock('ui/new_platform'); diff --git a/src/legacy/ui/public/agg_types/utils.ts b/src/legacy/ui/public/agg_types/utils.ts index 6721262d265f4..fd405d49625ed 100644 --- a/src/legacy/ui/public/agg_types/utils.ts +++ b/src/legacy/ui/public/agg_types/utils.ts @@ -17,7 +17,7 @@ * under the License. */ -import { isValidEsInterval } from '../../../core_plugins/data/common'; +import { isValidEsInterval } from '../../../core_plugins/data/public'; import { leastCommonInterval } from '../vis/lib/least_common_interval'; /** diff --git a/src/legacy/ui/public/courier/search_source/search_source.js b/src/legacy/ui/public/courier/search_source/search_source.js index 99bf165e72538..67e3a5cb816f0 100644 --- a/src/legacy/ui/public/courier/search_source/search_source.js +++ b/src/legacy/ui/public/courier/search_source/search_source.js @@ -71,13 +71,12 @@ import _ from 'lodash'; import angular from 'angular'; -import { getEsQueryConfig, buildEsQuery } from '@kbn/es-query'; import { normalizeSortRequest } from './_normalize_sort_request'; import { fetchSoon } from '../fetch'; import { fieldWildcardFilter } from '../../field_wildcard'; -import { getHighlightRequest } from '../../../../../plugins/data/public'; +import { getHighlightRequest, esQuery } from '../../../../../plugins/data/public'; import { npSetup } from 'ui/new_platform'; import chrome from '../../chrome'; import { RequestFailure } from '../fetch/errors'; @@ -491,8 +490,8 @@ export class SearchSource { _.set(flatData.body, '_source.includes', remainingFields); } - const esQueryConfigs = getEsQueryConfig(config); - flatData.body.query = buildEsQuery(flatData.index, flatData.query, flatData.filters, esQueryConfigs); + const esQueryConfigs = esQuery.getEsQueryConfig(config); + flatData.body.query = esQuery.buildEsQuery(flatData.index, flatData.query, flatData.filters, esQueryConfigs); if (flatData.highlightAll != null) { if (flatData.highlightAll && flatData.body.query) { diff --git a/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts b/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts index c5ebc75973d0c..6598da76f60ba 100644 --- a/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts +++ b/src/legacy/ui/public/visualize/loader/pipeline_helpers/utilities.ts @@ -22,7 +22,7 @@ import { identity } from 'lodash'; import { AggConfig, Vis } from 'ui/vis'; import { SerializedFieldFormat } from 'src/plugins/expressions/public'; -import { FieldFormat } from '../../../../../../plugins/data/common/field_formats'; +import { FieldFormat } from '../../../../../../plugins/data/public'; import { tabifyGetColumns } from '../../../agg_response/tabify/_get_columns'; import chrome from '../../../chrome'; diff --git a/packages/kbn-es-query/src/es_query/__tests__/build_es_query.js b/src/plugins/data/common/es_query/es_query/build_es_query.test.ts similarity index 61% rename from packages/kbn-es-query/src/es_query/__tests__/build_es_query.js rename to src/plugins/data/common/es_query/es_query/build_es_query.test.ts index fde3d063caaa6..3db23051b6ced 100644 --- a/packages/kbn-es-query/src/es_query/__tests__/build_es_query.js +++ b/src/plugins/data/common/es_query/es_query/build_es_query.test.ts @@ -17,99 +17,100 @@ * under the License. */ -import expect from '@kbn/expect'; -import { buildEsQuery } from '../build_es_query'; -import indexPattern from '../../__fixtures__/index_pattern_response.json'; -import { fromKueryExpression, toElasticsearchQuery } from '../../kuery'; -import { luceneStringToDsl } from '../lucene_string_to_dsl'; -import { decorateQuery } from '../decorate_query'; +import { buildEsQuery } from './build_es_query'; +import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { luceneStringToDsl } from './lucene_string_to_dsl'; +import { decorateQuery } from './decorate_query'; +import { IIndexPattern } from '../../index_patterns'; +import { MatchAllFilter } from '../filters'; +import { fields } from '../../index_patterns/mocks'; +import { Query } from '../../query/types'; -describe('build query', function () { - describe('buildEsQuery', function () { +describe('build query', () => { + const indexPattern: IIndexPattern = ({ + fields, + } as unknown) as IIndexPattern; - it('should return the parameters of an Elasticsearch bool query', function () { - const result = buildEsQuery(); + describe('buildEsQuery', () => { + it('should return the parameters of an Elasticsearch bool query', () => { + const result = buildEsQuery(indexPattern, [], []); const expected = { bool: { must: [], filter: [], should: [], must_not: [], - } + }, }; - expect(result).to.eql(expected); + expect(result).toEqual(expected); }); - it('should combine queries and filters from multiple query languages into a single ES bool query', function () { + it('should combine queries and filters from multiple query languages into a single ES bool query', () => { const queries = [ { query: 'extension:jpg', language: 'kuery' }, { query: 'bar:baz', language: 'lucene' }, - ]; + ] as Query[]; const filters = [ { match_all: {}, meta: { type: 'match_all' }, - }, + } as MatchAllFilter, ]; const config = { allowLeadingWildcards: true, queryStringOptions: {}, + ignoreFilterIfFieldNotInIndex: false, }; const expectedResult = { bool: { - must: [ - decorateQuery(luceneStringToDsl('bar:baz'), config.queryStringOptions), - ], + must: [decorateQuery(luceneStringToDsl('bar:baz'), config.queryStringOptions)], filter: [ toElasticsearchQuery(fromKueryExpression('extension:jpg'), indexPattern), { match_all: {} }, ], should: [], must_not: [], - } + }, }; const result = buildEsQuery(indexPattern, queries, filters, config); - expect(result).to.eql(expectedResult); + expect(result).toEqual(expectedResult); }); - it('should accept queries and filters as either single objects or arrays', function () { - const queries = { query: 'extension:jpg', language: 'lucene' }; + it('should accept queries and filters as either single objects or arrays', () => { + const queries = { query: 'extension:jpg', language: 'lucene' } as Query; const filters = { match_all: {}, meta: { type: 'match_all' }, - }; + } as MatchAllFilter; const config = { allowLeadingWildcards: true, queryStringOptions: {}, + ignoreFilterIfFieldNotInIndex: false, }; const expectedResult = { bool: { - must: [ - decorateQuery(luceneStringToDsl('extension:jpg'), config.queryStringOptions), - ], + must: [decorateQuery(luceneStringToDsl('extension:jpg'), config.queryStringOptions)], filter: [{ match_all: {} }], should: [], must_not: [], - } + }, }; const result = buildEsQuery(indexPattern, queries, filters, config); - expect(result).to.eql(expectedResult); + expect(result).toEqual(expectedResult); }); - it('should use the default time zone set in the Advanced Settings in queries and filters', function () { + it('should use the default time zone set in the Advanced Settings in queries and filters', () => { const queries = [ { query: '@timestamp:"2019-03-23T13:18:00"', language: 'kuery' }, - { query: '@timestamp:"2019-03-23T13:18:00"', language: 'lucene' } - ]; - const filters = [ - { match_all: {}, meta: { type: 'match_all' } } - ]; + { query: '@timestamp:"2019-03-23T13:18:00"', language: 'lucene' }, + ] as Query[]; + const filters = [{ match_all: {}, meta: { type: 'match_all' } } as MatchAllFilter]; const config = { allowLeadingWildcards: true, queryStringOptions: {}, @@ -120,20 +121,27 @@ describe('build query', function () { const expectedResult = { bool: { must: [ - decorateQuery(luceneStringToDsl('@timestamp:"2019-03-23T13:18:00"'), config.queryStringOptions, config.dateFormatTZ), + decorateQuery( + luceneStringToDsl('@timestamp:"2019-03-23T13:18:00"'), + config.queryStringOptions, + config.dateFormatTZ + ), ], filter: [ - toElasticsearchQuery(fromKueryExpression('@timestamp:"2019-03-23T13:18:00"'), indexPattern, config), - { match_all: {} } + toElasticsearchQuery( + fromKueryExpression('@timestamp:"2019-03-23T13:18:00"'), + indexPattern, + config + ), + { match_all: {} }, ], should: [], must_not: [], - } + }, }; const result = buildEsQuery(indexPattern, queries, filters, config); - expect(result).to.eql(expectedResult); - }); + expect(result).toEqual(expectedResult); + }); }); - }); diff --git a/packages/kbn-es-query/src/es_query/build_es_query.js b/src/plugins/data/common/es_query/es_query/build_es_query.ts similarity index 58% rename from packages/kbn-es-query/src/es_query/build_es_query.js rename to src/plugins/data/common/es_query/es_query/build_es_query.ts index d17147761d8bc..b754496793660 100644 --- a/packages/kbn-es-query/src/es_query/build_es_query.js +++ b/src/plugins/data/common/es_query/es_query/build_es_query.ts @@ -21,6 +21,16 @@ import { groupBy, has } from 'lodash'; import { buildQueryFromKuery } from './from_kuery'; import { buildQueryFromFilters } from './from_filters'; import { buildQueryFromLucene } from './from_lucene'; +import { IIndexPattern } from '../../index_patterns'; +import { Filter } from '../filters'; +import { Query } from '../../query/types'; + +export interface EsQueryConfig { + allowLeadingWildcards: boolean; + queryStringOptions: Record; + ignoreFilterIfFieldNotInIndex: boolean; + dateFormatTZ?: string; +} /** * @param indexPattern @@ -31,30 +41,43 @@ import { buildQueryFromLucene } from './from_lucene'; * config contains dateformat:tz */ export function buildEsQuery( - indexPattern, - queries = [], - filters = [], - config = { + indexPattern: IIndexPattern | null, + queries: Query | Query[], + filters: Filter | Filter[], + config: EsQueryConfig = { allowLeadingWildcards: false, queryStringOptions: {}, ignoreFilterIfFieldNotInIndex: false, - dateFormatTZ: null, - }) { + } +) { queries = Array.isArray(queries) ? queries : [queries]; filters = Array.isArray(filters) ? filters : [filters]; - const validQueries = queries.filter((query) => has(query, 'query')); + const validQueries = queries.filter(query => has(query, 'query')); const queriesByLanguage = groupBy(validQueries, 'language'); - const kueryQuery = buildQueryFromKuery(indexPattern, queriesByLanguage.kuery, config.allowLeadingWildcards, config.dateFormatTZ); - const luceneQuery = buildQueryFromLucene(queriesByLanguage.lucene, config.queryStringOptions, config.dateFormatTZ); - const filterQuery = buildQueryFromFilters(filters, indexPattern, config.ignoreFilterIfFieldNotInIndex); + const kueryQuery = buildQueryFromKuery( + indexPattern, + queriesByLanguage.kuery, + config.allowLeadingWildcards, + config.dateFormatTZ + ); + const luceneQuery = buildQueryFromLucene( + queriesByLanguage.lucene, + config.queryStringOptions, + config.dateFormatTZ + ); + const filterQuery = buildQueryFromFilters( + filters, + indexPattern, + config.ignoreFilterIfFieldNotInIndex + ); return { bool: { - must: [].concat(kueryQuery.must, luceneQuery.must, filterQuery.must), - filter: [].concat(kueryQuery.filter, luceneQuery.filter, filterQuery.filter), - should: [].concat(kueryQuery.should, luceneQuery.should, filterQuery.should), - must_not: [].concat(kueryQuery.must_not, luceneQuery.must_not, filterQuery.must_not), - } + must: [...kueryQuery.must, ...luceneQuery.must, ...filterQuery.must], + filter: [...kueryQuery.filter, ...luceneQuery.filter, ...filterQuery.filter], + should: [...kueryQuery.should, ...luceneQuery.should, ...filterQuery.should], + must_not: [...kueryQuery.must_not, ...luceneQuery.must_not, ...filterQuery.must_not], + }, }; } diff --git a/packages/kbn-es-query/src/es_query/__tests__/decorate_query.js b/src/plugins/data/common/es_query/es_query/decorate_query.test.ts similarity index 50% rename from packages/kbn-es-query/src/es_query/__tests__/decorate_query.js rename to src/plugins/data/common/es_query/es_query/decorate_query.test.ts index d5978716dac9e..d7cd82eb7108a 100644 --- a/packages/kbn-es-query/src/es_query/__tests__/decorate_query.js +++ b/src/plugins/data/common/es_query/es_query/decorate_query.test.ts @@ -17,21 +17,30 @@ * under the License. */ -import expect from '@kbn/expect'; -import { decorateQuery } from '../decorate_query'; +import { decorateQuery } from './decorate_query'; -describe('Query decorator', function () { - it('should be a function', function () { - expect(decorateQuery).to.be.a(Function); +describe('Query decorator', () => { + test('should be a function', () => { + expect(typeof decorateQuery).toBe('function'); }); - it('should merge in the query string options', function () { - const decoratedQuery = decorateQuery({ query_string: { query: '*' } }, { analyze_wildcard: true }); - expect(decoratedQuery).to.eql({ query_string: { query: '*', analyze_wildcard: true } }); + test('should merge in the query string options', () => { + const decoratedQuery = decorateQuery( + { query_string: { query: '*' } }, + { analyze_wildcard: true } + ); + + expect(decoratedQuery).toEqual({ query_string: { query: '*', analyze_wildcard: true } }); }); - it('should add a default of a time_zone parameter if one is provided', function () { - const decoratedQuery = decorateQuery({ query_string: { query: '*' } }, { analyze_wildcard: true }, 'America/Phoenix'); - expect(decoratedQuery).to.eql({ query_string: { query: '*', analyze_wildcard: true, time_zone: 'America/Phoenix' } }); + test('should add a default of a time_zone parameter if one is provided', () => { + const decoratedQuery = decorateQuery( + { query_string: { query: '*' } }, + { analyze_wildcard: true }, + 'America/Phoenix' + ); + expect(decoratedQuery).toEqual({ + query_string: { query: '*', analyze_wildcard: true, time_zone: 'America/Phoenix' }, + }); }); }); diff --git a/packages/kbn-es-query/src/es_query/decorate_query.js b/src/plugins/data/common/es_query/es_query/decorate_query.ts similarity index 69% rename from packages/kbn-es-query/src/es_query/decorate_query.js rename to src/plugins/data/common/es_query/es_query/decorate_query.ts index 8104707e0298a..891712d057886 100644 --- a/packages/kbn-es-query/src/es_query/decorate_query.js +++ b/src/plugins/data/common/es_query/es_query/decorate_query.ts @@ -17,8 +17,9 @@ * under the License. */ -import _ from 'lodash'; -import { getTimeZoneFromSettings } from '../utils/get_time_zone_from_settings'; +import { extend, defaults } from 'lodash'; +import { getTimeZoneFromSettings } from '../utils'; +import { DslQuery, isEsQueryString } from './es_query_dsl'; /** * Decorate queries with default parameters @@ -28,11 +29,17 @@ import { getTimeZoneFromSettings } from '../utils/get_time_zone_from_settings'; * @returns {object} */ -export function decorateQuery(query, queryStringOptions, dateFormatTZ = null) { - if (_.has(query, 'query_string.query')) { - _.extend(query.query_string, queryStringOptions); +export function decorateQuery( + query: DslQuery, + queryStringOptions: Record, + dateFormatTZ?: string +) { + if (isEsQueryString(query)) { + extend(query.query_string, queryStringOptions); if (dateFormatTZ) { - _.defaults(query.query_string, { time_zone: getTimeZoneFromSettings(dateFormatTZ) }); + defaults(query.query_string, { + time_zone: getTimeZoneFromSettings(dateFormatTZ), + }); } } diff --git a/src/plugins/data/common/es_query/es_query/es_query_dsl.ts b/src/plugins/data/common/es_query/es_query/es_query_dsl.ts new file mode 100644 index 0000000000000..d906ae5359ec2 --- /dev/null +++ b/src/plugins/data/common/es_query/es_query/es_query_dsl.ts @@ -0,0 +1,65 @@ +/* + * 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 { has } from 'lodash'; + +export interface DslRangeQuery { + range: { + [name: string]: { + gte: number; + lte: number; + format: string; + }; + }; +} + +export interface DslMatchQuery { + match: { + [name: string]: { + query: string; + operator: string; + zero_terms_query: string; + }; + }; +} + +export interface DslQueryStringQuery { + query_string: { + query: string; + analyze_wildcard?: boolean; + }; +} + +export interface DslMatchAllQuery { + match_all: Record; +} + +export interface DslTermQuery { + term: Record; +} + +export type DslQuery = + | DslRangeQuery + | DslMatchQuery + | DslQueryStringQuery + | DslMatchAllQuery + | DslTermQuery; + +export const isEsQueryString = (query: any): query is DslQueryStringQuery => + has(query, 'query_string.query'); diff --git a/packages/kbn-es-query/src/es_query/__tests__/filter_matches_index.js b/src/plugins/data/common/es_query/es_query/filter_matches_index.test.ts similarity index 62% rename from packages/kbn-es-query/src/es_query/__tests__/filter_matches_index.js rename to src/plugins/data/common/es_query/es_query/filter_matches_index.test.ts index 1c43230aeea30..6a5c7bdf8eea3 100644 --- a/packages/kbn-es-query/src/es_query/__tests__/filter_matches_index.js +++ b/src/plugins/data/common/es_query/es_query/filter_matches_index.test.ts @@ -17,31 +17,36 @@ * under the License. */ -import expect from '@kbn/expect'; -import { filterMatchesIndex } from '../filter_matches_index'; +import { Filter } from '../filters'; +import { filterMatchesIndex } from './filter_matches_index'; +import { IIndexPattern } from '../../index_patterns'; -describe('filterMatchesIndex', function () { +describe('filterMatchesIndex', () => { it('should return true if the filter has no meta', () => { - const filter = {}; - const indexPattern = { id: 'foo', fields: [{ name: 'bar' }] }; - expect(filterMatchesIndex(filter, indexPattern)).to.be(true); + const filter = {} as Filter; + const indexPattern = { id: 'foo', fields: [{ name: 'bar' }] } as IIndexPattern; + + expect(filterMatchesIndex(filter, indexPattern)).toBe(true); }); it('should return true if no index pattern is passed', () => { - const filter = { meta: { index: 'foo', key: 'bar' } }; - const indexPattern = undefined; - expect(filterMatchesIndex(filter, indexPattern)).to.be(true); + const filter = { meta: { index: 'foo', key: 'bar' } } as Filter; + const indexPattern = null; + + expect(filterMatchesIndex(filter, indexPattern)).toBe(true); }); it('should return true if the filter key matches a field name', () => { - const filter = { meta: { index: 'foo', key: 'bar' } }; - const indexPattern = { id: 'foo', fields: [{ name: 'bar' }] }; - expect(filterMatchesIndex(filter, indexPattern)).to.be(true); + const filter = { meta: { index: 'foo', key: 'bar' } } as Filter; + const indexPattern = { id: 'foo', fields: [{ name: 'bar' }] } as IIndexPattern; + + expect(filterMatchesIndex(filter, indexPattern)).toBe(true); }); it('should return false if the filter key does not match a field name', () => { - const filter = { meta: { index: 'foo', key: 'baz' } }; - const indexPattern = { id: 'foo', fields: [{ name: 'bar' }] }; - expect(filterMatchesIndex(filter, indexPattern)).to.be(false); + const filter = { meta: { index: 'foo', key: 'baz' } } as Filter; + const indexPattern = { id: 'foo', fields: [{ name: 'bar' }] } as IIndexPattern; + + expect(filterMatchesIndex(filter, indexPattern)).toBe(false); }); }); diff --git a/packages/kbn-es-query/src/es_query/filter_matches_index.js b/src/plugins/data/common/es_query/es_query/filter_matches_index.ts similarity index 68% rename from packages/kbn-es-query/src/es_query/filter_matches_index.js rename to src/plugins/data/common/es_query/es_query/filter_matches_index.ts index 602a9bbfbc925..496aab3ea585f 100644 --- a/packages/kbn-es-query/src/es_query/filter_matches_index.js +++ b/src/plugins/data/common/es_query/es_query/filter_matches_index.ts @@ -17,12 +17,17 @@ * under the License. */ -// TODO: We should base this on something better than `filter.meta.key`. We should probably modify -// this to check if `filter.meta.index` matches `indexPattern.id` instead, but that's a breaking -// change. -export function filterMatchesIndex(filter, indexPattern) { +import { IIndexPattern, IFieldType } from '../../index_patterns'; +import { Filter } from '../filters'; + +/* + * TODO: We should base this on something better than `filter.meta.key`. We should probably modify + * this to check if `filter.meta.index` matches `indexPattern.id` instead, but that's a breaking + * change. + */ +export function filterMatchesIndex(filter: Filter, indexPattern: IIndexPattern | null) { if (!filter.meta || !indexPattern) { return true; } - return indexPattern.fields.some(field => field.name === filter.meta.key); + return indexPattern.fields.some((field: IFieldType) => field.name === filter.meta.key); } diff --git a/src/plugins/data/common/es_query/es_query/from_filters.test.ts b/src/plugins/data/common/es_query/es_query/from_filters.test.ts new file mode 100644 index 0000000000000..8c1d990c389b8 --- /dev/null +++ b/src/plugins/data/common/es_query/es_query/from_filters.test.ts @@ -0,0 +1,148 @@ +/* + * 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 { buildQueryFromFilters } from './from_filters'; +import { IIndexPattern } from '../../index_patterns'; +import { ExistsFilter, Filter, MatchAllFilter } from '../filters'; +import { fields } from '../../index_patterns/mocks'; + +describe('build query', () => { + const indexPattern: IIndexPattern = ({ + fields, + } as unknown) as IIndexPattern; + + describe('buildQueryFromFilters', () => { + test('should return the parameters of an Elasticsearch bool query', () => { + const result = buildQueryFromFilters([], indexPattern, false); + const expected = { + must: [], + filter: [], + should: [], + must_not: [], + }; + expect(result).toEqual(expected); + }); + + test('should transform an array of kibana filters into ES queries combined in the bool clauses', () => { + const filters = [ + { + match_all: {}, + meta: { type: 'match_all' }, + } as MatchAllFilter, + { + exists: { field: 'foo' }, + meta: { type: 'exists' }, + } as ExistsFilter, + ] as Filter[]; + + const expectedESQueries = [{ match_all: {} }, { exists: { field: 'foo' } }]; + + const result = buildQueryFromFilters(filters, indexPattern, false); + + expect(result.filter).toEqual(expectedESQueries); + }); + + test('should remove disabled filters', () => { + const filters = [ + { + match_all: {}, + meta: { type: 'match_all', negate: true, disabled: true }, + } as MatchAllFilter, + ] as Filter[]; + const result = buildQueryFromFilters(filters, indexPattern, false); + + expect(result.must_not).toEqual([]); + }); + + test('should remove falsy filters', () => { + const filters = ([null, undefined] as unknown) as Filter[]; + const result = buildQueryFromFilters(filters, indexPattern, false); + + expect(result.must_not).toEqual([]); + expect(result.must).toEqual([]); + }); + + test('should place negated filters in the must_not clause', () => { + const filters = [ + { + match_all: {}, + meta: { type: 'match_all', negate: true }, + } as MatchAllFilter, + ] as Filter[]; + + const expectedESQueries = [{ match_all: {} }]; + + const result = buildQueryFromFilters(filters, indexPattern, false); + + expect(result.must_not).toEqual(expectedESQueries); + }); + + test('should translate old ES filter syntax into ES 5+ query objects', () => { + const filters = [ + { + query: { exists: { field: 'foo' } }, + meta: { type: 'exists' }, + }, + ] as Filter[]; + + const expectedESQueries = [ + { + exists: { field: 'foo' }, + }, + ]; + + const result = buildQueryFromFilters(filters, indexPattern, false); + + expect(result.filter).toEqual(expectedESQueries); + }); + + test('should migrate deprecated match syntax', () => { + const filters = [ + { + query: { match: { extension: { query: 'foo', type: 'phrase' } } }, + meta: { type: 'phrase' }, + }, + ] as Filter[]; + + const expectedESQueries = [ + { + match_phrase: { extension: { query: 'foo' } }, + }, + ]; + + const result = buildQueryFromFilters(filters, indexPattern, false); + + expect(result.filter).toEqual(expectedESQueries); + }); + + test('should not add query:queryString:options to query_string filters', () => { + const filters = [ + { + query: { query_string: { query: 'foo' } }, + meta: { type: 'query_string' }, + }, + ] as Filter[]; + + const expectedESQueries = [{ query_string: { query: 'foo' } }]; + const result = buildQueryFromFilters(filters, indexPattern, false); + + expect(result.filter).toEqual(expectedESQueries); + }); + }); +}); diff --git a/packages/kbn-es-query/src/es_query/from_filters.js b/src/plugins/data/common/es_query/es_query/from_filters.ts similarity index 69% rename from packages/kbn-es-query/src/es_query/from_filters.js rename to src/plugins/data/common/es_query/es_query/from_filters.ts index 10f9cf82fc972..1e0957d816590 100644 --- a/packages/kbn-es-query/src/es_query/from_filters.js +++ b/src/plugins/data/common/es_query/es_query/from_filters.ts @@ -16,10 +16,11 @@ * specific language governing permissions and limitations * under the License. */ - -import _ from 'lodash'; +import { isUndefined } from 'lodash'; import { migrateFilter } from './migrate_filter'; import { filterMatchesIndex } from './filter_matches_index'; +import { Filter, cleanFilter, isFilterDisabled } from '../filters'; +import { IIndexPattern } from '../../index_patterns'; /** * Create a filter that can be reversed for filters with negate set @@ -28,11 +29,12 @@ import { filterMatchesIndex } from './filter_matches_index'; * through otherwise it will filter out * @returns {function} */ -const filterNegate = function (reverse) { - return function (filter) { - if (_.isUndefined(filter.meta) || _.isUndefined(filter.meta.negate)) return !reverse; - return filter.meta && filter.meta.negate === reverse; - }; +const filterNegate = (reverse: boolean) => (filter: Filter) => { + if (isUndefined(filter.meta) || isUndefined(filter.meta.negate)) { + return !reverse; + } + + return filter.meta && filter.meta.negate === reverse; }; /** @@ -40,7 +42,7 @@ const filterNegate = function (reverse) { * @param {Object} filter - The filter to translate * @return {Object} the query version of that filter */ -const translateToQuery = function (filter) { +const translateToQuery = (filter: Filter) => { if (!filter) return; if (filter.query) { @@ -50,17 +52,13 @@ const translateToQuery = function (filter) { return filter; }; -/** - * Clean out any invalid attributes from the filters - * @param {object} filter The filter to clean - * @returns {object} - */ -const cleanFilter = function (filter) { - return _.omit(filter, ['meta', '$state']); -}; +export const buildQueryFromFilters = ( + filters: Filter[] = [], + indexPattern: IIndexPattern | null, + ignoreFilterIfFieldNotInIndex: boolean = false +) => { + filters = filters.filter(filter => filter && !isFilterDisabled(filter)); -export function buildQueryFromFilters(filters = [], indexPattern, ignoreFilterIfFieldNotInIndex) { - filters = filters.filter(filter => filter && !_.get(filter, ['meta', 'disabled'])); return { must: [], filter: filters @@ -68,17 +66,13 @@ export function buildQueryFromFilters(filters = [], indexPattern, ignoreFilterIf .filter(filter => !ignoreFilterIfFieldNotInIndex || filterMatchesIndex(filter, indexPattern)) .map(translateToQuery) .map(cleanFilter) - .map(filter => { - return migrateFilter(filter, indexPattern); - }), + .map(filter => migrateFilter(filter, indexPattern)), should: [], must_not: filters .filter(filterNegate(true)) .filter(filter => !ignoreFilterIfFieldNotInIndex || filterMatchesIndex(filter, indexPattern)) .map(translateToQuery) .map(cleanFilter) - .map(filter => { - return migrateFilter(filter, indexPattern); - }), + .map(filter => migrateFilter(filter, indexPattern)), }; -} +}; diff --git a/packages/kbn-es-query/src/es_query/__tests__/from_kuery.js b/src/plugins/data/common/es_query/es_query/from_kuery.test.ts similarity index 54% rename from packages/kbn-es-query/src/es_query/__tests__/from_kuery.js rename to src/plugins/data/common/es_query/es_query/from_kuery.test.ts index 18738b05ea69d..000815b51f620 100644 --- a/packages/kbn-es-query/src/es_query/__tests__/from_kuery.js +++ b/src/plugins/data/common/es_query/es_query/from_kuery.test.ts @@ -17,14 +17,19 @@ * under the License. */ -import { buildQueryFromKuery } from '../from_kuery'; -import indexPattern from '../../__fixtures__/index_pattern_response.json'; -import expect from '@kbn/expect'; -import { fromKueryExpression, toElasticsearchQuery } from '../../kuery'; - -describe('build query', function () { - describe('buildQueryFromKuery', function () { - it('should return the parameters of an Elasticsearch bool query', function () { +import { buildQueryFromKuery } from './from_kuery'; +import { fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { IIndexPattern } from '../../index_patterns'; +import { fields } from '../../index_patterns/mocks'; +import { Query } from '../../query/types'; + +describe('build query', () => { + const indexPattern: IIndexPattern = ({ + fields, + } as unknown) as IIndexPattern; + + describe('buildQueryFromKuery', () => { + test('should return the parameters of an Elasticsearch bool query', () => { const result = buildQueryFromKuery(null, [], true); const expected = { must: [], @@ -32,50 +37,48 @@ describe('build query', function () { should: [], must_not: [], }; - expect(result).to.eql(expected); + expect(result).toEqual(expected); }); - it('should transform an array of kuery queries into ES queries combined in the bool\'s filter clause', function () { + test("should transform an array of kuery queries into ES queries combined in the bool's filter clause", () => { const queries = [ { query: 'extension:jpg', language: 'kuery' }, { query: 'machine.os:osx', language: 'kuery' }, - ]; + ] as Query[]; - const expectedESQueries = queries.map( - (query) => { - return toElasticsearchQuery(fromKueryExpression(query.query), indexPattern); - } - ); + const expectedESQueries = queries.map(query => { + return toElasticsearchQuery(fromKueryExpression(query.query), indexPattern); + }); const result = buildQueryFromKuery(indexPattern, queries, true); - expect(result.filter).to.eql(expectedESQueries); + expect(result.filter).toEqual(expectedESQueries); }); - it('should accept a specific date format for a kuery query into an ES query in the bool\'s filter clause', function () { - const queries = [{ query: '@timestamp:"2018-04-03T19:04:17"', language: 'kuery' }]; - + test("should accept a specific date format for a kuery query into an ES query in the bool's filter clause", () => { + const queries = [{ query: '@timestamp:"2018-04-03T19:04:17"', language: 'kuery' }] as Query[]; const expectedESQueries = queries.map(query => { - return toElasticsearchQuery(fromKueryExpression(query.query), indexPattern, { dateFormatTZ: 'America/Phoenix' }); + return toElasticsearchQuery(fromKueryExpression(query.query), indexPattern, { + dateFormatTZ: 'America/Phoenix', + }); }); const result = buildQueryFromKuery(indexPattern, queries, true, 'America/Phoenix'); - expect(result.filter).to.eql(expectedESQueries); + expect(result.filter).toEqual(expectedESQueries); }); - it('should gracefully handle date queries when no date format is provided', function () { - const queries = [{ query: '@timestamp:"2018-04-03T19:04:17Z"', language: 'kuery' }]; - + test('should gracefully handle date queries when no date format is provided', () => { + const queries = [ + { query: '@timestamp:"2018-04-03T19:04:17Z"', language: 'kuery' }, + ] as Query[]; const expectedESQueries = queries.map(query => { return toElasticsearchQuery(fromKueryExpression(query.query), indexPattern); }); const result = buildQueryFromKuery(indexPattern, queries, true); - expect(result.filter).to.eql(expectedESQueries); + expect(result.filter).toEqual(expectedESQueries); }); - }); - }); diff --git a/packages/kbn-es-query/src/es_query/from_kuery.js b/src/plugins/data/common/es_query/es_query/from_kuery.ts similarity index 59% rename from packages/kbn-es-query/src/es_query/from_kuery.js rename to src/plugins/data/common/es_query/es_query/from_kuery.ts index 8e6e64b4c984e..f91c3d97b95b4 100644 --- a/packages/kbn-es-query/src/es_query/from_kuery.js +++ b/src/plugins/data/common/es_query/es_query/from_kuery.ts @@ -17,27 +17,40 @@ * under the License. */ -import { - fromKueryExpression, - toElasticsearchQuery, - nodeTypes, -} from '../kuery'; +import { fromKueryExpression, toElasticsearchQuery, nodeTypes, KueryNode } from '@kbn/es-query'; +import { IIndexPattern } from '../../index_patterns'; +import { Query } from '../../query/types'; -export function buildQueryFromKuery(indexPattern, queries = [], allowLeadingWildcards, dateFormatTZ = null) { +export function buildQueryFromKuery( + indexPattern: IIndexPattern | null, + queries: Query[] = [], + allowLeadingWildcards: boolean = false, + dateFormatTZ?: string +) { const queryASTs = queries.map(query => { return fromKueryExpression(query.query, { allowLeadingWildcards }); }); + return buildQuery(indexPattern, queryASTs, { dateFormatTZ }); } -function buildQuery(indexPattern, queryASTs, config = null) { - const compoundQueryAST = nodeTypes.function.buildNode('and', queryASTs); - const kueryQuery = toElasticsearchQuery(compoundQueryAST, indexPattern, config); +function buildQuery( + indexPattern: IIndexPattern | null, + queryASTs: KueryNode[], + config: Record = {} +) { + const compoundQueryAST: KueryNode = nodeTypes.function.buildNode('and', queryASTs); + const kueryQuery: Record = toElasticsearchQuery( + compoundQueryAST, + indexPattern, + config + ); + return { must: [], filter: [], should: [], must_not: [], - ...kueryQuery.bool + ...kueryQuery.bool, }; } diff --git a/src/plugins/data/common/es_query/es_query/from_lucene.test.ts b/src/plugins/data/common/es_query/es_query/from_lucene.test.ts new file mode 100644 index 0000000000000..fc85404a5060c --- /dev/null +++ b/src/plugins/data/common/es_query/es_query/from_lucene.test.ts @@ -0,0 +1,74 @@ +/* + * 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 { buildQueryFromLucene } from './from_lucene'; +import { decorateQuery } from './decorate_query'; +import { luceneStringToDsl } from './lucene_string_to_dsl'; +import { Query } from '../../query/types'; + +describe('build query', () => { + describe('buildQueryFromLucene', () => { + test('should return the parameters of an Elasticsearch bool query', () => { + const result = buildQueryFromLucene([], {}); + const expected = { + must: [], + filter: [], + should: [], + must_not: [], + }; + + expect(result).toEqual(expected); + }); + + test("should transform an array of lucene queries into ES queries combined in the bool's must clause", () => { + const queries = ([ + { query: 'foo:bar', language: 'lucene' }, + { query: 'bar:baz', language: 'lucene' }, + ] as unknown) as Query[]; + const expectedESQueries = queries.map(query => { + return decorateQuery(luceneStringToDsl(query.query), {}); + }); + + const result = buildQueryFromLucene(queries, {}); + + expect(result.must).toEqual(expectedESQueries); + }); + + test('should also accept queries in ES query DSL format, simply passing them through', () => { + const queries = ([{ query: { match_all: {} }, language: 'lucene' }] as unknown) as Query[]; + const result = buildQueryFromLucene(queries, {}); + + expect(result.must).toEqual([queries[0].query]); + }); + }); + + test("should accept a date format in the decorated queries and combine that into the bool's must clause", () => { + const queries = ([ + { query: 'foo:bar', language: 'lucene' }, + { query: 'bar:baz', language: 'lucene' }, + ] as unknown) as Query[]; + const dateFormatTZ = 'America/Phoenix'; + const expectedESQueries = queries.map(query => { + return decorateQuery(luceneStringToDsl(query.query), {}, dateFormatTZ); + }); + const result = buildQueryFromLucene(queries, {}, dateFormatTZ); + + expect(result.must).toEqual(expectedESQueries); + }); +}); diff --git a/packages/kbn-es-query/src/es_query/from_lucene.js b/src/plugins/data/common/es_query/es_query/from_lucene.ts similarity index 81% rename from packages/kbn-es-query/src/es_query/from_lucene.js rename to src/plugins/data/common/es_query/es_query/from_lucene.ts index 8845fd68efb4d..8babb6df4fba5 100644 --- a/packages/kbn-es-query/src/es_query/from_lucene.js +++ b/src/plugins/data/common/es_query/es_query/from_lucene.ts @@ -16,19 +16,23 @@ * specific language governing permissions and limitations * under the License. */ - -import _ from 'lodash'; import { decorateQuery } from './decorate_query'; import { luceneStringToDsl } from './lucene_string_to_dsl'; +import { Query } from '../../query/types'; -export function buildQueryFromLucene(queries, queryStringOptions, dateFormatTZ = null) { - const combinedQueries = _.map(queries, (query) => { +export function buildQueryFromLucene( + queries: Query[], + queryStringOptions: Record, + dateFormatTZ?: string +) { + const combinedQueries = (queries || []).map(query => { const queryDsl = luceneStringToDsl(query.query); + return decorateQuery(queryDsl, queryStringOptions, dateFormatTZ); }); return { - must: [].concat(combinedQueries), + must: combinedQueries, filter: [], should: [], must_not: [], diff --git a/packages/kbn-es-query/src/es_query/__tests__/get_es_query_config.js b/src/plugins/data/common/es_query/es_query/get_es_query_config.test.ts similarity index 69% rename from packages/kbn-es-query/src/es_query/__tests__/get_es_query_config.js rename to src/plugins/data/common/es_query/es_query/get_es_query_config.test.ts index 8ccb04dd4b25a..a4ab03687f92e 100644 --- a/packages/kbn-es-query/src/es_query/__tests__/get_es_query_config.js +++ b/src/plugins/data/common/es_query/es_query/get_es_query_config.test.ts @@ -16,13 +16,13 @@ * specific language governing permissions and limitations * under the License. */ +import { get } from 'lodash'; +import { getEsQueryConfig } from './get_es_query_config'; +import { UiSettingsClientContract } from 'kibana/public'; -import expect from '@kbn/expect'; -import { getEsQueryConfig } from '../get_es_query_config'; - -const config = { - get(item) { - return config[item]; +const config = ({ + get(item: string) { + return get(config, item); }, 'query:allowLeadingWildcards': { allowLeadingWildcards: true, @@ -36,10 +36,10 @@ const config = { 'dateFormat:tz': { dateFormatTZ: 'Browser', }, -}; +} as unknown) as UiSettingsClientContract; -describe('getEsQueryConfig', function () { - it('should return the parameters of an Elasticsearch query config requested', function () { +describe('getEsQueryConfig', () => { + test('should return the parameters of an Elasticsearch query config requested', () => { const result = getEsQueryConfig(config); const expected = { allowLeadingWildcards: { @@ -55,12 +55,12 @@ describe('getEsQueryConfig', function () { queryStringOptions: {}, }, }; - expect(result).to.eql(expected); - expect(result).to.have.keys( - 'allowLeadingWildcards', - 'dateFormatTZ', - 'ignoreFilterIfFieldNotInIndex', - 'queryStringOptions' - ); + + expect(result).toEqual(expected); + + expect(result).toHaveProperty('allowLeadingWildcards'); + expect(result).toHaveProperty('dateFormatTZ'); + expect(result).toHaveProperty('ignoreFilterIfFieldNotInIndex'); + expect(result).toHaveProperty('queryStringOptions'); }); }); diff --git a/packages/kbn-es-query/src/es_query/get_es_query_config.js b/src/plugins/data/common/es_query/es_query/get_es_query_config.ts similarity index 78% rename from packages/kbn-es-query/src/es_query/get_es_query_config.js rename to src/plugins/data/common/es_query/es_query/get_es_query_config.ts index 2518b1077462d..0a82cf03bdb44 100644 --- a/packages/kbn-es-query/src/es_query/get_es_query_config.js +++ b/src/plugins/data/common/es_query/es_query/get_es_query_config.ts @@ -17,10 +17,22 @@ * under the License. */ -export function getEsQueryConfig(config) { +import { EsQueryConfig } from './build_es_query'; + +interface KibanaConfig { + get(key: string): T; +} + +export function getEsQueryConfig(config: KibanaConfig) { const allowLeadingWildcards = config.get('query:allowLeadingWildcards'); const queryStringOptions = config.get('query:queryString:options'); const ignoreFilterIfFieldNotInIndex = config.get('courier:ignoreFilterIfFieldNotInIndex'); const dateFormatTZ = config.get('dateFormat:tz'); - return { allowLeadingWildcards, queryStringOptions, ignoreFilterIfFieldNotInIndex, dateFormatTZ }; + + return { + allowLeadingWildcards, + queryStringOptions, + ignoreFilterIfFieldNotInIndex, + dateFormatTZ, + } as EsQueryConfig; } diff --git a/packages/kbn-es-query/src/es_query/index.js b/src/plugins/data/common/es_query/es_query/index.ts similarity index 94% rename from packages/kbn-es-query/src/es_query/index.js rename to src/plugins/data/common/es_query/es_query/index.ts index 57dc31fd9fb6f..82cbc543e19db 100644 --- a/packages/kbn-es-query/src/es_query/index.js +++ b/src/plugins/data/common/es_query/es_query/index.ts @@ -17,7 +17,7 @@ * under the License. */ -export { buildEsQuery } from './build_es_query'; +export { buildEsQuery, EsQueryConfig } from './build_es_query'; export { buildQueryFromFilters } from './from_filters'; export { luceneStringToDsl } from './lucene_string_to_dsl'; export { migrateFilter } from './migrate_filter'; diff --git a/packages/kbn-es-query/src/es_query/__tests__/lucene_string_to_dsl.js b/src/plugins/data/common/es_query/es_query/lucene_string_to_dsl.test.ts similarity index 58% rename from packages/kbn-es-query/src/es_query/__tests__/lucene_string_to_dsl.js rename to src/plugins/data/common/es_query/es_query/lucene_string_to_dsl.test.ts index 04f6209ce6665..a0e94f2c46dcc 100644 --- a/packages/kbn-es-query/src/es_query/__tests__/lucene_string_to_dsl.js +++ b/src/plugins/data/common/es_query/es_query/lucene_string_to_dsl.test.ts @@ -17,37 +17,34 @@ * under the License. */ -import { luceneStringToDsl } from '../lucene_string_to_dsl'; -import expect from '@kbn/expect'; +import { luceneStringToDsl } from './lucene_string_to_dsl'; -describe('build query', function () { - - describe('luceneStringToDsl', function () { - - it('should wrap strings with an ES query_string query', function () { +describe('build query', () => { + describe('luceneStringToDsl', () => { + test('should wrap strings with an ES query_string query', () => { const result = luceneStringToDsl('foo:bar'); const expectedResult = { - query_string: { query: 'foo:bar' } + query_string: { query: 'foo:bar' }, }; - expect(result).to.eql(expectedResult); + + expect(result).toEqual(expectedResult); }); - it('should return a match_all query for empty strings and whitespace', function () { + test('should return a match_all query for empty strings and whitespace', () => { const expectedResult = { - match_all: {} + match_all: {}, }; - expect(luceneStringToDsl('')).to.eql(expectedResult); - expect(luceneStringToDsl(' ')).to.eql(expectedResult); + expect(luceneStringToDsl('')).toEqual(expectedResult); + expect(luceneStringToDsl(' ')).toEqual(expectedResult); }); - it('should return non-string arguments without modification', function () { + test('should return non-string arguments without modification', () => { const expectedResult = {}; const result = luceneStringToDsl(expectedResult); - expect(result).to.be(expectedResult); - expect(result).to.eql(expectedResult); - }); + expect(result).toBe(expectedResult); + expect(result).toEqual(expectedResult); + }); }); - }); diff --git a/src/plugins/data/common/es_query/es_query/lucene_string_to_dsl.ts b/src/plugins/data/common/es_query/es_query/lucene_string_to_dsl.ts new file mode 100644 index 0000000000000..6e8d519ec0ce2 --- /dev/null +++ b/src/plugins/data/common/es_query/es_query/lucene_string_to_dsl.ts @@ -0,0 +1,33 @@ +/* + * 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 { isString } from 'lodash'; +import { DslQuery } from './es_query_dsl'; + +export function luceneStringToDsl(query: string | any): DslQuery { + if (isString(query)) { + if (query.trim() === '') { + return { match_all: {} }; + } + + return { query_string: { query } }; + } + + return query; +} diff --git a/src/plugins/data/common/es_query/es_query/migrate_filter.test.ts b/src/plugins/data/common/es_query/es_query/migrate_filter.test.ts new file mode 100644 index 0000000000000..4617ee1a1c43d --- /dev/null +++ b/src/plugins/data/common/es_query/es_query/migrate_filter.test.ts @@ -0,0 +1,65 @@ +/* + * 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 { isEqual, clone } from 'lodash'; +import { migrateFilter, DeprecatedMatchPhraseFilter } from './migrate_filter'; +import { PhraseFilter, MatchAllFilter } from '../filters'; + +describe('migrateFilter', function() { + const oldMatchPhraseFilter = ({ + match: { + fieldFoo: { + query: 'foobar', + type: 'phrase', + }, + }, + } as unknown) as DeprecatedMatchPhraseFilter; + + const newMatchPhraseFilter = ({ + match_phrase: { + fieldFoo: { + query: 'foobar', + }, + }, + } as unknown) as PhraseFilter; + + it('should migrate match filters of type phrase', function() { + const migratedFilter = migrateFilter(oldMatchPhraseFilter, null); + + expect(isEqual(migratedFilter, newMatchPhraseFilter)).toBe(true); + }); + + it('should not modify the original filter', function() { + const oldMatchPhraseFilterCopy = clone(oldMatchPhraseFilter, true); + + migrateFilter(oldMatchPhraseFilter, null); + + expect(isEqual(oldMatchPhraseFilter, oldMatchPhraseFilterCopy)).toBe(true); + }); + + it('should return the original filter if no migration is necessary', function() { + const originalFilter = { + match_all: {}, + } as MatchAllFilter; + const migratedFilter = migrateFilter(originalFilter, null); + + expect(migratedFilter).toBe(originalFilter); + expect(isEqual(migratedFilter, originalFilter)).toBe(true); + }); +}); diff --git a/src/plugins/data/common/es_query/es_query/migrate_filter.ts b/src/plugins/data/common/es_query/es_query/migrate_filter.ts new file mode 100644 index 0000000000000..258ab9e703131 --- /dev/null +++ b/src/plugins/data/common/es_query/es_query/migrate_filter.ts @@ -0,0 +1,65 @@ +/* + * 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 { get, omit } from 'lodash'; +import { getConvertedValueForField } from '../filters'; +import { Filter } from '../filters'; +import { IIndexPattern } from '../../index_patterns'; + +/** @deprecated + * see https://github.com/elastic/elasticsearch/pull/17508 + * */ +export interface DeprecatedMatchPhraseFilter extends Filter { + match: { + [field: string]: { + query: any; + type: 'phrase'; + }; + }; +} + +/** @deprecated + * see https://github.com/elastic/elasticsearch/pull/17508 + * */ +function isMatchPhraseFilter(filter: any): filter is DeprecatedMatchPhraseFilter { + const fieldName = filter.match && Object.keys(filter.match)[0]; + + return Boolean(fieldName && get(filter, ['match', fieldName, 'type']) === 'phrase'); +} + +export function migrateFilter(filter: Filter, indexPattern: IIndexPattern | null) { + if (isMatchPhraseFilter(filter)) { + const fieldName = Object.keys(filter.match)[0]; + const params: Record = get(filter, ['match', fieldName]); + if (indexPattern) { + const field = indexPattern.fields.find(f => f.name === fieldName); + + if (field) { + params.query = getConvertedValueForField(field, params.query); + } + } + return { + match_phrase: { + [fieldName]: omit(params, 'type'), + }, + }; + } + + return filter; +} diff --git a/src/plugins/data/common/es_query/filters/exists_filter.ts b/src/plugins/data/common/es_query/filters/exists_filter.ts index 1a404ca415117..a20a4f0634766 100644 --- a/src/plugins/data/common/es_query/filters/exists_filter.ts +++ b/src/plugins/data/common/es_query/filters/exists_filter.ts @@ -18,7 +18,7 @@ */ import { Filter, FilterMeta } from './meta_filter'; -import { IndexPattern, Field } from '../../types'; +import { IIndexPattern, IFieldType } from '../../index_patterns'; export type ExistsFilterMeta = FilterMeta; @@ -33,7 +33,7 @@ export type ExistsFilter = Filter & { export const isExistsFilter = (filter: any): filter is ExistsFilter => filter && filter.exists; -export const buildExistsFilter = (field: Field, indexPattern: IndexPattern) => { +export const buildExistsFilter = (field: IFieldType, indexPattern: IIndexPattern) => { return { meta: { index: indexPattern.id, diff --git a/src/plugins/data/common/es_query/filters/index.ts b/src/plugins/data/common/es_query/filters/index.ts index e28ce9ba74975..c19545eb83a06 100644 --- a/src/plugins/data/common/es_query/filters/index.ts +++ b/src/plugins/data/common/es_query/filters/index.ts @@ -17,6 +17,9 @@ * under the License. */ +import { omit, get } from 'lodash'; +import { Filter } from './meta_filter'; + export * from './custom_filter'; export * from './exists_filter'; export * from './geo_bounding_box_filter'; @@ -30,3 +33,12 @@ export * from './query_string_filter'; export * from './range_filter'; export * from './types'; + +/** + * Clean out any invalid attributes from the filters + * @param {object} filter The filter to clean + * @returns {object} + */ +export const cleanFilter = (filter: Filter): Filter => omit(filter, ['meta', '$state']); + +export const isFilterDisabled = (filter: Filter): boolean => get(filter, 'meta.disabled', false); diff --git a/src/plugins/data/common/es_query/filters/phrase_filter.test.ts b/src/plugins/data/common/es_query/filters/phrase_filter.test.ts index 250ec792fbb57..3c7d00a80fecf 100644 --- a/src/plugins/data/common/es_query/filters/phrase_filter.test.ts +++ b/src/plugins/data/common/es_query/filters/phrase_filter.test.ts @@ -18,16 +18,16 @@ */ import { buildInlineScriptForPhraseFilter, buildPhraseFilter } from './phrase_filter'; -import { IndexPattern } from '../../types'; -import { getField } from '../__tests__/fields_mock'; +import { getField } from '../../index_patterns/mocks'; +import { IIndexPattern } from '../../index_patterns'; describe('Phrase filter builder', () => { - let indexPattern: IndexPattern; + let indexPattern: IIndexPattern; beforeEach(() => { indexPattern = { id: 'id', - }; + } as IIndexPattern; }); it('should be a function', () => { diff --git a/src/plugins/data/common/es_query/filters/phrase_filter.ts b/src/plugins/data/common/es_query/filters/phrase_filter.ts index 35110c924fe61..8b8c5f8915269 100644 --- a/src/plugins/data/common/es_query/filters/phrase_filter.ts +++ b/src/plugins/data/common/es_query/filters/phrase_filter.ts @@ -19,7 +19,7 @@ import { get, isPlainObject } from 'lodash'; import { Filter, FilterMeta } from './meta_filter'; -import { IndexPattern, Field } from '../../types'; +import { IIndexPattern, IFieldType } from '../../index_patterns'; export type PhraseFilterMeta = FilterMeta & { params?: { @@ -51,7 +51,7 @@ export const isPhraseFilter = (filter: any): filter is PhraseFilter => { filter.query.match && Object.values(filter.query.match).find((params: any) => params.type === 'phrase'); - return !!(isMatchPhraseQuery || isDeprecatedMatchPhraseQuery); + return Boolean(isMatchPhraseQuery || isDeprecatedMatchPhraseQuery); }; export const isScriptedPhraseFilter = (filter: any): filter is PhraseFilter => @@ -69,9 +69,9 @@ export const getPhraseFilterValue = (filter: PhraseFilter): PhraseFilterValue => }; export const buildPhraseFilter = ( - field: Field, + field: IFieldType, value: any, - indexPattern: IndexPattern + indexPattern: IIndexPattern ): PhraseFilter => { const convertedValue = getConvertedValueForField(field, value); @@ -92,7 +92,7 @@ export const buildPhraseFilter = ( } }; -export const getPhraseScript = (field: Field, value: string) => { +export const getPhraseScript = (field: IFieldType, value: string) => { const convertedValue = getConvertedValueForField(field, value); const script = buildInlineScriptForPhraseFilter(field); @@ -110,7 +110,7 @@ export const getPhraseScript = (field: Field, value: string) => { // See https://github.com/elastic/elasticsearch/issues/20941 and https://github.com/elastic/kibana/issues/8677 // and https://github.com/elastic/elasticsearch/pull/22201 // for the reason behind this change. Aggs now return boolean buckets with a key of 1 or 0. -export const getConvertedValueForField = (field: Field, value: any) => { +export const getConvertedValueForField = (field: IFieldType, value: any) => { if (typeof value !== 'boolean' && field.type === 'boolean') { if ([1, 'true'].includes(value)) { return true; diff --git a/src/plugins/data/common/es_query/filters/phrases_filter.ts b/src/plugins/data/common/es_query/filters/phrases_filter.ts index e207a3ff5961b..f7164f0ad3c83 100644 --- a/src/plugins/data/common/es_query/filters/phrases_filter.ts +++ b/src/plugins/data/common/es_query/filters/phrases_filter.ts @@ -18,8 +18,9 @@ */ import { Filter, FilterMeta } from './meta_filter'; -import { Field, IndexPattern } from '../../types'; import { getPhraseScript } from './phrase_filter'; +import { FILTERS } from './index'; +import { IIndexPattern, IFieldType } from '../../index_patterns'; export type PhrasesFilterMeta = FilterMeta & { params: string[]; // The unformatted values @@ -31,16 +32,16 @@ export type PhrasesFilter = Filter & { }; export const isPhrasesFilter = (filter: any): filter is PhrasesFilter => - filter && filter.meta.type === 'phrases'; + filter && filter.meta.type === FILTERS.PHRASES; // Creates a filter where the given field matches one or more of the given values // params should be an array of values -export const buildPhrasesFilter = (field: Field, params: any, indexPattern: IndexPattern) => { +export const buildPhrasesFilter = (field: IFieldType, params: any, indexPattern: IIndexPattern) => { const index = indexPattern.id; - const type = 'phrases'; + const type = FILTERS.PHRASES; const key = field.name; - const format = (f: Field, value: any) => + const format = (f: IFieldType, value: any) => f && f.format && f.format.convert ? f.format.convert(value) : value; const value = params.map((v: any) => format(field, v)).join(', '); diff --git a/src/plugins/data/common/es_query/filters/query_string_filter.test.ts b/src/plugins/data/common/es_query/filters/query_string_filter.test.ts index 5a580db0c57b8..4fcb15ccac44a 100644 --- a/src/plugins/data/common/es_query/filters/query_string_filter.test.ts +++ b/src/plugins/data/common/es_query/filters/query_string_filter.test.ts @@ -18,26 +18,17 @@ */ import { buildQueryFilter } from './query_string_filter'; -import { IndexPattern } from '../../types'; describe('Phrase filter builder', () => { - let indexPattern: IndexPattern; - - beforeEach(() => { - indexPattern = { - id: 'id', - }; - }); - it('should be a function', () => { expect(typeof buildQueryFilter).toBe('function'); }); it('should return a query filter when passed a standard field', () => { - expect(buildQueryFilter({ foo: 'bar' }, indexPattern.id, '')).toEqual({ + expect(buildQueryFilter({ foo: 'bar' }, 'index', '')).toEqual({ meta: { alias: '', - index: 'id', + index: 'index', }, query: { foo: 'bar', diff --git a/src/plugins/data/common/es_query/filters/query_string_filter.ts b/src/plugins/data/common/es_query/filters/query_string_filter.ts index d2374162b195f..a0e563eca6334 100644 --- a/src/plugins/data/common/es_query/filters/query_string_filter.ts +++ b/src/plugins/data/common/es_query/filters/query_string_filter.ts @@ -18,7 +18,6 @@ */ import { Filter, FilterMeta } from './meta_filter'; -import { IndexPattern } from '../../types'; export type QueryStringFilterMeta = FilterMeta; @@ -35,11 +34,7 @@ export const isQueryStringFilter = (filter: any): filter is QueryStringFilter => filter && filter.query && filter.query.query_string; // Creates a filter corresponding to a raw Elasticsearch query DSL object -export const buildQueryFilter = ( - query: QueryStringFilter['query'], - index: IndexPattern, - alias: string -) => +export const buildQueryFilter = (query: QueryStringFilter['query'], index: string, alias: string) => ({ query, meta: { diff --git a/src/plugins/data/common/es_query/filters/range_filter.test.ts b/src/plugins/data/common/es_query/filters/range_filter.test.ts index 017bb8e9cb7c5..56b63018b5153 100644 --- a/src/plugins/data/common/es_query/filters/range_filter.test.ts +++ b/src/plugins/data/common/es_query/filters/range_filter.test.ts @@ -19,16 +19,16 @@ import { each } from 'lodash'; import { buildRangeFilter, RangeFilter } from './range_filter'; -import { IndexPattern, Field } from '../../types'; -import { getField } from '../__tests__/fields_mock'; +import { getField } from '../../index_patterns/mocks'; +import { IIndexPattern, IFieldType } from '../../index_patterns'; describe('Range filter builder', () => { - let indexPattern: IndexPattern; + let indexPattern: IIndexPattern; beforeEach(() => { indexPattern = { id: 'id', - }; + } as IIndexPattern; }); it('should be a function', () => { @@ -118,7 +118,7 @@ describe('Range filter builder', () => { }); describe('when given params where one side is infinite', () => { - let field: Field; + let field: IFieldType; let filter: RangeFilter; beforeEach(() => { @@ -148,7 +148,7 @@ describe('Range filter builder', () => { }); describe('when given params where both sides are infinite', () => { - let field: Field; + let field: IFieldType; let filter: RangeFilter; beforeEach(() => { diff --git a/src/plugins/data/common/es_query/filters/range_filter.ts b/src/plugins/data/common/es_query/filters/range_filter.ts index c2513a9dc0c5e..fa07b3e611fa7 100644 --- a/src/plugins/data/common/es_query/filters/range_filter.ts +++ b/src/plugins/data/common/es_query/filters/range_filter.ts @@ -18,7 +18,7 @@ */ import { map, reduce, mapValues, get, keys, pick } from 'lodash'; import { Filter, FilterMeta } from './meta_filter'; -import { Field, IndexPattern } from '../../types'; +import { IIndexPattern, IFieldType } from '../../index_patterns'; const OPERANDS_IN_RANGE = 2; @@ -84,18 +84,18 @@ export const isScriptedRangeFilter = (filter: any): filter is RangeFilter => { return hasRangeKeys(params); }; -const formatValue = (field: Field, params: any[]) => +const formatValue = (field: IFieldType, params: any[]) => map(params, (val: any, key: string) => get(operators, key) + format(field, val)).join(' '); -const format = (field: Field, value: any) => +const format = (field: IFieldType, value: any) => field && field.format && field.format.convert ? field.format.convert(value) : value; // Creates a filter where the value for the given field is in the given range // params should be an object containing `lt`, `lte`, `gt`, and/or `gte` export const buildRangeFilter = ( - field: Field, + field: IFieldType, params: RangeFilterParams, - indexPattern: IndexPattern, + indexPattern: IIndexPattern, formattedValue?: string ): RangeFilter => { const filter: any = { meta: { index: indexPattern.id, params: {} } }; @@ -139,7 +139,7 @@ export const buildRangeFilter = ( return filter as RangeFilter; }; -export const getRangeScript = (field: IndexPattern, params: RangeFilterParams) => { +export const getRangeScript = (field: IFieldType, params: RangeFilterParams) => { const knownParams = pick(params, (val, key: any) => key in operators); let script = map( knownParams, diff --git a/src/plugins/data/common/es_query/index.ts b/src/plugins/data/common/es_query/index.ts index 88e14a43cfaae..56eb45c4b1dca 100644 --- a/src/plugins/data/common/es_query/index.ts +++ b/src/plugins/data/common/es_query/index.ts @@ -16,6 +16,8 @@ * specific language governing permissions and limitations * under the License. */ +import * as esQuery from './es_query'; import * as esFilters from './filters'; +import * as utils from './utils'; -export { esFilters }; +export { esFilters, esQuery, utils }; diff --git a/packages/kbn-es-query/src/es_query/lucene_string_to_dsl.js b/src/plugins/data/common/es_query/utils/get_time_zone_from_settings.ts similarity index 78% rename from packages/kbn-es-query/src/es_query/lucene_string_to_dsl.js rename to src/plugins/data/common/es_query/utils/get_time_zone_from_settings.ts index 36ff621e8a694..303bd3547f2ff 100644 --- a/packages/kbn-es-query/src/es_query/lucene_string_to_dsl.js +++ b/src/plugins/data/common/es_query/utils/get_time_zone_from_settings.ts @@ -17,16 +17,10 @@ * under the License. */ -import _ from 'lodash'; +import moment from 'moment-timezone'; -export function luceneStringToDsl(query) { - if (!_.isString(query)) { - return query; - } +export function getTimeZoneFromSettings(dateFormatTZ: string) { + const detectedTimezone = moment.tz.guess(); - if (query.trim() === '') { - return { match_all: {} }; - } - - return { query_string: { query } }; + return dateFormatTZ === 'Browser' ? detectedTimezone : dateFormatTZ; } diff --git a/src/plugins/data/common/es_query/utils/index.ts b/src/plugins/data/common/es_query/utils/index.ts new file mode 100644 index 0000000000000..27f51c1f44cf2 --- /dev/null +++ b/src/plugins/data/common/es_query/utils/index.ts @@ -0,0 +1,20 @@ +/* + * 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. + */ + +export * from './get_time_zone_from_settings'; diff --git a/src/plugins/data/common/field_formats/converters/color.test.ts b/src/plugins/data/common/field_formats/converters/color.test.ts index d3e1054f3db1f..b7fcbf61227eb 100644 --- a/src/plugins/data/common/field_formats/converters/color.test.ts +++ b/src/plugins/data/common/field_formats/converters/color.test.ts @@ -18,7 +18,7 @@ */ import { ColorFormat } from './color'; -import { HTML_CONTEXT_TYPE } from '../../index'; +import { HTML_CONTEXT_TYPE } from '../content_types'; describe('Color Format', () => { describe('field is a number', () => { diff --git a/src/plugins/data/common/field_formats/converters/url.test.ts b/src/plugins/data/common/field_formats/converters/url.test.ts index a194b499744a9..66307cefe08f7 100644 --- a/src/plugins/data/common/field_formats/converters/url.test.ts +++ b/src/plugins/data/common/field_formats/converters/url.test.ts @@ -18,7 +18,7 @@ */ import { UrlFormat } from './url'; -import { TEXT_CONTEXT_TYPE, HTML_CONTEXT_TYPE } from '../../index'; +import { TEXT_CONTEXT_TYPE, HTML_CONTEXT_TYPE } from '../content_types'; describe('UrlFormat', () => { test('outputs a simple tag by default', () => { diff --git a/src/plugins/data/common/index.ts b/src/plugins/data/common/index.ts index 42b5a03fcc926..f9bbeb5f4b3f3 100644 --- a/src/plugins/data/common/index.ts +++ b/src/plugins/data/common/index.ts @@ -20,6 +20,7 @@ export * from './query'; export * from './field_formats'; export * from './kbn_field_types'; +export * from './index_patterns'; export * from './es_query'; export * from './types'; diff --git a/src/plugins/data/common/es_query/__tests__/fields_mock.ts b/src/plugins/data/common/index_patterns/fields/fields.mocks.ts.ts similarity index 98% rename from src/plugins/data/common/es_query/__tests__/fields_mock.ts rename to src/plugins/data/common/index_patterns/fields/fields.mocks.ts.ts index 83fdf588af00c..c27ff42b1e9d2 100644 --- a/src/plugins/data/common/es_query/__tests__/fields_mock.ts +++ b/src/plugins/data/common/index_patterns/fields/fields.mocks.ts.ts @@ -16,8 +16,9 @@ * specific language governing permissions and limitations * under the License. */ +import { IFieldType } from './types'; -export const fields = [ +export const fields: IFieldType[] = [ { name: 'bytes', type: 'number', @@ -317,4 +318,4 @@ export const fields = [ }, ]; -export const getField = (name: string) => fields.find(field => field.name === name); +export const getField = (name: string) => fields.find(field => field.name === name) as IFieldType; diff --git a/src/plugins/data/common/index_patterns/fields/index.ts b/src/plugins/data/common/index_patterns/fields/index.ts new file mode 100644 index 0000000000000..d8f7b5091eb8f --- /dev/null +++ b/src/plugins/data/common/index_patterns/fields/index.ts @@ -0,0 +1,20 @@ +/* + * 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. + */ + +export * from './types'; diff --git a/src/plugins/data/common/index_patterns/fields/types.ts b/src/plugins/data/common/index_patterns/fields/types.ts new file mode 100644 index 0000000000000..c336472a1e7d6 --- /dev/null +++ b/src/plugins/data/common/index_patterns/fields/types.ts @@ -0,0 +1,44 @@ +/* + * 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. + */ + +export interface IFieldSubType { + multi?: { parent: string }; + nested?: { path: string }; +} + +export interface IFieldType { + name: string; + type: string; + script?: string; + lang?: string; + count?: number; + // esTypes might be undefined on old index patterns that have not been refreshed since we added + // this prop. It is also undefined on scripted fields. + esTypes?: string[]; + aggregatable?: boolean; + filterable?: boolean; + searchable?: boolean; + sortable?: boolean; + visualizable?: boolean; + readFromDocValues?: boolean; + scripted?: boolean; + subType?: IFieldSubType; + displayName?: string; + format?: any; +} diff --git a/src/plugins/data/common/index_patterns/index.ts b/src/plugins/data/common/index_patterns/index.ts new file mode 100644 index 0000000000000..d26587efccc0f --- /dev/null +++ b/src/plugins/data/common/index_patterns/index.ts @@ -0,0 +1,21 @@ +/* + * 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. + */ + +export * from './fields'; +export * from './types'; diff --git a/src/plugins/data/common/index_patterns/mocks.ts b/src/plugins/data/common/index_patterns/mocks.ts new file mode 100644 index 0000000000000..6036c08fa2b10 --- /dev/null +++ b/src/plugins/data/common/index_patterns/mocks.ts @@ -0,0 +1,20 @@ +/* + * 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. + */ + +export * from './fields/fields.mocks.ts'; diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts new file mode 100644 index 0000000000000..614ae7dd34efd --- /dev/null +++ b/src/plugins/data/common/index_patterns/types.ts @@ -0,0 +1,36 @@ +/* + * 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 { IFieldType } from './fields'; + +export interface IIndexPattern { + fields: IFieldType[]; + title: string; + id?: string; + type?: string; + timeFieldName?: string; + intervalName?: string | null; + fieldFormatMap?: Record< + string, + { + id: string; + params: unknown; + } + >; +} diff --git a/src/plugins/data/common/types.ts b/src/plugins/data/common/types.ts index ec8d8b006317f..bc0d0c323bafa 100644 --- a/src/plugins/data/common/types.ts +++ b/src/plugins/data/common/types.ts @@ -21,10 +21,4 @@ export * from './field_formats/types'; export * from './timefilter/types'; export * from './query/types'; export * from './kbn_field_types/types'; - -// We can't import the real types from the data plugin, so need to either duplicate -// them here or figure out another solution, perhaps housing them in this package -// will be replaces after Fieds / IndexPattern will be moved into new platform -export type Field = any; -export type IndexPattern = any; -export type StaticIndexPattern = any; +export * from './index_patterns/types'; diff --git a/src/plugins/data/public/autocomplete_provider/types.ts b/src/plugins/data/public/autocomplete_provider/types.ts index d838e54e9ead4..3d34b1bc4a2d2 100644 --- a/src/plugins/data/public/autocomplete_provider/types.ts +++ b/src/plugins/data/public/autocomplete_provider/types.ts @@ -18,7 +18,7 @@ */ import { AutocompleteProviderRegister } from '.'; -import { Field, StaticIndexPattern } from '..'; +import { IIndexPattern, IFieldType } from '../../common'; export type AutocompletePublicPluginSetup = Pick< AutocompleteProviderRegister, @@ -31,7 +31,7 @@ export type AutocompleteProvider = (args: { config: { get(configKey: string): any; }; - indexPatterns: StaticIndexPattern[]; + indexPatterns: IIndexPattern[]; boolFilter?: any; }) => GetSuggestions; @@ -67,5 +67,5 @@ interface BasicAutocompleteSuggestion { export type FieldAutocompleteSuggestion = BasicAutocompleteSuggestion & { type: 'field'; - field: Field; + field: IFieldType; }; diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 32153df69f367..4477c6defbc81 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -25,11 +25,10 @@ export function plugin(initializerContext: PluginInitializerContext) { } export { DataPublicPlugin as Plugin }; -export { DataPublicPluginSetup, DataPublicPluginStart } from './types'; export * from '../common'; -export * from './autocomplete_provider'; +export * from './autocomplete_provider'; export * from './types'; export { IRequestTypesMap, IResponseTypesMap } from './search'; diff --git a/src/plugins/data/public/index_patterns/field.stub.ts b/src/plugins/data/public/index_patterns/field.stub.ts index 315894cd212c4..2e94f4b45f400 100644 --- a/src/plugins/data/public/index_patterns/field.stub.ts +++ b/src/plugins/data/public/index_patterns/field.stub.ts @@ -17,9 +17,9 @@ * under the License. */ -import { Field } from '../../common'; +import { IFieldType } from '../../../../plugins/data/public'; -export const stubFields: Field[] = [ +export const stubFields: IFieldType[] = [ { name: 'machine.os', esTypes: ['text'], diff --git a/src/plugins/data/public/index_patterns/index_pattern.stub.ts b/src/plugins/data/public/index_patterns/index_pattern.stub.ts index 444e65cd0cd4b..3d5151752a080 100644 --- a/src/plugins/data/public/index_patterns/index_pattern.stub.ts +++ b/src/plugins/data/public/index_patterns/index_pattern.stub.ts @@ -17,10 +17,10 @@ * under the License. */ -import { IndexPattern } from '../../common'; +import { IIndexPattern } from '../../common'; import { stubFields } from './field.stub'; -export const stubIndexPattern: IndexPattern = { +export const stubIndexPattern: IIndexPattern = { id: 'logstash-*', fields: stubFields, title: 'logstash-*', diff --git a/src/plugins/data/public/query/filter_manager/filter_manager.test.ts b/src/plugins/data/public/query/filter_manager/filter_manager.test.ts index 33f9c4ccd795d..7857e2989bda6 100644 --- a/src/plugins/data/public/query/filter_manager/filter_manager.test.ts +++ b/src/plugins/data/public/query/filter_manager/filter_manager.test.ts @@ -24,7 +24,7 @@ import { Subscription } from 'rxjs'; import { FilterManager } from './filter_manager'; import { getFilter } from './test_helpers/get_stub_filter'; import { getFiltersArray } from './test_helpers/get_filters_array'; -import { esFilters } from '../../../common/es_query'; +import { esFilters } from '../../../common'; import { coreMock } from '../../../../../core/public/mocks'; const setupMock = coreMock.createSetup(); diff --git a/src/plugins/data/public/query/filter_manager/filter_manager.ts b/src/plugins/data/public/query/filter_manager/filter_manager.ts index 06e2b77dca238..feab75ed7457f 100644 --- a/src/plugins/data/public/query/filter_manager/filter_manager.ts +++ b/src/plugins/data/public/query/filter_manager/filter_manager.ts @@ -27,7 +27,7 @@ import { mapAndFlattenFilters } from './lib/map_and_flatten_filters'; import { uniqFilters } from './lib/uniq_filters'; import { onlyDisabledFiltersChanged } from './lib/only_disabled'; import { PartitionedFilters } from './types'; -import { esFilters } from '../../../common/es_query'; +import { esFilters } from '../../../common'; export class FilterManager { private filters: esFilters.Filter[] = []; diff --git a/src/plugins/data/public/query/filter_manager/lib/compare_filters.test.ts b/src/plugins/data/public/query/filter_manager/lib/compare_filters.test.ts index 6bde6b528d07b..34fd662c4ba46 100644 --- a/src/plugins/data/public/query/filter_manager/lib/compare_filters.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/compare_filters.test.ts @@ -18,7 +18,7 @@ */ import { compareFilters } from './compare_filters'; -import { esFilters } from '../../../../common/es_query'; +import { esFilters } from '../../../../common'; describe('filter manager utilities', () => { describe('compare filters', () => { diff --git a/src/plugins/data/public/query/filter_manager/lib/compare_filters.ts b/src/plugins/data/public/query/filter_manager/lib/compare_filters.ts index 2a7cbe6e3303b..9b171ab0aacb2 100644 --- a/src/plugins/data/public/query/filter_manager/lib/compare_filters.ts +++ b/src/plugins/data/public/query/filter_manager/lib/compare_filters.ts @@ -18,7 +18,7 @@ */ import { defaults, isEqual, omit } from 'lodash'; -import { esFilters } from '../../../../common/es_query'; +import { esFilters } from '../../../../common'; /** * Compare two filters to see if they match diff --git a/src/plugins/data/public/query/filter_manager/lib/dedup_filters.test.ts b/src/plugins/data/public/query/filter_manager/lib/dedup_filters.test.ts index 9b493add0886c..ebad5ad6b02c5 100644 --- a/src/plugins/data/public/query/filter_manager/lib/dedup_filters.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/dedup_filters.test.ts @@ -18,13 +18,26 @@ */ import { dedupFilters } from './dedup_filters'; -import { esFilters } from '../../../../common/es_query'; +import { esFilters, IIndexPattern, IFieldType } from '../../../../common'; describe('filter manager utilities', () => { + let indexPattern: IIndexPattern; + + beforeEach(() => { + indexPattern = { + id: 'index', + } as IIndexPattern; + }); + describe('dedupFilters(existing, filters)', () => { test('should return only filters which are not in the existing', () => { const existing: esFilters.Filter[] = [ - esFilters.buildRangeFilter({ name: 'bytes' }, { from: 0, to: 1024 }, 'index', ''), + esFilters.buildRangeFilter( + { name: 'bytes' } as IFieldType, + { from: 0, to: 1024 }, + indexPattern, + '' + ), esFilters.buildQueryFilter( { match: { _term: { query: 'apache', type: 'phrase' } } }, 'index', @@ -32,7 +45,12 @@ describe('filter manager utilities', () => { ), ]; const filters: esFilters.Filter[] = [ - esFilters.buildRangeFilter({ name: 'bytes' }, { from: 1024, to: 2048 }, 'index', ''), + esFilters.buildRangeFilter( + { name: 'bytes' } as IFieldType, + { from: 1024, to: 2048 }, + indexPattern, + '' + ), esFilters.buildQueryFilter( { match: { _term: { query: 'apache', type: 'phrase' } } }, 'index', @@ -47,7 +65,12 @@ describe('filter manager utilities', () => { test('should ignore the disabled attribute when comparing ', () => { const existing: esFilters.Filter[] = [ - esFilters.buildRangeFilter({ name: 'bytes' }, { from: 0, to: 1024 }, 'index', ''), + esFilters.buildRangeFilter( + { name: 'bytes' } as IFieldType, + { from: 0, to: 1024 }, + indexPattern, + '' + ), { ...esFilters.buildQueryFilter( { match: { _term: { query: 'apache', type: 'phrase' } } }, @@ -58,7 +81,12 @@ describe('filter manager utilities', () => { }, ]; const filters: esFilters.Filter[] = [ - esFilters.buildRangeFilter({ name: 'bytes' }, { from: 1024, to: 2048 }, 'index', ''), + esFilters.buildRangeFilter( + { name: 'bytes' } as IFieldType, + { from: 1024, to: 2048 }, + indexPattern, + '' + ), esFilters.buildQueryFilter( { match: { _term: { query: 'apache', type: 'phrase' } } }, 'index1', @@ -73,7 +101,12 @@ describe('filter manager utilities', () => { test('should ignore $state attribute', () => { const existing: esFilters.Filter[] = [ - esFilters.buildRangeFilter({ name: 'bytes' }, { from: 0, to: 1024 }, 'index', ''), + esFilters.buildRangeFilter( + { name: 'bytes' } as IFieldType, + { from: 0, to: 1024 }, + indexPattern, + '' + ), { ...esFilters.buildQueryFilter( { match: { _term: { query: 'apache', type: 'phrase' } } }, @@ -84,7 +117,12 @@ describe('filter manager utilities', () => { }, ]; const filters: esFilters.Filter[] = [ - esFilters.buildRangeFilter({ name: 'bytes' }, { from: 1024, to: 2048 }, 'index', ''), + esFilters.buildRangeFilter( + { name: 'bytes' } as IFieldType, + { from: 1024, to: 2048 }, + indexPattern, + '' + ), { ...esFilters.buildQueryFilter( { match: { _term: { query: 'apache', type: 'phrase' } } }, diff --git a/src/plugins/data/public/query/filter_manager/lib/dedup_filters.ts b/src/plugins/data/public/query/filter_manager/lib/dedup_filters.ts index 6d6f49cb5e833..6dae14f480b4f 100644 --- a/src/plugins/data/public/query/filter_manager/lib/dedup_filters.ts +++ b/src/plugins/data/public/query/filter_manager/lib/dedup_filters.ts @@ -19,7 +19,7 @@ import { filter, find } from 'lodash'; import { compareFilters } from './compare_filters'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters } from '../../../../common'; /** * Combine 2 filter collections, removing duplicates diff --git a/src/plugins/data/public/query/filter_manager/lib/generate_filter.test.ts b/src/plugins/data/public/query/filter_manager/lib/generate_filter.test.ts index 46cf0fd9c111e..b8de08fc3a610 100644 --- a/src/plugins/data/public/query/filter_manager/lib/generate_filter.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/generate_filter.test.ts @@ -19,13 +19,14 @@ import { generateFilters } from './generate_filters'; import { FilterManager } from '../filter_manager'; -import { esFilters } from '../../..'; + +import { esFilters, IFieldType, IIndexPattern } from '../../../../common'; const INDEX_NAME = 'my-index'; const EXISTS_FIELD_NAME = '_exists_'; const FIELD = { name: 'my-field', -}; +} as IFieldType; const PHRASE_VALUE = 'my-value'; describe('Generate filters', () => { @@ -70,7 +71,7 @@ describe('Generate filters', () => { }); it('should update and re-enable EXISTING exists filter', () => { - const filter = esFilters.buildExistsFilter(FIELD, { id: INDEX_NAME }); + const filter = esFilters.buildExistsFilter(FIELD, { id: INDEX_NAME } as IIndexPattern); filter.meta.disabled = true; filtersArray.push(filter); diff --git a/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts b/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts index 5c4cdc2717338..42607843df3ba 100644 --- a/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts +++ b/src/plugins/data/public/query/filter_manager/lib/generate_filters.ts @@ -18,7 +18,8 @@ */ import _ from 'lodash'; -import { FilterManager, esFilters, Field } from '../../..'; +import { esFilters, IFieldType, IIndexPattern } from '../../../../common'; +import { FilterManager } from '../filter_manager'; function getExistingFilter( appFilters: esFilters.Filter[], @@ -67,17 +68,17 @@ function updateExistingFilter(existingFilter: esFilters.Filter, negate: boolean) */ export function generateFilters( filterManager: FilterManager, - field: Field | string, + field: IFieldType | string, values: any, operation: string, index: string ): esFilters.Filter[] { values = Array.isArray(values) ? values : [values]; - const fieldObj = _.isObject(field) + const fieldObj = (_.isObject(field) ? field : { name: field, - }; + }) as IFieldType; const fieldName = fieldObj.name; const newFilters: esFilters.Filter[] = []; const appFilters = filterManager.getAppFilters(); @@ -92,7 +93,8 @@ export function generateFilters( updateExistingFilter(existing, negate); filter = existing; } else { - const tmpIndexPattern = { id: index }; + const tmpIndexPattern = { id: index } as IIndexPattern; + switch (fieldName) { case '_exists_': filter = esFilters.buildExistsFilter(fieldObj, tmpIndexPattern); diff --git a/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.test.ts b/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.test.ts index dfe3a093c6614..9e386bdc7c80d 100644 --- a/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.test.ts @@ -19,7 +19,7 @@ import sinon from 'sinon'; import { generateMappingChain } from './generate_mapping_chain'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters } from '../../../../common'; describe('filter manager utilities', () => { let mapping: any; diff --git a/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.ts b/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.ts index b6764389e0db9..1af8482a96e0f 100644 --- a/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.ts +++ b/src/plugins/data/public/query/filter_manager/lib/generate_mapping_chain.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters } from '../../../../common'; const noop = () => { throw new Error('No mappings have been found for filter.'); diff --git a/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.test.ts b/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.test.ts index 9a0d0d93698f6..3190b6777a9e1 100644 --- a/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.test.ts @@ -18,7 +18,7 @@ */ import { mapAndFlattenFilters } from './map_and_flatten_filters'; -import { esFilters } from '../../../../../data/public'; +import { esFilters } from '../../../../common'; describe('filter manager utilities', () => { describe('mapAndFlattenFilters()', () => { diff --git a/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.ts b/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.ts index 5326d59f3e32b..28b5e8d151ff6 100644 --- a/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.ts +++ b/src/plugins/data/public/query/filter_manager/lib/map_and_flatten_filters.ts @@ -19,7 +19,7 @@ import { compact, flatten } from 'lodash'; import { mapFilter } from './map_filter'; -import { esFilters } from '../../../../../data/public'; +import { esFilters } from '../../../../common'; export const mapAndFlattenFilters = (filters: esFilters.Filter[]) => { return compact(flatten(filters)).map((item: esFilters.Filter) => mapFilter(item)); diff --git a/src/plugins/data/public/query/filter_manager/lib/map_filter.test.ts b/src/plugins/data/public/query/filter_manager/lib/map_filter.test.ts index 0d115125451ee..9df07718d5bcb 100644 --- a/src/plugins/data/public/query/filter_manager/lib/map_filter.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/map_filter.test.ts @@ -18,7 +18,7 @@ */ import { mapFilter } from './map_filter'; -import { esFilters } from '../../../../../data/public'; +import { esFilters } from '../../../../common'; describe('filter manager utilities', () => { function getDisplayName(filter: esFilters.Filter) { diff --git a/src/plugins/data/public/query/filter_manager/lib/map_filter.ts b/src/plugins/data/public/query/filter_manager/lib/map_filter.ts index 2dc855caabfd3..a68eafe6bf1c2 100644 --- a/src/plugins/data/public/query/filter_manager/lib/map_filter.ts +++ b/src/plugins/data/public/query/filter_manager/lib/map_filter.ts @@ -30,7 +30,7 @@ import { mapGeoBoundingBox } from './mappers/map_geo_bounding_box'; import { mapGeoPolygon } from './mappers/map_geo_polygon'; import { mapDefault } from './mappers/map_default'; import { generateMappingChain } from './generate_mapping_chain'; -import { esFilters } from '../../../../../data/public'; +import { esFilters } from '../../../../common'; export function mapFilter(filter: esFilters.Filter) { /** Mappers **/ diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.test.ts index f10766901e5b7..f6baaa9218d74 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.test.ts @@ -18,7 +18,7 @@ */ import { mapDefault } from './map_default'; -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; describe('filter manager utilities', () => { describe('mapDefault()', () => { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.ts index fd84c5c742589..3fee6a063be5a 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_default.ts @@ -18,7 +18,7 @@ */ import { find, keys, get } from 'lodash'; -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; export const mapDefault = (filter: esFilters.Filter) => { const metaProperty = /(^\$|meta)/; diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.test.ts index ff0ed4f4e4d94..2f0ab136bc59f 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.test.ts @@ -19,12 +19,20 @@ import { mapExists } from './map_exists'; import { mapQueryString } from './map_query_string'; -import { esFilters } from '../../../../../common/es_query'; +import { esFilters, IIndexPattern, IFieldType } from '../../../../../common'; describe('filter manager utilities', () => { describe('mapExists()', () => { + let indexPattern: IIndexPattern; + + beforeEach(() => { + indexPattern = { + id: 'index', + } as IIndexPattern; + }); + test('should return the key and value for matching filters', async () => { - const filter = esFilters.buildExistsFilter({ name: '_type' }, 'index'); + const filter = esFilters.buildExistsFilter({ name: '_type' } as IFieldType, indexPattern); const result = mapExists(filter); expect(result).toHaveProperty('key', '_type'); diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.ts index 63665bdd88ccb..38f9b1554c5c8 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_exists.ts @@ -18,7 +18,7 @@ */ import { get } from 'lodash'; -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; export const mapExists = (filter: esFilters.Filter) => { if (esFilters.isExistsFilter(filter)) { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.test.ts index 5fca4a652bad8..322b086c2cf49 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.test.ts @@ -18,7 +18,7 @@ */ import { mapGeoBoundingBox } from './map_geo_bounding_box'; -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; describe('filter manager utilities', () => { describe('mapGeoBoundingBox()', () => { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.ts index 091e9a3f34000..be63d2de5b0df 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_bounding_box.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; const getFormattedValueFn = (params: any) => { return (formatter?: esFilters.FilterValueFormatter) => { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.test.ts index 1847296016c73..2713f0fd17734 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.test.ts @@ -18,7 +18,7 @@ */ import { mapGeoPolygon } from './map_geo_polygon'; -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; describe('filter manager utilities', () => { let filter: esFilters.GeoPolygonFilter; diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.ts index a7881b4a145a1..8cca92a81cb5f 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_geo_polygon.ts @@ -17,7 +17,7 @@ * under the License. */ -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; const POINTS_SEPARATOR = ', '; diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.test.ts index 4fc6d0b492414..4d6bba6429b47 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.test.ts @@ -18,7 +18,7 @@ */ import { mapMatchAll } from './map_match_all'; -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; describe('filter_manager/lib', () => { describe('mapMatchAll()', () => { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.ts index 4e93b1d41e9a8..9a4ea8430a305 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_match_all.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; export const mapMatchAll = (filter: esFilters.Filter) => { if (esFilters.isMatchAllFilter(filter)) { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.test.ts index 1847eb37ca42f..faf4b54989e20 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.test.ts @@ -18,7 +18,7 @@ */ import { mapMissing } from './map_missing'; -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; describe('filter manager utilities', () => { describe('mapMissing()', () => { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.ts index 51dee89ad884b..a1b6474365f40 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_missing.ts @@ -17,7 +17,7 @@ * under the License. */ -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; export const mapMissing = (filter: esFilters.Filter) => { if (esFilters.isMissingFilter(filter)) { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.test.ts index 05372d37264b0..5150b32f118ae 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.test.ts @@ -17,7 +17,7 @@ * under the License. */ import { mapPhrase } from './map_phrase'; -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; describe('filter manager utilities', () => { describe('mapPhrase()', () => { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts index b6e9c2007db97..ae7701bf3a501 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrase.ts @@ -18,7 +18,7 @@ */ import { get } from 'lodash'; -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; const getScriptedPhraseValue = (filter: esFilters.PhraseFilter) => get(filter, ['script', 'script', 'params', 'value']); diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts index 7240d87d02b5a..f8f2aba1309b7 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_phrases.ts @@ -17,7 +17,7 @@ * under the License. */ -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; export const mapPhrases = (filter: esFilters.Filter) => { if (!esFilters.isPhrasesFilter(filter)) { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.test.ts index c60e7d3454fe0..c65bc00b7df61 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.test.ts @@ -18,7 +18,7 @@ */ import { mapQueryString } from './map_query_string'; -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; describe('filter manager utilities', () => { describe('mapQueryString()', () => { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.ts index 20c3555639a3e..e8e4e68318973 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_query_string.ts @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; export const mapQueryString = (filter: esFilters.Filter) => { if (esFilters.isQueryStringFilter(filter)) { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.test.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.test.ts index c0d5773d6f2c1..2d312351c0f31 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.test.ts @@ -18,7 +18,7 @@ */ import { mapRange } from './map_range'; -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; describe('filter manager utilities', () => { describe('mapRange()', () => { diff --git a/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts b/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts index 51fb970f5f03e..affc8e6343076 100644 --- a/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts +++ b/src/plugins/data/public/query/filter_manager/lib/mappers/map_range.ts @@ -18,7 +18,7 @@ */ import { get, has } from 'lodash'; -import { esFilters } from '../../../../../common/es_query'; +import { esFilters } from '../../../../../common'; const getFormattedValueFn = (left: any, right: any) => { return (formatter?: esFilters.FilterValueFormatter) => { diff --git a/src/plugins/data/public/query/filter_manager/lib/only_disabled.test.ts b/src/plugins/data/public/query/filter_manager/lib/only_disabled.test.ts index b9731797c9ee3..a9863696d47cd 100644 --- a/src/plugins/data/public/query/filter_manager/lib/only_disabled.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/only_disabled.test.ts @@ -18,7 +18,7 @@ */ import { onlyDisabledFiltersChanged } from './only_disabled'; -import { esFilters } from '../../../../../data/public'; +import { esFilters } from '../../../../common'; describe('filter manager utilities', () => { describe('onlyDisabledFiltersChanged()', () => { diff --git a/src/plugins/data/public/query/filter_manager/lib/only_disabled.ts b/src/plugins/data/public/query/filter_manager/lib/only_disabled.ts index 0fb6894a297a1..c040d2f2960c7 100644 --- a/src/plugins/data/public/query/filter_manager/lib/only_disabled.ts +++ b/src/plugins/data/public/query/filter_manager/lib/only_disabled.ts @@ -18,7 +18,7 @@ */ import { filter, isEqual } from 'lodash'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters } from '../../../../common'; const isEnabled = (f: esFilters.Filter) => f && f.meta && !f.meta.disabled; diff --git a/src/plugins/data/public/query/filter_manager/lib/uniq_filters.test.ts b/src/plugins/data/public/query/filter_manager/lib/uniq_filters.test.ts index 08eeabc1497e3..f71ac2940f32b 100644 --- a/src/plugins/data/public/query/filter_manager/lib/uniq_filters.test.ts +++ b/src/plugins/data/public/query/filter_manager/lib/uniq_filters.test.ts @@ -18,7 +18,7 @@ */ import { uniqFilters } from './uniq_filters'; -import { esFilters } from '../../../../../data/public'; +import { esFilters } from '../../../../common'; describe('filter manager utilities', () => { describe('niqFilter', () => { diff --git a/src/plugins/data/public/query/filter_manager/lib/uniq_filters.ts b/src/plugins/data/public/query/filter_manager/lib/uniq_filters.ts index e96c52e6db3de..b6001d698c5f1 100644 --- a/src/plugins/data/public/query/filter_manager/lib/uniq_filters.ts +++ b/src/plugins/data/public/query/filter_manager/lib/uniq_filters.ts @@ -18,7 +18,7 @@ */ import { each, union } from 'lodash'; import { dedupFilters } from './dedup_filters'; -import { esFilters } from '../../../../../data/public'; +import { esFilters } from '../../../../common'; /** * Remove duplicate filters from an array of filters diff --git a/src/plugins/data/public/query/filter_manager/test_helpers/get_filters_array.ts b/src/plugins/data/public/query/filter_manager/test_helpers/get_filters_array.ts index aa047647c5751..c5f0b11ce13f8 100644 --- a/src/plugins/data/public/query/filter_manager/test_helpers/get_filters_array.ts +++ b/src/plugins/data/public/query/filter_manager/test_helpers/get_filters_array.ts @@ -17,7 +17,7 @@ * under the License. */ -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters } from '../../../../common'; export function getFiltersArray(): esFilters.Filter[] { return [ diff --git a/src/plugins/data/public/query/filter_manager/test_helpers/get_stub_filter.ts b/src/plugins/data/public/query/filter_manager/test_helpers/get_stub_filter.ts index adc72c961b08b..a531ce7e03984 100644 --- a/src/plugins/data/public/query/filter_manager/test_helpers/get_stub_filter.ts +++ b/src/plugins/data/public/query/filter_manager/test_helpers/get_stub_filter.ts @@ -17,7 +17,7 @@ * under the License. */ -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters } from '../../../../common'; export function getFilter( store: esFilters.FilterStateStore, diff --git a/src/plugins/data/public/query/filter_manager/types.ts b/src/plugins/data/public/query/filter_manager/types.ts index 0b3dbca2d6e0a..0f74a243ca91a 100644 --- a/src/plugins/data/public/query/filter_manager/types.ts +++ b/src/plugins/data/public/query/filter_manager/types.ts @@ -17,7 +17,7 @@ * under the License. */ -import { esFilters } from '../../../../../plugins/data/public'; +import { esFilters } from '../../../common'; export interface PartitionedFilters { globalFilters: esFilters.Filter[]; diff --git a/src/plugins/data/public/query/timefilter/get_time.ts b/src/plugins/data/public/query/timefilter/get_time.ts index 55ee6527fbb1a..41ad1a49af0ff 100644 --- a/src/plugins/data/public/query/timefilter/get_time.ts +++ b/src/plugins/data/public/query/timefilter/get_time.ts @@ -18,7 +18,7 @@ */ import dateMath from '@elastic/datemath'; -import { TimeRange } from 'src/plugins/data/public'; +import { TimeRange } from '../../../common'; // TODO: remove this import { IndexPattern, Field } from '../../../../../legacy/core_plugins/data/public/index_patterns'; diff --git a/src/plugins/data/public/query/timefilter/lib/change_time_filter.test.ts b/src/plugins/data/public/query/timefilter/lib/change_time_filter.test.ts index df3e33060b01f..62805cde15936 100644 --- a/src/plugins/data/public/query/timefilter/lib/change_time_filter.test.ts +++ b/src/plugins/data/public/query/timefilter/lib/change_time_filter.test.ts @@ -17,9 +17,8 @@ * under the License. */ import { changeTimeFilter } from './change_time_filter'; -import { TimeRange } from 'src/plugins/data/public'; import { timefilterServiceMock } from '../timefilter_service.mock'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { TimeRange, esFilters } from '../../../../common'; const timefilterMock = timefilterServiceMock.createSetupContract(); const timefilter = timefilterMock.timefilter; diff --git a/src/plugins/data/public/query/timefilter/lib/change_time_filter.ts b/src/plugins/data/public/query/timefilter/lib/change_time_filter.ts index 7943aab3c151f..cae464f1449bc 100644 --- a/src/plugins/data/public/query/timefilter/lib/change_time_filter.ts +++ b/src/plugins/data/public/query/timefilter/lib/change_time_filter.ts @@ -19,8 +19,8 @@ import moment from 'moment'; import { keys } from 'lodash'; -import { TimefilterContract } from '../timefilter'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { TimefilterContract } from '../../timefilter'; +import { esFilters } from '../../../../common'; export function convertRangeFilterToTimeRange(filter: esFilters.RangeFilter) { const key = keys(filter.range)[0]; diff --git a/src/plugins/data/public/query/timefilter/lib/diff_time_picker_vals.ts b/src/plugins/data/public/query/timefilter/lib/diff_time_picker_vals.ts index 27da80661dbce..850c87635be9c 100644 --- a/src/plugins/data/public/query/timefilter/lib/diff_time_picker_vals.ts +++ b/src/plugins/data/public/query/timefilter/lib/diff_time_picker_vals.ts @@ -19,7 +19,7 @@ import _ from 'lodash'; -import { RefreshInterval } from 'src/plugins/data/public'; +import { RefreshInterval } from '../../../../common'; import { InputTimeRange } from '../types'; const valueOf = function(o: any) { diff --git a/src/plugins/data/public/query/timefilter/lib/extract_time_filter.test.ts b/src/plugins/data/public/query/timefilter/lib/extract_time_filter.test.ts index 981c50844c4f3..d371f4587d2b8 100644 --- a/src/plugins/data/public/query/timefilter/lib/extract_time_filter.test.ts +++ b/src/plugins/data/public/query/timefilter/lib/extract_time_filter.test.ts @@ -18,9 +18,17 @@ */ import { extractTimeFilter } from './extract_time_filter'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters, IIndexPattern, IFieldType } from '../../../../common'; describe('filter manager utilities', () => { + let indexPattern: IIndexPattern; + + beforeEach(() => { + indexPattern = { + id: 'logstash-*', + } as IIndexPattern; + }); + describe('extractTimeFilter()', () => { test('should detect timeFilter', async () => { const filters: esFilters.Filter[] = [ @@ -30,9 +38,9 @@ describe('filter manager utilities', () => { '' ), esFilters.buildRangeFilter( - { name: 'time' }, + { name: 'time' } as IFieldType, { gt: 1388559600000, lt: 1388646000000 }, - 'logstash-*' + indexPattern ), ]; const result = await extractTimeFilter('time', filters); @@ -48,7 +56,12 @@ describe('filter manager utilities', () => { 'logstash-*', '' ), - esFilters.buildRangeFilter({ name: '@timestamp' }, { from: 1, to: 2 }, 'logstash-*', ''), + esFilters.buildRangeFilter( + { name: '@timestamp' } as IFieldType, + { from: 1, to: 2 }, + indexPattern, + '' + ), ]; const result = await extractTimeFilter('time', filters); @@ -63,7 +76,7 @@ describe('filter manager utilities', () => { 'logstash-*', '' ), - esFilters.buildPhraseFilter({ name: 'time' }, 'banana', 'logstash-*'), + esFilters.buildPhraseFilter({ name: 'time' } as IFieldType, 'banana', indexPattern), ]; const result = await extractTimeFilter('time', filters); diff --git a/src/plugins/data/public/query/timefilter/lib/extract_time_filter.ts b/src/plugins/data/public/query/timefilter/lib/extract_time_filter.ts index 4281610cb63e4..af2e8be65fb62 100644 --- a/src/plugins/data/public/query/timefilter/lib/extract_time_filter.ts +++ b/src/plugins/data/public/query/timefilter/lib/extract_time_filter.ts @@ -18,7 +18,7 @@ */ import { keys, partition } from 'lodash'; -import { esFilters } from '../../../../../../plugins/data/public'; +import { esFilters } from '../../../../common'; export function extractTimeFilter(timeFieldName: string, filters: esFilters.Filter[]) { const [timeRangeFilter, restOfFilters] = partition(filters, (obj: esFilters.Filter) => { diff --git a/src/plugins/data/public/query/timefilter/time_history.ts b/src/plugins/data/public/query/timefilter/time_history.ts index e14c9ac0bc7ca..4dabbb557e9db 100644 --- a/src/plugins/data/public/query/timefilter/time_history.ts +++ b/src/plugins/data/public/query/timefilter/time_history.ts @@ -18,9 +18,9 @@ */ import moment from 'moment'; -import { TimeRange } from 'src/plugins/data/public'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { PersistedLog } from '../persisted_log'; +import { TimeRange } from '../../../common'; export class TimeHistory { private history: PersistedLog; diff --git a/src/plugins/data/public/query/timefilter/timefilter.test.ts b/src/plugins/data/public/query/timefilter/timefilter.test.ts index cca646508b539..cd904c76ac4d9 100644 --- a/src/plugins/data/public/query/timefilter/timefilter.test.ts +++ b/src/plugins/data/public/query/timefilter/timefilter.test.ts @@ -34,7 +34,7 @@ import expect from '@kbn/expect'; import moment from 'moment'; import { Timefilter } from './timefilter'; import { Subscription } from 'rxjs'; -import { TimeRange, RefreshInterval } from 'src/plugins/data/public'; +import { TimeRange, RefreshInterval } from '../../../common'; import { timefilterServiceMock } from './timefilter_service.mock'; const timefilterSetupMock = timefilterServiceMock.createSetupContract(); diff --git a/src/plugins/data/public/query/timefilter/timefilter.ts b/src/plugins/data/public/query/timefilter/timefilter.ts index 137e5100aa20e..639f3f4a66f18 100644 --- a/src/plugins/data/public/query/timefilter/timefilter.ts +++ b/src/plugins/data/public/query/timefilter/timefilter.ts @@ -20,12 +20,13 @@ import _ from 'lodash'; import { Subject, BehaviorSubject } from 'rxjs'; import moment from 'moment'; -import { RefreshInterval, TimeRange, TimeHistoryContract } from 'src/plugins/data/public'; import { IndexPattern } from 'src/legacy/core_plugins/data/public'; import { areRefreshIntervalsDifferent, areTimeRangesDifferent } from './lib/diff_time_picker_vals'; import { parseQueryString } from './lib/parse_querystring'; import { calculateBounds, getTime } from './get_time'; import { TimefilterConfig, InputTimeRange, TimeRangeBounds } from './types'; +import { RefreshInterval, TimeRange } from '../../../common'; +import { TimeHistoryContract } from './time_history'; // TODO: remove! diff --git a/src/plugins/data/public/query/timefilter/types.ts b/src/plugins/data/public/query/timefilter/types.ts index 879776cee178e..8b8deea43f808 100644 --- a/src/plugins/data/public/query/timefilter/types.ts +++ b/src/plugins/data/public/query/timefilter/types.ts @@ -17,7 +17,7 @@ * under the License. */ import { Moment } from 'moment'; -import { RefreshInterval, TimeRange } from 'src/plugins/data/public'; +import { TimeRange, RefreshInterval } from '../../../common'; export interface TimefilterConfig { timeDefaults: TimeRange; diff --git a/src/plugins/data/public/suggestions_provider/types.ts b/src/plugins/data/public/suggestions_provider/types.ts index 988b5fcd43fa8..a13ecfb10143f 100644 --- a/src/plugins/data/public/suggestions_provider/types.ts +++ b/src/plugins/data/public/suggestions_provider/types.ts @@ -16,6 +16,11 @@ * specific language governing permissions and limitations * under the License. */ -import { Field } from '..'; +import { IFieldType } from '../../common'; -export type IGetSuggestions = (index: string, field: Field, query: string, boolFilter?: any) => any; +export type IGetSuggestions = ( + index: string, + field: IFieldType, + query: string, + boolFilter?: any +) => any; diff --git a/src/plugins/data/public/suggestions_provider/value_suggestions.ts b/src/plugins/data/public/suggestions_provider/value_suggestions.ts index c769f64025b0e..3bc1b45d87395 100644 --- a/src/plugins/data/public/suggestions_provider/value_suggestions.ts +++ b/src/plugins/data/public/suggestions_provider/value_suggestions.ts @@ -21,14 +21,14 @@ import { memoize } from 'lodash'; import { UiSettingsClientContract, HttpServiceBase } from 'src/core/public'; import { IGetSuggestions } from './types'; -import { Field } from '..'; +import { IFieldType } from '../../common'; export function getSuggestionsProvider( uiSettings: UiSettingsClientContract, http: HttpServiceBase ): IGetSuggestions { const requestSuggestions = memoize( - (index: string, field: Field, query: string, boolFilter: any = []) => { + (index: string, field: IFieldType, query: string, boolFilter: any = []) => { return http.fetch(`/api/kibana/suggestions/values/${index}`, { method: 'POST', body: JSON.stringify({ query, field: field.name, boolFilter }), @@ -37,7 +37,7 @@ export function getSuggestionsProvider( resolver ); - return async (index: string, field: Field, query: string, boolFilter?: any) => { + return async (index: string, field: IFieldType, query: string, boolFilter?: any) => { const shouldSuggestValues = uiSettings.get('filterEditor:suggestValues'); if (field.type === 'boolean') { return [true, false]; @@ -48,7 +48,7 @@ export function getSuggestionsProvider( }; } -function resolver(index: string, field: Field, query: string, boolFilter: any) { +function resolver(index: string, field: IFieldType, query: string, boolFilter: any) { // Only cache results for a minute const ttl = Math.floor(Date.now() / 1000 / 60); return [ttl, query, index, field.name, JSON.stringify(boolFilter)].join('|'); diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index f0b6117b928cd..81906a63bd49d 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -32,5 +32,6 @@ export { } from './index_patterns'; export * from './search'; +export * from '../common'; export { IRequestTypesMap, IResponseTypesMap } from './search'; diff --git a/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_caps_response.test.js b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_caps_response.test.js index cf4af615b9577..88d2d873521cb 100644 --- a/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_caps_response.test.js +++ b/src/plugins/data/server/index_patterns/fetcher/lib/field_capabilities/field_caps_response.test.js @@ -24,7 +24,7 @@ import sinon from 'sinon'; import * as shouldReadFieldFromDocValuesNS from './should_read_field_from_doc_values'; import { shouldReadFieldFromDocValues } from './should_read_field_from_doc_values'; -import { getKbnFieldType } from '../../../../../../data/common'; +import { getKbnFieldType } from '../../../../../../data/server'; import { readFieldCapsResponse } from './field_caps_response'; import esResponse from './__fixtures__/es_field_caps_response.json'; diff --git a/src/plugins/data/server/search/index.ts b/src/plugins/data/server/search/index.ts index e160fd4026c58..298a665fd5b2c 100644 --- a/src/plugins/data/server/search/index.ts +++ b/src/plugins/data/server/search/index.ts @@ -18,7 +18,6 @@ */ export { ISearchSetup } from './i_search_setup'; -export * from '../../common'; export { ISearchContext } from './i_search_context'; diff --git a/src/plugins/kibana_utils/public/field_mapping/mapping_setup.test.ts b/src/plugins/kibana_utils/public/field_mapping/mapping_setup.test.ts index e57699e879a87..ca40685db0ebf 100644 --- a/src/plugins/kibana_utils/public/field_mapping/mapping_setup.test.ts +++ b/src/plugins/kibana_utils/public/field_mapping/mapping_setup.test.ts @@ -18,7 +18,7 @@ */ import { expandShorthand } from './mapping_setup'; -import { ES_FIELD_TYPES } from '../../../data/common'; +import { ES_FIELD_TYPES } from '../../../data/public'; describe('mapping_setup', () => { it('allows shortcuts for field types by just setting the value to the type name', () => { diff --git a/src/plugins/kibana_utils/public/field_mapping/mapping_setup.ts b/src/plugins/kibana_utils/public/field_mapping/mapping_setup.ts index 495338735337c..72f3716147efa 100644 --- a/src/plugins/kibana_utils/public/field_mapping/mapping_setup.ts +++ b/src/plugins/kibana_utils/public/field_mapping/mapping_setup.ts @@ -18,26 +18,25 @@ */ import { mapValues, isString } from 'lodash'; -import { ES_FIELD_TYPES } from '../../../../plugins/data/common'; import { FieldMappingSpec, MappingObject } from './types'; +import { ES_FIELD_TYPES } from '../../../data/public'; /** @private */ type ShorthandFieldMapObject = FieldMappingSpec | ES_FIELD_TYPES | 'json'; -const json: FieldMappingSpec = { - type: ES_FIELD_TYPES.TEXT, - _serialize(v) { - if (v) return JSON.stringify(v); - }, - _deserialize(v) { - if (v) return JSON.parse(v); - }, -}; - /** @public */ export const expandShorthand = (sh: Record): MappingObject => { return mapValues>(sh, (val: ShorthandFieldMapObject) => { const fieldMap = isString(val) ? { type: val } : val; + const json: FieldMappingSpec = { + type: ES_FIELD_TYPES.TEXT, + _serialize(v) { + if (v) return JSON.stringify(v); + }, + _deserialize(v) { + if (v) return JSON.parse(v); + }, + }; return fieldMap.type === 'json' ? json : fieldMap; }) as MappingObject; diff --git a/src/plugins/kibana_utils/public/field_mapping/types.ts b/src/plugins/kibana_utils/public/field_mapping/types.ts index 973a58d3baec4..f3fb9b000e45a 100644 --- a/src/plugins/kibana_utils/public/field_mapping/types.ts +++ b/src/plugins/kibana_utils/public/field_mapping/types.ts @@ -17,7 +17,7 @@ * under the License. */ -import { ES_FIELD_TYPES } from '../../../data/common'; +import { ES_FIELD_TYPES } from '../../../data/public'; /** @public */ export interface FieldMappingSpec { diff --git a/test/plugin_functional/plugins/demo_search/common/index.ts b/test/plugin_functional/plugins/demo_search/common/index.ts index 0339e8fbda8c5..9254412ece291 100644 --- a/test/plugin_functional/plugins/demo_search/common/index.ts +++ b/test/plugin_functional/plugins/demo_search/common/index.ts @@ -20,7 +20,7 @@ import { IKibanaSearchRequest, IKibanaSearchResponse, -} from '../../../../../src/plugins/data/common/search'; +} from '../../../../../src/plugins/data/public'; export const DEMO_SEARCH_STRATEGY = 'DEMO_SEARCH_STRATEGY'; diff --git a/x-pack/legacy/plugins/canvas/server/lib/build_embeddable_filters.ts b/x-pack/legacy/plugins/canvas/server/lib/build_embeddable_filters.ts index ca34246531bff..254a7ad8b3637 100644 --- a/x-pack/legacy/plugins/canvas/server/lib/build_embeddable_filters.ts +++ b/x-pack/legacy/plugins/canvas/server/lib/build_embeddable_filters.ts @@ -4,11 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ -import { TimeRange } from 'src/plugins/data/public'; import { Filter } from '../../types'; // @ts-ignore Untyped Local import { buildBoolArray } from './build_bool_array'; -import { esFilters } from '../../../../../../src/plugins/data/common'; +import { TimeRange, esFilters } from '../../../../../../src/plugins/data/server'; export interface EmbeddableFilterInput { filters: esFilters.Filter[]; diff --git a/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx b/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx index 157537ad574c1..3fcb6609f28f1 100644 --- a/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx +++ b/x-pack/legacy/plugins/lens/public/app_plugin/app.test.tsx @@ -12,8 +12,7 @@ import { EditorFrameInstance } from '../types'; import { Storage } from '../../../../../../src/plugins/kibana_utils/public'; import { Document, SavedObjectStore } from '../persistence'; import { mount } from 'enzyme'; -import { esFilters } from '../../../../../../src/plugins/data/public'; - +import { esFilters, IFieldType, IIndexPattern } from '../../../../../../src/plugins/data/public'; import { dataPluginMock } from '../../../../../../src/plugins/data/public/mocks'; const dataStartMock = dataPluginMock.createStartContract(); @@ -593,17 +592,17 @@ describe('Lens App', () => { args.editorFrame = frame; const instance = mount(); + const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; + const field = ({ name: 'myfield' } as unknown) as IFieldType; - args.data.query.filterManager.setFilters([ - esFilters.buildExistsFilter({ name: 'myfield' }, { id: 'index1' }), - ]); + args.data.query.filterManager.setFilters([esFilters.buildExistsFilter(field, indexPattern)]); instance.update(); expect(frame.mount).toHaveBeenCalledWith( expect.any(Element), expect.objectContaining({ - filters: [esFilters.buildExistsFilter({ name: 'myfield' }, { id: 'index1' })], + filters: [esFilters.buildExistsFilter(field, indexPattern)], }) ); }); @@ -725,9 +724,10 @@ describe('Lens App', () => { query: { query: 'new', language: 'lucene' }, }); - args.data.query.filterManager.setFilters([ - esFilters.buildExistsFilter({ name: 'myfield' }, { id: 'index1' }), - ]); + const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; + const field = ({ name: 'myfield' } as unknown) as IFieldType; + + args.data.query.filterManager.setFilters([esFilters.buildExistsFilter(field, indexPattern)]); instance.update(); instance.find(TopNavMenu).prop('onClearSavedQuery')!(); diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/data_panel_wrapper.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/data_panel_wrapper.tsx index a5509cdc94559..8ecb6d0599bc7 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/data_panel_wrapper.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/data_panel_wrapper.tsx @@ -7,13 +7,12 @@ import React, { useMemo, memo, useContext, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiPopover, EuiButtonIcon, EuiContextMenuPanel, EuiContextMenuItem } from '@elastic/eui'; -import { Query } from 'src/plugins/data/common'; import { DatasourceDataPanelProps, Datasource } from '../../../public'; import { NativeRenderer } from '../../native_renderer'; import { Action } from './state_management'; import { DragContext } from '../../drag_drop'; import { StateSetter, FramePublicAPI } from '../../types'; -import { esFilters } from '../../../../../../../src/plugins/data/public'; +import { Query, esFilters } from '../../../../../../../src/plugins/data/public'; interface DataPanelWrapperProps { datasourceState: unknown; diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/save.test.ts b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/save.test.ts index f223a9c06a2a7..158a6cb8c979a 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/save.test.ts +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/save.test.ts @@ -6,12 +6,15 @@ import { getSavedObjectFormat, Props } from './save'; import { createMockDatasource, createMockVisualization } from '../mocks'; -import { esFilters } from '../../../../../../../src/plugins/data/public'; +import { esFilters, IIndexPattern, IFieldType } from '../../../../../../../src/plugins/data/public'; describe('save editor frame state', () => { const mockVisualization = createMockVisualization(); mockVisualization.getPersistableState.mockImplementation(x => x); const mockDatasource = createMockDatasource(); + const mockIndexPattern = ({ id: 'indexpattern' } as unknown) as IIndexPattern; + const mockField = ({ name: '@timestamp' } as unknown) as IFieldType; + mockDatasource.getPersistableState.mockImplementation(x => x); const saveArgs: Props = { activeDatasources: { @@ -37,7 +40,7 @@ describe('save editor frame state', () => { }, query: { query: '', language: 'lucene' }, dateRange: { fromDate: 'now-7d', toDate: 'now' }, - filters: [esFilters.buildExistsFilter({ name: '@timestamp' }, { id: 'indexpattern' })], + filters: [esFilters.buildExistsFilter(mockField, mockIndexPattern)], }, }; diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.test.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.test.tsx index fd35ecd702d23..d1d0f7e8e8f63 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.test.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.test.tsx @@ -20,7 +20,7 @@ import { ReactWrapper } from 'enzyme'; import { DragDrop, ChildDragDropProvider } from '../../drag_drop'; import { Ast } from '@kbn/interpreter/common'; import { coreMock } from 'src/core/public/mocks'; -import { esFilters } from '../../../../../../../src/plugins/data/public'; +import { esFilters, IFieldType, IIndexPattern } from '../../../../../../../src/plugins/data/public'; const waitForPromises = () => new Promise(resolve => setTimeout(resolve)); @@ -378,10 +378,13 @@ describe('workspace_panel', () => { expect(expressionRendererMock).toHaveBeenCalledTimes(1); + const indexPattern = ({ id: 'index1' } as unknown) as IIndexPattern; + const field = ({ name: 'myfield' } as unknown) as IFieldType; + instance.setProps({ framePublicAPI: { ...framePublicAPI, - filters: [esFilters.buildExistsFilter({ name: 'myfield' }, { id: 'index1' })], + filters: [esFilters.buildExistsFilter(field, indexPattern)], }, }); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx index 962eece4977e0..3536ad8053891 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx @@ -35,9 +35,12 @@ import { niceTimeFormatter, } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; -import { buildEsQuery, getEsQueryConfig } from '@kbn/es-query'; -import { Query } from 'src/plugins/data/common'; -import { esFilters } from '../../../../../../src/plugins/data/public'; +import { + Query, + esFilters, + esQuery, + IIndexPattern, +} from '../../../../../../src/plugins/data/public'; // @ts-ignore import { fieldFormats } from '../../../../../../src/legacy/ui/public/registry/field_formats'; import { DraggedField } from './indexpattern'; @@ -128,7 +131,12 @@ export function FieldItem(props: FieldItemProps) { core.http .post(`/api/lens/index_stats/${indexPattern.title}/field`, { body: JSON.stringify({ - dslQuery: buildEsQuery(indexPattern, query, filters, getEsQueryConfig(core.uiSettings)), + dslQuery: esQuery.buildEsQuery( + indexPattern as IIndexPattern, + query, + filters, + esQuery.getEsQueryConfig(core.uiSettings) + ), fromDate: dateRange.fromDate, toDate: dateRange.toDate, timeFieldName: indexPattern.timeFieldName, diff --git a/x-pack/legacy/plugins/lens/public/persistence/saved_object_store.ts b/x-pack/legacy/plugins/lens/public/persistence/saved_object_store.ts index 460cd76e8390d..4337482663a9c 100644 --- a/x-pack/legacy/plugins/lens/public/persistence/saved_object_store.ts +++ b/x-pack/legacy/plugins/lens/public/persistence/saved_object_store.ts @@ -6,8 +6,7 @@ // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { SavedObjectAttributes } from 'src/core/server'; -import { Query } from 'src/plugins/data/common'; -import { esFilters } from '../../../../../../src/plugins/data/public'; +import { Query, esFilters } from '../../../../../../src/plugins/data/public'; export interface Document { id?: string; diff --git a/x-pack/legacy/plugins/lens/public/types.ts b/x-pack/legacy/plugins/lens/public/types.ts index b66bb7bee8f8a..896284e8097d8 100644 --- a/x-pack/legacy/plugins/lens/public/types.ts +++ b/x-pack/legacy/plugins/lens/public/types.ts @@ -7,13 +7,12 @@ import { Ast } from '@kbn/interpreter/common'; import { IconType } from '@elastic/eui/src/components/icon/icon'; import { CoreSetup } from 'src/core/public'; -import { Query } from 'src/plugins/data/common'; import { SavedQuery } from 'src/legacy/core_plugins/data/public'; import { KibanaDatatable } from '../../../../../src/legacy/core_plugins/interpreter/common'; import { DragContextState } from './drag_drop'; import { Document } from './persistence'; import { DateRange } from '../common'; -import { esFilters } from '../../../../../src/plugins/data/public'; +import { Query, esFilters } from '../../../../../src/plugins/data/public'; // eslint-disable-next-line export interface EditorFrameOptions {} diff --git a/x-pack/legacy/plugins/ml/common/types/fields.ts b/x-pack/legacy/plugins/ml/common/types/fields.ts index 9e1b992eec907..f9d9b6b0161e2 100644 --- a/x-pack/legacy/plugins/ml/common/types/fields.ts +++ b/x-pack/legacy/plugins/ml/common/types/fields.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { ES_FIELD_TYPES } from '../../../../../../src/plugins/data/common'; +import { ES_FIELD_TYPES } from '../../../../../../src/plugins/data/public'; import { ML_JOB_AGGREGATION, KIBANA_AGGREGATION, diff --git a/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/page.tsx b/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/page.tsx index 0e72bf41f177b..84b614c1b89c8 100644 --- a/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/page.tsx +++ b/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/page.tsx @@ -6,9 +6,6 @@ import React, { FC, Fragment, useEffect, useState } from 'react'; import { merge } from 'rxjs'; - -// @ts-ignore -import { decorateQuery, luceneStringToDsl } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; import { FieldType } from 'ui/index_patterns'; @@ -25,9 +22,8 @@ import { EuiSpacer, EuiTitle, } from '@elastic/eui'; - +import { KBN_FIELD_TYPES, esQuery } from '../../../../../../../src/plugins/data/public'; import { NavigationMenu } from '../../components/navigation_menu'; -import { KBN_FIELD_TYPES } from '../../../../../../../src/plugins/data/public'; import { ML_JOB_FIELD_TYPES } from '../../../common/constants/field_types'; import { SEARCH_QUERY_LANGUAGE } from '../../../common/constants/search'; // @ts-ignore @@ -194,8 +190,8 @@ export const Page: FC = () => { }, }; } else { - qry = luceneStringToDsl(qryString); - decorateQuery(qry, kibanaConfig.get('query:queryString:options')); + qry = esQuery.luceneStringToDsl(qryString); + esQuery.decorateQuery(qry, kibanaConfig.get('query:queryString:options')); } setSearchQuery(qry); diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/common/job_creator/job_creator.ts b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/common/job_creator/job_creator.ts index aa16da08e3a3a..e5c6964f0f118 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/common/job_creator/job_creator.ts +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/common/job_creator/job_creator.ts @@ -8,7 +8,7 @@ import { SavedSearch } from 'src/legacy/core_plugins/kibana/public/discover/type import { IndexPattern } from 'ui/index_patterns'; import { IndexPatternTitle } from '../../../../../common/types/kibana'; import { ML_JOB_AGGREGATION } from '../../../../../common/constants/aggregation_types'; -import { ES_FIELD_TYPES } from '../../../../../../../../../src/plugins/data/common'; +import { ES_FIELD_TYPES } from '../../../../../../../../../src/plugins/data/public'; import { Job, Datafeed, Detector, JobId, DatafeedId, BucketSpan } from './configs'; import { Aggregation, Field } from '../../../../../common/types/fields'; import { createEmptyJob, createEmptyDatafeed } from './util/default_configs'; diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/utils/new_job_utils.ts b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/utils/new_job_utils.ts index 0a37cff050eb0..85beb32fffa3c 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/utils/new_job_utils.ts +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/utils/new_job_utils.ts @@ -5,13 +5,12 @@ */ import { SavedSearch } from 'src/legacy/core_plugins/kibana/public/discover/types'; -import { IndexPattern } from 'ui/index_patterns'; -import { buildEsQuery, getEsQueryConfig } from '@kbn/es-query'; import { KibanaConfigTypeFix } from '../../../contexts/kibana'; import { InjectorService } from '../../../../common/types/angular'; +import { esQuery, IIndexPattern } from '../../../../../../../../src/plugins/data/public'; export interface SearchItems { - indexPattern: IndexPattern; + indexPattern: IIndexPattern; savedSearch: SavedSearch; query: any; combinedQuery: any; @@ -55,8 +54,8 @@ export function SearchItemsProvider($injector: InjectorService) { const filters = fs.length ? fs : []; - const esQueryConfigs = getEsQueryConfig(kibanaConfig); - combinedQuery = buildEsQuery(indexPattern, [query], filters, esQueryConfigs); + const esQueryConfigs = esQuery.getEsQueryConfig(kibanaConfig); + combinedQuery = esQuery.buildEsQuery(indexPattern, [query], filters, esQueryConfigs); } return { diff --git a/x-pack/legacy/plugins/ml/server/models/job_service/new_job_caps/field_service.ts b/x-pack/legacy/plugins/ml/server/models/job_service/new_job_caps/field_service.ts index d7c26bda77d20..32309ad8177f4 100644 --- a/x-pack/legacy/plugins/ml/server/models/job_service/new_job_caps/field_service.ts +++ b/x-pack/legacy/plugins/ml/server/models/job_service/new_job_caps/field_service.ts @@ -13,7 +13,7 @@ import { NewJobCaps, METRIC_AGG_TYPE, } from '../../../../common/types/fields'; -import { ES_FIELD_TYPES } from '../../../../../../../../src/plugins/data/common'; +import { ES_FIELD_TYPES } from '../../../../../../../../src/plugins/data/server'; import { ML_JOB_AGGREGATION } from '../../../../common/constants/aggregation_types'; import { rollupServiceProvider, RollupJob, RollupFields } from './rollup'; import { aggregations, mlOnlyAggregations } from './aggregations'; diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/validate_time_range.js b/x-pack/legacy/plugins/ml/server/models/job_validation/validate_time_range.js index c75f3c0cc3827..9c48fae1739b9 100644 --- a/x-pack/legacy/plugins/ml/server/models/job_validation/validate_time_range.js +++ b/x-pack/legacy/plugins/ml/server/models/job_validation/validate_time_range.js @@ -8,7 +8,7 @@ import _ from 'lodash'; -import { ES_FIELD_TYPES } from '../../../../../../../src/plugins/data/common'; +import { ES_FIELD_TYPES } from '../../../../../../../src/plugins/data/server'; import { parseInterval } from '../../../common/util/parse_interval.js'; import { validateJobObject } from './validate_job_object'; diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/__tests__/execute_job.js b/x-pack/legacy/plugins/reporting/export_types/csv/server/__tests__/execute_job.js index 0cf059f842f2e..16408b09d5953 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/__tests__/execute_job.js +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/__tests__/execute_job.js @@ -11,7 +11,7 @@ import nodeCrypto from '@elastic/node-crypto'; import { CancellationToken } from '../../../../common/cancellation_token'; import { FieldFormatsService } from '../../../../../../../../src/legacy/ui/field_formats/mixin/field_formats_service'; -import { StringFormat } from '../../../../../../../../src/plugins/data/common'; +import { StringFormat } from '../../../../../../../../src/plugins/data/server'; import { executeJobFactory } from '../execute_job'; diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/__tests__/field_format_map.js b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/__tests__/field_format_map.js index 00a3236d1f75a..e9da43aa1dd32 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/__tests__/field_format_map.js +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/__tests__/field_format_map.js @@ -7,7 +7,7 @@ import expect from '@kbn/expect'; import { FieldFormatsService } from '../../../../../../../../../src/legacy/ui/field_formats/mixin/field_formats_service'; -import { BytesFormat, NumberFormat } from '../../../../../../../../../src/plugins/data/common'; +import { BytesFormat, NumberFormat } from '../../../../../../../../../src/plugins/data/server'; import { fieldFormatMapFactory } from '../field_format_map'; diff --git a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts index 2e2c8635fca5f..0ab103f0ab4c7 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts @@ -4,30 +4,26 @@ * you may not use this file except in compliance with the Elastic License. */ -import { buildEsQuery } from '@kbn/es-query'; // @ts-ignore no module definition import { createGenerateCsv } from '../../../csv/server/lib/generate_csv'; import { CancellationToken } from '../../../../common/cancellation_token'; import { ServerFacade, RequestFacade, Logger } from '../../../../types'; -import { - IndexPatternSavedObject, - SavedSearchObjectAttributes, - SearchPanel, - SearchRequest, - SearchSource, - SearchSourceQuery, -} from '../../types'; +import { SavedSearchObjectAttributes, SearchPanel, SearchRequest, SearchSource } from '../../types'; import { CsvResultFromSearch, - ESQueryConfig, GenerateCsvParams, - Filter, IndexPatternField, QueryFilter, } from '../../types'; import { getDataSource } from './get_data_source'; import { getFilters } from './get_filters'; import { JobParamsDiscoverCsv } from '../../../csv/types'; +import { + esQuery, + esFilters, + IIndexPattern, + Query, +} from '../../../../../../../../src/plugins/data/server'; const getEsQueryConfig = async (config: any) => { const configs = await Promise.all([ @@ -36,7 +32,11 @@ const getEsQueryConfig = async (config: any) => { config.get('courier:ignoreFilterIfFieldNotInIndex'), ]); const [allowLeadingWildcards, queryStringOptions, ignoreFilterIfFieldNotInIndex] = configs; - return { allowLeadingWildcards, queryStringOptions, ignoreFilterIfFieldNotInIndex }; + return { + allowLeadingWildcards, + queryStringOptions, + ignoreFilterIfFieldNotInIndex, + } as esQuery.EsQueryConfig; }; const getUiSettings = async (config: any) => { @@ -116,21 +116,17 @@ export async function generateCsvSearch( }; }, {}); const docValueFields = indexPatternTimeField ? [indexPatternTimeField] : undefined; - - // this array helps ensure the params are passed to buildEsQuery (non-Typescript) in the right order - const buildCsvParams: [IndexPatternSavedObject, SearchSourceQuery, Filter[], ESQueryConfig] = [ - indexPatternSavedObject, - searchSourceQuery, - combinedFilter, - esQueryConfig, - ]; - const searchRequest: SearchRequest = { index: esIndex, body: { _source: { includes }, docvalue_fields: docValueFields, - query: buildEsQuery(...buildCsvParams), + query: esQuery.buildEsQuery( + indexPatternSavedObject as IIndexPattern, + (searchSourceQuery as unknown) as Query, + (combinedFilter as unknown) as esFilters.Filter, + esQueryConfig + ), script_fields: scriptFieldsConfig, sort: sortConfig, }, diff --git a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/types.d.ts b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/types.d.ts index f8692c336b292..72caf551d2a3d 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/types.d.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/types.d.ts @@ -234,12 +234,6 @@ export interface SearchSourceFilter extends Filter { isSearchSourceFilter: boolean; } -export interface ESQueryConfig { - allowLeadingWildcards: boolean; - queryStringOptions: boolean; - ignoreFilterIfFieldNotInIndex: boolean; -} - export interface IndexPatternField { scripted: boolean; lang?: string; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx index b9c28105e99d1..794e26b417012 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx @@ -7,7 +7,6 @@ import { EuiLink, EuiText } from '@elastic/eui'; import React, { useEffect, useState } from 'react'; import { createPortalNode, InPortal } from 'react-reverse-portal'; -import { Query } from 'src/plugins/data/common'; import styled, { css } from 'styled-components'; import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links'; import { SavedObjectFinder } from 'ui/saved_objects/components/saved_object_finder'; @@ -29,7 +28,7 @@ import { IndexPatternsMissingPrompt } from './index_patterns_missing_prompt'; import { MapToolTip } from './map_tool_tip/map_tool_tip'; import * as i18n from './translations'; import { MapEmbeddable, SetQuery } from './types'; -import { esFilters } from '../../../../../../../src/plugins/data/public'; +import { Query, esFilters } from '../../../../../../../src/plugins/data/public'; interface EmbeddableMapProps { maintainRatio?: boolean; diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx index a50cfa98fc977..03c4492b77f1b 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map_helpers.tsx @@ -7,7 +7,6 @@ import uuid from 'uuid'; import React from 'react'; import { OutPortal, PortalNode } from 'react-reverse-portal'; -import { Query } from 'src/plugins/data/common'; import { PluginsStart } from 'ui/new_platform/new_platform'; import { ActionToaster, AppToast } from '../toasters'; @@ -27,7 +26,7 @@ import { getLayerList } from './map_config'; // @ts-ignore Missing type defs as maps moves to Typescript import { MAP_SAVED_OBJECT_TYPE } from '../../../../maps/common/constants'; import * as i18n from './translations'; -import { esFilters } from '../../../../../../../src/plugins/data/public'; +import { Query, esFilters } from '../../../../../../../src/plugins/data/public'; /** * Displays an error toast for the provided title and message diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts b/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts index 10412ecdb5013..fdda9f949280a 100644 --- a/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/embeddables/types.ts @@ -4,7 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Query } from 'src/plugins/data/common'; import { TimeRange } from 'src/plugins/data/public'; import { EmbeddableInput, @@ -13,7 +12,7 @@ import { EmbeddableFactory, } from '../../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; import { inputsModel } from '../../store/inputs'; -import { esFilters } from '../../../../../../../src/plugins/data/public'; +import { Query, esFilters } from '../../../../../../../src/plugins/data/public'; export interface MapEmbeddableInput extends EmbeddableInput { filters: esFilters.Filter[]; diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx index 6b79a6402586e..2b588283bd198 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx @@ -5,12 +5,10 @@ */ import { EuiPanel } from '@elastic/eui'; -import { getEsQueryConfig } from '@kbn/es-query'; import { getOr, isEmpty, isEqual } from 'lodash/fp'; import React from 'react'; import styled from 'styled-components'; import { StaticIndexPattern } from 'ui/index_patterns'; -import { Query } from 'src/plugins/data/common'; import { BrowserFields } from '../../containers/source'; import { TimelineQuery } from '../../containers/timeline'; @@ -31,7 +29,7 @@ import { TimelineRefetch } from '../timeline/refetch_timeline'; import { isCompactFooter } from '../timeline/timeline'; import { ManageTimelineContext } from '../timeline/timeline_context'; import * as i18n from './translations'; -import { esFilters } from '../../../../../../../src/plugins/data/public'; +import { Query, esFilters, esQuery } from '../../../../../../../src/plugins/data/public'; const DEFAULT_EVENTS_VIEWER_HEIGHT = 500; @@ -85,7 +83,7 @@ export const EventsViewer = React.memo( const columnsHeader = isEmpty(columns) ? defaultHeaders : columns; const core = useKibanaCore(); const combinedQueries = combineQueries({ - config: getEsQueryConfig(core.uiSettings), + config: esQuery.getEsQueryConfig(core.uiSettings), dataProviders, indexPattern, browserFields, diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx index 68ad6c4e79623..3514fda4efe29 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/index.tsx @@ -8,8 +8,6 @@ import { isEqual } from 'lodash/fp'; import React, { useEffect, useState, useCallback } from 'react'; import { connect } from 'react-redux'; import { ActionCreator } from 'typescript-fsa'; -import { Query } from 'src/plugins/data/common'; - import { WithSource } from '../../containers/source'; import { inputsModel, inputsSelectors, State, timelineSelectors } from '../../store'; import { timelineActions, inputsActions } from '../../store/actions'; @@ -18,7 +16,7 @@ import { ColumnHeader } from '../timeline/body/column_headers/column_header'; import { DataProvider } from '../timeline/data_providers/data_provider'; import { Sort } from '../timeline/body/sort'; import { OnChangeItemsPerPage } from '../timeline/events'; -import { esFilters } from '../../../../../../../src/plugins/data/public'; +import { Query, esFilters } from '../../../../../../../src/plugins/data/public'; import { EventsViewer } from './events_viewer'; import { InputsModelId } from '../../store/inputs/constants'; diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts b/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts index 23cd855cc028a..b61ef7165dcc3 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/navigation/helpers.ts @@ -6,7 +6,6 @@ import { isEmpty } from 'lodash/fp'; import { Location } from 'history'; -import { Query } from 'src/plugins/data/common'; import { UrlInputsModel } from '../../store/inputs/model'; import { CONSTANTS } from '../url_state/constants'; @@ -16,7 +15,7 @@ import { replaceStateKeyInQueryString, getQueryStringFromLocation, } from '../url_state/helpers'; -import { esFilters } from '../../../../../../../src/plugins/data/public'; +import { Query, esFilters } from '../../../../../../../src/plugins/data/public'; import { TabNavigationProps } from './tab_navigation/types'; import { SearchNavTab } from './types'; diff --git a/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx b/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx index f5a99c631131f..f0d69092b3a68 100644 --- a/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/search_bar/index.tsx @@ -11,8 +11,6 @@ import { Dispatch } from 'redux'; import { Subscription } from 'rxjs'; import styled from 'styled-components'; import { StaticIndexPattern, IndexPattern } from 'ui/index_patterns'; - -import { TimeRange, Query } from 'src/plugins/data/common/types'; import { SavedQuery } from 'src/legacy/core_plugins/data/public'; import { OnTimeChangeProps } from '@elastic/eui'; @@ -36,7 +34,7 @@ import { toStrSelector, } from './selectors'; import { timelineActions, hostsActions, networkActions } from '../../store/actions'; -import { esFilters } from '../../../../../../../src/plugins/data/public'; +import { TimeRange, Query, esFilters } from '../../../../../../../src/plugins/data/public'; const { ui: { SearchBar }, diff --git a/x-pack/legacy/plugins/siem/public/components/search_bar/selectors.ts b/x-pack/legacy/plugins/siem/public/components/search_bar/selectors.ts index acf91067c4291..f501466db9ed9 100644 --- a/x-pack/legacy/plugins/siem/public/components/search_bar/selectors.ts +++ b/x-pack/legacy/plugins/siem/public/components/search_bar/selectors.ts @@ -5,9 +5,9 @@ */ import { createSelector } from 'reselect'; -import { Query } from 'src/plugins/data/common'; import { SavedQuery } from 'src/legacy/core_plugins/data/public'; import { InputsRange } from '../../store/inputs/model'; +import { Query } from '../../../../../../../src/plugins/data/public'; export { endSelector, diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/helpers.test.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/helpers.test.tsx index b30771760bad3..2ed28869043a0 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/helpers.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/helpers.test.tsx @@ -11,7 +11,7 @@ import { mockIndexPattern } from '../../mock'; import { mockDataProviders } from './data_providers/mock/mock_data_providers'; import { buildGlobalQuery, combineQueries } from './helpers'; import { mockBrowserFields } from '../../containers/source/mock'; -import { EsQueryConfig } from '../../lib/keury'; +import { esQuery } from '../../../../../../../src/plugins/data/public'; const cleanUpKqlQuery = (str: string) => str.replace(/\n/g, '').replace(/\s\s+/g, ' '); const startDate = new Date('2018-03-23T18:49:23.132Z').valueOf(); @@ -117,7 +117,7 @@ describe('Build KQL Query', () => { }); describe('Combined Queries', () => { - const config: EsQueryConfig = { + const config: esQuery.EsQueryConfig = { allowLeadingWildcards: true, queryStringOptions: {}, ignoreFilterIfFieldNotInIndex: true, diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx index 6182fca6e2e99..edf5799802e9c 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/helpers.tsx @@ -7,12 +7,12 @@ import { isEmpty, isNumber, get } from 'lodash/fp'; import memoizeOne from 'memoize-one'; import { StaticIndexPattern } from 'ui/index_patterns'; -import { Query, esFilters } from 'src/plugins/data/public'; -import { escapeQueryValue, convertToBuildEsQuery, EsQueryConfig } from '../../lib/keury'; +import { escapeQueryValue, convertToBuildEsQuery } from '../../lib/keury'; import { DataProvider, DataProvidersAnd, EXISTS_OPERATOR } from './data_providers/data_provider'; import { BrowserFields } from '../../containers/source'; +import { Query, esQuery, esFilters } from '../../../../../../../src/plugins/data/public'; const convertDateFieldToQuery = (field: string, value: string | number) => `${field}: ${isNumber(value) ? value : new Date(value).valueOf()}`; @@ -101,7 +101,7 @@ export const combineQueries = ({ end, isEventViewer, }: { - config: EsQueryConfig; + config: esQuery.EsQueryConfig; dataProviders: DataProvider[]; indexPattern: StaticIndexPattern; browserFields: BrowserFields; diff --git a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx index fb62b636398c2..48ef8b1914745 100644 --- a/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx +++ b/x-pack/legacy/plugins/siem/public/components/timeline/timeline.tsx @@ -5,7 +5,6 @@ */ import { EuiFlexGroup } from '@elastic/eui'; -import { getEsQueryConfig } from '@kbn/es-query'; import { getOr, isEmpty } from 'lodash/fp'; import * as React from 'react'; import styled from 'styled-components'; @@ -37,6 +36,7 @@ import { TimelineHeader } from './header'; import { calculateBodyHeight, combineQueries } from './helpers'; import { TimelineRefetch } from './refetch_timeline'; import { ManageTimelineContext } from './timeline_context'; +import { esQuery } from '../../../../../../../src/plugins/data/public'; const WrappedByAutoSizer = styled.div` width: 100%; @@ -115,7 +115,7 @@ export const Timeline = React.memo( }) => { const core = useKibanaCore(); const combinedQueries = combineQueries({ - config: getEsQueryConfig(core.uiSettings), + config: esQuery.getEsQueryConfig(core.uiSettings), dataProviders, indexPattern, browserFields, diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/test_dependencies.ts b/x-pack/legacy/plugins/siem/public/components/url_state/test_dependencies.ts index 39c540b3bd355..d58295de5ce9d 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/test_dependencies.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/test_dependencies.ts @@ -5,8 +5,6 @@ */ import { ActionCreator } from 'typescript-fsa'; -import { Query } from 'src/plugins/data/common'; - import { DispatchUpdateTimeline } from '../open_timeline/types'; import { navTabs } from '../../pages/home/home_navigations'; import { SiemPageName } from '../../pages/home/types'; @@ -17,6 +15,7 @@ import { HostsTableType } from '../../store/hosts/model'; import { CONSTANTS } from './constants'; import { dispatchSetInitialStateFromUrl } from './initialize_redux_by_url'; import { UrlStateContainerPropTypes, LocationTypes } from './types'; +import { Query } from '../../../../../../../src/plugins/data/public'; type Action = 'PUSH' | 'POP' | 'REPLACE'; const pop: Action = 'POP'; diff --git a/x-pack/legacy/plugins/siem/public/lib/keury/index.ts b/x-pack/legacy/plugins/siem/public/lib/keury/index.ts index e9f4c95a80b74..b6819e54575d6 100644 --- a/x-pack/legacy/plugins/siem/public/lib/keury/index.ts +++ b/x-pack/legacy/plugins/siem/public/lib/keury/index.ts @@ -4,17 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ -import { buildEsQuery, fromKueryExpression, toElasticsearchQuery } from '@kbn/es-query'; +import { fromKueryExpression, toElasticsearchQuery, JsonObject } from '@kbn/es-query'; import { isEmpty, isString, flow } from 'lodash/fp'; -import { StaticIndexPattern } from 'ui/index_patterns'; -import { Query } from 'src/plugins/data/common'; -import { esFilters } from '../../../../../../../src/plugins/data/public'; +import { + Query, + esFilters, + esQuery, + IIndexPattern, +} from '../../../../../../../src/plugins/data/public'; import { KueryFilterQuery } from '../../store'; export const convertKueryToElasticSearchQuery = ( kueryExpression: string, - indexPattern: StaticIndexPattern + indexPattern: IIndexPattern ) => { try { return kueryExpression @@ -25,6 +28,19 @@ export const convertKueryToElasticSearchQuery = ( } }; +export const convertKueryToDslFilter = ( + kueryExpression: string, + indexPattern: IIndexPattern +): JsonObject => { + try { + return kueryExpression + ? toElasticsearchQuery(fromKueryExpression(kueryExpression), indexPattern) + : {}; + } catch (err) { + return {}; + } +}; + export const escapeQueryValue = (val: number | string = ''): string | number => { if (isString(val)) { if (isEmpty(val)) { @@ -63,33 +79,26 @@ const escapeNot = (val: string) => val.replace(/not(\s+)/gi, '\\$&'); export const escapeKuery = flow(escapeSpecialCharacters, escapeAndOr, escapeNot, escapeWhitespace); -export interface EsQueryConfig { - allowLeadingWildcards: boolean; - queryStringOptions: unknown; - ignoreFilterIfFieldNotInIndex: boolean; - dateFormatTZ?: string | null; -} - export const convertToBuildEsQuery = ({ config, indexPattern, queries, filters, }: { - config: EsQueryConfig; - indexPattern: StaticIndexPattern; + config: esQuery.EsQueryConfig; + indexPattern: IIndexPattern; queries: Query[]; filters: esFilters.Filter[]; }) => { try { return JSON.stringify( - buildEsQuery( + esQuery.buildEsQuery( indexPattern, queries, filters.filter(f => f.meta.disabled === false), { ...config, - dateFormatTZ: null, + dateFormatTZ: undefined, } ) ); diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx index b51cbf36293dc..738c0771ffc32 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx @@ -5,7 +5,6 @@ */ import { EuiHorizontalRule, EuiSpacer } from '@elastic/eui'; -import { getEsQueryConfig } from '@kbn/es-query'; import React, { useContext, useEffect } from 'react'; import { compose } from 'redux'; import { connect } from 'react-redux'; @@ -34,6 +33,7 @@ import { convertToBuildEsQuery } from '../../../lib/keury'; import { setAbsoluteRangeDatePicker as dispatchAbsoluteRangeDatePicker } from '../../../store/inputs/actions'; import { SpyRoute } from '../../../utils/route/spy_routes'; import { useKibanaCore } from '../../../lib/compose/kibana_core'; +import { esQuery } from '../../../../../../../../src/plugins/data/public'; import { HostsEmptyPage } from '../hosts_empty_page'; import { navTabsHostDetails } from './nav_tabs'; @@ -68,7 +68,7 @@ const HostDetailsComponent = React.memo( {({ indicesExist, indexPattern }) => { const filterQuery = convertToBuildEsQuery({ - config: getEsQueryConfig(core.uiSettings), + config: esQuery.getEsQueryConfig(core.uiSettings), indexPattern, queries: [query], filters: [ diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx index fae2b476bc7b9..a149b5224bd67 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx @@ -5,7 +5,6 @@ */ import { EuiSpacer } from '@elastic/eui'; -import { getEsQueryConfig } from '@kbn/es-query'; import * as React from 'react'; import { compose } from 'redux'; import { connect } from 'react-redux'; @@ -30,6 +29,7 @@ import { setAbsoluteRangeDatePicker as dispatchSetAbsoluteRangeDatePicker } from import { SpyRoute } from '../../utils/route/spy_routes'; import { useKibanaCore } from '../../lib/compose/kibana_core'; +import { esQuery } from '../../../../../../../src/plugins/data/public'; import { HostsEmptyPage } from './hosts_empty_page'; import { navTabsHosts } from './nav_tabs'; import * as i18n from './translations'; @@ -58,7 +58,7 @@ const HostsComponent = React.memo( {({ indicesExist, indexPattern }) => { const filterQuery = convertToBuildEsQuery({ - config: getEsQueryConfig(core.uiSettings), + config: esQuery.getEsQueryConfig(core.uiSettings), indexPattern, queries: [query], filters, diff --git a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx index d4e6ff220e975..66a96e71808ef 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx @@ -5,7 +5,6 @@ */ import { EuiHorizontalRule, EuiSpacer, EuiFlexItem } from '@elastic/eui'; -import { getEsQueryConfig } from '@kbn/es-query'; import React, { useCallback, useEffect } from 'react'; import { connect } from 'react-redux'; import { StickyContainer } from 'react-sticky'; @@ -40,6 +39,7 @@ import { UsersQueryTable } from './users_query_table'; import { NetworkTopNFlowQueryTable } from './network_top_n_flow_query_table'; import { NetworkHttpQueryTable } from './network_http_query_table'; import { NetworkTopCountriesQueryTable } from './network_top_countries_query_table'; +import { esQuery } from '../../../../../../../../src/plugins/data/public'; import { useKibanaCore } from '../../../lib/compose/kibana_core'; const IpOverviewManage = manageQuery(IpOverview); @@ -80,7 +80,7 @@ export const IPDetailsComponent = React.memo( {({ indicesExist, indexPattern }) => { const ip = decodeIpv6(detailName); const filterQuery = convertToBuildEsQuery({ - config: getEsQueryConfig(core.uiSettings), + config: esQuery.getEsQueryConfig(core.uiSettings), indexPattern, queries: [query], filters, diff --git a/x-pack/legacy/plugins/siem/public/pages/network/network.tsx b/x-pack/legacy/plugins/siem/public/pages/network/network.tsx index 943e60b13c164..33e901d5dbac8 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/network.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/network.tsx @@ -5,7 +5,6 @@ */ import { EuiSpacer } from '@elastic/eui'; -import { getEsQueryConfig } from '@kbn/es-query'; import React from 'react'; import { connect } from 'react-redux'; import { StickyContainer } from 'react-sticky'; @@ -30,6 +29,7 @@ import { navTabsNetwork, NetworkRoutes, NetworkRoutesLoading } from './navigatio import { NetworkEmptyPage } from './network_empty_page'; import * as i18n from './translations'; import { NetworkComponentProps } from './types'; +import { esQuery } from '../../../../../../../src/plugins/data/public'; const KpiNetworkComponentManage = manageQuery(KpiNetworkComponent); const sourceId = 'default'; @@ -53,7 +53,7 @@ const NetworkComponent = React.memo( {({ indicesExist, indexPattern }) => { const filterQuery = convertToBuildEsQuery({ - config: getEsQueryConfig(core.uiSettings), + config: esQuery.getEsQueryConfig(core.uiSettings), indexPattern, queries: [query], filters, diff --git a/x-pack/legacy/plugins/siem/public/pages/network/types.ts b/x-pack/legacy/plugins/siem/public/pages/network/types.ts index e440d0c27e467..1941d8f9bfb7b 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/network/types.ts @@ -6,10 +6,9 @@ import { RouteComponentProps } from 'react-router-dom'; import { ActionCreator } from 'typescript-fsa'; -import { Query, esFilters } from 'src/plugins/data/common'; - import { GlobalTimeArgs } from '../../containers/global_time'; import { InputsModelId } from '../../store/inputs/constants'; +import { Query, esFilters } from '../../../../../../../src/plugins/data/public'; export type SetAbsoluteRangeDatePicker = ActionCreator<{ id: InputsModelId; diff --git a/x-pack/legacy/plugins/siem/public/store/inputs/model.ts b/x-pack/legacy/plugins/siem/public/store/inputs/model.ts index 01cf386311d77..54409b1f74454 100644 --- a/x-pack/legacy/plugins/siem/public/store/inputs/model.ts +++ b/x-pack/legacy/plugins/siem/public/store/inputs/model.ts @@ -5,12 +5,11 @@ */ import { Dispatch } from 'redux'; -import { Query } from 'src/plugins/data/common/query'; import { SavedQuery } from 'src/legacy/core_plugins/data/public'; import { Omit } from '../../../common/utility_types'; import { InputsModelId } from './constants'; import { CONSTANTS } from '../../components/url_state/constants'; -import { esFilters } from '../../../../../../../src/plugins/data/public'; +import { Query, esFilters } from '../../../../../../../src/plugins/data/public'; export interface AbsoluteTimeRange { kind: 'absolute'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/get_filter.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/get_filter.ts index 22ebccbaef6a7..ae79e9bc01480 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/get_filter.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/get_filter.ts @@ -4,11 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { buildEsQuery } from '@kbn/es-query'; -import { Query } from 'src/plugins/data/common/query'; import { AlertServices } from '../../../../../alerting/server/types'; import { SignalAlertParams, PartialFilter } from './types'; import { assertUnreachable } from '../../../utils/build_query'; +import { + Query, + esQuery, + esFilters, + IIndexPattern, +} from '../../../../../../../../src/plugins/data/server'; export const getQueryFilter = ( query: string, @@ -19,7 +23,8 @@ export const getQueryFilter = ( const indexPattern = { fields: [], title: index.join(), - }; + } as IIndexPattern; + const queries: Query[] = [{ query, language }]; const config = { allowLeadingWildcards: true, @@ -27,13 +32,12 @@ export const getQueryFilter = ( ignoreFilterIfFieldNotInIndex: false, dateFormatTZ: 'Zulu', }; - const esQuery = buildEsQuery( - indexPattern, - queries, - filters.filter(f => f.meta != null && f.meta.disabled === false), - config + + const enabledFilters = ((filters as unknown) as esFilters.Filter[]).filter( + f => f && !esFilters.isFilterDisabled(f) ); - return esQuery; + + return esQuery.buildEsQuery(indexPattern, queries, enabledFilters, config); }; interface GetFilterArgs { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/types.ts index 7edc60618c251..35561165859b1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/types.ts @@ -7,7 +7,6 @@ import { get } from 'lodash/fp'; import Hapi from 'hapi'; -import { esFilters } from '../../../../../../../../src/plugins/data/common'; import { SIGNALS_ID } from '../../../../common/constants'; import { Alert, @@ -18,6 +17,7 @@ import { import { AlertsClient } from '../../../../../alerting/server/alerts_client'; import { ActionsClient } from '../../../../../actions/server/actions_client'; import { SearchResponse } from '../../types'; +import { esFilters } from '../../../../../../../../src/plugins/data/server'; export type PartialFilter = Partial; diff --git a/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts b/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts index 7291eba04057d..16100089a9e56 100644 --- a/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts +++ b/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts @@ -4,15 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ -// @ts-ignore -import { buildEsQuery, getEsQueryConfig } from '@kbn/es-query'; - import { SavedObjectsClientContract, UiSettingsClientContract } from 'src/core/public'; - import { IndexPattern as IndexPatternType, IndexPatterns as IndexPatternsType, } from 'ui/index_patterns'; +import { esQuery } from '../../../../../../../../src/plugins/data/public'; type IndexPatternId = string; type SavedSearchId = string; @@ -106,8 +103,8 @@ export function createSearchItems( const filters = fs.length ? fs : []; - const esQueryConfigs = getEsQueryConfig(config); - combinedQuery = buildEsQuery(indexPattern, [query], filters, esQueryConfigs); + const esQueryConfigs = esQuery.getEsQueryConfig(config); + combinedQuery = esQuery.buildEsQuery(indexPattern || null, [query], filters, esQueryConfigs); } return { From 2ff8e9f8d4e7bb4852bca696fc92b67a9c758b0c Mon Sep 17 00:00:00 2001 From: Tim Roes Date: Sat, 16 Nov 2019 15:14:10 +0100 Subject: [PATCH 02/17] Make babel understand TypeScript 3.7 syntax (#50772) (#50802) * Use correct babel plugins * Make one change using new operator * Fix i18n babel config --- packages/kbn-babel-preset/common_preset.js | 7 +++++ packages/kbn-babel-preset/package.json | 2 ++ src/dev/i18n/extractors/code.js | 11 ++++++- .../adapters/request/request_adapter.ts | 2 +- yarn.lock | 30 +++++++++++++++++++ 5 files changed, 50 insertions(+), 2 deletions(-) diff --git a/packages/kbn-babel-preset/common_preset.js b/packages/kbn-babel-preset/common_preset.js index d2bad41e8de86..d1b7bc20dd9f9 100644 --- a/packages/kbn-babel-preset/common_preset.js +++ b/packages/kbn-babel-preset/common_preset.js @@ -27,6 +27,13 @@ const plugins = [ // // See https://github.com/babel/proposals/issues/12 for progress require.resolve('@babel/plugin-proposal-class-properties'), + + // Optional Chaining proposal is stage 3 (https://github.com/tc39/proposal-optional-chaining) + // Need this since we are using TypeScript 3.7+ + require.resolve('@babel/plugin-proposal-optional-chaining'), + // Nullish coalescing proposal is stage 3 (https://github.com/tc39/proposal-nullish-coalescing) + // Need this since we are using TypeScript 3.7+ + require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'), ]; const isTestEnv = process.env.BABEL_ENV === 'test' || process.env.NODE_ENV === 'test'; diff --git a/packages/kbn-babel-preset/package.json b/packages/kbn-babel-preset/package.json index c22cf175b29e5..1913301e21a76 100644 --- a/packages/kbn-babel-preset/package.json +++ b/packages/kbn-babel-preset/package.json @@ -5,6 +5,8 @@ "license": "Apache-2.0", "dependencies": { "@babel/plugin-proposal-class-properties": "^7.5.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.4.4", + "@babel/plugin-proposal-optional-chaining": "^7.6.0", "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/plugin-transform-modules-commonjs": "^7.5.0", "@babel/preset-env": "^7.5.5", diff --git a/src/dev/i18n/extractors/code.js b/src/dev/i18n/extractors/code.js index fa0d834824e97..6439f8ceff332 100644 --- a/src/dev/i18n/extractors/code.js +++ b/src/dev/i18n/extractors/code.js @@ -67,7 +67,16 @@ export function* extractCodeMessages(buffer, reporter) { try { ast = parse(buffer.toString(), { sourceType: 'module', - plugins: ['jsx', 'typescript', 'objectRestSpread', 'classProperties', 'asyncGenerators', 'dynamicImport'], + plugins: [ + 'jsx', + 'typescript', + 'objectRestSpread', + 'classProperties', + 'asyncGenerators', + 'dynamicImport', + 'nullishCoalescingOperator', + 'optionalChaining', + ], }); } catch (error) { if (error instanceof SyntaxError) { diff --git a/src/plugins/inspector/public/adapters/request/request_adapter.ts b/src/plugins/inspector/public/adapters/request/request_adapter.ts index a1a1463b8f2f6..70af6b5b51d18 100644 --- a/src/plugins/inspector/public/adapters/request/request_adapter.ts +++ b/src/plugins/inspector/public/adapters/request/request_adapter.ts @@ -54,7 +54,7 @@ class RequestAdapter extends EventEmitter { name, startTime: Date.now(), status: RequestStatus.PENDING, - id: _.get(params, 'id', uuid()), + id: params.id ?? uuid(), }; this.requests.set(req.id, req); this._onChange(); diff --git a/yarn.lock b/yarn.lock index b52e933317385..069d43894be88 100644 --- a/yarn.lock +++ b/yarn.lock @@ -346,6 +346,14 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-json-strings" "^7.2.0" +"@babel/plugin-proposal-nullish-coalescing-operator@^7.4.4": + version "7.4.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.4.4.tgz#41c360d59481d88e0ce3a3f837df10121a769b39" + integrity sha512-Amph7Epui1Dh/xxUxS2+K22/MUi6+6JVTvy3P58tja3B6yKTSjwwx0/d83rF7551D6PVSSoplQb8GCwqec7HRw== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.2.0" + "@babel/plugin-proposal-object-rest-spread@7.5.5", "@babel/plugin-proposal-object-rest-spread@^7.3.2", "@babel/plugin-proposal-object-rest-spread@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.5.5.tgz#61939744f71ba76a3ae46b5eea18a54c16d22e58" @@ -362,6 +370,14 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" +"@babel/plugin-proposal-optional-chaining@^7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.6.0.tgz#e9bf1f9b9ba10c77c033082da75f068389041af8" + integrity sha512-kj4gkZ6qUggkprRq3Uh5KP8XnE1MdIO0J7MhdDX8+rAbB6dJ2UrensGIS+0NPZAaaJ1Vr0PN6oLUgXMU1uMcSg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-optional-chaining" "^7.2.0" + "@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz#501ffd9826c0b91da22690720722ac7cb1ca9c78" @@ -413,6 +429,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-nullish-coalescing-operator@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.2.0.tgz#f75083dfd5ade73e783db729bbd87e7b9efb7624" + integrity sha512-lRCEaKE+LTxDQtgbYajI04ddt6WW0WJq57xqkAZ+s11h4YgfRHhVA/Y2VhfPzzFD4qeLHWg32DMp9HooY4Kqlg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e" @@ -427,6 +450,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-optional-chaining@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.2.0.tgz#a59d6ae8c167e7608eaa443fda9fa8fa6bf21dff" + integrity sha512-HtGCtvp5Uq/jH/WNUPkK6b7rufnCPLLlDAFN7cmACoIjaOOiXxUt3SswU5loHqrhtqTsa/WoLQ1OQ1AGuZqaWA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-typescript@^7.2.0": version "7.3.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz#a7cc3f66119a9f7ebe2de5383cce193473d65991" From 5aa6eecff7be1deb3b9288791a3cc361cb04075f Mon Sep 17 00:00:00 2001 From: "Devin W. Hurley" Date: Sat, 16 Nov 2019 11:27:19 -0500 Subject: [PATCH 03/17] [SIEM] Tests for search_after and bulk index (#50129) (#50825) * tests for detection engine get/put utils * increases unit test code statement coverage to 100% for search_after / bulk index reindexer * removes mockLogger declaration from individual test cases - clears mock counts before each test case runs so as to not accumulate method calls after each test case * resets default paging size to 1000 - typo from when I was working through my tests * updates tests after rebase with master * fixes type check after fixing test from rebase with master * removes undefined from maxSignals in type definition, updates tests with pure jest function implementations of logger and services - modifying only the return values or creating a mock implementation when necessary, removed some overlapping test cases * fixes type issue * replaces mock implementation with mock return value for unit test * removes mock logger expected counts, just check if error logs are called, don't care about debug / warn etc. * fixes more type checks after rebase with master --- .../legacy/plugins/siem/common/constants.ts | 1 + .../alerts/__mocks__/es_results.ts | 150 ++++++++ .../alerts/build_events_query.test.ts | 148 +++++++- .../alerts/build_events_query.ts | 3 +- .../alerts/signals_alert_type.ts | 8 +- .../lib/detection_engine/alerts/utils.test.ts | 330 ++++++++++++++++++ .../lib/detection_engine/alerts/utils.ts | 37 +- 7 files changed, 656 insertions(+), 21 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/__mocks__/es_results.ts create mode 100644 x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.test.ts diff --git a/x-pack/legacy/plugins/siem/common/constants.ts b/x-pack/legacy/plugins/siem/common/constants.ts index 6845648ee921d..5c3bc8ab5b309 100644 --- a/x-pack/legacy/plugins/siem/common/constants.ts +++ b/x-pack/legacy/plugins/siem/common/constants.ts @@ -15,6 +15,7 @@ export const DEFAULT_TIME_RANGE = 'timepicker:timeDefaults'; export const DEFAULT_REFRESH_RATE_INTERVAL = 'timepicker:refreshIntervalDefaults'; export const DEFAULT_SIEM_TIME_RANGE = 'siem:timeDefaults'; export const DEFAULT_SIEM_REFRESH_INTERVAL = 'siem:refreshIntervalDefaults'; +export const DEFAULT_SIGNALS_INDEX = '.siem-signals'; export const DEFAULT_ANOMALY_SCORE = 'siem:defaultAnomalyScore'; export const DEFAULT_MAX_TABLE_QUERY_SIZE = 10000; export const DEFAULT_SCALE_DATE_FORMAT = 'dateFormat:scaled'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/__mocks__/es_results.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/__mocks__/es_results.ts new file mode 100644 index 0000000000000..895af32cc7af3 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/__mocks__/es_results.ts @@ -0,0 +1,150 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { SignalSourceHit, SignalSearchResponse, SignalAlertParams } from '../types'; + +export const sampleSignalAlertParams = (maxSignals: number | undefined): SignalAlertParams => ({ + id: 'rule-1', + description: 'Detecting root and admin users', + falsePositives: [], + immutable: false, + index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + interval: '5m', + name: 'Detect Root/Admin Users', + type: 'query', + from: 'now-6m', + tags: ['some fake tag'], + to: 'now', + severity: 'high', + query: 'user.name: root or user.name: admin', + language: 'kuery', + references: ['http://google.com'], + maxSignals: maxSignals ? maxSignals : 10000, + enabled: true, + filter: undefined, + filters: undefined, + savedId: undefined, + size: 1000, +}); + +export const sampleDocNoSortId: SignalSourceHit = { + _index: 'myFakeSignalIndex', + _type: 'doc', + _score: 100, + _version: 1, + _id: 'someFakeId', + _source: { + someKey: 'someValue', + '@timestamp': 'someTimeStamp', + }, +}; + +export const sampleDocWithSortId: SignalSourceHit = { + _index: 'myFakeSignalIndex', + _type: 'doc', + _score: 100, + _version: 1, + _id: 'someFakeId', + _source: { + someKey: 'someValue', + '@timestamp': 'someTimeStamp', + }, + sort: ['1234567891111'], +}; + +export const sampleEmptyDocSearchResults: SignalSearchResponse = { + took: 10, + timed_out: false, + _shards: { + total: 10, + successful: 10, + failed: 0, + skipped: 0, + }, + hits: { + total: 0, + max_score: 100, + hits: [], + }, +}; + +export const sampleDocSearchResultsNoSortId: SignalSearchResponse = { + took: 10, + timed_out: false, + _shards: { + total: 10, + successful: 10, + failed: 0, + skipped: 0, + }, + hits: { + total: 100, + max_score: 100, + hits: [ + { + ...sampleDocNoSortId, + }, + ], + }, +}; + +export const sampleDocSearchResultsNoSortIdNoHits: SignalSearchResponse = { + took: 10, + timed_out: false, + _shards: { + total: 10, + successful: 10, + failed: 0, + skipped: 0, + }, + hits: { + total: 0, + max_score: 100, + hits: [ + { + ...sampleDocNoSortId, + }, + ], + }, +}; + +export const repeatedSearchResultsWithSortId = (repeat: number) => ({ + took: 10, + timed_out: false, + _shards: { + total: 10, + successful: 10, + failed: 0, + skipped: 0, + }, + hits: { + total: repeat, + max_score: 100, + hits: Array.from({ length: repeat }).map(x => ({ + ...sampleDocWithSortId, + })), + }, +}); + +export const sampleDocSearchResultsWithSortId: SignalSearchResponse = { + took: 10, + timed_out: false, + _shards: { + total: 10, + successful: 10, + failed: 0, + skipped: 0, + }, + hits: { + total: 1, + max_score: 100, + hits: [ + { + ...sampleDocWithSortId, + }, + ], + }, +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/build_events_query.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/build_events_query.test.ts index 6c95e82485e45..b368c8fe36054 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/build_events_query.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/build_events_query.test.ts @@ -66,7 +66,7 @@ describe('create_signals', () => { ], }, }, - track_total_hits: true, + sort: [ { '@timestamp': { @@ -136,7 +136,150 @@ describe('create_signals', () => { ], }, }, - track_total_hits: true, + + sort: [ + { + '@timestamp': { + order: 'asc', + }, + }, + ], + }, + }); + }); + test('if searchAfterSortId is a valid sortId string', () => { + const fakeSortId = '123456789012'; + const query = buildEventsSearchQuery({ + index: ['auditbeat-*'], + from: 'now-5m', + to: 'today', + filter: {}, + size: 100, + searchAfterSortId: fakeSortId, + }); + expect(query).toEqual({ + allowNoIndices: true, + index: ['auditbeat-*'], + size: 100, + ignoreUnavailable: true, + body: { + query: { + bool: { + filter: [ + {}, + { + bool: { + filter: [ + { + bool: { + should: [ + { + range: { + '@timestamp': { + gte: 'now-5m', + }, + }, + }, + ], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [ + { + range: { + '@timestamp': { + lte: 'today', + }, + }, + }, + ], + minimum_should_match: 1, + }, + }, + ], + }, + }, + { + match_all: {}, + }, + ], + }, + }, + + sort: [ + { + '@timestamp': { + order: 'asc', + }, + }, + ], + search_after: [fakeSortId], + }, + }); + }); + test('if searchAfterSortId is a valid sortId number', () => { + const fakeSortIdNumber = 123456789012; + const query = buildEventsSearchQuery({ + index: ['auditbeat-*'], + from: 'now-5m', + to: 'today', + filter: {}, + size: 100, + searchAfterSortId: fakeSortIdNumber, + }); + expect(query).toEqual({ + allowNoIndices: true, + index: ['auditbeat-*'], + size: 100, + ignoreUnavailable: true, + body: { + query: { + bool: { + filter: [ + {}, + { + bool: { + filter: [ + { + bool: { + should: [ + { + range: { + '@timestamp': { + gte: 'now-5m', + }, + }, + }, + ], + minimum_should_match: 1, + }, + }, + { + bool: { + should: [ + { + range: { + '@timestamp': { + lte: 'today', + }, + }, + }, + ], + minimum_should_match: 1, + }, + }, + ], + }, + }, + { + match_all: {}, + }, + ], + }, + }, + sort: [ { '@timestamp': { @@ -144,6 +287,7 @@ describe('create_signals', () => { }, }, ], + search_after: [fakeSortIdNumber], }, }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/build_events_query.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/build_events_query.ts index d2fb7c21f66f5..c75dddf896fd1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/build_events_query.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/build_events_query.ts @@ -10,7 +10,7 @@ interface BuildEventsSearchQuery { to: string; filter: unknown; size: number; - searchAfterSortId?: string; + searchAfterSortId: string | number | undefined; } export const buildEventsSearchQuery = ({ @@ -74,7 +74,6 @@ export const buildEventsSearchQuery = ({ ], }, }, - track_total_hits: true, sort: [ { '@timestamp': { diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/signals_alert_type.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/signals_alert_type.ts index 44175360f80b3..be763f2b5386d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/signals_alert_type.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/signals_alert_type.ts @@ -5,9 +5,8 @@ */ import { schema } from '@kbn/config-schema'; -import { SIGNALS_ID } from '../../../../common/constants'; +import { SIGNALS_ID, DEFAULT_SIGNALS_INDEX } from '../../../../common/constants'; import { Logger } from '../../../../../../../../src/core/server'; - // TODO: Remove this for the build_events_query call eventually import { buildEventsReIndex } from './build_events_reindex'; @@ -34,7 +33,7 @@ export const signalsAlertType = ({ logger }: { logger: Logger }): SignalAlertTyp savedId: schema.nullable(schema.string()), query: schema.nullable(schema.string()), filters: schema.nullable(schema.arrayOf(schema.object({}, { allowUnknowns: true }))), - maxSignals: schema.number({ defaultValue: 100 }), + maxSignals: schema.number({ defaultValue: 10000 }), severity: schema.string(), tags: schema.arrayOf(schema.string(), { defaultValue: [] }), to: schema.string(), @@ -82,6 +81,7 @@ export const signalsAlertType = ({ logger }: { logger: Logger }): SignalAlertTyp to, filter: esFilter, size: searchAfterSize, + searchAfterSortId: undefined, }); try { @@ -93,7 +93,7 @@ export const signalsAlertType = ({ logger }: { logger: Logger }): SignalAlertTyp to, // TODO: Change this out once we have solved // https://github.com/elastic/kibana/issues/47002 - signalsIndex: process.env.SIGNALS_INDEX || '.siem-signals-10-01-2019', + signalsIndex: process.env.SIGNALS_INDEX || DEFAULT_SIGNALS_INDEX, severity, description, name, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.test.ts new file mode 100644 index 0000000000000..c3ffb6e8c230a --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.test.ts @@ -0,0 +1,330 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { savedObjectsClientMock } from 'src/core/server/mocks'; + +import { Logger } from '../../../../../../../../src/core/server'; +import { + buildBulkBody, + singleBulkIndex, + singleSearchAfter, + searchAfterAndBulkIndex, +} from './utils'; +import { + sampleDocNoSortId, + sampleSignalAlertParams, + sampleDocSearchResultsNoSortId, + sampleDocSearchResultsNoSortIdNoHits, + sampleDocSearchResultsWithSortId, + sampleEmptyDocSearchResults, + repeatedSearchResultsWithSortId, +} from './__mocks__/es_results'; + +const mockLogger: Logger = { + log: jest.fn(), + trace: jest.fn(), + debug: jest.fn(), + info: jest.fn(), + warn: jest.fn(), + error: jest.fn(), + fatal: jest.fn(), +}; + +const mockService = { + callCluster: jest.fn(), + alertInstanceFactory: jest.fn(), + savedObjectsClient: savedObjectsClientMock.create(), +}; + +describe('utils', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('buildBulkBody', () => { + test('if bulk body builds well-defined body', () => { + const sampleParams = sampleSignalAlertParams(undefined); + const fakeSignalSourceHit = buildBulkBody(sampleDocNoSortId, sampleParams); + expect(fakeSignalSourceHit).toEqual({ + someKey: 'someValue', + '@timestamp': 'someTimeStamp', + signal: { + '@timestamp': fakeSignalSourceHit.signal['@timestamp'], // timestamp generated in the body + rule_revision: 1, + rule_id: sampleParams.id, + rule_type: sampleParams.type, + parent: { + id: sampleDocNoSortId._id, + type: 'event', + index: sampleDocNoSortId._index, + depth: 1, + }, + name: sampleParams.name, + severity: sampleParams.severity, + description: sampleParams.description, + original_time: sampleDocNoSortId._source['@timestamp'], + index_patterns: sampleParams.index, + references: sampleParams.references, + }, + }); + }); + }); + describe('singleBulkIndex', () => { + test('create successful bulk index', async () => { + const sampleParams = sampleSignalAlertParams(undefined); + const sampleSearchResult = sampleDocSearchResultsNoSortId; + mockService.callCluster.mockReturnValueOnce({ + took: 100, + errors: false, + items: [ + { + fakeItemValue: 'fakeItemKey', + }, + ], + }); + const successfulSingleBulkIndex = await singleBulkIndex( + sampleSearchResult, + sampleParams, + mockService, + mockLogger + ); + expect(successfulSingleBulkIndex).toEqual(true); + }); + test('create unsuccessful bulk index due to empty search results', async () => { + const sampleParams = sampleSignalAlertParams(undefined); + const sampleSearchResult = sampleEmptyDocSearchResults; + mockService.callCluster.mockReturnValue(false); + const successfulSingleBulkIndex = await singleBulkIndex( + sampleSearchResult, + sampleParams, + mockService, + mockLogger + ); + expect(successfulSingleBulkIndex).toEqual(true); + }); + test('create unsuccessful bulk index due to bulk index errors', async () => { + // need a sample search result, sample signal params, mock service, mock logger + const sampleParams = sampleSignalAlertParams(undefined); + const sampleSearchResult = sampleDocSearchResultsNoSortId; + mockService.callCluster.mockReturnValue({ + took: 100, + errors: true, + }); + const successfulSingleBulkIndex = await singleBulkIndex( + sampleSearchResult, + sampleParams, + mockService, + mockLogger + ); + expect(mockLogger.error).toHaveBeenCalled(); + expect(successfulSingleBulkIndex).toEqual(false); + }); + }); + describe('singleSearchAfter', () => { + test('if singleSearchAfter works without a given sort id', async () => { + let searchAfterSortId; + const sampleParams = sampleSignalAlertParams(undefined); + mockService.callCluster.mockReturnValue(sampleDocSearchResultsNoSortId); + await expect( + singleSearchAfter(searchAfterSortId, sampleParams, mockService, mockLogger) + ).rejects.toThrow('Attempted to search after with empty sort id'); + }); + test('if singleSearchAfter works with a given sort id', async () => { + const searchAfterSortId = '1234567891111'; + const sampleParams = sampleSignalAlertParams(undefined); + mockService.callCluster.mockReturnValue(sampleDocSearchResultsWithSortId); + const searchAfterResult = await singleSearchAfter( + searchAfterSortId, + sampleParams, + mockService, + mockLogger + ); + expect(searchAfterResult).toEqual(sampleDocSearchResultsWithSortId); + }); + test('if singleSearchAfter throws error', async () => { + const searchAfterSortId = '1234567891111'; + const sampleParams = sampleSignalAlertParams(undefined); + mockService.callCluster.mockImplementation(async () => { + throw Error('Fake Error'); + }); + await expect( + singleSearchAfter(searchAfterSortId, sampleParams, mockService, mockLogger) + ).rejects.toThrow('Fake Error'); + }); + }); + describe('searchAfterAndBulkIndex', () => { + test('if successful with empty search results', async () => { + const sampleParams = sampleSignalAlertParams(undefined); + const result = await searchAfterAndBulkIndex( + sampleEmptyDocSearchResults, + sampleParams, + mockService, + mockLogger + ); + expect(mockService.callCluster).toHaveBeenCalledTimes(0); + expect(result).toEqual(true); + }); + test('if successful iteration of while loop with maxDocs', async () => { + const sampleParams = sampleSignalAlertParams(10); + mockService.callCluster + .mockReturnValueOnce({ + took: 100, + errors: false, + items: [ + { + fakeItemValue: 'fakeItemKey', + }, + ], + }) + .mockReturnValueOnce(repeatedSearchResultsWithSortId(4)) + .mockReturnValueOnce({ + took: 100, + errors: false, + items: [ + { + fakeItemValue: 'fakeItemKey', + }, + ], + }) + .mockReturnValueOnce(repeatedSearchResultsWithSortId(4)) + .mockReturnValueOnce({ + took: 100, + errors: false, + items: [ + { + fakeItemValue: 'fakeItemKey', + }, + ], + }); + const result = await searchAfterAndBulkIndex( + repeatedSearchResultsWithSortId(4), + sampleParams, + mockService, + mockLogger + ); + expect(mockService.callCluster).toHaveBeenCalledTimes(5); + expect(result).toEqual(true); + }); + test('if unsuccessful first bulk index', async () => { + const sampleParams = sampleSignalAlertParams(10); + mockService.callCluster.mockReturnValue({ + took: 100, + errors: true, // will cause singleBulkIndex to return false + }); + const result = await searchAfterAndBulkIndex( + repeatedSearchResultsWithSortId(4), + sampleParams, + mockService, + mockLogger + ); + expect(mockLogger.error).toHaveBeenCalled(); + expect(result).toEqual(false); + }); + test('if unsuccessful iteration of searchAfterAndBulkIndex due to empty sort ids', async () => { + const sampleParams = sampleSignalAlertParams(undefined); + + mockService.callCluster.mockReturnValueOnce({ + took: 100, + errors: false, + items: [ + { + fakeItemValue: 'fakeItemKey', + }, + ], + }); + const result = await searchAfterAndBulkIndex( + sampleDocSearchResultsNoSortId, + sampleParams, + mockService, + mockLogger + ); + expect(mockLogger.error).toHaveBeenCalled(); + expect(result).toEqual(false); + }); + test('if unsuccessful iteration of searchAfterAndBulkIndex due to empty sort ids and 0 total hits', async () => { + const sampleParams = sampleSignalAlertParams(undefined); + mockService.callCluster.mockReturnValueOnce({ + took: 100, + errors: false, + items: [ + { + fakeItemValue: 'fakeItemKey', + }, + ], + }); + const result = await searchAfterAndBulkIndex( + sampleDocSearchResultsNoSortIdNoHits, + sampleParams, + mockService, + mockLogger + ); + expect(result).toEqual(true); + }); + test('if successful iteration of while loop with maxDocs and search after returns results with no sort ids', async () => { + const sampleParams = sampleSignalAlertParams(10); + mockService.callCluster + .mockReturnValueOnce({ + took: 100, + errors: false, + items: [ + { + fakeItemValue: 'fakeItemKey', + }, + ], + }) + .mockReturnValueOnce(sampleDocSearchResultsNoSortId); + const result = await searchAfterAndBulkIndex( + repeatedSearchResultsWithSortId(4), + sampleParams, + mockService, + mockLogger + ); + expect(result).toEqual(true); + }); + test('if logs error when iteration is unsuccessful when bulk index results in a failure', async () => { + const sampleParams = sampleSignalAlertParams(5); + mockService.callCluster + .mockReturnValueOnce({ + // first bulk insert + took: 100, + errors: false, + items: [ + { + fakeItemValue: 'fakeItemKey', + }, + ], + }) + .mockReturnValueOnce(sampleDocSearchResultsWithSortId); // get some more docs + const result = await searchAfterAndBulkIndex( + repeatedSearchResultsWithSortId(4), + sampleParams, + mockService, + mockLogger + ); + expect(mockLogger.error).toHaveBeenCalled(); + expect(result).toEqual(true); + }); + test('if returns false when singleSearchAfter throws an exception', async () => { + const sampleParams = sampleSignalAlertParams(10); + mockService.callCluster + .mockReturnValueOnce({ + took: 100, + errors: false, + items: [ + { + fakeItemValue: 'fakeItemKey', + }, + ], + }) + .mockRejectedValueOnce(Error('Fake Error')); + const result = await searchAfterAndBulkIndex( + repeatedSearchResultsWithSortId(4), + sampleParams, + mockService, + mockLogger + ); + expect(result).toEqual(false); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.ts index a514baa186fd2..509181f915f55 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.ts @@ -5,6 +5,7 @@ */ import { performance } from 'perf_hooks'; import { SignalHit } from '../../types'; +import { DEFAULT_SIGNALS_INDEX } from '../../../../common/constants'; import { Logger } from '../../../../../../../../src/core/server'; import { AlertServices } from '../../../../../alerting/server/types'; import { SignalSourceHit, SignalSearchResponse, SignalAlertParams, BulkResponse } from './types'; @@ -48,7 +49,7 @@ export const singleBulkIndex = async ( const bulkBody = sr.hits.hits.flatMap(doc => [ { index: { - _index: process.env.SIGNALS_INDEX || '.siem-signals-10-01-2019', + _index: process.env.SIGNALS_INDEX || DEFAULT_SIGNALS_INDEX, _id: doc._id, }, }, @@ -56,7 +57,7 @@ export const singleBulkIndex = async ( ]); const time1 = performance.now(); const firstResult: BulkResponse = await service.callCluster('bulk', { - index: process.env.SIGNALS_INDEX || '.siem-signals-10-01-2019', + index: process.env.SIGNALS_INDEX || DEFAULT_SIGNALS_INDEX, refresh: false, body: bulkBody, }); @@ -64,7 +65,7 @@ export const singleBulkIndex = async ( logger.debug(`individual bulk process time took: ${time2 - time1} milliseconds`); logger.debug(`took property says bulk took: ${firstResult.took} milliseconds`); if (firstResult.errors) { - logger.error(`[-] bulkResponse had errors: ${JSON.stringify(firstResult.errors, null, 2)}}`); + logger.error(`[-] bulkResponse had errors: ${JSON.stringify(firstResult.errors, null, 2)}`); return false; } return true; @@ -89,7 +90,10 @@ export const singleSearchAfter = async ( size: params.size ? params.size : 1000, searchAfterSortId, }); - const nextSearchAfterResult = await service.callCluster('search', searchAfterQuery); + const nextSearchAfterResult: SignalSearchResponse = await service.callCluster( + 'search', + searchAfterQuery + ); return nextSearchAfterResult; } catch (exc) { logger.error(`[-] nextSearchAfter threw an error ${exc}`); @@ -117,11 +121,18 @@ export const searchAfterAndBulkIndex = async ( const totalHits = typeof someResult.hits.total === 'number' ? someResult.hits.total : someResult.hits.total.value; - let size = someResult.hits.hits.length - 1; - logger.debug(`first size: ${size}`); + // maxTotalHitsSize represents the total number of docs to + // query for. If maxSignals is present we will only query + // up to max signals - otherwise use the value + // from track_total_hits. + const maxTotalHitsSize = params.maxSignals ? params.maxSignals : totalHits; + + // number of docs in the current search result + let hitsSize = someResult.hits.hits.length; + logger.debug(`first size: ${hitsSize}`); let sortIds = someResult.hits.hits[0].sort; if (sortIds == null && totalHits > 0) { - logger.error(`sortIds was empty on first search when encountering ${totalHits}`); + logger.error('sortIds was empty on first search but expected more'); return false; } else if (sortIds == null && totalHits === 0) { return true; @@ -130,8 +141,7 @@ export const searchAfterAndBulkIndex = async ( if (sortIds != null) { sortId = sortIds[0]; } - while (size < totalHits) { - // utilize track_total_hits instead of true + while (hitsSize < maxTotalHitsSize && hitsSize !== 0) { try { logger.debug(`sortIds: ${sortIds}`); const searchAfterResult: SignalSearchResponse = await singleSearchAfter( @@ -140,12 +150,13 @@ export const searchAfterAndBulkIndex = async ( service, logger ); - size += searchAfterResult.hits.hits.length - 1; - logger.debug(`size adjusted: ${size}`); + sortIds = searchAfterResult.hits.hits[0].sort; + hitsSize += searchAfterResult.hits.hits.length; + logger.debug(`size adjusted: ${hitsSize}`); sortIds = searchAfterResult.hits.hits[0].sort; if (sortIds == null) { - logger.error('sortIds was empty search when running a signal rule'); - return false; + logger.debug('sortIds was empty on search'); + return true; // no more search results } sortId = sortIds[0]; logger.debug('next bulk index'); From fbe479af6e20e29ef8503ad02c194456dd33d7dc Mon Sep 17 00:00:00 2001 From: Frank Hassanabad Date: Sat, 16 Nov 2019 10:52:50 -0700 Subject: [PATCH 04/17] [SIEM][Detection Engine] REST API improvements and changes from UI/UX feedback (#50797) (#50851) ## Summary Updated REST API from feedback from the UI/UX * Changes the `id` to be `rule_id` on PUT/POST and makes it optional for a POST (create). * On data return sets both `id` and `rule_id` is returned. If `rule_id` is not set, a uuid.v4() will b assigned to the rule_id and the value will be returned. * Transforms output of all endpoints to be 1-1 to the input. * Fixes delete to return the deleted rule * Changes the URL to be `/api/detection_engine/rules` * Changes the POST behavior to fail with a `409 (conflict)` if the rule already exists (For creates) * Changes the POST behavior where sending in a `rule_id` is now optional. If none are sent in it does not create a `rule_id` and instead returns `null` for the `rule_id` and the autogenerated one. * Changes the PUT behavior to fail with a `404 (not found)` if the rule does not already exist (For updates) * Deletes the actions code and just uses an empty array since we don't have actions yet * Makes all error conditions consistent and does not expose the underlying error codes. Only exception to the rule is if an error condition returns non `404` or something unexpected. In which case it will show that error upstream. Example post output: ```ts $ ./post_signal.sh { "created_by": "elastic", "description": "Detecting root and admin users", "enabled": true, "false_positives": [], "from": "now-6m", "id": "8277a0e8-474c-4507-9c11-5f197b5fe2d5", "index": [ "auditbeat-*", "filebeat-*", "packetbeat-*", "winlogbeat-*" ], "interval": "5m", "rule_id": "rule-1", "language": "kuery", "max_signals": 100, "name": "Detect Root/Admin Users", "query": "user.name: root or user.name: admin", "references": [ "http://www.example.com", "https://ww.example.com" ], "severity": "high", "updated_by": "elastic", "tags": [], "to": "now", "type": "query" } ``` Example delete and get URL's now (see scripts for more details): ```ts ${KIBANA_URL}/api/detection_engine/rules?rule_id="rule-1" ${KIBANA_URL}/api/detection_engine/rules?id="04128c15-0d1b-4716-a4c5-46997ac7f3bd" ``` ### Checklist Use ~~strikethroughs~~ to remove checklist items you don't feel are applicable to this PR. ~~- [ ] This was checked for cross-browser compatibility, [including a check against IE11](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility)~~ ~~- [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/master/packages/kbn-i18n/README.md)~~ ~~- [ ] [Documentation](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#writing-documentation) was added for features that require explanation or tutorials~~ - [x] [Unit or functional tests](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#cross-browser-compatibility) were updated or added to match the most common scenarios ~~- [ ] This was checked for [keyboard-only and screenreader accessibility](https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility#Accessibility_testing_checklist)~~ ### For maintainers ~~- [ ] This was checked for breaking API changes and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process)~~ - [x] This includes a feature addition or change that requires a release note and was [labeled appropriately](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md#release-notes-process) --- .../legacy/plugins/siem/common/constants.ts | 6 + .../convert_saved_search_to_signals.js | 2 +- .../server/lib/detection_engine/README.md | 33 +- .../alerts/__mocks__/es_results.ts | 4 +- .../alerts/build_events_reindex.ts | 5 +- .../detection_engine/alerts/create_signals.ts | 176 ++------ .../detection_engine/alerts/delete_signals.ts | 46 +-- .../lib/detection_engine/alerts/get_filter.ts | 12 +- .../alerts/read_signals.test.ts | 238 +++++++++++ .../detection_engine/alerts/read_signals.ts | 41 +- .../alerts/signals_alert_type.ts | 34 +- .../lib/detection_engine/alerts/types.ts | 57 ++- .../detection_engine/alerts/update_signals.ts | 8 +- .../lib/detection_engine/alerts/utils.test.ts | 39 +- .../lib/detection_engine/alerts/utils.ts | 21 +- .../routes/__mocks__/request_responses.ts | 137 +++--- .../routes/create_signals_route.test.ts | 30 +- .../routes/create_signals_route.ts | 21 +- .../routes/delete_signals_route.test.ts | 35 +- .../routes/delete_signals_route.ts | 20 +- .../routes/find_signals_route.test.ts | 6 +- .../routes/find_signals_route.ts | 7 +- .../routes/read_signals_route.test.ts | 16 +- .../routes/read_signals_route.ts | 19 +- .../detection_engine/routes/schemas.test.ts | 389 ++++++++++++++---- .../lib/detection_engine/routes/schemas.ts | 11 +- .../routes/update_signals_route.test.ts | 82 ++-- .../routes/update_signals_route.ts | 25 +- .../lib/detection_engine/routes/utils.test.ts | 268 ++++++++++++ .../lib/detection_engine/routes/utils.ts | 73 ++++ ...elete_signal.sh => delete_signal_by_id.sh} | 4 +- .../scripts/delete_signal_by_rule_id.sh | 16 + .../detection_engine/scripts/find_signals.sh | 2 +- .../{get_signal.sh => get_signal_by_id.sh} | 4 +- .../scripts/get_signal_by_rule_id.sh | 15 + .../detection_engine/scripts/post_signal.sh | 2 +- .../scripts/post_x_signals.sh | 6 +- .../scripts/signals/root_or_admin_1.json | 2 +- .../scripts/signals/root_or_admin_10.json | 13 + .../scripts/signals/root_or_admin_2.json | 2 +- .../scripts/signals/root_or_admin_3.json | 2 +- .../scripts/signals/root_or_admin_4.json | 2 +- .../scripts/signals/root_or_admin_5.json | 2 +- .../scripts/signals/root_or_admin_6.json | 2 +- .../scripts/signals/root_or_admin_7.json | 2 +- .../scripts/signals/root_or_admin_8.json | 2 +- .../scripts/signals/root_or_admin_9.json | 2 +- .../signals/root_or_admin_filter_9998.json | 2 +- .../signals/root_or_admin_filter_9999.json | 2 +- .../signals/root_or_admin_saved_query_1.json | 2 +- .../signals/root_or_admin_update_1.json | 4 +- .../signals/root_or_admin_update_2.json | 2 +- .../scripts/signals/watch_longmont.json | 2 +- .../detection_engine/scripts/update_signal.sh | 27 +- .../lib/detection_engine/signals_mapping.json | 3 + .../legacy/plugins/siem/server/lib/types.ts | 3 +- 56 files changed, 1430 insertions(+), 558 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/read_signals.test.ts create mode 100644 x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts create mode 100644 x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts rename x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/{delete_signal.sh => delete_signal_by_id.sh} (77%) create mode 100755 x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/delete_signal_by_rule_id.sh rename x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/{get_signal.sh => get_signal_by_id.sh} (76%) create mode 100755 x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_signal_by_rule_id.sh create mode 100644 x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_10.json diff --git a/x-pack/legacy/plugins/siem/common/constants.ts b/x-pack/legacy/plugins/siem/common/constants.ts index 5c3bc8ab5b309..4738c17e157b2 100644 --- a/x-pack/legacy/plugins/siem/common/constants.ts +++ b/x-pack/legacy/plugins/siem/common/constants.ts @@ -31,3 +31,9 @@ export const DEFAULT_INTERVAL_VALUE = 300000; // ms * Id for the SIGNALS alerting type */ export const SIGNALS_ID = `${APP_ID}.signals`; + +/** + * Detection engine route + */ +export const DETECTION_ENGINE_URL = '/api/detection_engine'; +export const DETECTION_ENGINE_RULES_URL = `${DETECTION_ENGINE_URL}/rules`; diff --git a/x-pack/legacy/plugins/siem/scripts/convert_saved_search_to_signals.js b/x-pack/legacy/plugins/siem/scripts/convert_saved_search_to_signals.js index e6dcefd6fb4f8..a1889a400a183 100644 --- a/x-pack/legacy/plugins/siem/scripts/convert_saved_search_to_signals.js +++ b/x-pack/legacy/plugins/siem/scripts/convert_saved_search_to_signals.js @@ -118,7 +118,7 @@ async function main() { if (query != null && query.trim() !== '') { const outputMessage = { - id: fileToWrite, + rule_id: fileToWrite, description: description || title, immutable: IMMUTABLE, index: INDEX, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/README.md b/x-pack/legacy/plugins/siem/server/lib/detection_engine/README.md index 2c635a17ef583..5b2318e15a469 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/README.md +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/README.md @@ -5,7 +5,7 @@ See these two other pages for references: https://github.com/elastic/kibana/blob/master/x-pack/legacy/plugins/alerting/README.md https://github.com/elastic/kibana/tree/master/x-pack/legacy/plugins/actions -Since there is no UI yet and a lot of backend areas that are not created, you +Since there is no UI yet and a lot of backend areas that are not created, you should install the kbn-action and kbn-alert project from here: https://github.com/pmuellr/kbn-action @@ -18,6 +18,7 @@ brew install jq ``` Open up your .zshrc/.bashrc and add these lines with the variables filled in: + ``` export ELASTICSEARCH_USERNAME=${user} export ELASTICSEARCH_PASSWORD=${password} @@ -41,6 +42,7 @@ export USE_REINDEX_API=true ``` Add these lines to your `kibana.dev.yml` to turn on the feature toggles of alerting and actions: + ``` # Feature flag to turn on alerting xpack.alerting.enabled: true @@ -64,6 +66,7 @@ while commenting out the other require statement: ``` Restart Kibana and you should see alerting and actions starting up + ``` server log [22:05:22.277] [info][status][plugin:alerting@8.0.0] Status changed from uninitialized to green - Ready server log [22:05:22.270] [info][status][plugin:actions@8.0.0] Status changed from uninitialized to green - Ready @@ -84,12 +87,12 @@ Open a terminal and go into the scripts folder `cd kibana/x-pack/legacy/plugins/ which will: -* Delete any existing actions you have -* Delete any existing alerts you have -* Delete any existing alert tasks you have -* Delete any existing signal mapping you might have had. -* Add the latest signal index and its mappings -* Posts a sample signal which checks for root or admin every 5 minutes +- Delete any existing actions you have +- Delete any existing alerts you have +- Delete any existing alert tasks you have +- Delete any existing signal mapping you might have had. +- Add the latest signal index and its mappings +- Posts a sample signal which checks for root or admin every 5 minutes Now you can run @@ -98,20 +101,13 @@ Now you can run ``` You should see the new alert instance created like so: + ```ts { "id": "908a6af1-ac63-4d52-a856-fc635a00db0f", "alertTypeId": "siem.signals", "interval": "5m", - "actions": [ - { - "group": "default", - "params": { - "message": "SIEM Alert Fired" - }, - "id": "7edd7e98-9286-4fdb-a5c5-16de776bc7c7" - } - ], + "actions": [ ], "alertTypeParams": {}, "enabled": true, "throttle": null, @@ -128,13 +124,13 @@ Every 5 minutes you should see this message in your terminal now: server log [22:17:33.945] [info][alerting] SIEM Alert Fired ``` -See the scripts folder and the tools for more command line fun. +See the scripts folder and the tools for more command line fun. Add the `.siem-signals-${your user id}` to your advanced SIEM settings to see any signals created which should update once every 5 minutes at this point. Also add the `.siem-signals-${your user id}` as a kibana index for Maps to be able to see the -signals +signals Optionally you can add these debug statements to your `kibana.dev.yml` to see more information when running the detection engine @@ -149,4 +145,3 @@ logging.events: ops: __no-ops__, } ``` - diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/__mocks__/es_results.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/__mocks__/es_results.ts index 895af32cc7af3..76df961535fcd 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/__mocks__/es_results.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/__mocks__/es_results.ts @@ -7,7 +7,7 @@ import { SignalSourceHit, SignalSearchResponse, SignalAlertParams } from '../types'; export const sampleSignalAlertParams = (maxSignals: number | undefined): SignalAlertParams => ({ - id: 'rule-1', + ruleId: 'rule-1', description: 'Detecting root and admin users', falsePositives: [], immutable: false, @@ -148,3 +148,5 @@ export const sampleDocSearchResultsWithSortId: SignalSearchResponse = { ], }, }; + +export const sampleSignalId = '04128c15-0d1b-4716-a4c5-46997ac7f3bd'; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/build_events_reindex.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/build_events_reindex.ts index 4c5d981614cf1..0e8d95e4f7ac1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/build_events_reindex.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/build_events_reindex.ts @@ -22,6 +22,7 @@ interface BuildEventsReIndexParams { timeDetected: string; ruleRevision: number; id: string; + ruleId: string | undefined | null; type: string; references: string[]; } @@ -39,6 +40,7 @@ export const buildEventsReIndex = ({ timeDetected, ruleRevision, id, + ruleId, type, references, }: BuildEventsReIndexParams) => { @@ -120,8 +122,9 @@ export const buildEventsReIndex = ({ ]; def signal = [ + "id": "${id}", "rule_revision": "${ruleRevision}", - "rule_id": "${id}", + "rule_id": "${ruleId}", "rule_type": "${type}", "parent": parent, "name": "${name}", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/create_signals.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/create_signals.ts index 73e3d96f5332e..d8284c3203682 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/create_signals.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/create_signals.ts @@ -5,75 +5,11 @@ */ import { SIGNALS_ID } from '../../../../common/constants'; -import { updateSignal } from './update_signals'; import { SignalParams } from './types'; -// TODO: This updateIfIdExists should be temporary and we will remove it once we can POST id's directly to -// the alerting framework. -export const updateIfIdExists = async ({ - alertsClient, - actionsClient, - description, - enabled, - falsePositives, - filter, - from, - immutable, - query, - language, - savedId, - filters, - id, - index, - interval, - maxSignals, - name, - severity, - size, - tags, - to, - type, - references, -}: SignalParams) => { - try { - const signal = await updateSignal({ - alertsClient, - actionsClient, - description, - enabled, - falsePositives, - filter, - from, - immutable, - query, - language, - savedId, - filters, - id, - index, - interval, - maxSignals, - name, - severity, - size, - tags, - to, - type, - references, - }); - return signal; - } catch (err) { - if (err.output.statusCode === 404) { - return null; - } else { - return err; - } - } -}; - export const createSignals = async ({ alertsClient, - actionsClient, + actionsClient, // TODO: Use this actionsClient once we have actions such as email, etc... description, enabled, falsePositives, @@ -83,7 +19,7 @@ export const createSignals = async ({ language, savedId, filters, - id, + ruleId, immutable, index, interval, @@ -96,85 +32,33 @@ export const createSignals = async ({ type, references, }: SignalParams) => { - // TODO: Once we can post directly to _id we will not have to do this part anymore. - const signalUpdating = await updateIfIdExists({ - alertsClient, - actionsClient, - description, - enabled, - falsePositives, - filter, - from, - query, - language, - savedId, - filters, - id, - immutable, - index, - interval, - maxSignals, - name, - severity, - size, - tags, - to, - type, - references, - }); - if (signalUpdating == null) { - // TODO: Right now we are using the .server-log as the default action as each alert has to have - // at least one action or it will not be able to do in-memory persistence. When adding in actions - // such as email, slack, etc... this should be the default action if no action is specified to - // create signals - const actionResults = await actionsClient.create({ - action: { - actionTypeId: '.server-log', - description: 'SIEM Alerts Log', - config: {}, - secrets: {}, - }, - }); - - return alertsClient.create({ - data: { - name, - alertTypeId: SIGNALS_ID, - alertTypeParams: { - description, - id, - index, - falsePositives, - from, - filter, - immutable, - query, - language, - savedId, - filters, - maxSignals, - severity, - tags, - to, - type, - references, - }, - interval, - enabled, - actions: [ - { - group: 'default', - id: actionResults.id, - params: { - message: 'SIEM Alert Fired', - level: 'info', - }, - }, - ], - throttle: null, + return alertsClient.create({ + data: { + name, + alertTypeId: SIGNALS_ID, + alertTypeParams: { + description, + ruleId, + index, + falsePositives, + from, + filter, + immutable, + query, + language, + savedId, + filters, + maxSignals, + severity, + tags, + to, + type, + references, }, - }); - } else { - return signalUpdating; - } + interval, + enabled, + actions: [], // TODO: Create and add actions here once we have email, etc... + throttle: null, + }, + }); }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/delete_signals.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/delete_signals.ts index 7a69c11ecf2e5..d89895772f1ef 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/delete_signals.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/delete_signals.ts @@ -4,35 +4,35 @@ * you may not use this file except in compliance with the Elastic License. */ -import { AlertAction } from '../../../../../alerting/server/types'; -import { ActionsClient } from '../../../../../actions/server/actions_client'; import { readSignals } from './read_signals'; import { DeleteSignalParams } from './types'; -export const deleteAllSignalActions = async ( - actionsClient: ActionsClient, - actions: AlertAction[] -): Promise => { - try { - await Promise.all(actions.map(async ({ id }) => actionsClient.delete({ id }))); +export const deleteSignals = async ({ + alertsClient, + actionsClient, // TODO: Use this when we have actions such as email, etc... + id, + ruleId, +}: DeleteSignalParams) => { + const signal = await readSignals({ alertsClient, id, ruleId }); + if (signal == null) { return null; - } catch (error) { - return error; } -}; - -export const deleteSignals = async ({ alertsClient, actionsClient, id }: DeleteSignalParams) => { - const signal = await readSignals({ alertsClient, id }); - // TODO: Remove this as cast as soon as signal.actions TypeScript bug is fixed - // where it is trying to return AlertAction[] or RawAlertAction[] - const actions = (signal.actions as AlertAction[] | undefined) || []; - - const actionsErrors = await deleteAllSignalActions(actionsClient, actions); - const deletedAlert = await alertsClient.delete({ id: signal.id }); - if (actionsErrors != null) { - throw actionsErrors; + if (ruleId != null) { + await alertsClient.delete({ id: signal.id }); + return signal; + } else if (id != null) { + try { + await alertsClient.delete({ id }); + return signal; + } catch (err) { + if (err.output.statusCode === 404) { + return null; + } else { + throw err; + } + } } else { - return deletedAlert; + return null; } }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/get_filter.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/get_filter.ts index ae79e9bc01480..bc5d11d70586d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/get_filter.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/get_filter.ts @@ -42,13 +42,13 @@ export const getQueryFilter = ( interface GetFilterArgs { type: SignalAlertParams['type']; - filter: Record | undefined; - filters: PartialFilter[] | undefined; - language: string | undefined; - query: string | undefined; - savedId: string | undefined; + filter: Record | undefined | null; + filters: PartialFilter[] | undefined | null; + language: string | undefined | null; + query: string | undefined | null; + savedId: string | undefined | null; services: AlertServices; - index: string[] | undefined; + index: string[] | undefined | null; } export const getFilter = async ({ diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/read_signals.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/read_signals.test.ts new file mode 100644 index 0000000000000..dde3f19b1c66d --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/read_signals.test.ts @@ -0,0 +1,238 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { alertsClientMock } from '../../../../../alerting/server/alerts_client.mock'; +import { readSignals, readSignalByRuleId, findSignalInArrayByRuleId } from './read_signals'; +import { AlertsClient } from '../../../../../alerting'; +import { + getResult, + getFindResultWithSingleHit, + getFindResultWithMultiHits, +} from '../routes/__mocks__/request_responses'; +import { SIGNALS_ID } from '../../../../common/constants'; + +describe('read_signals', () => { + describe('readSignals', () => { + test('should return the output from alertsClient if id is set but ruleId is undefined', async () => { + const alertsClient = alertsClientMock.create(); + alertsClient.get.mockResolvedValue(getResult()); + + const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; + const signal = await readSignals({ + alertsClient: unsafeCast, + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + ruleId: undefined, + }); + expect(signal).toEqual(getResult()); + }); + + test('should return the output from alertsClient if id is set but ruleId is null', async () => { + const alertsClient = alertsClientMock.create(); + alertsClient.get.mockResolvedValue(getResult()); + + const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; + const signal = await readSignals({ + alertsClient: unsafeCast, + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + ruleId: null, + }); + expect(signal).toEqual(getResult()); + }); + + test('should return the output from alertsClient if id is undefined but ruleId is set', async () => { + const alertsClient = alertsClientMock.create(); + alertsClient.get.mockResolvedValue(getResult()); + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + + const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; + const signal = await readSignals({ + alertsClient: unsafeCast, + id: undefined, + ruleId: 'rule-1', + }); + expect(signal).toEqual(getResult()); + }); + + test('should return the output from alertsClient if id is null but ruleId is set', async () => { + const alertsClient = alertsClientMock.create(); + alertsClient.get.mockResolvedValue(getResult()); + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + + const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; + const signal = await readSignals({ + alertsClient: unsafeCast, + id: null, + ruleId: 'rule-1', + }); + expect(signal).toEqual(getResult()); + }); + + test('should return null if id and ruleId are null', async () => { + const alertsClient = alertsClientMock.create(); + alertsClient.get.mockResolvedValue(getResult()); + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + + const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; + const signal = await readSignals({ + alertsClient: unsafeCast, + id: null, + ruleId: null, + }); + expect(signal).toEqual(null); + }); + + test('should return null if id and ruleId are undefined', async () => { + const alertsClient = alertsClientMock.create(); + alertsClient.get.mockResolvedValue(getResult()); + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + + const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; + const signal = await readSignals({ + alertsClient: unsafeCast, + id: undefined, + ruleId: undefined, + }); + expect(signal).toEqual(null); + }); + }); + + describe('readSignalByRuleId', () => { + test('should return a single value if the rule id matches', async () => { + const alertsClient = alertsClientMock.create(); + alertsClient.get.mockResolvedValue(getResult()); + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + + const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; + const signal = await readSignalByRuleId({ + alertsClient: unsafeCast, + ruleId: 'rule-1', + }); + expect(signal).toEqual(getResult()); + }); + + test('should not return a single value if the rule id does not match', async () => { + const alertsClient = alertsClientMock.create(); + alertsClient.get.mockResolvedValue(getResult()); + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + + const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; + const signal = await readSignalByRuleId({ + alertsClient: unsafeCast, + ruleId: 'rule-that-should-not-match-anything', + }); + expect(signal).toEqual(null); + }); + + test('should return a single value of rule-1 with multiple values', async () => { + const result1 = getResult(); + result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; + result1.alertTypeParams.ruleId = 'rule-1'; + + const result2 = getResult(); + result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; + result2.alertTypeParams.ruleId = 'rule-2'; + + const alertsClient = alertsClientMock.create(); + alertsClient.get.mockResolvedValue(getResult()); + alertsClient.find.mockResolvedValue(getFindResultWithMultiHits([result1, result2])); + + const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; + const signal = await readSignalByRuleId({ + alertsClient: unsafeCast, + ruleId: 'rule-1', + }); + expect(signal).toEqual(result1); + }); + + test('should return a single value of rule-2 with multiple values', async () => { + const result1 = getResult(); + result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; + result1.alertTypeParams.ruleId = 'rule-1'; + + const result2 = getResult(); + result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; + result2.alertTypeParams.ruleId = 'rule-2'; + + const alertsClient = alertsClientMock.create(); + alertsClient.get.mockResolvedValue(getResult()); + alertsClient.find.mockResolvedValue(getFindResultWithMultiHits([result1, result2])); + + const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; + const signal = await readSignalByRuleId({ + alertsClient: unsafeCast, + ruleId: 'rule-2', + }); + expect(signal).toEqual(result2); + }); + + test('should return null for a made up value with multiple values', async () => { + const result1 = getResult(); + result1.id = '4baa53f8-96da-44ee-ad58-41bccb7f9f3d'; + result1.alertTypeParams.ruleId = 'rule-1'; + + const result2 = getResult(); + result2.id = '5baa53f8-96da-44ee-ad58-41bccb7f9f3d'; + result2.alertTypeParams.ruleId = 'rule-2'; + + const alertsClient = alertsClientMock.create(); + alertsClient.get.mockResolvedValue(getResult()); + alertsClient.find.mockResolvedValue(getFindResultWithMultiHits([result1, result2])); + + const unsafeCast: AlertsClient = (alertsClient as unknown) as AlertsClient; + const signal = await readSignalByRuleId({ + alertsClient: unsafeCast, + ruleId: 'rule-that-should-not-match-anything', + }); + expect(signal).toEqual(null); + }); + }); + + describe('findSignalInArrayByRuleId', () => { + test('returns null if the objects are not of a signal rule type', () => { + const signal = findSignalInArrayByRuleId( + [ + { alertTypeId: 'made up 1', alertTypeParams: { ruleId: '123' } }, + { alertTypeId: 'made up 2', alertTypeParams: { ruleId: '456' } }, + ], + '123' + ); + expect(signal).toEqual(null); + }); + + test('returns correct type if the objects are of a signal rule type', () => { + const signal = findSignalInArrayByRuleId( + [ + { alertTypeId: SIGNALS_ID, alertTypeParams: { ruleId: '123' } }, + { alertTypeId: 'made up 2', alertTypeParams: { ruleId: '456' } }, + ], + '123' + ); + expect(signal).toEqual({ alertTypeId: 'siem.signals', alertTypeParams: { ruleId: '123' } }); + }); + + test('returns second correct type if the objects are of a signal rule type', () => { + const signal = findSignalInArrayByRuleId( + [ + { alertTypeId: SIGNALS_ID, alertTypeParams: { ruleId: '123' } }, + { alertTypeId: SIGNALS_ID, alertTypeParams: { ruleId: '456' } }, + ], + '456' + ); + expect(signal).toEqual({ alertTypeId: 'siem.signals', alertTypeParams: { ruleId: '456' } }); + }); + + test('returns null with correct types but data does not exist', () => { + const signal = findSignalInArrayByRuleId( + [ + { alertTypeId: SIGNALS_ID, alertTypeParams: { ruleId: '123' } }, + { alertTypeId: SIGNALS_ID, alertTypeParams: { ruleId: '456' } }, + ], + '892' + ); + expect(signal).toEqual(null); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/read_signals.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/read_signals.ts index 0ef13f39e793b..f73074b560cb2 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/read_signals.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/read_signals.ts @@ -5,13 +5,16 @@ */ import { findSignals } from './find_signals'; -import { SignalAlertType, isAlertTypeArray, ReadSignalParams } from './types'; +import { SignalAlertType, isAlertTypeArray, ReadSignalParams, ReadSignalByRuleId } from './types'; -export const findSignalInArrayById = (objects: object[], id: string): SignalAlertType | null => { +export const findSignalInArrayByRuleId = ( + objects: object[], + ruleId: string +): SignalAlertType | null => { if (isAlertTypeArray(objects)) { const signals: SignalAlertType[] = objects; const signal: SignalAlertType[] = signals.filter(datum => { - return datum.alertTypeParams.id === id; + return datum.alertTypeParams.ruleId === ruleId; }); if (signal.length !== 0) { return signal[0]; @@ -28,12 +31,12 @@ export const findSignalInArrayById = (objects: object[], id: string): SignalAler // not indexed and I cannot push in my own _id when I create an alert at the moment. // TODO: Once we can directly push in the _id, then we should no longer need this way. // TODO: This is meant to be _very_ temporary. -export const readSignalById = async ({ +export const readSignalByRuleId = async ({ alertsClient, - id, -}: ReadSignalParams): Promise => { + ruleId, +}: ReadSignalByRuleId): Promise => { const firstSignals = await findSignals({ alertsClient, page: 1 }); - const firstSignal = findSignalInArrayById(firstSignals.data, id); + const firstSignal = findSignalInArrayByRuleId(firstSignals.data, ruleId); if (firstSignal != null) { return firstSignal; } else { @@ -46,7 +49,7 @@ export const readSignalById = async ({ }) .reduce>(async (accum, findSignal) => { const signals = await findSignal; - const signal = findSignalInArrayById(signals.data, id); + const signal = findSignalInArrayByRuleId(signals.data, ruleId); if (signal != null) { return signal; } else { @@ -56,11 +59,23 @@ export const readSignalById = async ({ } }; -export const readSignals = async ({ alertsClient, id }: ReadSignalParams) => { - const signalById = await readSignalById({ alertsClient, id }); - if (signalById != null) { - return signalById; +export const readSignals = async ({ alertsClient, id, ruleId }: ReadSignalParams) => { + if (id != null) { + try { + const output = await alertsClient.get({ id }); + return output; + } catch (err) { + if (err.output.statusCode === 404) { + return null; + } else { + // throw non-404 as they would be 500 or other internal errors + throw err; + } + } + } else if (ruleId != null) { + return readSignalByRuleId({ alertsClient, ruleId }); } else { - return alertsClient.get({ id }); + // should never get here, and yet here we are. + return null; } }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/signals_alert_type.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/signals_alert_type.ts index be763f2b5386d..0f15cf8fcf5f4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/signals_alert_type.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/signals_alert_type.ts @@ -26,7 +26,7 @@ export const signalsAlertType = ({ logger }: { logger: Logger }): SignalAlertTyp falsePositives: schema.arrayOf(schema.string(), { defaultValue: [] }), from: schema.string(), filter: schema.nullable(schema.object({}, { allowUnknowns: true })), - id: schema.string(), + ruleId: schema.string(), immutable: schema.boolean({ defaultValue: false }), index: schema.arrayOf(schema.string()), language: schema.nullable(schema.string()), @@ -42,19 +42,18 @@ export const signalsAlertType = ({ logger }: { logger: Logger }): SignalAlertTyp size: schema.maybe(schema.number()), }), }, - async executor({ services, params }) { + async executor({ alertId, services, params }) { const { description, filter, from, - id, + ruleId, index, filters, language, savedId, query, maxSignals, - name, references, severity, to, @@ -62,6 +61,9 @@ export const signalsAlertType = ({ logger }: { logger: Logger }): SignalAlertTyp size, } = params; + // TODO: Remove this hard extraction of name once this is fixed: https://github.com/elastic/kibana/issues/50522 + const savedObject = await services.savedObjectsClient.get('alert', alertId); + const name = savedObject.attributes.name; const searchAfterSize = size ? size : 1000; const esFilter = await getFilter({ @@ -85,7 +87,7 @@ export const signalsAlertType = ({ logger }: { logger: Logger }): SignalAlertTyp }); try { - logger.debug(`Starting signal rule "${id}"`); + logger.debug(`Starting signal rule "id: ${alertId}", "ruleId: ${ruleId}"`); if (process.env.USE_REINDEX_API === 'true') { const reIndex = buildEventsReIndex({ index, @@ -101,22 +103,25 @@ export const signalsAlertType = ({ logger }: { logger: Logger }): SignalAlertTyp filter: esFilter, maxDocs: maxSignals, ruleRevision: 1, - id, + id: alertId, + ruleId, type, references, }); const result = await services.callCluster('reindex', reIndex); if (result.total > 0) { logger.info( - `Total signals found from signal rule "${id}" (reindex algorithm): ${result.total}` + `Total signals found from signal rule "id: ${alertId}", "ruleId: ${ruleId}" (reindex algorithm): ${result.total}` ); } } else { - logger.debug(`[+] Initial search call of signal rule "${id}"`); + logger.debug( + `[+] Initial search call of signal rule "id: ${alertId}", "ruleId: ${ruleId}"` + ); const noReIndexResult = await services.callCluster('search', noReIndex); if (noReIndexResult.hits.total.value !== 0) { logger.info( - `Total signals found from signal rule "${id}": ${noReIndexResult.hits.total.value}` + `Total signals found from signal rule "id: ${alertId}", "ruleId: ${ruleId}": ${noReIndexResult.hits.total.value}` ); } @@ -124,19 +129,22 @@ export const signalsAlertType = ({ logger }: { logger: Logger }): SignalAlertTyp noReIndexResult, params, services, - logger + logger, + alertId ); if (bulkIndexResult) { - logger.debug(`Finished signal rule "${id}"`); + logger.debug(`Finished signal rule "id: ${alertId}", "ruleId: ${ruleId}"`); } else { - logger.error(`Error processing signal rule "${id}"`); + logger.error(`Error processing signal rule "id: ${alertId}", "ruleId: ${ruleId}"`); } } } catch (err) { // TODO: Error handling and writing of errors into a signal that has error // handling/conditions - logger.error(`Error from signal rule "${id}", ${err.message}`); + logger.error( + `Error from signal rule "id: ${alertId}", "ruleId: ${ruleId}", ${err.message}` + ); } }, }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/types.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/types.ts index 35561165859b1..d7a02c6698b27 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/types.ts @@ -25,21 +25,21 @@ export interface SignalAlertParams { description: string; enabled: boolean; falsePositives: string[]; - filter: Record | undefined; - filters: PartialFilter[] | undefined; + filter: Record | undefined | null; + filters: PartialFilter[] | undefined | null; from: string; immutable: boolean; index: string[]; interval: string; - id: string; - language: string | undefined; + ruleId: string | undefined | null; + language: string | undefined | null; maxSignals: number; name: string; - query: string | undefined; + query: string | undefined | null; references: string[]; - savedId: string | undefined; + savedId: string | undefined | null; severity: string; - size: number | undefined; + size: number | undefined | null; tags: string[]; to: string; type: 'filter' | 'query' | 'saved_query'; @@ -47,15 +47,23 @@ export interface SignalAlertParams { export type SignalAlertParamsRest = Omit< SignalAlertParams, - 'falsePositives' | 'maxSignals' | 'saved_id' + 'ruleId' | 'falsePositives' | 'maxSignals' | 'savedId' > & { + rule_id: SignalAlertParams['ruleId']; false_positives: SignalAlertParams['falsePositives']; saved_id: SignalAlertParams['savedId']; max_signals: SignalAlertParams['maxSignals']; }; -export type UpdateSignalAlertParamsRest = Partial> & { - id: SignalAlertParams['id']; +export type OutputSignalAlertRest = SignalAlertParamsRest & { + id: string; + created_by: string | undefined | null; + updated_by: string | undefined | null; +}; + +export type UpdateSignalAlertParamsRest = Partial & { + id: string | undefined; + rule_id: SignalAlertParams['ruleId'] | undefined; }; export interface FindParamsRest { @@ -72,11 +80,14 @@ export interface Clients { export type SignalParams = SignalAlertParams & Clients; -export type UpdateSignalParams = Partial> & { - id: SignalAlertParams['id']; +export type UpdateSignalParams = Partial & { + id: string | undefined | null; } & Clients; -export type DeleteSignalParams = Clients & { id: string }; +export type DeleteSignalParams = Clients & { + id: string | undefined; + ruleId: string | undefined | null; +}; export interface FindSignalsRequest extends Omit { query: { @@ -98,12 +109,20 @@ export interface FindSignalParams { export interface ReadSignalParams { alertsClient: AlertsClient; - id: string; + id?: string | undefined | null; + ruleId?: string | undefined | null; +} + +export interface ReadSignalByRuleId { + alertsClient: AlertsClient; + ruleId: string; } +export type AlertTypeParams = Omit; + export type SignalAlertType = Alert & { id: string; - alertTypeParams: SignalAlertParams; + alertTypeParams: AlertTypeParams; }; export interface SignalsRequest extends Hapi.Request { @@ -145,6 +164,10 @@ export interface BulkResponse { export type SignalSearchResponse = SearchResponse; export type SignalSourceHit = SignalSearchResponse['hits']['hits'][0]; +export type QueryRequest = Omit & { + query: { id: string | undefined; rule_id: string | undefined }; +}; + // This returns true because by default a SignalAlertTypeDefinition is an AlertType // since we are only increasing the strictness of params. export const isAlertExecutor = (obj: SignalAlertTypeDefinition): obj is AlertType => { @@ -155,6 +178,10 @@ export type SignalAlertTypeDefinition = Omit & { executor: ({ services, params, state }: SignalExecutorOptions) => Promise; }; +export const isAlertTypes = (obj: unknown[]): obj is SignalAlertType[] => { + return obj.every(signal => isAlertType(signal)); +}; + export const isAlertType = (obj: unknown): obj is SignalAlertType => { return get('alertTypeId', obj) === SIGNALS_ID; }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/update_signals.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/update_signals.ts index b4639ee5b9ee5..cba5f0e31cfd4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/update_signals.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/update_signals.ts @@ -55,6 +55,7 @@ export const updateSignal = async ({ from, immutable, id, + ruleId, index, interval, maxSignals, @@ -65,9 +66,10 @@ export const updateSignal = async ({ type, references, }: UpdateSignalParams) => { - // TODO: Error handling and abstraction. Right now if this is an error then what happens is we get the error of - // "message": "Saved object [alert/{id}] not found" - const signal = await readSignals({ alertsClient, id }); + const signal = await readSignals({ alertsClient, ruleId, id }); + if (signal == null) { + return null; + } // TODO: Remove this as cast as soon as signal.actions TypeScript bug is fixed // where it is trying to return AlertAction[] or RawAlertAction[] diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.test.ts index c3ffb6e8c230a..a5e6d03a3378b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.test.ts @@ -20,6 +20,7 @@ import { sampleDocSearchResultsWithSortId, sampleEmptyDocSearchResults, repeatedSearchResultsWithSortId, + sampleSignalId, } from './__mocks__/es_results'; const mockLogger: Logger = { @@ -45,14 +46,15 @@ describe('utils', () => { describe('buildBulkBody', () => { test('if bulk body builds well-defined body', () => { const sampleParams = sampleSignalAlertParams(undefined); - const fakeSignalSourceHit = buildBulkBody(sampleDocNoSortId, sampleParams); + const fakeSignalSourceHit = buildBulkBody(sampleDocNoSortId, sampleParams, sampleSignalId); expect(fakeSignalSourceHit).toEqual({ someKey: 'someValue', '@timestamp': 'someTimeStamp', signal: { + id: sampleSignalId, '@timestamp': fakeSignalSourceHit.signal['@timestamp'], // timestamp generated in the body rule_revision: 1, - rule_id: sampleParams.id, + rule_id: sampleParams.ruleId, rule_type: sampleParams.type, parent: { id: sampleDocNoSortId._id, @@ -87,7 +89,8 @@ describe('utils', () => { sampleSearchResult, sampleParams, mockService, - mockLogger + mockLogger, + sampleSignalId ); expect(successfulSingleBulkIndex).toEqual(true); }); @@ -99,7 +102,8 @@ describe('utils', () => { sampleSearchResult, sampleParams, mockService, - mockLogger + mockLogger, + sampleSignalId ); expect(successfulSingleBulkIndex).toEqual(true); }); @@ -115,7 +119,8 @@ describe('utils', () => { sampleSearchResult, sampleParams, mockService, - mockLogger + mockLogger, + sampleSignalId ); expect(mockLogger.error).toHaveBeenCalled(); expect(successfulSingleBulkIndex).toEqual(false); @@ -160,7 +165,8 @@ describe('utils', () => { sampleEmptyDocSearchResults, sampleParams, mockService, - mockLogger + mockLogger, + sampleSignalId ); expect(mockService.callCluster).toHaveBeenCalledTimes(0); expect(result).toEqual(true); @@ -201,7 +207,8 @@ describe('utils', () => { repeatedSearchResultsWithSortId(4), sampleParams, mockService, - mockLogger + mockLogger, + sampleSignalId ); expect(mockService.callCluster).toHaveBeenCalledTimes(5); expect(result).toEqual(true); @@ -216,7 +223,8 @@ describe('utils', () => { repeatedSearchResultsWithSortId(4), sampleParams, mockService, - mockLogger + mockLogger, + sampleSignalId ); expect(mockLogger.error).toHaveBeenCalled(); expect(result).toEqual(false); @@ -237,7 +245,8 @@ describe('utils', () => { sampleDocSearchResultsNoSortId, sampleParams, mockService, - mockLogger + mockLogger, + sampleSignalId ); expect(mockLogger.error).toHaveBeenCalled(); expect(result).toEqual(false); @@ -257,7 +266,8 @@ describe('utils', () => { sampleDocSearchResultsNoSortIdNoHits, sampleParams, mockService, - mockLogger + mockLogger, + sampleSignalId ); expect(result).toEqual(true); }); @@ -278,7 +288,8 @@ describe('utils', () => { repeatedSearchResultsWithSortId(4), sampleParams, mockService, - mockLogger + mockLogger, + sampleSignalId ); expect(result).toEqual(true); }); @@ -300,7 +311,8 @@ describe('utils', () => { repeatedSearchResultsWithSortId(4), sampleParams, mockService, - mockLogger + mockLogger, + sampleSignalId ); expect(mockLogger.error).toHaveBeenCalled(); expect(result).toEqual(true); @@ -322,7 +334,8 @@ describe('utils', () => { repeatedSearchResultsWithSortId(4), sampleParams, mockService, - mockLogger + mockLogger, + sampleSignalId ); expect(result).toEqual(false); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.ts index 509181f915f55..2967f41ffb697 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/alerts/utils.ts @@ -12,13 +12,18 @@ import { SignalSourceHit, SignalSearchResponse, SignalAlertParams, BulkResponse import { buildEventsSearchQuery } from './build_events_query'; // format search_after result for signals index. -export const buildBulkBody = (doc: SignalSourceHit, signalParams: SignalAlertParams): SignalHit => { +export const buildBulkBody = ( + doc: SignalSourceHit, + signalParams: SignalAlertParams, + id: string +): SignalHit => { return { ...doc._source, signal: { '@timestamp': new Date().toISOString(), + id, rule_revision: 1, - rule_id: signalParams.id, + rule_id: signalParams.ruleId, rule_type: signalParams.type, parent: { id: doc._id, @@ -41,7 +46,8 @@ export const singleBulkIndex = async ( sr: SignalSearchResponse, params: SignalAlertParams, service: AlertServices, - logger: Logger + logger: Logger, + id: string ): Promise => { if (sr.hits.hits.length === 0) { return true; @@ -53,7 +59,7 @@ export const singleBulkIndex = async ( _id: doc._id, }, }, - buildBulkBody(doc, params), + buildBulkBody(doc, params, id), ]); const time1 = performance.now(); const firstResult: BulkResponse = await service.callCluster('bulk', { @@ -106,14 +112,15 @@ export const searchAfterAndBulkIndex = async ( someResult: SignalSearchResponse, params: SignalAlertParams, service: AlertServices, - logger: Logger + logger: Logger, + id: string ): Promise => { if (someResult.hits.hits.length === 0) { return true; } logger.debug('[+] starting bulk insertion'); - const firstBulkIndexSuccess = await singleBulkIndex(someResult, params, service, logger); + const firstBulkIndexSuccess = await singleBulkIndex(someResult, params, service, logger, id); if (!firstBulkIndexSuccess) { logger.error('First bulk index was unsuccessful'); return false; @@ -160,7 +167,7 @@ export const searchAfterAndBulkIndex = async ( } sortId = sortIds[0]; logger.debug('next bulk index'); - const bulkSuccess = await singleBulkIndex(searchAfterResult, params, service, logger); + const bulkSuccess = await singleBulkIndex(searchAfterResult, params, service, logger, id); logger.debug('finished next bulk index'); if (!bulkSuccess) { logger.error('[-] bulk index failed but continuing'); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts index 7b3778d606e47..4c2f6a7592a17 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/__mocks__/request_responses.ts @@ -6,13 +6,14 @@ import { ServerInjectOptions } from 'hapi'; import { ActionResult } from '../../../../../../actions/server/types'; -import { SignalAlertParamsRest } from '../../alerts/types'; +import { SignalAlertParamsRest, SignalAlertType } from '../../alerts/types'; +import { DETECTION_ENGINE_RULES_URL } from '../../../../../common/constants'; // The Omit of filter is because of a Hapi Server Typing issue that I am unclear // where it comes from. I would hope to remove the "filter" as an omit at some point // when we upgrade and Hapi Server is ok with the filter. export const typicalPayload = (): Partial> => ({ - id: 'rule-1', + rule_id: 'rule-1', description: 'Detecting root and admin users', index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], interval: '5m', @@ -25,9 +26,22 @@ export const typicalPayload = (): Partial> language: 'kuery', }); +export const typicalFilterPayload = (): Partial => ({ + rule_id: 'rule-1', + description: 'Detecting root and admin users', + index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + interval: '5m', + name: 'Detect Root/Admin Users', + type: 'filter', + from: 'now-6m', + to: 'now', + severity: 'high', + filter: {}, +}); + export const getUpdateRequest = (): ServerInjectOptions => ({ method: 'PUT', - url: '/api/siem/signals', + url: DETECTION_ENGINE_RULES_URL, payload: { ...typicalPayload(), }, @@ -35,29 +49,55 @@ export const getUpdateRequest = (): ServerInjectOptions => ({ export const getReadRequest = (): ServerInjectOptions => ({ method: 'GET', - url: '/api/siem/signals/rule-1', + url: `${DETECTION_ENGINE_RULES_URL}?rule_id=rule-1`, }); export const getFindRequest = (): ServerInjectOptions => ({ method: 'GET', - url: '/api/siem/signals/_find', + url: `${DETECTION_ENGINE_RULES_URL}/_find`, }); -export const getFindResult = () => ({ +interface FindHit { + page: number; + perPage: number; + total: number; + data: SignalAlertType[]; +} + +export const getFindResult = (): FindHit => ({ page: 1, perPage: 1, total: 0, data: [], }); +export const getFindResultWithSingleHit = (): FindHit => ({ + page: 1, + perPage: 1, + total: 0, + data: [getResult()], +}); + +export const getFindResultWithMultiHits = (data: SignalAlertType[]): FindHit => ({ + page: 1, + perPage: 1, + total: 2, + data, +}); + export const getDeleteRequest = (): ServerInjectOptions => ({ method: 'DELETE', - url: '/api/siem/signals/rule-1', + url: `${DETECTION_ENGINE_RULES_URL}?rule_id=rule-1`, +}); + +export const getDeleteRequestById = (): ServerInjectOptions => ({ + method: 'DELETE', + url: `${DETECTION_ENGINE_RULES_URL}?id=04128c15-0d1b-4716-a4c5-46997ac7f3bd`, }); export const getCreateRequest = (): ServerInjectOptions => ({ method: 'POST', - url: '/api/siem/signals', + url: DETECTION_ENGINE_RULES_URL, payload: { ...typicalPayload(), }, @@ -70,52 +110,40 @@ export const createActionResult = (): ActionResult => ({ config: {}, }); -export const createAlertResult = () => ({ - id: 'rule-1', +export const getResult = (): SignalAlertType => ({ + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + name: 'Detect Root/Admin Users', alertTypeId: 'siem.signals', alertTypeParams: { description: 'Detecting root and admin users', - id: 'rule-1', + ruleId: 'rule-1', index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + falsePositives: [], from: 'now-6m', - filter: null, + filter: undefined, + immutable: false, query: 'user.name: root or user.name: admin', + language: 'kuery', + savedId: undefined, + filters: undefined, maxSignals: 100, - name: 'Detect Root/Admin Users', + size: 1, severity: 'high', + tags: [], to: 'now', type: 'query', - language: 'kuery', - references: [], + references: ['http://www.example.com', 'https://ww.example.com'], }, interval: '5m', enabled: true, - actions: [ - { - group: 'default', - params: { - message: 'SIEM Alert Fired', - level: 'info', - }, - id: '9c3846a3-dbf9-40ce-ba7e-ef635499afa6', - }, - ], + actions: [], throttle: null, createdBy: 'elastic', updatedBy: 'elastic', apiKeyOwner: 'elastic', muteAll: false, mutedInstanceIds: [], - scheduledTaskId: '78d036d0-f042-11e9-a9ae-51b9a11630ec', -}); - -export const getResult = () => ({ - id: 'result-1', - enabled: false, - alertTypeId: '', - interval: undefined, - actions: undefined, - alertTypeParams: undefined, + scheduledTaskId: '2dabe330-0702-11ea-8b50-773b89126888', }); export const updateActionResult = (): ActionResult => ({ @@ -124,42 +152,3 @@ export const updateActionResult = (): ActionResult => ({ description: '', config: {}, }); - -export const updateAlertResult = () => ({ - id: 'rule-1', - alertTypeId: 'siem.signals', - alertTypeParams: { - description: 'Detecting root and admin users', - id: 'rule-1', - index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], - from: 'now-6m', - filter: null, - query: 'user.name: root or user.name: admin', - maxSignals: 100, - name: 'Detect Root/Admin Users', - severity: 'high', - to: 'now', - type: 'query', - language: 'kuery', - references: [], - }, - interval: '5m', - enabled: true, - actions: [ - { - group: 'default', - params: { - message: 'SIEM Alert Fired', - level: 'info', - }, - id: '9c3846a3-dbf9-40ce-ba7e-ef635499afa6', - }, - ], - throttle: null, - createdBy: 'elastic', - updatedBy: 'elastic', - apiKeyOwner: 'elastic', - muteAll: false, - mutedInstanceIds: [], - scheduledTaskId: '78d036d0-f042-11e9-a9ae-51b9a11630ec', -}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/create_signals_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/create_signals_route.test.ts index 0fe88cc856d62..1232fe3ce219d 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/create_signals_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/create_signals_route.test.ts @@ -16,10 +16,10 @@ import { getFindResult, getResult, createActionResult, - createAlertResult, getCreateRequest, typicalPayload, } from './__mocks__/request_responses'; +import { DETECTION_ENGINE_RULES_URL } from '../../../../common/constants'; describe('create_signals', () => { let { server, alertsClient, actionsClient } = createMockServer(); @@ -35,7 +35,7 @@ describe('create_signals', () => { alertsClient.find.mockResolvedValue(getFindResult()); alertsClient.get.mockResolvedValue(getResult()); actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(createAlertResult()); + alertsClient.create.mockResolvedValue(getResult()); const { statusCode } = await server.inject(getCreateRequest()); expect(statusCode).toBe(200); }); @@ -65,31 +65,31 @@ describe('create_signals', () => { }); describe('validation', () => { - test('returns 400 if id is not given', async () => { + test('returns 200 if rule_id is not given as the id is auto generated from the alert framework', async () => { alertsClient.find.mockResolvedValue(getFindResult()); alertsClient.get.mockResolvedValue(getResult()); actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(createAlertResult()); - // missing id should throw a 400 - const { id, ...noId } = typicalPayload(); + alertsClient.create.mockResolvedValue(getResult()); + // missing rule_id should return 200 as it will be auto generated if not given + const { rule_id, ...noRuleId } = typicalPayload(); const request: ServerInjectOptions = { method: 'POST', - url: '/api/siem/signals', - payload: noId, + url: DETECTION_ENGINE_RULES_URL, + payload: noRuleId, }; const { statusCode } = await server.inject(request); - expect(statusCode).toBe(400); + expect(statusCode).toBe(200); }); test('returns 200 if type is query', async () => { alertsClient.find.mockResolvedValue(getFindResult()); alertsClient.get.mockResolvedValue(getResult()); actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(createAlertResult()); + alertsClient.create.mockResolvedValue(getResult()); const { type, ...noType } = typicalPayload(); const request: ServerInjectOptions = { method: 'POST', - url: '/api/siem/signals', + url: DETECTION_ENGINE_RULES_URL, payload: { ...noType, type: 'query', @@ -103,13 +103,13 @@ describe('create_signals', () => { alertsClient.find.mockResolvedValue(getFindResult()); alertsClient.get.mockResolvedValue(getResult()); actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(createAlertResult()); + alertsClient.create.mockResolvedValue(getResult()); // Cannot type request with a ServerInjectOptions as the type system complains // about the property filter involving Hapi types, so I left it off for now const { language, query, type, ...noType } = typicalPayload(); const request = { method: 'POST', - url: '/api/siem/signals', + url: DETECTION_ENGINE_RULES_URL, payload: { ...noType, type: 'filter', @@ -124,11 +124,11 @@ describe('create_signals', () => { alertsClient.find.mockResolvedValue(getFindResult()); alertsClient.get.mockResolvedValue(getResult()); actionsClient.create.mockResolvedValue(createActionResult()); - alertsClient.create.mockResolvedValue(createAlertResult()); + alertsClient.create.mockResolvedValue(getResult()); const { type, ...noType } = typicalPayload(); const request: ServerInjectOptions = { method: 'POST', - url: '/api/siem/signals', + url: DETECTION_ENGINE_RULES_URL, payload: { ...noType, type: 'something-made-up', diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/create_signals_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/create_signals_route.ts index d3474f214194c..856866fb443ba 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/create_signals_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/create_signals_route.ts @@ -6,13 +6,18 @@ import Hapi from 'hapi'; import { isFunction } from 'lodash/fp'; +import Boom from 'boom'; +import uuid from 'uuid'; +import { DETECTION_ENGINE_RULES_URL } from '../../../../common/constants'; import { createSignals } from '../alerts/create_signals'; import { SignalsRequest } from '../alerts/types'; import { createSignalsSchema } from './schemas'; +import { readSignals } from '../alerts/read_signals'; +import { transformOrError } from './utils'; export const createCreateSignalsRoute: Hapi.ServerRoute = { method: 'POST', - path: '/api/siem/signals', + path: DETECTION_ENGINE_RULES_URL, options: { tags: ['access:signals-all'], validate: { @@ -36,7 +41,8 @@ export const createCreateSignalsRoute: Hapi.ServerRoute = { // eslint-disable-next-line @typescript-eslint/camelcase saved_id: savedId, filters, - id, + // eslint-disable-next-line @typescript-eslint/camelcase + rule_id: ruleId, index, interval, // eslint-disable-next-line @typescript-eslint/camelcase @@ -57,7 +63,13 @@ export const createCreateSignalsRoute: Hapi.ServerRoute = { return headers.response().code(404); } - return createSignals({ + if (ruleId != null) { + const signal = await readSignals({ alertsClient, ruleId }); + if (signal != null) { + return new Boom(`Signal rule_id ${ruleId} already exists`, { statusCode: 409 }); + } + } + const createdSignal = await createSignals({ alertsClient, actionsClient, description, @@ -70,7 +82,7 @@ export const createCreateSignalsRoute: Hapi.ServerRoute = { language, savedId, filters, - id, + ruleId: ruleId != null ? ruleId : uuid.v4(), index, interval, maxSignals, @@ -82,6 +94,7 @@ export const createCreateSignalsRoute: Hapi.ServerRoute = { type, references, }); + return transformOrError(createdSignal); }, }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/delete_signals_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/delete_signals_route.test.ts index db74cf6508be6..95816aa55d1fe 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/delete_signals_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/delete_signals_route.test.ts @@ -13,7 +13,14 @@ import { import { deleteSignalsRoute } from './delete_signals_route'; import { ServerInjectOptions } from 'hapi'; -import { getFindResult, getResult, getDeleteRequest } from './__mocks__/request_responses'; +import { + getFindResult, + getResult, + getDeleteRequest, + getFindResultWithSingleHit, + getDeleteRequestById, +} from './__mocks__/request_responses'; +import { DETECTION_ENGINE_RULES_URL } from '../../../../common/constants'; describe('delete_signals', () => { let { server, alertsClient } = createMockServer(); @@ -28,14 +35,30 @@ describe('delete_signals', () => { }); describe('status codes with actionClient and alertClient', () => { - test('returns 200 when deleting a single signal with a valid actionClient and alertClient', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); + test('returns 200 when deleting a single signal with a valid actionClient and alertClient by alertId', async () => { + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); alertsClient.get.mockResolvedValue(getResult()); alertsClient.delete.mockResolvedValue({}); const { statusCode } = await server.inject(getDeleteRequest()); expect(statusCode).toBe(200); }); + test('returns 200 when deleting a single signal with a valid actionClient and alertClient by id', async () => { + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); + alertsClient.get.mockResolvedValue(getResult()); + alertsClient.delete.mockResolvedValue({}); + const { statusCode } = await server.inject(getDeleteRequestById()); + expect(statusCode).toBe(200); + }); + + test('returns 404 when deleting a single signal that does not exist with a valid actionClient and alertClient', async () => { + alertsClient.find.mockResolvedValue(getFindResult()); + alertsClient.get.mockResolvedValue(getResult()); + alertsClient.delete.mockResolvedValue({}); + const { statusCode } = await server.inject(getDeleteRequest()); + expect(statusCode).toBe(404); + }); + test('returns 404 if actionClient is not available on the route', async () => { const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration(); deleteSignalsRoute(serverWithoutActionClient); @@ -61,16 +84,16 @@ describe('delete_signals', () => { }); describe('validation', () => { - test('returns 404 if given a non-existent id', async () => { + test('returns 400 if given a non-existent id', async () => { alertsClient.find.mockResolvedValue(getFindResult()); alertsClient.get.mockResolvedValue(getResult()); alertsClient.delete.mockResolvedValue({}); const request: ServerInjectOptions = { method: 'DELETE', - url: '/api/siem/signals', + url: DETECTION_ENGINE_RULES_URL, }; const { statusCode } = await server.inject(request); - expect(statusCode).toBe(404); + expect(statusCode).toBe(400); }); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/delete_signals_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/delete_signals_route.ts index d89d996eb06a2..46565bc9950a5 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/delete_signals_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/delete_signals_route.ts @@ -7,21 +7,26 @@ import Hapi from 'hapi'; import { isFunction } from 'lodash/fp'; +import { DETECTION_ENGINE_RULES_URL } from '../../../../common/constants'; import { deleteSignals } from '../alerts/delete_signals'; +import { querySignalSchema } from './schemas'; +import { QueryRequest } from '../alerts/types'; +import { getIdError, transformOrError } from './utils'; export const createDeleteSignalsRoute: Hapi.ServerRoute = { method: 'DELETE', - path: '/api/siem/signals/{id}', + path: DETECTION_ENGINE_RULES_URL, options: { tags: ['access:signals-all'], validate: { options: { abortEarly: false, }, + query: querySignalSchema, }, }, - async handler(request: Hapi.Request, headers) { - const { id } = request.params; + async handler(request: QueryRequest, headers) { + const { id, rule_id: ruleId } = request.query; const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; const actionsClient = isFunction(request.getActionsClient) ? request.getActionsClient() : null; @@ -29,11 +34,18 @@ export const createDeleteSignalsRoute: Hapi.ServerRoute = { return headers.response().code(404); } - return deleteSignals({ + const signal = await deleteSignals({ actionsClient, alertsClient, id, + ruleId, }); + + if (signal != null) { + return transformOrError(signal); + } else { + return getIdError({ id, ruleId }); + } }, }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/find_signals_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/find_signals_route.test.ts index 331f8874eb29b..be3dce36e8716 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/find_signals_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/find_signals_route.test.ts @@ -14,6 +14,7 @@ import { import { findSignalsRoute } from './find_signals_route'; import { ServerInjectOptions } from 'hapi'; import { getFindResult, getResult, getFindRequest } from './__mocks__/request_responses'; +import { DETECTION_ENGINE_RULES_URL } from '../../../../common/constants'; describe('find_signals', () => { let { server, alertsClient, actionsClient } = createMockServer(); @@ -71,7 +72,7 @@ describe('find_signals', () => { alertsClient.get.mockResolvedValue(getResult()); const request: ServerInjectOptions = { method: 'GET', - url: '/api/siem/signals/_find?invalid_value=500', + url: `${DETECTION_ENGINE_RULES_URL}/_find?invalid_value=500`, }; const { statusCode } = await server.inject(request); expect(statusCode).toBe(400); @@ -82,8 +83,7 @@ describe('find_signals', () => { alertsClient.get.mockResolvedValue(getResult()); const request: ServerInjectOptions = { method: 'GET', - url: - '/api/siem/signals/_find?page=2&per_page=20&sort_field=timestamp&fields=["field-1","field-2","field-3]', + url: `${DETECTION_ENGINE_RULES_URL}/_find?page=2&per_page=20&sort_field=timestamp&fields=["field-1","field-2","field-3]`, }; const { statusCode } = await server.inject(request); expect(statusCode).toBe(200); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/find_signals_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/find_signals_route.ts index e6f4703ff2e63..6be2c145edc6c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/find_signals_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/find_signals_route.ts @@ -6,13 +6,15 @@ import Hapi from 'hapi'; import { isFunction } from 'lodash/fp'; +import { DETECTION_ENGINE_RULES_URL } from '../../../../common/constants'; import { findSignals } from '../alerts/find_signals'; import { FindSignalsRequest } from '../alerts/types'; import { findSignalsSchema } from './schemas'; +import { transformFindAlertsOrError } from './utils'; export const createFindSignalRoute: Hapi.ServerRoute = { method: 'GET', - path: '/api/siem/signals/_find', + path: `${DETECTION_ENGINE_RULES_URL}/_find`, options: { tags: ['access:signals-all'], validate: { @@ -31,12 +33,13 @@ export const createFindSignalRoute: Hapi.ServerRoute = { return headers.response().code(404); } - return findSignals({ + const signals = await findSignals({ alertsClient, perPage: query.per_page, page: query.page, sortField: query.sort_field, }); + return transformFindAlertsOrError(signals); }, }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/read_signals_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/read_signals_route.test.ts index 43c96792606a2..021bcc7b8b48e 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/read_signals_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/read_signals_route.test.ts @@ -13,7 +13,13 @@ import { import { readSignalsRoute } from './read_signals_route'; import { ServerInjectOptions } from 'hapi'; -import { getFindResult, getResult, getReadRequest } from './__mocks__/request_responses'; +import { + getFindResult, + getResult, + getReadRequest, + getFindResultWithSingleHit, +} from './__mocks__/request_responses'; +import { DETECTION_ENGINE_RULES_URL } from '../../../../common/constants'; describe('read_signals', () => { let { server, alertsClient } = createMockServer(); @@ -29,7 +35,7 @@ describe('read_signals', () => { describe('status codes with actionClient and alertClient', () => { test('returns 200 when reading a single signal with a valid actionClient and alertClient', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); alertsClient.get.mockResolvedValue(getResult()); const { statusCode } = await server.inject(getReadRequest()); expect(statusCode).toBe(200); @@ -60,16 +66,16 @@ describe('read_signals', () => { }); describe('validation', () => { - test('returns 404 if given a non-existent id', async () => { + test('returns 400 if given a non-existent id', async () => { alertsClient.find.mockResolvedValue(getFindResult()); alertsClient.get.mockResolvedValue(getResult()); alertsClient.delete.mockResolvedValue({}); const request: ServerInjectOptions = { method: 'GET', - url: '/api/siem/signals/', + url: DETECTION_ENGINE_RULES_URL, }; const { statusCode } = await server.inject(request); - expect(statusCode).toBe(404); + expect(statusCode).toBe(400); }); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/read_signals_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/read_signals_route.ts index b26c8c17f32dc..71a1afc921bef 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/read_signals_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/read_signals_route.ts @@ -6,32 +6,43 @@ import Hapi from 'hapi'; import { isFunction } from 'lodash/fp'; +import { DETECTION_ENGINE_RULES_URL } from '../../../../common/constants'; +import { getIdError, transformOrError } from './utils'; import { readSignals } from '../alerts/read_signals'; +import { querySignalSchema } from './schemas'; +import { QueryRequest } from '../alerts/types'; export const createReadSignalsRoute: Hapi.ServerRoute = { method: 'GET', - path: '/api/siem/signals/{id}', + path: DETECTION_ENGINE_RULES_URL, options: { tags: ['access:signals-all'], validate: { options: { abortEarly: false, }, + query: querySignalSchema, }, }, - async handler(request: Hapi.Request, headers) { - const { id } = request.params; + async handler(request: QueryRequest, headers) { + const { id, rule_id: ruleId } = request.query; const alertsClient = isFunction(request.getAlertsClient) ? request.getAlertsClient() : null; const actionsClient = isFunction(request.getActionsClient) ? request.getActionsClient() : null; if (!alertsClient || !actionsClient) { return headers.response().code(404); } - return readSignals({ + const signal = await readSignals({ alertsClient, id, + ruleId, }); + if (signal != null) { + return transformOrError(signal); + } else { + return getIdError({ id, ruleId }); + } }, }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.test.ts index 35432c2bf56b5..ecb42399932f6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.test.ts @@ -4,10 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import { createSignalsSchema, updateSignalSchema, findSignalsSchema } from './schemas'; -import { SignalAlertParamsRest, FindParamsRest } from '../alerts/types'; - -describe('update_signals', () => { +import { + createSignalsSchema, + updateSignalSchema, + findSignalsSchema, + querySignalSchema, +} from './schemas'; +import { + SignalAlertParamsRest, + FindParamsRest, + UpdateSignalAlertParamsRest, +} from '../alerts/types'; + +describe('schemas', () => { describe('create signals schema', () => { test('empty objects do not validate', () => { expect(createSignalsSchema.validate>({}).error).toBeTruthy(); @@ -21,37 +30,37 @@ describe('update_signals', () => { ).toBeTruthy(); }); - test('[id] does not validate', () => { + test('[rule_id] does not validate', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', }).error ).toBeTruthy(); }); - test('[id, description] does not validate', () => { + test('[rule_id, description] does not validate', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', }).error ).toBeTruthy(); }); - test('[id, description, from] does not validate', () => { + test('[rule_id, description, from] does not validate', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', }).error ).toBeTruthy(); }); - test('[id, description, from, to] does not validate', () => { + test('[rule_id, description, from, to] does not validate', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -59,10 +68,10 @@ describe('update_signals', () => { ).toBeTruthy(); }); - test('[id, description, from, to, name] does not validate', () => { + test('[rule_id, description, from, to, name] does not validate', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -71,10 +80,10 @@ describe('update_signals', () => { ).toBeTruthy(); }); - test('[id, description, from, to, name, severity] does not validate', () => { + test('[rule_id, description, from, to, name, severity] does not validate', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -84,10 +93,10 @@ describe('update_signals', () => { ).toBeTruthy(); }); - test('[id, description, from, to, name, severity, type] does not validate', () => { + test('[rule_id, description, from, to, name, severity, type] does not validate', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -98,10 +107,10 @@ describe('update_signals', () => { ).toBeTruthy(); }); - test('[id, description, from, to, name, severity, type, interval] does not validate', () => { + test('[rule_id, description, from, to, name, severity, type, interval] does not validate', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -113,10 +122,10 @@ describe('update_signals', () => { ).toBeTruthy(); }); - test('[id, description, from, to, name, severity, type, interval, index] does not validate', () => { + test('[rule_id, description, from, to, name, severity, type, interval, index] does not validate', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -129,10 +138,10 @@ describe('update_signals', () => { ).toBeTruthy(); }); - test('[id, description, from, to, name, severity, type, query, index, interval] does not validate', () => { + test('[rule_id, description, from, to, name, severity, type, query, index, interval] does not validate', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -146,10 +155,10 @@ describe('update_signals', () => { ).toBeTruthy(); }); - test('[id, description, from, to, index, name, severity, interval, type, query, language] does validate', () => { + test('[rule_id, description, from, to, index, name, severity, interval, type, query, language] does validate', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -164,10 +173,10 @@ describe('update_signals', () => { ).toBeFalsy(); }); - test('[id, description, from, to, index, name, severity, interval, type, filter] does validate', () => { + test('[rule_id, description, from, to, index, name, severity, interval, type, filter] does validate', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -184,7 +193,7 @@ describe('update_signals', () => { test('If filter type is set then filter is required', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -200,7 +209,7 @@ describe('update_signals', () => { test('If filter type is set then query is not allowed', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -218,7 +227,7 @@ describe('update_signals', () => { test('If filter type is set then language is not allowed', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -236,7 +245,7 @@ describe('update_signals', () => { test('If filter type is set then filters are not allowed', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -254,7 +263,7 @@ describe('update_signals', () => { test('allows references to be sent as valid', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -273,7 +282,7 @@ describe('update_signals', () => { test('defaults references to an array', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -293,7 +302,7 @@ describe('update_signals', () => { createSignalsSchema.validate< Partial> & { references: number[] } >({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -314,7 +323,7 @@ describe('update_signals', () => { createSignalsSchema.validate< Partial> & { index: number[] } >({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -332,7 +341,7 @@ describe('update_signals', () => { test('defaults interval to 5 min', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -347,7 +356,7 @@ describe('update_signals', () => { test('defaults max signals to 100', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -363,7 +372,7 @@ describe('update_signals', () => { test('filter and filters cannot exist together', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -381,7 +390,7 @@ describe('update_signals', () => { test('saved_id is required when type is saved_query and will not validate without out', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -397,7 +406,7 @@ describe('update_signals', () => { test('saved_id is required when type is saved_query and validates with it', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -414,7 +423,7 @@ describe('update_signals', () => { test('saved_query type cannot have filters with it', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -432,7 +441,7 @@ describe('update_signals', () => { test('saved_query type cannot have filter with it', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -450,7 +459,7 @@ describe('update_signals', () => { test('language validates with kuery', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -469,7 +478,7 @@ describe('update_signals', () => { test('language validates with lucene', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -488,7 +497,7 @@ describe('update_signals', () => { test('language does not validate with something made up', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -507,7 +516,7 @@ describe('update_signals', () => { test('max_signals cannot be negative', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -527,7 +536,7 @@ describe('update_signals', () => { test('max_signals cannot be zero', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -547,7 +556,7 @@ describe('update_signals', () => { test('max_signals can be 1', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -567,7 +576,7 @@ describe('update_signals', () => { test('You can optionally send in an array of tags', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -590,7 +599,7 @@ describe('update_signals', () => { createSignalsSchema.validate< Partial> & { tags: number[] } >({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -611,7 +620,7 @@ describe('update_signals', () => { test('You can optionally send in an array of false positives', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', false_positives: ['false_1', 'false_2'], from: 'now-5m', @@ -634,7 +643,7 @@ describe('update_signals', () => { createSignalsSchema.validate< Partial> & { false_positives: number[] } >({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', false_positives: [5, 4], from: 'now-5m', @@ -655,7 +664,7 @@ describe('update_signals', () => { test('You can optionally set the immutable to be true', () => { expect( createSignalsSchema.validate>({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -678,7 +687,7 @@ describe('update_signals', () => { createSignalsSchema.validate< Partial> & { immutable: number } >({ - id: 'rule-1', + rule_id: 'rule-1', description: 'some description', from: 'now-5m', to: 'now', @@ -698,13 +707,15 @@ describe('update_signals', () => { }); describe('update signals schema', () => { - test('empty objects do validate', () => { - expect(updateSignalSchema.validate>({}).error).toBeFalsy(); + test('empty objects do not validate as they require at least id or rule_id', () => { + expect( + updateSignalSchema.validate>({}).error + ).toBeTruthy(); }); test('made up values do not validate', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ madeUp: 'hi', }).error ).toBeTruthy(); @@ -712,24 +723,60 @@ describe('update_signals', () => { test('[id] does validate', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', }).error ).toBeFalsy(); }); + test('[rule_id] does validate', () => { + expect( + updateSignalSchema.validate>({ + rule_id: 'rule-1', + }).error + ).toBeFalsy(); + }); + + test('[id and rule_id] does not validate', () => { + expect( + updateSignalSchema.validate>({ + id: 'id-1', + rule_id: 'rule-1', + }).error + ).toBeTruthy(); + }); + + test('[rule_id, description] does validate', () => { + expect( + updateSignalSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + }).error + ).toBeFalsy(); + }); + test('[id, description] does validate', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', }).error ).toBeFalsy(); }); + test('[rule_id, description, from] does validate', () => { + expect( + updateSignalSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + }).error + ).toBeFalsy(); + }); + test('[id, description, from] does validate', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -737,9 +784,20 @@ describe('update_signals', () => { ).toBeFalsy(); }); + test('[rule_id, description, from, to] does validate', () => { + expect( + updateSignalSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + }).error + ).toBeFalsy(); + }); + test('[id, description, from, to] does validate', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -748,9 +806,21 @@ describe('update_signals', () => { ).toBeFalsy(); }); + test('[rule_id, description, from, to, name] does validate', () => { + expect( + updateSignalSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + name: 'some-name', + }).error + ).toBeFalsy(); + }); + test('[id, description, from, to, name] does validate', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -760,9 +830,22 @@ describe('update_signals', () => { ).toBeFalsy(); }); + test('[rule_id, description, from, to, name, severity] does validate', () => { + expect( + updateSignalSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + name: 'some-name', + severity: 'severity', + }).error + ).toBeFalsy(); + }); + test('[id, description, from, to, name, severity] does validate', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -773,9 +856,23 @@ describe('update_signals', () => { ).toBeFalsy(); }); + test('[rule_id, description, from, to, name, severity, type] does validate', () => { + expect( + updateSignalSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + name: 'some-name', + severity: 'severity', + type: 'query', + }).error + ).toBeFalsy(); + }); + test('[id, description, from, to, name, severity, type] does validate', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -787,9 +884,24 @@ describe('update_signals', () => { ).toBeFalsy(); }); + test('[rule_id, description, from, to, name, severity, type, interval] does validate', () => { + expect( + updateSignalSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + }).error + ).toBeFalsy(); + }); + test('[id, description, from, to, name, severity, type, interval] does validate', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -802,9 +914,25 @@ describe('update_signals', () => { ).toBeFalsy(); }); + test('[rule_id, description, from, to, index, name, severity, interval, type] does validate', () => { + expect( + updateSignalSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + }).error + ).toBeFalsy(); + }); + test('[id, description, from, to, index, name, severity, interval, type] does validate', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -818,9 +946,26 @@ describe('update_signals', () => { ).toBeFalsy(); }); + test('[rule_id, description, from, to, index, name, severity, interval, type, query] does validate', () => { + expect( + updateSignalSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + query: 'some query', + }).error + ).toBeFalsy(); + }); + test('[id, description, from, to, index, name, severity, interval, type, query] does validate', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -835,9 +980,27 @@ describe('update_signals', () => { ).toBeFalsy(); }); + test('[rule_id, description, from, to, index, name, severity, interval, type, query, language] does validate', () => { + expect( + updateSignalSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'query', + query: 'some query', + language: 'kuery', + }).error + ).toBeFalsy(); + }); + test('[id, description, from, to, index, name, severity, interval, type, query, language] does validate', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -853,9 +1016,26 @@ describe('update_signals', () => { ).toBeFalsy(); }); + test('[rule_id, description, from, to, index, name, severity, type, filter] does validate', () => { + expect( + updateSignalSchema.validate>({ + rule_id: 'rule-1', + description: 'some description', + from: 'now-5m', + to: 'now', + index: ['index-1'], + name: 'some-name', + severity: 'severity', + interval: '5m', + type: 'filter', + filter: {}, + }).error + ).toBeFalsy(); + }); + test('[id, description, from, to, index, name, severity, type, filter] does validate', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -872,7 +1052,7 @@ describe('update_signals', () => { test('If filter type is set then filter is still not required', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -888,7 +1068,7 @@ describe('update_signals', () => { test('If filter type is set then query is not allowed', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -906,7 +1086,7 @@ describe('update_signals', () => { test('If filter type is set then language is not allowed', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -924,7 +1104,7 @@ describe('update_signals', () => { test('If filter type is set then filters are not allowed', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -942,7 +1122,7 @@ describe('update_signals', () => { test('allows references to be sent as a valid value to update with', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -961,7 +1141,7 @@ describe('update_signals', () => { test('does not default references to an array', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -979,7 +1159,7 @@ describe('update_signals', () => { test('does not default interval', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -994,7 +1174,7 @@ describe('update_signals', () => { test('does not default max signal', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -1011,7 +1191,7 @@ describe('update_signals', () => { test('references cannot be numbers', () => { expect( updateSignalSchema.validate< - Partial> & { references: number[] } + Partial> & { references: number[] } >({ id: 'rule-1', description: 'some description', @@ -1032,7 +1212,7 @@ describe('update_signals', () => { test('indexes cannot be numbers', () => { expect( updateSignalSchema.validate< - Partial> & { index: number[] } + Partial> & { index: number[] } >({ id: 'rule-1', description: 'some description', @@ -1051,7 +1231,7 @@ describe('update_signals', () => { test('filter and filters cannot exist together', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -1069,7 +1249,7 @@ describe('update_signals', () => { test('saved_id is not required when type is saved_query and will validate without it', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -1085,7 +1265,7 @@ describe('update_signals', () => { test('saved_id validates with saved_query', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -1102,7 +1282,7 @@ describe('update_signals', () => { test('saved_query type cannot have filters with it', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -1120,7 +1300,7 @@ describe('update_signals', () => { test('saved_query type cannot have filter with it', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -1138,7 +1318,7 @@ describe('update_signals', () => { test('language validates with kuery', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -1157,7 +1337,7 @@ describe('update_signals', () => { test('language validates with lucene', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -1176,7 +1356,7 @@ describe('update_signals', () => { test('language does not validate with something made up', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -1195,7 +1375,7 @@ describe('update_signals', () => { test('max_signals cannot be negative', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -1215,7 +1395,7 @@ describe('update_signals', () => { test('max_signals cannot be zero', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -1235,7 +1415,7 @@ describe('update_signals', () => { test('max_signals can be 1', () => { expect( - updateSignalSchema.validate>({ + updateSignalSchema.validate>({ id: 'rule-1', description: 'some description', from: 'now-5m', @@ -1326,4 +1506,31 @@ describe('update_signals', () => { expect(findSignalsSchema.validate>({}).value.page).toEqual(1); }); }); + + describe('querySignalSchema', () => { + test('empty objects do not validate', () => { + expect( + querySignalSchema.validate>({}).error + ).toBeTruthy(); + }); + + test('both rule_id and id being supplied dot not validate', () => { + expect( + querySignalSchema.validate>({ rule_id: '1', id: '1' }) + .error + ).toBeTruthy(); + }); + + test('only id validates', () => { + expect( + querySignalSchema.validate>({ id: '1' }).error + ).toBeFalsy(); + }); + + test('only rule_id validates', () => { + expect( + querySignalSchema.validate>({ rule_id: '1' }).error + ).toBeFalsy(); + }); + }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.ts index ba2c74b8bf0a9..596850b4a11e4 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/schemas.ts @@ -14,6 +14,7 @@ const filter = Joi.object(); const filters = Joi.array(); const from = Joi.string(); const immutable = Joi.boolean(); +const rule_id = Joi.string(); const id = Joi.string(); const index = Joi.array() .items(Joi.string()) @@ -50,7 +51,7 @@ export const createSignalsSchema = Joi.object({ filter: filter.when('type', { is: 'filter', then: Joi.required(), otherwise: Joi.forbidden() }), filters: filters.when('type', { is: 'query', then: Joi.optional(), otherwise: Joi.forbidden() }), from: from.required(), - id: id.required(), + rule_id, immutable: immutable.default(false), index: index.required(), interval: interval.default('5m'), @@ -81,6 +82,7 @@ export const updateSignalSchema = Joi.object({ filter: filter.when('type', { is: 'filter', then: Joi.optional(), otherwise: Joi.forbidden() }), filters: filters.when('type', { is: 'query', then: Joi.optional(), otherwise: Joi.forbidden() }), from, + rule_id, id, immutable, index, @@ -103,7 +105,12 @@ export const updateSignalSchema = Joi.object({ to, type, references, -}); +}).xor('id', 'rule_id'); + +export const querySignalSchema = Joi.object({ + rule_id, + id, +}).xor('id', 'rule_id'); export const findSignalsSchema = Joi.object({ per_page, diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/update_signals_route.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/update_signals_route.test.ts index c553a8bd40973..7288d18628316 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/update_signals_route.test.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/update_signals_route.test.ts @@ -17,10 +17,12 @@ import { getFindResult, getResult, updateActionResult, - updateAlertResult, getUpdateRequest, typicalPayload, + getFindResultWithSingleHit, + typicalFilterPayload, } from './__mocks__/request_responses'; +import { DETECTION_ENGINE_RULES_URL } from '../../../../common/constants'; describe('update_signals', () => { let { server, alertsClient, actionsClient } = createMockServer(); @@ -33,14 +35,23 @@ describe('update_signals', () => { describe('status codes with actionClient and alertClient', () => { test('returns 200 when updating a single signal with a valid actionClient and alertClient', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); alertsClient.get.mockResolvedValue(getResult()); actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(updateAlertResult()); + alertsClient.update.mockResolvedValue(getResult()); const { statusCode } = await server.inject(getUpdateRequest()); expect(statusCode).toBe(200); }); + test('returns 404 when updating a single signal that does not exist', async () => { + alertsClient.find.mockResolvedValue(getFindResult()); + alertsClient.get.mockResolvedValue(getResult()); + actionsClient.update.mockResolvedValue(updateActionResult()); + alertsClient.update.mockResolvedValue(getResult()); + const { statusCode } = await server.inject(getUpdateRequest()); + expect(statusCode).toBe(404); + }); + test('returns 404 if actionClient is not available on the route', async () => { const { serverWithoutActionClient } = createMockServerWithoutActionClientDecoration(); updateSignalsRoute(serverWithoutActionClient); @@ -67,12 +78,12 @@ describe('update_signals', () => { describe('validation', () => { test('returns 400 if id is not given in either the body or the url', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); alertsClient.get.mockResolvedValue(getResult()); - const { id, ...noId } = typicalPayload(); + const { rule_id, ...noId } = typicalPayload(); const request: ServerInjectOptions = { method: 'PUT', - url: '/api/siem/signals', + url: DETECTION_ENGINE_RULES_URL, payload: { payload: noId, }, @@ -81,51 +92,56 @@ describe('update_signals', () => { expect(statusCode).toBe(400); }); - test('returns 200 if type is query', async () => { + test('returns 404 if the record does not exist yet', async () => { alertsClient.find.mockResolvedValue(getFindResult()); + actionsClient.update.mockResolvedValue(updateActionResult()); + alertsClient.update.mockResolvedValue(getResult()); + const request: ServerInjectOptions = { + method: 'PUT', + url: DETECTION_ENGINE_RULES_URL, + payload: typicalPayload(), + }; + const { statusCode } = await server.inject(request); + expect(statusCode).toBe(404); + }); + + test('returns 200 if type is query', async () => { + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); alertsClient.get.mockResolvedValue(getResult()); actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(updateAlertResult()); - const { type, ...noType } = typicalPayload(); + alertsClient.update.mockResolvedValue(getResult()); const request: ServerInjectOptions = { method: 'PUT', - url: '/api/siem/signals', - payload: { - ...noType, - type: 'query', - }, + url: DETECTION_ENGINE_RULES_URL, + payload: typicalPayload(), }; const { statusCode } = await server.inject(request); expect(statusCode).toBe(200); }); test('returns 200 if type is filter', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); alertsClient.get.mockResolvedValue(getResult()); actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(updateAlertResult()); - const { language, query, type, ...noType } = typicalPayload(); + alertsClient.update.mockResolvedValue(getResult()); const request: ServerInjectOptions = { method: 'PUT', - url: '/api/siem/signals', - payload: { - ...noType, - type: 'filter', - }, + url: DETECTION_ENGINE_RULES_URL, + payload: typicalFilterPayload(), }; const { statusCode } = await server.inject(request); expect(statusCode).toBe(200); }); test('returns 400 if type is not filter or kql', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); + alertsClient.find.mockResolvedValue(getFindResultWithSingleHit()); alertsClient.get.mockResolvedValue(getResult()); actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(updateAlertResult()); + alertsClient.update.mockResolvedValue(getResult()); const { type, ...noType } = typicalPayload(); const request: ServerInjectOptions = { method: 'PUT', - url: '/api/siem/signals', + url: DETECTION_ENGINE_RULES_URL, payload: { ...noType, type: 'something-made-up', @@ -134,21 +150,5 @@ describe('update_signals', () => { const { statusCode } = await server.inject(request); expect(statusCode).toBe(400); }); - - test('returns 200 if id is given in the url but not the payload', async () => { - alertsClient.find.mockResolvedValue(getFindResult()); - alertsClient.get.mockResolvedValue(getResult()); - actionsClient.update.mockResolvedValue(updateActionResult()); - alertsClient.update.mockResolvedValue(updateAlertResult()); - // missing id should throw a 400 - const { id, ...noId } = typicalPayload(); - const request: ServerInjectOptions = { - method: 'PUT', - url: '/api/siem/signals/rule-1', - payload: noId, - }; - const { statusCode } = await server.inject(request); - expect(statusCode).toBe(200); - }); }); }); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/update_signals_route.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/update_signals_route.ts index a1c5af1158a47..9852fd0c5d271 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/update_signals_route.ts +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/update_signals_route.ts @@ -5,28 +5,22 @@ */ import Hapi from 'hapi'; -import Joi from 'joi'; import { isFunction } from 'lodash/fp'; +import { DETECTION_ENGINE_RULES_URL } from '../../../../common/constants'; import { updateSignal } from '../alerts/update_signals'; import { UpdateSignalsRequest } from '../alerts/types'; import { updateSignalSchema } from './schemas'; +import { getIdError, transformOrError } from './utils'; export const createUpdateSignalsRoute: Hapi.ServerRoute = { method: 'PUT', - path: '/api/siem/signals/{id?}', + path: DETECTION_ENGINE_RULES_URL, options: { tags: ['access:signals-all'], validate: { options: { abortEarly: false, }, - params: { - id: Joi.when(Joi.ref('$payload.id'), { - is: Joi.exist(), - then: Joi.string().optional(), - otherwise: Joi.string().required(), - }), - }, payload: updateSignalSchema, }, }, @@ -43,6 +37,8 @@ export const createUpdateSignalsRoute: Hapi.ServerRoute = { // eslint-disable-next-line @typescript-eslint/camelcase saved_id: savedId, filters, + // eslint-disable-next-line @typescript-eslint/camelcase + rule_id: ruleId, id, index, interval, @@ -63,7 +59,8 @@ export const createUpdateSignalsRoute: Hapi.ServerRoute = { if (!alertsClient || !actionsClient) { return headers.response().code(404); } - return updateSignal({ + + const signal = await updateSignal({ alertsClient, actionsClient, description, @@ -76,7 +73,8 @@ export const createUpdateSignalsRoute: Hapi.ServerRoute = { language, savedId, filters, - id: request.params.id ? request.params.id : id, + id, + ruleId, index, interval, maxSignals, @@ -88,6 +86,11 @@ export const createUpdateSignalsRoute: Hapi.ServerRoute = { type, references, }); + if (signal != null) { + return transformOrError(signal); + } else { + return getIdError({ id, ruleId }); + } }, }; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts new file mode 100644 index 0000000000000..69f25e84d995c --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.test.ts @@ -0,0 +1,268 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Boom from 'boom'; +import { + transformAlertToSignal, + getIdError, + transformFindAlertsOrError, + transformOrError, +} from './utils'; +import { getResult } from './__mocks__/request_responses'; + +describe('utils', () => { + describe('transformAlertToSignal', () => { + test('should work with a full data set', () => { + const fullSignal = getResult(); + const signal = transformAlertToSignal(fullSignal); + expect(signal).toEqual({ + created_by: 'elastic', + description: 'Detecting root and admin users', + enabled: true, + false_positives: [], + from: 'now-6m', + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + interval: '5m', + rule_id: 'rule-1', + language: 'kuery', + max_signals: 100, + name: 'Detect Root/Admin Users', + query: 'user.name: root or user.name: admin', + references: ['http://www.example.com', 'https://ww.example.com'], + severity: 'high', + size: 1, + updated_by: 'elastic', + tags: [], + to: 'now', + type: 'query', + }); + }); + + test('should work with a partial data set missing data', () => { + const fullSignal = getResult(); + const { from, language, ...omitData } = transformAlertToSignal(fullSignal); + expect(omitData).toEqual({ + created_by: 'elastic', + description: 'Detecting root and admin users', + enabled: true, + false_positives: [], + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + interval: '5m', + rule_id: 'rule-1', + max_signals: 100, + name: 'Detect Root/Admin Users', + query: 'user.name: root or user.name: admin', + references: ['http://www.example.com', 'https://ww.example.com'], + severity: 'high', + size: 1, + updated_by: 'elastic', + tags: [], + to: 'now', + type: 'query', + }); + }); + + test('should omit query if query is null', () => { + const fullSignal = getResult(); + fullSignal.alertTypeParams.query = null; + const signal = transformAlertToSignal(fullSignal); + expect(signal).toEqual({ + created_by: 'elastic', + description: 'Detecting root and admin users', + enabled: true, + false_positives: [], + from: 'now-6m', + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + interval: '5m', + rule_id: 'rule-1', + language: 'kuery', + max_signals: 100, + name: 'Detect Root/Admin Users', + references: ['http://www.example.com', 'https://ww.example.com'], + severity: 'high', + size: 1, + updated_by: 'elastic', + tags: [], + to: 'now', + type: 'query', + }); + }); + + test('should omit query if query is undefined', () => { + const fullSignal = getResult(); + fullSignal.alertTypeParams.query = undefined; + const signal = transformAlertToSignal(fullSignal); + expect(signal).toEqual({ + created_by: 'elastic', + description: 'Detecting root and admin users', + enabled: true, + false_positives: [], + from: 'now-6m', + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + interval: '5m', + rule_id: 'rule-1', + language: 'kuery', + max_signals: 100, + name: 'Detect Root/Admin Users', + references: ['http://www.example.com', 'https://ww.example.com'], + severity: 'high', + size: 1, + updated_by: 'elastic', + tags: [], + to: 'now', + type: 'query', + }); + }); + + test('should omit a mix of undefined, null, and missing fields', () => { + const fullSignal = getResult(); + fullSignal.alertTypeParams.query = undefined; + fullSignal.alertTypeParams.language = null; + const { from, enabled, ...omitData } = transformAlertToSignal(fullSignal); + expect(omitData).toEqual({ + created_by: 'elastic', + description: 'Detecting root and admin users', + false_positives: [], + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + interval: '5m', + rule_id: 'rule-1', + max_signals: 100, + name: 'Detect Root/Admin Users', + references: ['http://www.example.com', 'https://ww.example.com'], + severity: 'high', + size: 1, + updated_by: 'elastic', + tags: [], + to: 'now', + type: 'query', + }); + }); + }); + + describe('getIdError', () => { + test('outputs message about id not being found if only id is defined and ruleId is undefined', () => { + const boom = getIdError({ id: '123', ruleId: undefined }); + expect(boom.message).toEqual('id of 123 not found'); + }); + + test('outputs message about id not being found if only id is defined and ruleId is null', () => { + const boom = getIdError({ id: '123', ruleId: null }); + expect(boom.message).toEqual('id of 123 not found'); + }); + + test('outputs message about ruleId not being found if only ruleId is defined and id is undefined', () => { + const boom = getIdError({ id: undefined, ruleId: 'rule-id-123' }); + expect(boom.message).toEqual('rule_id of rule-id-123 not found'); + }); + + test('outputs message about ruleId not being found if only ruleId is defined and id is null', () => { + const boom = getIdError({ id: null, ruleId: 'rule-id-123' }); + expect(boom.message).toEqual('rule_id of rule-id-123 not found'); + }); + + test('outputs message about both being not defined when both are undefined', () => { + const boom = getIdError({ id: undefined, ruleId: undefined }); + expect(boom.message).toEqual('id or rule_id should have been defined'); + }); + + test('outputs message about both being not defined when both are null', () => { + const boom = getIdError({ id: null, ruleId: null }); + expect(boom.message).toEqual('id or rule_id should have been defined'); + }); + + test('outputs message about both being not defined when id is null and ruleId is undefined', () => { + const boom = getIdError({ id: null, ruleId: undefined }); + expect(boom.message).toEqual('id or rule_id should have been defined'); + }); + + test('outputs message about both being not defined when id is undefined and ruleId is null', () => { + const boom = getIdError({ id: undefined, ruleId: null }); + expect(boom.message).toEqual('id or rule_id should have been defined'); + }); + }); + + describe('transformFindAlertsOrError', () => { + test('outputs empty data set when data set is empty correct', () => { + const output = transformFindAlertsOrError({ data: [] }); + expect(output).toEqual({ data: [] }); + }); + + test('outputs 200 if the data is of type siem alert', () => { + const output = transformFindAlertsOrError({ + data: [getResult()], + }); + expect(output).toEqual({ + data: [ + { + created_by: 'elastic', + description: 'Detecting root and admin users', + enabled: true, + false_positives: [], + from: 'now-6m', + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + interval: '5m', + rule_id: 'rule-1', + language: 'kuery', + max_signals: 100, + name: 'Detect Root/Admin Users', + query: 'user.name: root or user.name: admin', + references: ['http://www.example.com', 'https://ww.example.com'], + severity: 'high', + size: 1, + updated_by: 'elastic', + tags: [], + to: 'now', + type: 'query', + }, + ], + }); + }); + + test('returns 500 if the data is not of type siem alert', () => { + const output = transformFindAlertsOrError({ data: [{ random: 1 }] }); + expect((output as Boom).message).toEqual('Internal error transforming'); + }); + }); + + describe('transformOrError', () => { + test('outputs 200 if the data is of type siem alert', () => { + const output = transformOrError(getResult()); + expect(output).toEqual({ + created_by: 'elastic', + description: 'Detecting root and admin users', + enabled: true, + false_positives: [], + from: 'now-6m', + id: '04128c15-0d1b-4716-a4c5-46997ac7f3bd', + index: ['auditbeat-*', 'filebeat-*', 'packetbeat-*', 'winlogbeat-*'], + interval: '5m', + rule_id: 'rule-1', + language: 'kuery', + max_signals: 100, + name: 'Detect Root/Admin Users', + query: 'user.name: root or user.name: admin', + references: ['http://www.example.com', 'https://ww.example.com'], + severity: 'high', + size: 1, + updated_by: 'elastic', + tags: [], + to: 'now', + type: 'query', + }); + }); + + test('returns 500 if the data is not of type siem alert', () => { + const output = transformOrError({ data: [{ random: 1 }] }); + expect((output as Boom).message).toEqual('Internal error transforming'); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts new file mode 100644 index 0000000000000..4d653210b2bff --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/routes/utils.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import Boom from 'boom'; +import { pickBy, identity } from 'lodash/fp'; +import { SignalAlertType, isAlertType, OutputSignalAlertRest, isAlertTypes } from '../alerts/types'; + +export const getIdError = ({ + id, + ruleId, +}: { + id: string | undefined | null; + ruleId: string | undefined | null; +}) => { + if (id != null) { + return new Boom(`id of ${id} not found`, { statusCode: 404 }); + } else if (ruleId != null) { + return new Boom(`rule_id of ${ruleId} not found`, { statusCode: 404 }); + } else { + return new Boom(`id or rule_id should have been defined`, { statusCode: 404 }); + } +}; + +// Transforms the data but will remove any null or undefined it encounters and not include +// those on the export +export const transformAlertToSignal = (signal: SignalAlertType): Partial => { + return pickBy(identity, { + created_by: signal.createdBy, + description: signal.alertTypeParams.description, + enabled: signal.enabled, + false_positives: signal.alertTypeParams.falsePositives, + filter: signal.alertTypeParams.filter, + filters: signal.alertTypeParams.filters, + from: signal.alertTypeParams.from, + id: signal.id, + immutable: signal.alertTypeParams.immutable, + index: signal.alertTypeParams.index, + interval: signal.interval, + rule_id: signal.alertTypeParams.ruleId, + language: signal.alertTypeParams.language, + max_signals: signal.alertTypeParams.maxSignals, + name: signal.name, + query: signal.alertTypeParams.query, + references: signal.alertTypeParams.references, + saved_id: signal.alertTypeParams.savedId, + severity: signal.alertTypeParams.severity, + size: signal.alertTypeParams.size, + updated_by: signal.updatedBy, + tags: signal.alertTypeParams.tags, + to: signal.alertTypeParams.to, + type: signal.alertTypeParams.type, + }); +}; + +export const transformFindAlertsOrError = (findResults: { data: unknown[] }): unknown | Boom => { + if (isAlertTypes(findResults.data)) { + findResults.data = findResults.data.map(signal => transformAlertToSignal(signal)); + return findResults; + } else { + return new Boom('Internal error transforming', { statusCode: 500 }); + } +}; + +export const transformOrError = (signal: unknown): Partial | Boom => { + if (isAlertType(signal)) { + return transformAlertToSignal(signal); + } else { + return new Boom('Internal error transforming', { statusCode: 500 }); + } +}; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/delete_signal.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/delete_signal_by_id.sh similarity index 77% rename from x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/delete_signal.sh rename to x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/delete_signal_by_id.sh index c393665315e25..73882c78edfb8 100755 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/delete_signal.sh +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/delete_signal_by_id.sh @@ -9,8 +9,8 @@ set -e ./check_env_variables.sh -# Example: ./delete_signal.sh ${id} +# Example: ./delete_signal_by_id.sh ${id} curl -s -k \ -H 'kbn-xsrf: 123' \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ - -X DELETE ${KIBANA_URL}/api/siem/signals/$1 | jq . + -X DELETE ${KIBANA_URL}/api/detection_engine/rules?id="$1" | jq . diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/delete_signal_by_rule_id.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/delete_signal_by_rule_id.sh new file mode 100755 index 0000000000000..2b51146e6e1a0 --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/delete_signal_by_rule_id.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./delete_signal_by_rule_id.sh ${rule_id} +curl -s -k \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X DELETE ${KIBANA_URL}/api/detection_engine/rules?rule_id="$1" | jq . diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/find_signals.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/find_signals.sh index f851bda0c12c9..473c786936190 100755 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/find_signals.sh +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/find_signals.sh @@ -12,4 +12,4 @@ set -e # Example: ./find_signals.sh curl -s -k \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ - -X GET ${KIBANA_URL}/api/siem/signals/_find | jq . + -X GET ${KIBANA_URL}/api/detection_engine/rules/_find | jq . diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_signal.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_signal_by_id.sh similarity index 76% rename from x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_signal.sh rename to x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_signal_by_id.sh index 7eb07e6e2dedf..d10f347ff1f9e 100755 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_signal.sh +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_signal_by_id.sh @@ -9,7 +9,7 @@ set -e ./check_env_variables.sh -# Example: ./read_signal.sh {id} +# Example: ./get_signal_by_id.sh {rule_id} curl -s -k \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ - -X GET ${KIBANA_URL}/api/siem/signals/$1 | jq . + -X GET ${KIBANA_URL}/api/detection_engine/rules?id="$1" | jq . diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_signal_by_rule_id.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_signal_by_rule_id.sh new file mode 100755 index 0000000000000..302936fcb523e --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/get_signal_by_rule_id.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# +# Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one +# or more contributor license agreements. Licensed under the Elastic License; +# you may not use this file except in compliance with the Elastic License. +# + +set -e +./check_env_variables.sh + +# Example: ./get_signal_by_rule_id.sh {rule_id} +curl -s -k \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X GET ${KIBANA_URL}/api/detection_engine/rules?rule_id="$1" | jq . diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/post_signal.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/post_signal.sh index 6d79856ffd4fb..837454dea71e6 100755 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/post_signal.sh +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/post_signal.sh @@ -22,7 +22,7 @@ do { -H 'Content-Type: application/json' \ -H 'kbn-xsrf: 123' \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ - -X POST ${KIBANA_URL}/api/siem/signals \ + -X POST ${KIBANA_URL}/api/detection_engine/rules \ -d @${SIGNAL} \ | jq .; } & diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/post_x_signals.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/post_x_signals.sh index 4736fbeda3cf4..326d47280c306 100755 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/post_x_signals.sh +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/post_x_signals.sh @@ -20,9 +20,9 @@ do { -H 'Content-Type: application/json' \ -H 'kbn-xsrf: 123' \ -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ - -X POST ${KIBANA_URL}/api/siem/signals \ + -X POST ${KIBANA_URL}/api/detection_engine/rules \ --data "{ - \"id\": \"${i}\", + \"rule_id\": \"${i}\", \"description\": \"Detecting root and admin users\", \"index\": [\"auditbeat-*\", \"filebeat-*\", \"packetbeat-*\", \"winlogbeat-*\"], \"interval\": \"24h\", @@ -31,7 +31,7 @@ do { \"type\": \"query\", \"from\": \"now-6m\", \"to\": \"now\", - \"query\": \"user.name: root or user.name: admin\" + \"query\": \"user.name: root or user.name: admin\", \"language\": \"kuery\" }" \ | jq .; diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_1.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_1.json index 0b6d222451303..c6b2999b0e0c0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_1.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_1.json @@ -1,5 +1,5 @@ { - "id": "rule-1", + "rule_id": "rule-1", "description": "Detecting root and admin users", "index": ["auditbeat-*", "filebeat-*", "packetbeat-*", "winlogbeat-*"], "interval": "5m", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_10.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_10.json new file mode 100644 index 0000000000000..001b39bda5cbe --- /dev/null +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_10.json @@ -0,0 +1,13 @@ +{ + "description": "Detecting root and admin users", + "index": ["auditbeat-*", "filebeat-*", "packetbeat-*", "winlogbeat-*"], + "interval": "5m", + "name": "Detect Root/Admin Users", + "severity": "high", + "type": "query", + "from": "now-6m", + "to": "now", + "query": "user.name: root or user.name: admin", + "language": "kuery", + "references": ["http://www.example.com", "https://ww.example.com"] +} diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_2.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_2.json index ad154e2904542..0b16d1ab03bd6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_2.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_2.json @@ -1,5 +1,5 @@ { - "id": "rule-2", + "rule_id": "rule-2", "description": "Detecting root and admin users over a long period of time", "index": ["auditbeat-*", "filebeat-*", "packetbeat-*", "winlogbeat-*"], "interval": "24h", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_3.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_3.json index be98c7757c1e2..f2d599b260f7b 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_3.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_3.json @@ -1,5 +1,5 @@ { - "id": "rule-3", + "rule_id": "rule-3", "description": "Detecting root and admin users as an empty set", "index": ["auditbeat-*", "filebeat-*", "packetbeat-*", "winlogbeat-*"], "interval": "5m", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_4.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_4.json index 3c917af93fca8..392877ec77df0 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_4.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_4.json @@ -1,5 +1,5 @@ { - "id": "rule-4", + "rule_id": "rule-4", "description": "Detecting root and admin users with lucene", "index": ["auditbeat-*", "filebeat-*", "packetbeat-*", "winlogbeat-*"], "interval": "5m", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_5.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_5.json index 63728186b8f12..f99f718f2adee 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_5.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_5.json @@ -1,5 +1,5 @@ { - "id": "rule-5", + "rule_id": "rule-5", "description": "Detecting root and admin users over 24 hours on windows", "index": ["auditbeat-*", "filebeat-*", "packetbeat-*", "winlogbeat-*"], "interval": "5m", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_6.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_6.json index 58aefe12fb2d3..c2d2eb1128deb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_6.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_6.json @@ -1,5 +1,5 @@ { - "id": "rule-6", + "rule_id": "rule-6", "description": "Detecting root and admin users", "index": ["auditbeat-*", "filebeat-*", "packetbeat-*", "winlogbeat-*"], "interval": "5m", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_7.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_7.json index ac0c41dc3d215..99bb9b69c462a 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_7.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_7.json @@ -1,5 +1,5 @@ { - "id": "rule-7", + "rule_id": "rule-7", "description": "Detecting root and admin users", "index": ["auditbeat-*", "filebeat-*", "packetbeat-*", "winlogbeat-*"], "interval": "5m", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_8.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_8.json index 66f308fc5e2ff..07ffb26ea0eca 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_8.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_8.json @@ -1,5 +1,5 @@ { - "id": "rule-8", + "rule_id": "rule-8", "description": "Detecting root and admin users", "index": ["auditbeat-*", "filebeat-*", "packetbeat-*", "winlogbeat-*"], "interval": "5m", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_9.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_9.json index e7c348a98a7f2..06e66bbac4b60 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_9.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_9.json @@ -1,5 +1,5 @@ { - "id": "rule-9", + "rule_id": "rule-9", "description": "Detecting root and admin users", "index": ["auditbeat-*", "filebeat-*", "packetbeat-*", "winlogbeat-*"], "interval": "5m", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_filter_9998.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_filter_9998.json index fc5f08234368d..cc4c3d2f7407f 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_filter_9998.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_filter_9998.json @@ -1,5 +1,5 @@ { - "id": "rule-9999", + "rule_id": "rule-9999", "description": "Detecting root and admin users", "index": ["auditbeat-*", "filebeat-*", "packetbeat-*", "winlogbeat-*"], "interval": "5m", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_filter_9999.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_filter_9999.json index 9ab529ad4d9ce..0dfb92b9098cb 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_filter_9999.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_filter_9999.json @@ -1,5 +1,5 @@ { - "id": "rule-9999", + "rule_id": "rule-9999", "description": "Detecting root and admin users", "index": ["auditbeat-*", "filebeat-*", "packetbeat-*", "winlogbeat-*"], "interval": "5m", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_saved_query_1.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_saved_query_1.json index cd1a6efa73ad0..17dc207a62fa6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_saved_query_1.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_saved_query_1.json @@ -1,5 +1,5 @@ { - "id": "saved-query-1", + "rule_id": "saved-query-1", "description": "Detecting root and admin users", "index": ["auditbeat-*", "filebeat-*", "packetbeat-*", "winlogbeat-*"], "interval": "5m", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_update_1.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_update_1.json index 589583d417a13..5592ef7bdfd0c 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_update_1.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_update_1.json @@ -1,7 +1,7 @@ { - "id": "rule-1", + "rule_id": "rule-1", "description": "Changed Description of only detecting root user", - "index": ["auditbeat-*"], + "index": ["auditbeat-*"], "interval": "50m", "name": "A different name", "severity": "high", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_update_2.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_update_2.json index 6b99e54d6a9b8..a15f671d6a0b1 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_update_2.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/root_or_admin_update_2.json @@ -1,5 +1,5 @@ { - "id": "rule-1", + "rule_id": "rule-1", "description": "Changed Description of only detecting root user", "index": ["auditbeat-*"], "interval": "50m", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/watch_longmont.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/watch_longmont.json index 2f5457c352712..d18ed01bd13d6 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/watch_longmont.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/signals/watch_longmont.json @@ -1,5 +1,5 @@ { - "id": "rule-longmont", + "rule_id": "rule-longmont", "description": "Detect Longmont activity", "index": ["auditbeat-*", "filebeat-*", "packetbeat-*", "winlogbeat-*"], "interval": "24h", diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/update_signal.sh b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/update_signal.sh index 8cf69dc41e0be..1d16aa6fc7062 100755 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/update_signal.sh +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/scripts/update_signal.sh @@ -10,13 +10,22 @@ set -e ./check_env_variables.sh # Uses a default if no argument is specified -SIGNAL=${1:-./signals/root_or_admin_update_1.json} +SIGNALS=(${@:-./signals/root_or_admin_update_1.json}) -# Example: ./update_signal.sh {id} ./signals/root_or_admin_1.json -curl -s -k \ - -H 'Content-Type: application/json' \ - -H 'kbn-xsrf: 123' \ - -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ - -X PUT ${KIBANA_URL}/api/siem/signals \ - -d @${SIGNAL} \ - | jq . +# Example: ./update_signal.sh +# Example: ./update_signal.sh ./signals/root_or_admin_1.json +# Example glob: ./post_signal.sh ./signals/* +for SIGNAL in "${SIGNALS[@]}" +do { + [ -e "$SIGNAL" ] || continue + curl -s -k \ + -H 'Content-Type: application/json' \ + -H 'kbn-xsrf: 123' \ + -u ${ELASTICSEARCH_USERNAME}:${ELASTICSEARCH_PASSWORD} \ + -X PUT ${KIBANA_URL}/api/detection_engine/rules \ + -d @${SIGNAL} \ + | jq .; +} & +done + +wait diff --git a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals_mapping.json b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals_mapping.json index df4ea9bc3a0b2..dd80e786a3121 100644 --- a/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals_mapping.json +++ b/x-pack/legacy/plugins/siem/server/lib/detection_engine/signals_mapping.json @@ -23,6 +23,9 @@ } } }, + "id": { + "type": "keyword" + }, "original_time": { "type": "date" }, diff --git a/x-pack/legacy/plugins/siem/server/lib/types.ts b/x-pack/legacy/plugins/siem/server/lib/types.ts index ad099b728db36..2769199ad1fb5 100644 --- a/x-pack/legacy/plugins/siem/server/lib/types.ts +++ b/x-pack/legacy/plugins/siem/server/lib/types.ts @@ -65,8 +65,9 @@ export interface SiemContext { export interface SignalHit { signal: { '@timestamp': string; + id: string; rule_revision: number; - rule_id: string; + rule_id: string | undefined | null; rule_type: string; parent: { id: string; From 3337a6191162596fb27f6c6e8c9d97d349888e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B8ren=20Louv-Jansen?= Date: Sun, 17 Nov 2019 07:53:51 +0100 Subject: [PATCH 05/17] Improve "Browser client is out of date" error message (#50296) (#50861) * improve "Browser client is out of date" error message * Improve wording of error message Co-Authored-By: Josh Dover * Fox formatting * Fix format --- src/legacy/server/http/version_check.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/legacy/server/http/version_check.js b/src/legacy/server/http/version_check.js index 8bd2bb6e22df1..12666c9a0f3f6 100644 --- a/src/legacy/server/http/version_check.js +++ b/src/legacy/server/http/version_check.js @@ -27,10 +27,11 @@ export function setupVersionCheck(server, config) { const versionRequested = req.headers[versionHeader]; if (versionRequested && versionRequested !== actualVersion) { - throw badRequest('Browser client is out of date, please refresh the page', { - expected: actualVersion, - got: versionRequested - }); + throw badRequest( + `Browser client is out of date, \ + please refresh the page ("${versionHeader}" header was "${versionRequested}" but should be "${actualVersion}")`, + { expected: actualVersion, got: versionRequested } + ); } return h.continue; From b9cb99a6582fae4dd1728605aaa1d7025e623539 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Sun, 17 Nov 2019 15:30:10 +0200 Subject: [PATCH 06/17] [i18n] integrate latest translations (#50865) --- .../translations/translations/ja-JP.json | 2630 +++++++++++++++- .../translations/translations/zh-CN.json | 2637 ++++++++++++++++- 2 files changed, 5121 insertions(+), 146 deletions(-) diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index de362809dbb39..645b8c427dc15 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -576,6 +576,11 @@ "common.ui.directives.fieldNameIcons.geoShapeFieldAriaLabel": "地理情報図形", "common.ui.vis.editors.agg.errorsAriaLabel": "集約にエラーがあります", "common.ui.vislib.heatmap.maxBucketsText": "定義された数列が多すぎます ({nr}).構成されている最高値は {max} です。", + "common.ui.aggTypes.ranges.greaterThanOrEqualPrepend": "≥", + "common.ui.aggTypes.ranges.lessThanPrepend": "<", + "common.ui.aggTypes.scaleMetricsLabel": "メトリック値のスケーリング (廃止)", + "common.ui.aggTypes.scaleMetricsTooltip": "これを有効にすると、手動最低間隔を選択し、広い間隔が使用された場合、カウントと合計メトリックが手動で選択された間隔にスケーリングされます。", + "common.ui.aggTypes.buckets.ranges.rangesFormatMessage": "{gte} {from} と {lt} {to}", "core.ui.overlays.banner.attentionTitle": "注意", "core.ui.overlays.banner.closeButtonLabel": "閉じる", "core.ui.chrome.headerGlobalNav.goHomePageIconAriaLabel": "ホームページに移動", @@ -655,6 +660,8 @@ "core.euiSuperUpdateButton.refreshButtonLabel": "更新", "core.euiSuperUpdateButton.updateButtonLabel": "更新", "core.euiSuperUpdateButton.updatingButtonLabel": "更新中", + "core.application.appNotFound.pageDescription": "この URL にアプリケーションが見つかりませんでした。前の画面に戻るか、メニューからアプリを選択してみてください。", + "core.application.appNotFound.title": "アプリケーションが見つかりません", "kibana-react.tableListView.listing.deleteButtonMessage": "{itemCount} 件の {entityName} を削除", "kibana-react.tableListView.listing.deleteConfirmModalDescription": "削除された {entityNamePlural} は復元できません。", "kibana-react.tableListView.listing.deleteSelectedItemsConfirmModal.cancelButtonLabel": "キャンセル", @@ -672,6 +679,20 @@ "kibana-react.exitFullScreenButton.exitFullScreenModeButtonAreaLabel": "全画面モードを終了", "kibana-react.exitFullScreenButton.exitFullScreenModeButtonLabel": "全画面を終了", "kibana-react.exitFullScreenButton.fullScreenModeDescription": "ESC キーで全画面モードを終了します。", + "kibana-react.savedObjects.finder.filterButtonLabel": "タイプ", + "kibana-react.savedObjects.finder.searchPlaceholder": "検索...", + "kibana-react.savedObjects.finder.sortAsc": "昇順", + "kibana-react.savedObjects.finder.sortAuto": "ベストマッチ", + "kibana-react.savedObjects.finder.sortButtonLabel": "並べ替え", + "kibana-react.savedObjects.finder.sortDesc": "降順", + "kibana-react.savedObjects.saveModal.cancelButtonLabel": "キャンセル", + "kibana-react.savedObjects.saveModal.descriptionLabel": "説明", + "kibana-react.savedObjects.saveModal.duplicateTitleDescription": "{confirmSaveLabel} をクリックすると、既存の {objectType} が上書きされます。", + "kibana-react.savedObjects.saveModal.duplicateTitleLabel": "「{title}」というタイトルの {objectType} が既に存在します", + "kibana-react.savedObjects.saveModal.saveAsNewLabel": "新規 {objectType} として保存", + "kibana-react.savedObjects.saveModal.saveButtonLabel": "保存", + "kibana-react.savedObjects.saveModal.saveTitle": "{objectType} を保存", + "kibana-react.savedObjects.saveModal.titleLabel": "タイトル", "inspector.closeButton": "インスペクターを閉じる", "inspector.reqTimestampDescription": "リクエストの開始が記録された時刻です", "inspector.reqTimestampKey": "リクエストのタイムスタンプ", @@ -765,6 +786,7 @@ "console.welcomePage.quickTips.useWrenchMenuDescription": "レンチメニューで他の便利な機能が使えます。", "console.welcomePage.quickTipsTitle": "今のうちにいくつか簡単なコツをお教えします", "console.welcomePage.supportedRequestFormatTitle": "コンソールは cURL と同様に、コンパクトなフォーマットのリクエストを理解できます。", + "console.consoleMenu.copyAsCurlMessage": "リクエストが URL としてコピーされました", "data.filter.applyFilters.popupHeader": "適用するフィルターの選択", "data.filter.applyFiltersPopup.cancelButtonLabel": "キャンセル", "data.filter.applyFiltersPopup.saveButtonLabel": "適用", @@ -847,7 +869,7 @@ "data.search.searchBar.savedQueryFormTitle": "クエリを保存", "data.search.searchBar.savedQueryIncludeFiltersLabelText": "フィルターを含める", "data.search.searchBar.savedQueryIncludeTimeFilterLabelText": "時間フィルターを含める", - "data.search.searchBar.savedQueryNameHelpText": "名前の始めと終わりにはスペースを使用できません名前は固有でなければなりません。", + "data.search.searchBar.savedQueryNameHelpText": "名前が必要です。タイトルの始めと終わりにはスペースを使用できません。名前は固有でなければなりません。", "data.search.searchBar.savedQueryNameLabelText": "名前", "data.search.searchBar.savedQueryNoSavedQueriesText": "保存されたクエリがありません。", "data.search.searchBar.savedQueryPopoverButtonText": "保存されたクエリを表示", @@ -866,6 +888,13 @@ "data.search.searchBar.savedQueryPopoverSavedQueryListItemDescriptionAriaLabel": "{savedQueryName} の説明", "data.search.searchBar.savedQueryPopoverSavedQueryListItemSelectedButtonAriaLabel": "選択されたクエリボタン {savedQueryName} を保存しました。変更を破棄するには押してください。", "data.search.searchBar.savedQueryPopoverTitleText": "保存されたクエリ", + "data.filter.filterEditor.operatorSelectPlaceholderSelect": "選択してください", + "data.filter.filterEditor.operatorSelectPlaceholderWaiting": "待機中", + "data.filter.filterEditor.rangeInputLabel": "範囲", + "data.query.queryBar.comboboxAriaLabel": "{pageType} ページの検索とフィルタリング", + "data.search.searchBar.savedQueryForm.titleMissingText": "名前が必要です", + "data.query.queryBar.searchInputAriaLabel": "{pageType} ページの検索とフィルタリングを行うには入力を開始してください", + "data.search.searchBar.savedQueryPopoverSaveAsNewButtonAriaLabel": "新規保存クエリを保存", "embeddableApi.actions.applyFilterActionTitle": "現在のビューにフィルターを適用", "embeddableApi.addPanel.createNewDefaultOption": "新規作成...", "embeddableApi.addPanel.displayName": "パネルの追加", @@ -891,6 +920,9 @@ "embeddableApi.samples.filterableContainer.displayName": "フィルター可能なダッシュボード", "embeddableApi.samples.filterableEmbeddable.displayName": "フィルター可能", "embeddableApi.samples.helloworld.displayName": "こんにちは", + "embeddableApi.panel.enhancedDashboardPanelAriaLabel": "ダッシュボードパネル: {title}", + "embeddableApi.panel.optionsMenu.panelOptionsButtonEnhancedAriaLabel": "{title} のパネルオプション", + "embeddableApi.panel.dashboardPanelAriaLabel": "ダッシュボードパネル", "inputControl.control.noIndexPatternTooltip": "index-pattern id が見つかりませんでした: {indexPatternId}.", "inputControl.control.notInitializedTooltip": "コントロールが初期化されていません", "inputControl.control.noValuesDisableTooltip": "「{indexPatternName}」インデックスパターンでいずれのドキュメントにも存在しない「{fieldName}」フィールドがフィルターの対象になっています。異なるフィールドを選択するか、このフィールドに値が入力されているドキュメントをインデックスしてください。", @@ -939,6 +971,8 @@ "visualizations.function.visDimension.error.accessor": "入力された列名は無効です。", "visualizations.function.visDimension.help": "visConfig ディメンションオブジェクトを生成します", "interpreter.functions.esaggs.help": "AggConfig 集約を実行します", + "interpreter.functions.esaggs.inspector.dataRequest.description": "このリクエストは Elasticsearch にクエリし、ビジュアライゼーション用のデータを取得します。", + "interpreter.functions.esaggs.inspector.dataRequest.title": "データ", "expressions_np.functions.kibana_context.help": "Kibana グローバルコンテキストを更新します", "expressions_np.functions.kibana.help": "Kibana グローバルコンテキストを取得します", "expressions_np.functions.font.args.alignHelpText": "水平テキスト配置", @@ -2466,6 +2500,38 @@ "kbn.discover.reloadSavedSearchButton": "検索をリセット", "kbn.server.tutorials.apm.dotNetClient.download.textPre": "[NuGet]({allNuGetPackagesLink}) から .NET アプリケーションにエージェントパッケージを追加してください。用途の異なる複数の NuGet パッケージがあります。\n\nEntity Framework Core の ASP.NET Core アプリケーションの場合は、[Elastic.Apm.NetCoreAll]({netCoreAllApmPackageLink}) パッケージをダウンロードしてください。このパッケージは、自動的にすべてのエージェントコンポーネントをアプリケーションに追加します。\n\n 依存性を最低限に抑えたい場合、ASP.NET Core の監視のみに [Elastic.Apm.AspNetCore]({aspNetCorePackageLink}) パッケージ、または Entity Framework Core の監視のみに [Elastic.Apm.EfCore]({efCorePackageLink}) パッケージを使用することができます。\n\n 手動インストルメンテーションのみにパブリック Agent API を使用する場合は、[Elastic.Apm]({elasticApmPackageLink}) パッケージを使用してください。", "kbn.server.tutorials.apm.elasticCloud.textPre": "APM サーバーを有効にするには、[the Elastic Cloud console](https://cloud.elastic.co/deployments?q={cloudId}) に移動し、展開設定で APM を有効にします。有効にした後、このページを更新してください。", + "kbn.advancedSettings.defaultRoute.defaultRouteText": "この設定は、Kibana 起動時のデフォルトのルートを設定します。この設定で、Kibana 起動時のランディングページを変更できます。ルートはスラッシュ (\"/\") で始まる必要があります。", + "kbn.advancedSettings.defaultRoute.defaultRouteTitle": "デフォルトのルート", + "kbn.advancedSettings.defaultRoute.defaultRouteValidationMessage": "ルートはスラッシュ (\"/\") で始まる必要があります。", + "kbn.context.loadButtonLabel": "読み込み", + "kbn.context.newerDocumentsAriaLabel": "新しいドキュメントの数", + "kbn.context.newerDocumentsWarning": "アンカーよりも新しいドキュメントは {docCount} 件しか見つかりませんでした。", + "kbn.context.newerDocumentsWarningZero": "アンカーよりも新しいドキュメントは見つかりませんでした。", + "kbn.context.olderDocumentsAriaLabel": "古いドキュメントの数", + "kbn.context.olderDocumentsWarning": "アンカーよりも古いドキュメントは {docCount} 件しか見つかりませんでした。", + "kbn.context.olderDocumentsWarningZero": "アンカーよりも古いドキュメントは見つかりませんでした。", + "kbn.discover.fieldChooser.fieldFilterFacetButtonLabel": "フィルタリングされたフィールド", + "kbn.discover.fieldChooser.indexPattern.changeLinkLabel": "変更", + "kbn.discover.fieldChooser.indexPattern.changeLinkTooltip": "現在のインデックスパターンを変更", + "kbn.discover.fieldChooser.searchPlaceHolder": "検索フィールド", + "kbn.discover.histogram.partialData.bucketTooltipText": "選択された時間範囲にはこのバケット全体は含まれていませんが、一部データが含まれている可能性があります。", + "kbn.doc.failedToLocateIndexPattern": "ID {indexPatternId} に一致するインデックスパターンがありません", + "kbn.doc.somethingWentWrongDescriptionAddon": "インデックスが存在することを確認してください。", + "kbn.management.objects.objectsTable.export.successWithMissingRefsNotification": "ファイルはバックグラウンドでダウンロード中です。一部の関連オブジェクトが見つかりませんでした。足りないオブジェクトの一覧は、エクスポートされたファイルの最後の行をご覧ください。", + "kbn.management.settings.categoryNames.siemLabel": "SIEM", + "kbn.management.settings.field.imageTooLargeErrorMessage": "画像が大きすぎます。最大サイズは {maxSizeDescription} です", + "kbn.server.tutorials.couchdbMetrics.artifacts.dashboards.linkLabel": "CouchDB メトリックダッシュボード", + "kbn.server.tutorials.couchdbMetrics.longDescription": "「couchdb」Metricbeat モジュールが、CouchDB から監視メトリックを取得します。[詳細] ({learnMoreLink})。", + "kbn.server.tutorials.couchdbMetrics.nameTitle": "CouchDB メトリック", + "kbn.server.tutorials.couchdbMetrics.shortDescription": "CouchdB サーバーから監視メトリックを取得します。", + "kbn.visualize.listing.betaTitle": "ベータ", + "kbn.visualize.listing.betaTooltip": "このビジュアライゼーションはベータ段階で、変更される可能性があります。デザインとコードはオフィシャル GA 機能よりも完成度が低く、現状のまま保証なしで提供されています。ベータ機能にはオフィシャル GA 機能の SLA が適用されません", + "kbn.visualize.newVisWizard.betaDescription": "このビジュアライゼーションはベータ段階で、変更される可能性があります。デザインとコードはオフィシャル GA 機能よりも完成度が低く、現状のまま保証なしで提供されています。ベータ機能にはオフィシャル GA 機能の SLA が適用されません", + "kbn.visualize.newVisWizard.betaTitle": "ベータ", + "kbn.visualize.pageHeading": "{chartName} {chartType} ビジュアライゼーション", + "kbn.advancedSettings.courier.batchSearchesText": "無効の場合、ダッシュボードパネルは個々に読み込まれ、検索リクエストはユーザーが移動するか\n クエリを更新すると停止します。有効の場合、ダッシュボードパネルはすべてのデータが読み込まれると同時に読み込まれ、\n 検索は停止しません。", + "kbn.doc.couldNotFindDocumentsDescription": "その ID に一致するドキュメントがありません。", + "kbn.doc.somethingWentWrongDescription": "{indexName} が欠けています。", "kbnDocViews.table.fieldNamesBeginningWithUnderscoreUnsupportedTooltip": "{underscoreSign} で始まるフィールド名はサポートされていません", "kbnDocViews.table.filterForFieldPresentButtonAriaLabel": "現在のフィールドのフィルター", "kbnDocViews.table.filterForFieldPresentButtonTooltip": "現在のフィールドのフィルター", @@ -2613,6 +2679,59 @@ "kbnVislibVisTypes.editors.pie.showLabelsLabel": "ラベルを表示", "kbnVislibVisTypes.editors.pie.showTopLevelOnlyLabel": "トップレベルのみ表示", "kbnVislibVisTypes.editors.pie.showValuesLabel": "値を表示", + "kbnVislibVisTypes.area.countText": "カウント", + "kbnVislibVisTypes.area.tabs.metricsAxesTitle": "メトリックと軸", + "kbnVislibVisTypes.area.tabs.panelSettingsTitle": "パネル設定", + "kbnVislibVisTypes.axisModes.normalText": "標準", + "kbnVislibVisTypes.axisModes.percentageText": "パーセンテージ", + "kbnVislibVisTypes.axisModes.silhouetteText": "シルエット", + "kbnVislibVisTypes.axisModes.wiggleText": "振動", + "kbnVislibVisTypes.categoryAxis.rotate.angledText": "傾斜", + "kbnVislibVisTypes.categoryAxis.rotate.horizontalText": "横", + "kbnVislibVisTypes.categoryAxis.rotate.verticalText": "縦", + "kbnVislibVisTypes.chartModes.normalText": "標準", + "kbnVislibVisTypes.chartModes.stackedText": "スタック", + "kbnVislibVisTypes.chartTypes.areaText": "エリア", + "kbnVislibVisTypes.chartTypes.barText": "バー", + "kbnVislibVisTypes.chartTypes.lineText": "折れ線", + "kbnVislibVisTypes.controls.colorRanges.errorText": "各範囲は前の範囲よりも大きくなければなりません。", + "kbnVislibVisTypes.controls.colorSchema.colorSchemaLabel": "カラー図表", + "kbnVislibVisTypes.controls.colorSchema.howToChangeColorsDescription": "それぞれの色は凡例で変更できます。", + "kbnVislibVisTypes.controls.colorSchema.resetColorsButtonLabel": "色をリセット", + "kbnVislibVisTypes.controls.colorSchema.reverseColorSchemaLabel": "図表を反転", + "kbnVislibVisTypes.controls.heatmapOptions.labelsTitle": "ラベル", + "kbnVislibVisTypes.controls.heatmapOptions.useCustomRangesLabel": "カスタム範囲を使用", + "kbnVislibVisTypes.controls.pointSeries.categoryAxis.alignLabel": "配置", + "kbnVislibVisTypes.controls.pointSeries.series.showDotsLabel": "点を表示", + "kbnVislibVisTypes.controls.pointSeries.seriesAccordionAriaLabel": "{agg} オプションを切り替える", + "kbnVislibVisTypes.controls.pointSeries.valueAxes.addButtonTooltip": "Y 軸を追加します", + "kbnVislibVisTypes.controls.pointSeries.valueAxes.customExtentsLabel": "カスタム範囲", + "kbnVislibVisTypes.controls.pointSeries.valueAxes.minErrorMessage": "最低値は最高値よりも低く設定する必要があります", + "kbnVislibVisTypes.controls.pointSeries.valueAxes.removeButtonTooltip": "Y 軸を削除します", + "kbnVislibVisTypes.controls.pointSeries.valueAxes.toggleCustomExtendsAriaLabel": "カスタム範囲を切り替える", + "kbnVislibVisTypes.controls.pointSeries.valueAxes.toggleOptionsAriaLabel": "{axisName} オプションを切り替える", + "kbnVislibVisTypes.editors.heatmap.highlightLabelTooltip": "チャートのカーソルを当てた部分と凡例の対応するラベルをハイライトします。", + "kbnVislibVisTypes.editors.pointSeries.thresholdLine.colorLabel": "ラインカラー", + "kbnVislibVisTypes.editors.pointSeries.thresholdLine.showLabel": "しきい線を表示", + "kbnVislibVisTypes.editors.pointSeries.thresholdLine.styleLabel": "ラインスタイル", + "kbnVislibVisTypes.editors.pointSeries.thresholdLineSettingsTitle": "しきい線", + "kbnVislibVisTypes.interpolationModes.smoothedText": "スムーズ", + "kbnVislibVisTypes.interpolationModes.steppedText": "ステップ", + "kbnVislibVisTypes.interpolationModes.straightText": "直線", + "kbnVislibVisTypes.scaleTypes.linearText": "直線", + "kbnVislibVisTypes.scaleTypes.logText": "ログ", + "kbnVislibVisTypes.scaleTypes.squareRootText": "平方根", + "kbnVislibVisTypes.thresholdLine.style.dashedText": "鎖線", + "kbnVislibVisTypes.thresholdLine.style.dotdashedText": "点線", + "kbnVislibVisTypes.thresholdLine.style.fullText": "完全", + "kbnVislibVisTypes.controls.heatmapOptions.colorScaleLabel": "カラースケール", + "kbnVislibVisTypes.controls.heatmapOptions.percentageModeLabel": "パーセンテージモード", + "kbnVislibVisTypes.controls.heatmapOptions.scaleToDataBoundsLabel": "データバウンドに合わせる", + "kbnVislibVisTypes.controls.pointSeries.valueAxes.scaleToDataBounds.boundsMargin": "境界マージン", + "kbnVislibVisTypes.controls.pointSeries.valueAxes.scaleToDataBounds.minNeededBoundsMargin": "境界マージンは 0 以上でなければなりません", + "kbnVislibVisTypes.editors.heatmap.basicSettingsTitle": "基本設定", + "kbnVislibVisTypes.editors.heatmap.heatmapSettingsTitle": "ヒートマップ設定", + "kbnVislibVisTypes.editors.heatmap.highlightLabel": "ハイライト範囲", "visTypeMetric.colorModes.backgroundOptionLabel": "背景", "visTypeMetric.colorModes.labelsOptionLabel": "ラベル", "visTypeMetric.colorModes.noneOptionLabel": "なし", @@ -2635,6 +2754,11 @@ "visTypeMetric.function.showLabels.help": "メトリック値の下にラベルを表示します。", "visTypeMetric.function.subText.help": "メトリックの下に表示するカスタムテキスト", "visTypeMetric.function.useRanges.help": "有効な色範囲です。", + "visTypeMetric.params.settingsTitle": "設定", + "visTypeMetric.params.showTitleLabel": "タイトルを表示", + "visTypeMetric.params.color.useForLabel": "使用する色", + "visTypeMetric.params.percentageModeLabel": "パーセンテージモード", + "visTypeMetric.params.style.fontSizeLabel": "ポイント単位のメトリックフォントサイズ", "regionMap.choroplethLayer.downloadingVectorData404ErrorMessage": "{name} の取得時にサーバーから「404」が返されます。指定された場所にファイルが存在することを確認してください。", "regionMap.choroplethLayer.downloadingVectorDataErrorMessage": "{name} ファイルをダウンロードできません。サーバーの CORS 構成で、このホストの Kibana アプリケーションからのリクエストが許可されていることを確認してください。", "regionMap.choroplethLayer.downloadingVectorDataErrorMessageTitle": "ベクトルデータのダウンロード中にエラーが発生しました", @@ -2751,6 +2875,13 @@ "visTypeTable.vis.noResultsFoundTitle": "結果が見つかりませんでした", "visTypeTable.params.PercentageColLabel": "パーセンテージ列", "visTypeTable.params.percentageTableColumnName": "{title} パーセント", + "visTypeTable.params.defaultPercentageCol": "非表示", + "visTypeTable.totalAggregations.averageText": "平均", + "visTypeTable.totalAggregations.countText": "カウント", + "visTypeTable.totalAggregations.maxText": "最高", + "visTypeTable.totalAggregations.minText": "最低", + "visTypeTable.totalAggregations.sumText": "合計", + "visTypeTable.params.perPageLabel": "ページごとの行数", "visTypeTagCloud.feedbackMessage.tooSmallContainerDescription": "コンテナーが小さすぎてクラウド全体を表示できません。タグが切り取られたか省略されている可能性があります。", "visTypeTagCloud.feedbackMessage.truncatedTagsDescription": "描写時間が長くなるのを防ぐため、タグの数が切り捨てられています。", "visTypeTagCloud.function.bucket.help": "バケットディメンションの構成です。", @@ -3138,6 +3269,7 @@ "timelion.vis.expressionLabel": "Timelion 式", "timelion.vis.intervalLabel": "間隔", "timelion.help.functions.notAllowedGraphiteUrl": "この Graphite URL は kibana.yml ファイルで構成されていません。\n 「'timelion.graphiteUrls」で kibana.yml ファイルの Graphite サーバーリストを構成して\n Kibana の高度な設定で選択してください", + "timelion.emptyExpressionErrorMessage": "Timelion エラー式が入力されていません", "visTypeVega.editor.formatError": "仕様のフォーマット中にエラーが発生", "visTypeVega.editor.reformatAsHJSONButtonLabel": "HJSON に変換", "visTypeVega.editor.reformatAsJSONButtonLabel": "JSON に変換しコメントを削除", @@ -3224,7 +3356,7 @@ "xpack.apm.errorGroupDetails.occurrencesLongLabel": "{occCount} 件", "xpack.apm.errorGroupDetails.occurrencesShortLabel": "{occCount} 件", "xpack.apm.errorGroupDetails.unhandledLabel": "未対応", - "xpack.apm.errorGroupDetails.viewOccurrencesInDiscoverButtonLabel": "ディスカバリで {occurrencesCount} 件のオカレンスを表示", + "xpack.apm.errorGroupDetails.viewOccurrencesInDiscoverButtonLabel": "ディスカバリで {occurrencesCount} 件の{occurrencesCount, plural, one {ドキュメント} other {ドキュメント}}を表示。", "xpack.apm.errorsTable.errorMessageAndCulpritColumnLabel": "エラーメッセージと原因", "xpack.apm.errorsTable.groupIdColumnLabel": "グループ ID", "xpack.apm.errorsTable.latestOccurrenceColumnLabel": "最近のオカレンス", @@ -3454,6 +3586,89 @@ "xpack.apm.transactionDetails.traceSampleTitle": "トレースのサンプル", "xpack.apm.transactionsTable.notFoundLabel": "トランザクションが見つかりませんでした。", "xpack.apm.waterfall.exceedsMax": "このトレースの項目数は表示されている範囲を超えています", + "xpack.apm.agentMetrics.java.gcRate": "GC レート", + "xpack.apm.agentMetrics.java.gcRateChartTitle": "1 分ごとのごみ収集レート", + "xpack.apm.agentMetrics.java.gcTime": "GC 時間", + "xpack.apm.agentMetrics.java.gcTimeChartTitle": "1 分ごとのごみ収集の時間", + "xpack.apm.breadcrumb.nodesTitle": "JVM", + "xpack.apm.breadcrumb.serviceMapTitle": "サービスマップ", + "xpack.apm.errorGroupDetails.relatedTransactionSample": "関連トランザクションサンプル", + "xpack.apm.home.serviceMapTabLabel": "サービスマップ", + "xpack.apm.jvmsTable.cpuColumnLabel": "CPU 平均", + "xpack.apm.jvmsTable.explainServiceNodeNameMissing": "これらのメトリックが所属する JVM を特定できませんでした。7.5 よりも古い APM Server を実行していることが原因である可能性が高いです。この問題は APM Server 7.5 以降にアップグレードすることで解決されます。", + "xpack.apm.jvmsTable.heapMemoryColumnLabel": "ヒープ領域の平均", + "xpack.apm.jvmsTable.nameColumnLabel": "名前", + "xpack.apm.jvmsTable.nameExplanation": "JVM 名はデフォルトでコンピューター ID (該当する場合) またはホスト名ですが、エージェントの「'service_node_name」で手動で構成することもできます。", + "xpack.apm.jvmsTable.noJvmsLabel": "JVM が見つかりませんでした", + "xpack.apm.jvmsTable.nonHeapMemoryColumnLabel": "非ヒープ領域の平均", + "xpack.apm.jvmsTable.threadCountColumnLabel": "最大スレッド数", + "xpack.apm.metadataTable.section.errorLabel": "エラー", + "xpack.apm.metadataTable.section.pageLabel": "ページ", + "xpack.apm.metadataTable.section.spanLabel": "スパン", + "xpack.apm.metadataTable.section.traceLabel": "トレース", + "xpack.apm.metadataTable.section.transactionLabel": "トランザクション", + "xpack.apm.metadataTable.section.userAgentLabel": "ユーザーエージェント", + "xpack.apm.percentOfParent": "({parentType, select, transaction { 件中 {value} 件のトランザクション} トレース {trace} })", + "xpack.apm.serviceDetails.nodesTabLabel": "JVM", + "xpack.apm.serviceMap.fullscreen": "全画面", + "xpack.apm.serviceMap.zoomIn": "ズームイン", + "xpack.apm.serviceMap.zoomOut": "ズームアウト", + "xpack.apm.serviceNodeMetrics.containerId": "コンテナー ID", + "xpack.apm.serviceNodeMetrics.host": "ホスト", + "xpack.apm.serviceNodeMetrics.unidentifiedServiceNodesWarningDocumentationLink": "APM Server のドキュメンテーション", + "xpack.apm.serviceNodeMetrics.unidentifiedServiceNodesWarningText": "これらのメトリックが所属する JVM を特定できませんでした。7.5 よりも古い APM Server を実行していることが原因である可能性が高いです。この問題は APM Server 7.5 以降にアップグレードすることで解決されます。アップグレードに関する詳細は、{link} をご覧ください。代わりに Kibana クエリバーを使ってホスト名、コンテナー ID、またはその他フィールドでフィルタリングすることもできます。", + "xpack.apm.serviceNodeMetrics.unidentifiedServiceNodesWarningTitle": "JVM を特定できませんでした", + "xpack.apm.serviceNodeNameMissing": "(空)", + "xpack.apm.settings.agentConf.allOptionLabel": "すべて", + "xpack.apm.settings.agentConf.cancelButtonLabel": "キャンセル", + "xpack.apm.settings.agentConf.configTable.appliedTooltipMessage": "1 つまたは複数のエージェントにより適用されました。", + "xpack.apm.settings.agentConf.configTable.captureBodyColumnLabel": "キャプチャ本文", + "xpack.apm.settings.agentConf.configTable.configTable.failurePromptText": "エージェントの構成一覧を取得できませんでした。ユーザーに十分なパーミッションがない可能性があります。", + "xpack.apm.settings.agentConf.configTable.createConfigButtonLabel": "構成の作成", + "xpack.apm.settings.agentConf.configTable.notAppliedTooltipMessage": "まだエージェントにより適用されていません", + "xpack.apm.settings.agentConf.configTable.transactionMaxSpansColumnLabel": "トランザクションの最大範囲", + "xpack.apm.settings.agentConf.createConfigTitle": "構成の作成", + "xpack.apm.settings.agentConf.editConfigTitle": "構成の編集", + "xpack.apm.settings.agentConf.flyout.deleteSection.buttonLabel": "削除", + "xpack.apm.settings.agentConf.flyout.deleteSection.deleteConfigFailedText": "{serviceName} の構成を削除中にエラーが発生しました。エラー「{errorMessage}」", + "xpack.apm.settings.agentConf.flyout.deleteSection.deleteConfigFailedTitle": "構成を削除できませんでした", + "xpack.apm.settings.agentConf.flyout.deleteSection.deleteConfigSucceededText": "「{serviceName}」の構成が正常に削除されました。エージェントに反映されるまでに少し時間がかかります。", + "xpack.apm.settings.agentConf.flyout.deleteSection.deleteConfigSucceededTitle": "構成が削除されました", + "xpack.apm.settings.agentConf.flyOut.serviceSection.alreadyConfiguredOption": "既に構成済み", + "xpack.apm.settings.agentConf.flyOut.serviceSection.selectPlaceholder": "選択してください", + "xpack.apm.settings.agentConf.flyOut.serviceSection.serviceEnvironmentSelectHelpText": "構成ごとに 1 つの環境のみがサポートされます。", + "xpack.apm.settings.agentConf.flyOut.serviceSection.serviceEnvironmentSelectLabel": "環境", + "xpack.apm.settings.agentConf.flyOut.serviceSection.serviceNameSelectHelpText": "構成するサービスを選択してください。", + "xpack.apm.settings.agentConf.flyOut.serviceSection.serviceNameSelectLabel": "名前", + "xpack.apm.settings.agentConf.flyOut.serviceSection.title": "サービス", + "xpack.apm.settings.agentConf.flyOut.settingsSection.captureBodyInputHelpText": "HTTP リクエストのトランザクションの場合、エージェントはリクエスト本文 (POST 変数など) をキャプチャすることができます。デフォルトは「off」です。", + "xpack.apm.settings.agentConf.flyOut.settingsSection.captureBodyInputLabel": "本文をキャプチャ", + "xpack.apm.settings.agentConf.flyOut.settingsSection.captureBodyInputPlaceholderText": "オプションを選択", + "xpack.apm.settings.agentConf.flyOut.settingsSection.sampleRateConfigurationInputErrorText": "サンプルレートは 0.000 ~ 1 の範囲でなければなりません", + "xpack.apm.settings.agentConf.flyOut.settingsSection.sampleRateConfigurationInputHelpText": "0.000 ~ 1.0 の範囲のレートを選択してください。デフォルトは 1.0 (トレースの 100%) です。", + "xpack.apm.settings.agentConf.flyOut.settingsSection.sampleRateConfigurationInputLabel": "トランザクションのサンプルレート", + "xpack.apm.settings.agentConf.flyOut.settingsSection.sampleRateConfigurationInputPlaceholderText": "サンプルレートを設定", + "xpack.apm.settings.agentConf.flyOut.settingsSection.title": "オプション", + "xpack.apm.settings.agentConf.flyOut.settingsSection.transactionMaxSpansConfigInputErrorText": "0 と 32000 の間でなければなりません", + "xpack.apm.settings.agentConf.flyOut.settingsSection.transactionMaxSpansConfigInputHelpText": "トランザクションごとに記録される範囲を制限します。デフォルトは 500 です。", + "xpack.apm.settings.agentConf.flyOut.settingsSection.transactionMaxSpansConfigInputLabel": "トランザクションの最大範囲", + "xpack.apm.settings.agentConf.flyOut.settingsSection.transactionMaxSpansConfigInputPlaceholderText": "トランザクションの最大範囲を設定", + "xpack.apm.settings.agentConf.saveConfig.failed.text": "「{serviceName}」の構成を保存中にエラーが発生しました。エラー「{errorMessage}」", + "xpack.apm.settings.agentConf.saveConfig.failed.title": "構成を保存できませんでした", + "xpack.apm.settings.agentConf.saveConfig.succeeded.text": "「{serviceName}」の構成が保存されました。エージェントに反映されるまでに少し時間がかかります。", + "xpack.apm.settings.agentConf.saveConfig.succeeded.title": "構成が保存されました", + "xpack.apm.settings.agentConf.saveConfigurationButtonLabel": "保存", + "xpack.apm.tracesTable.impactColumnDescription": "ご利用のサービスで最も頻繁に使用されていて、最も遅いエンドポイントです。相対的平均時間に 1 分ごとのトランザクション数をかけて計算されます。", + "xpack.apm.transactionBreakdown.noData": "この時間範囲のデータがありません。", + "xpack.apm.transactionDetails.errorCount": "{errorCount, number} {errorCount, plural, one {件のエラー} other {件のエラー}}", + "xpack.apm.transactionDetails.requestMethodLabel": "リクエストメソッド", + "xpack.apm.transactionDetails.spanFlyout.spanAction": "アクション", + "xpack.apm.transactionDetails.spanFlyout.spanSubtype": "サブタイプ", + "xpack.apm.transactionDetails.spanFlyout.spanType": "タイプ", + "xpack.apm.transactionDetails.statusCode": "ステータスコード", + "xpack.apm.transactionDetails.userAgentAndVersionLabel": "ユーザーエージェントとバージョン", + "xpack.apm.transactionDurationLabel": "期間", + "xpack.apm.transactionsTable.impactColumnDescription": "ご利用のサービスで最も頻繁に使用されていて、最も遅いエンドポイントです。相対的平均時間に 1 分ごとのトランザクション数をかけて計算されます。", "xpack.beatsManagement.beat.actionSectionTypeLabel": "タイプ: {beatType}。", "xpack.beatsManagement.beat.actionSectionVersionLabel": "バージョン: {beatVersion}", "xpack.beatsManagement.beat.beatNameAndIdTitle": "ビート: {nameOrNoName} (ID: {id})", @@ -3936,6 +4151,712 @@ "xpack.canvas.functions.urlparam.args.defaultHelpText": "{URL} パラメーターが指定されていないときに戻される文字列です。", "xpack.canvas.functions.urlparam.args.paramHelpText": "取得する {URL} ハッシュパラメーターです。", "xpack.canvas.functions.urlparamHelpText": "表現で使用する {URL} パラメーターを取得します。{urlparamFn} 関数は常に {TYPE_STRING} を戻します。たとえば、値 {value} を {URL} {example} のパラメーター {myVar} から取得できます)。", + "xpack.canvas.app.loadErrorMessage": "メッセージ: {error}", + "xpack.canvas.app.loadErrorTitle": "Canvas の読み込みに失敗", + "xpack.canvas.app.loadingMessage": "Canvas を読み込み中", + "xpack.canvas.argAddPopover.addAriaLabel": "引数を追加", + "xpack.canvas.argFormAdvancedFailure.applyButtonLabel": "適用", + "xpack.canvas.argFormAdvancedFailure.resetButtonLabel": "リセット", + "xpack.canvas.argFormAdvancedFailure.rowErrorMessage": "無効な表現", + "xpack.canvas.argFormArgSimpleForm.removeAriaLabel": "削除", + "xpack.canvas.argFormArgSimpleForm.requiredTooltip": "この引数は必須です。数値を入力してください。", + "xpack.canvas.argFormPendingArgValue.loadingMessage": "読み込み中", + "xpack.canvas.argFormSimpleFailure.failureTooltip": "この引数のインターフェースが値を解析できなかったため、フォールバックインプットが使用されています", + "xpack.canvas.asset.copyAssetTooltip": "ID をクリップボードにコピー", + "xpack.canvas.asset.createImageTooltip": "画像エレメントを作成", + "xpack.canvas.asset.deleteAssetTooltip": "削除", + "xpack.canvas.asset.downloadAssetTooltip": "ダウンロード", + "xpack.canvas.asset.thumbnailAltText": "アセットのサムネイル", + "xpack.canvas.assetManager.confirmModalButtonLabel": "削除", + "xpack.canvas.assetManager.confirmModalDetail": "このアセットを削除してよろしいですか?", + "xpack.canvas.assetManager.confirmModalTitle": "アセットの削除", + "xpack.canvas.assetManager.manageButtonLabel": "アセットの管理", + "xpack.canvas.assetModal.emptyAssetsDescription": "アセットをインポートして開始します", + "xpack.canvas.assetModal.filePickerPromptText": "画像を選択するかドラッグ &amp; ドロップしてください", + "xpack.canvas.assetModal.loadingText": "画像をアップロード中", + "xpack.canvas.assetModal.modalCloseButtonLabel": "閉じる", + "xpack.canvas.assetModal.modalDescription": "以下はこのワークパッドの画像アセットです。現在使用中のアセットは現時点で特定できません。スペースを取り戻すには、アセットを削除してください。", + "xpack.canvas.assetModal.modalTitle": "ワークパッドアセットの管理", + "xpack.canvas.assetModal.spacedUsedText": "{percentageUsed}% のスペースを使用中", + "xpack.canvas.assetpicker.assetAltText": "アセットのサムネイル", + "xpack.canvas.colorManager.addAriaLabel": "色を追加", + "xpack.canvas.colorManager.codePlaceholder": "カラーコード", + "xpack.canvas.colorManager.removeAriaLabel": "色を削除", + "xpack.canvas.customElementModal.cancelButtonLabel": "キャンセル", + "xpack.canvas.customElementModal.descriptionInputLabel": "説明", + "xpack.canvas.customElementModal.elementPreviewTitle": "エレメントのプレビュー", + "xpack.canvas.customElementModal.imageFilePickerPlaceholder": "画像を選択するかドラッグ &amp; ドロップしてください", + "xpack.canvas.customElementModal.imageInputDescription": "エレメントのスクリーンショットを作成してここにアップロードします。保存後に行うこともできます。", + "xpack.canvas.customElementModal.imageInputLabel": "サムネイル画像", + "xpack.canvas.customElementModal.nameInputLabel": "名前", + "xpack.canvas.customElementModal.remainingCharactersDescription": "残り {numberOfRemainingCharacter} 文字", + "xpack.canvas.customElementModal.saveButtonLabel": "保存", + "xpack.canvas.datasourceDatasourceComponent.changeButtonLabel": "データソースを変更", + "xpack.canvas.datasourceDatasourceComponent.previewButtonLabel": "プレビュー", + "xpack.canvas.datasourceDatasourceComponent.saveButtonLabel": "保存", + "xpack.canvas.datasourceDatasourcePreview.emptyFirstLineDescription": "検索条件に一致するドキュメントが見つかりませんでした。", + "xpack.canvas.datasourceDatasourcePreview.emptySecondLineDescription": "データソース設定を確認して再試行してください。", + "xpack.canvas.datasourceDatasourcePreview.emptyTitle": "ドキュメントが見つかりませんでした", + "xpack.canvas.datasourceDatasourcePreview.modalDescription": "このデータを使用するには、サイドバーの {saveLabel} をクリックしてください。", + "xpack.canvas.datasourceDatasourcePreview.modalTitle": "データソースのプレビュー", + "xpack.canvas.datasourceNoDatasource.panelDescription": "このエレメントにはデータソースが添付されていません。これは通常、エレメントが画像または他の不動アセットであることが原因です。その場合、表現が正しい形式であることを確認することをお勧めします。", + "xpack.canvas.datasourceNoDatasource.panelTitle": "データソースなし", + "xpack.canvas.elementConfig.failedLabel": "失敗", + "xpack.canvas.elementConfig.loadedLabel": "読み込み済み", + "xpack.canvas.elementConfig.progressLabel": "進捗", + "xpack.canvas.elementConfig.title": "エレメント", + "xpack.canvas.elementConfig.totalLabel": "合計", + "xpack.canvas.elementControls.deleteAriaLabel": "エレメントを削除", + "xpack.canvas.elementControls.deleteToolTip": "削除", + "xpack.canvas.elementControls.editAriaLabel": "エレメントを編集", + "xpack.canvas.elementControls.editToolTip": "編集", + "xpack.canvas.elementSettings.dataTabLabel": "データ", + "xpack.canvas.elementSettings.displayTabLabel": "表示", + "xpack.canvas.elementTypes.addNewElementDescription": "ワークパッドのエレメントをグループ化して保存し、新規エレメントを作成します", + "xpack.canvas.elementTypes.addNewElementTitle": "新規エレメントの作成", + "xpack.canvas.elementTypes.cancelButtonLabel": "キャンセル", + "xpack.canvas.elementTypes.deleteButtonLabel": "削除", + "xpack.canvas.elementTypes.deleteElementDescription": "このエレメントを削除してよろしいですか?", + "xpack.canvas.elementTypes.deleteElementTitle": "エレメント「{elementName}」を削除しますか?", + "xpack.canvas.elementTypes.editElementTitle": "エレメントを編集", + "xpack.canvas.elementTypes.elementsTitle": "エレメント", + "xpack.canvas.elementTypes.findElementPlaceholder": "エレメントを検索", + "xpack.canvas.elementTypes.myElementsTitle": "マイエレメント", + "xpack.canvas.embedObject.noMatchingObjectsMessage": "一致するオブジェクトが見つかりませんでした。", + "xpack.canvas.embedObject.titleText": "オブジェクトの埋め込み", + "xpack.canvas.error.actionsElements.invaludArgIndexErrorMessage": "無効な引数インデックス: {index}", + "xpack.canvas.error.downloadWorkpad.downloadFailureErrorMessage": "ワークパッドをダウンロードできませんでした", + "xpack.canvas.error.downloadWorkpad.downloadRenderedWorkpadFailureErrorMessage": "レンダリングされたワークパッドをダウンロードできませんでした", + "xpack.canvas.error.downloadWorkpad.downloadRuntimeFailureErrorMessage": "共有可能なランタイムをダウンロードできませんでした", + "xpack.canvas.error.downloadWorkpad.downloadZippedRuntimeFailureErrorMessage": "ZIP ファイルをダウンロードできませんでした", + "xpack.canvas.error.esPersist.saveFailureTitle": "変更を Elasticsearch に保存できませんでした", + "xpack.canvas.error.esPersist.tooLargeErrorMessage": "サーバーからワークパッドデータが大きすぎるという返答が返されました。これは通常、アップロードされた画像アセットが Kibana またはプロキシに大きすぎることを意味します。アセットマネージャーでいくつかのアセットを削除してみてください。", + "xpack.canvas.error.esPersist.updateFailureTitle": "ワークパッドを更新できませんでした", + "xpack.canvas.error.esService.defaultIndexFetchErrorMessage": "デフォルトのインデックスを取得できませんでした", + "xpack.canvas.error.esService.fieldsFetchErrorMessage": "「{index}」の Elasticsearch フィールドを取得できませんでした", + "xpack.canvas.error.esService.indicesFetchErrorMessage": "Elasticsearch インデックスを取得できませんでした", + "xpack.canvas.error.RenderWithFn.renderErrorMessage": "「{functionName}」フィールドをレンダリング中", + "xpack.canvas.error.repeatImage.missingMaxArgument": "{emptyImageArgument} を提供する場合、{maxArgument} の設定が必要です。", + "xpack.canvas.error.workpadLoader.cloneFailureErrorMessage": "ワークパッドのクローンを作成できませんでした", + "xpack.canvas.error.workpadLoader.deleteFailureErrorMessage": "すべてのワークパッドを削除できませんでした", + "xpack.canvas.error.workpadLoader.findFailureErrorMessage": "ワークパッドが見つかりませんでした", + "xpack.canvas.error.workpadLoader.uploadFailureErrorMessage": "ワークパッドをアップロードできませんでした", + "xpack.canvas.error.workpadRoutes.createFailureErrorMessage": "ワークパッドを作成できませんでした", + "xpack.canvas.error.workpadRoutes.loadFailureErrorMessage": "ID でワークパッドを読み込めませんでした", + "xpack.canvas.error.workpadUpload.acceptJSONOnlyErrorMessage": "{JSON} 個のファイルしか受け付けられませんでした", + "xpack.canvas.error.workpadUpload.fileUploadFailureWithoutFileNameErrorMessage": "ファイルをアップロードできませんでした", + "xpack.canvas.error.workpadUpload.missingPropertiesErrorMessage": "{CANVAS} ワークパッドに必要なプロパティの一部が欠けています。 {JSON} ファイルを編集して正しいプロパティ値を入力し、再試行してください。", + "xpack.canvas.errorComponent.description": "表現が失敗し次のメッセージが返されました:", + "xpack.canvas.errorComponent.title": "おっと!表現が失敗しました", + "xpack.canvas.errors.workpadUpload.fileUploadFileWithFileNameErrorMessage": "「{fileName}」をアップロードできませんでした", + "xpack.canvas.expression.cancelButtonLabel": "キャンセル", + "xpack.canvas.expression.closeButtonLabel": "閉じる", + "xpack.canvas.expression.learnLinkText": "表現構文の詳細", + "xpack.canvas.expression.maximizeButtonLabel": "エディターを最大化", + "xpack.canvas.expression.minimizeButtonLabel": "エディターを最小化", + "xpack.canvas.expression.runButtonLabel": "実行", + "xpack.canvas.expression.runTooltip": "表現を実行", + "xpack.canvas.expressionElementNotSelected.closeButtonLabel": "閉じる", + "xpack.canvas.expressionElementNotSelected.selectDescription": "表現インプットを表示するエレメントを選択します", + "xpack.canvas.expressionInput.argReferenceAliasesDetail": "{BOLD_MD_TOKEN}Aliases{BOLD_MD_TOKEN}: {aliases}", + "xpack.canvas.expressionInput.argReferenceDefaultDetail": "{BOLD_MD_TOKEN}Default{BOLD_MD_TOKEN}: {defaultVal}", + "xpack.canvas.expressionInput.argReferenceRequiredDetail": "{BOLD_MD_TOKEN}Required{BOLD_MD_TOKEN}: {required}", + "xpack.canvas.expressionInput.argReferenceTypesDetail": "{BOLD_MD_TOKEN}Types{BOLD_MD_TOKEN}: {types}", + "xpack.canvas.expressionInput.functionReferenceAccepts": "{BOLD_MD_TOKEN}Accepts{BOLD_MD_TOKEN}: {acceptTypes}", + "xpack.canvas.expressionInput.functionReferenceReturns": "{BOLD_MD_TOKEN}Returns{BOLD_MD_TOKEN}: {returnType}", + "xpack.canvas.expressionTypes.argTypes.colorDisplayName": "色", + "xpack.canvas.expressionTypes.argTypes.colorHelp": "カラーピッカー", + "xpack.canvas.expressionTypes.argTypes.containerStyle.appearanceTitle": "見た目", + "xpack.canvas.expressionTypes.argTypes.containerStyle.borderTitle": "境界", + "xpack.canvas.expressionTypes.argTypes.containerStyle.colorLabel": "色", + "xpack.canvas.expressionTypes.argTypes.containerStyle.opacityLabel": "レイヤーの透明度", + "xpack.canvas.expressionTypes.argTypes.containerStyle.overflowHiddenDropDown": "非表示", + "xpack.canvas.expressionTypes.argTypes.containerStyle.overflowLabel": "オーバーフロー", + "xpack.canvas.expressionTypes.argTypes.containerStyle.overflowVisibleDropDown": "表示", + "xpack.canvas.expressionTypes.argTypes.containerStyle.paddingLabel": "パッド", + "xpack.canvas.expressionTypes.argTypes.containerStyle.radiusLabel": "半径", + "xpack.canvas.expressionTypes.argTypes.containerStyle.styleLabel": "スタイル", + "xpack.canvas.expressionTypes.argTypes.containerStyle.thicknessLabel": "太さ", + "xpack.canvas.expressionTypes.argTypes.containerStyleLabel": "エレメントコンテナーの見た目の調整", + "xpack.canvas.expressionTypes.argTypes.containerStyleTitle": "コンテナースタイル", + "xpack.canvas.expressionTypes.argTypes.fontHelpLabel": "フォント、サイズ、色を設定します", + "xpack.canvas.expressionTypes.argTypes.fontTitle": "テキスト設定", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.barLabel": "バー", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.colorLabel": "色", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.lineLabel": "折れ線", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.noneDropDown": "なし", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.noSeriesTooltip": "データにスタイリングする数列がありません。カラーディメンションを追加してください", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.pointLabel": "ポイント", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.removeAriaLabel": "数列カラーを削除", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.selectSeriesDropDown": "数列を選択します", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.seriesIdentifierLabel": "数列識別子", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.styleLabel": "スタイル", + "xpack.canvas.expressionTypes.argTypes.seriesStyleLabel": "選択された名前付きの数列のスタイルを設定", + "xpack.canvas.expressionTypes.argTypes.seriesStyleTitle": "数列スタイル", + "xpack.canvas.expressionTypes.datasources.esdocs.ascendingDropDown": "昇順", + "xpack.canvas.expressionTypes.datasources.esdocs.descendingDropDown": "降順", + "xpack.canvas.expressionTypes.datasources.esdocs.fieldsLabel": "抽出するフィールドです。Kibana スクリプトフィールドは現在利用できません", + "xpack.canvas.expressionTypes.datasources.esdocs.fieldsTitle": "フィールド", + "xpack.canvas.expressionTypes.datasources.esdocs.fieldsWarningLabel": "このデータソースは、10 個以下のフィールドで最も高い性能を発揮します", + "xpack.canvas.expressionTypes.datasources.esdocs.indexLabel": "インデックス名を入力するか、インデックスパターンを選択してください", + "xpack.canvas.expressionTypes.datasources.esdocs.indexTitle": "インデックス", + "xpack.canvas.expressionTypes.datasources.esdocs.queryLabel": "{lucene} クエリ文字列の構文", + "xpack.canvas.expressionTypes.datasources.esdocs.queryTitle": "クエリ", + "xpack.canvas.expressionTypes.datasources.esdocs.sortFieldLabel": "ドキュメントソートフィールド", + "xpack.canvas.expressionTypes.datasources.esdocs.sortFieldTitle": "ソートフィールド", + "xpack.canvas.expressionTypes.datasources.esdocs.sortOrderLabel": "ドキュメントの並べ替え順", + "xpack.canvas.expressionTypes.datasources.esdocs.sortOrderTitle": "並べ替え順", + "xpack.canvas.expressionTypes.datasources.esdocs.warningTitle": "ご注意ください", + "xpack.canvas.expressionTypes.datasources.esdocsLabel": "Elasticsearch から未加工のドキュメントを読み込みます", + "xpack.canvas.expressionTypes.datasources.esdocsTitle": "Elasticsearch 未加工ドキュメント", + "xpack.canvas.functionForm.contextError": "エラー: {errorMessage}", + "xpack.canvas.functionForm.functionUnknown.unknownArgumentTypeError": "未知の表現タイプ「{expressionType}」", + "xpack.canvas.functions.asset.args.id": "読み込むアセットの ID です。", + "xpack.canvas.functions.asset.invalidAssetId": "ID「{assetId}」でアセットを取得できませんでした", + "xpack.canvas.functions.assetHelpText": "引数値を提供するために、Canvas ワークパッドアセットオブジェクトを取得します。通常画像です。", + "xpack.canvas.functions.filters.args.group": "使用するフィルターグループの名前です。", + "xpack.canvas.functions.filters.args.ungrouped": "フィルターグループに属するフィルターを除外しますか?", + "xpack.canvas.functions.filtersHelpText": "ワークパッドのエレメントフィルターを他 (通常データソース) で使用できるように集約します。", + "xpack.canvas.functions.savedMapHelpText": "保存されたマップオブジェクトの埋め込み可能なオブジェクトを返します", + "xpack.canvas.functions.savedSearchHelpText": "保存検索オブジェクトの埋め込み可能なオブジェクトを返します", + "xpack.canvas.functions.savedVisualizationHelpText": "保存されたビジュアライゼーションオブジェクトの埋め込み可能なオブジェクトを返します", + "xpack.canvas.functions.timelion.args.from": "時間範囲の始めの {ELASTICSEARCH} {DATEMATH} 文字列です。", + "xpack.canvas.functions.timelion.args.interval": "時系列のバケット間隔です。", + "xpack.canvas.functions.timelion.args.query": "Timelion クエリ", + "xpack.canvas.functions.timelion.args.timezone": "時間範囲のタイムゾーンです。{MOMENTJS_TIMEZONE_URL} をご覧ください。", + "xpack.canvas.functions.timelion.args.to": "時間範囲の終わりの {ELASTICSEARCH} {DATEMATH} 文字列です。", + "xpack.canvas.functions.timelionHelpText": "多くのソースから単独または複数の時系列を抽出するために、Timelion を使用します。", + "xpack.canvas.functions.to.args.type": "表現言語の既知のデータ型です。", + "xpack.canvas.functions.to.missingType": "型キャストを指定する必要があります", + "xpack.canvas.functions.toHelpText": "{CONTEXT} の型を指定された型に明確にキャストします。", + "xpack.canvas.groupSettings.multipleElementsActionsDescription": "個々の設定を編集するには、これらのエレメントの選択を解除し、 ({gKey}) を押してグループ化するか、この選択をワークパッド全体で再利用できるように新規エレメントとして保存します。", + "xpack.canvas.groupSettings.multipleElementsDescription": "現在複数エレメントが選択されています。", + "xpack.canvas.groupSettings.saveGroupDescription": "ワークパッド全体で再利用できるように、このグループを新規エレメントとして保存します。", + "xpack.canvas.groupSettings.ungroupDescription": "個々のエレメントの設定を編集できるように、({uKey}) のグループを解除します。", + "xpack.canvas.helpMenu.description": "{CANVAS} に関する情報", + "xpack.canvas.helpMenu.documentationLinkLabel": "{CANVAS} ドキュメント", + "xpack.canvas.helpMenu.keyboardShortcutsLinkLabel": "キーボードショートカット", + "xpack.canvas.keyboardShortcuts.bringFowardShortcutHelpText": "表面に移動", + "xpack.canvas.keyboardShortcuts.bringToFrontShortcutHelpText": "前に移動", + "xpack.canvas.keyboardShortcuts.cloneShortcutHelpText": "クローンを作成", + "xpack.canvas.keyboardShortcuts.copyShortcutHelpText": "コピー", + "xpack.canvas.keyboardShortcuts.cutShortcutHelpText": "切り取り", + "xpack.canvas.keyboardShortcuts.deleteShortcutHelpText": "削除", + "xpack.canvas.keyboardShortcuts.editingShortcutHelpText": "編集モードを切り替えます", + "xpack.canvas.keyboardShortcuts.fullscreenExitShortcutHelpText": "プレゼンテーションモードを終了します", + "xpack.canvas.keyboardShortcuts.fullscreenShortcutHelpText": "プレゼンテーションモードを開始します", + "xpack.canvas.keyboardShortcuts.gridShortcutHelpText": "グリッドを表示します", + "xpack.canvas.keyboardShortcuts.groupShortcutHelpText": "グループ", + "xpack.canvas.keyboardShortcuts.ignoreSnapShortcutHelpText": "スナップせずに移動、サイズ変更、回転します", + "xpack.canvas.keyboardShortcuts.multiselectShortcutHelpText": "複数エレメントを選択します", + "xpack.canvas.keyboardShortcuts.namespace.editorDisplayName": "エディターコントロール", + "xpack.canvas.keyboardShortcuts.namespace.elementDisplayName": "エレメントコントロール", + "xpack.canvas.keyboardShortcuts.namespace.expressionDisplayName": "表現コントロール", + "xpack.canvas.keyboardShortcuts.namespace.presentationDisplayName": "プレゼンテーションコントロール", + "xpack.canvas.keyboardShortcuts.nextShortcutHelpText": "次のページに移動します", + "xpack.canvas.keyboardShortcuts.nudgeDownShortcutHelpText": "{ELEMENT_NUDGE_OFFSET}px 下に移動させます", + "xpack.canvas.keyboardShortcuts.nudgeLeftShortcutHelpText": "{ELEMENT_NUDGE_OFFSET}px 左に移動させます", + "xpack.canvas.keyboardShortcuts.nudgeRightShortcutHelpText": "{ELEMENT_NUDGE_OFFSET}px 右に移動させます", + "xpack.canvas.keyboardShortcuts.nudgeUpShortcutHelpText": "{ELEMENT_NUDGE_OFFSET}px 上に移動させます", + "xpack.canvas.keyboardShortcuts.pageCycleToggleShortcutHelpText": "ページ周期を切り替えます", + "xpack.canvas.keyboardShortcuts.pasteShortcutHelpText": "貼り付け", + "xpack.canvas.keyboardShortcuts.prevShortcutHelpText": "前のページに移動します", + "xpack.canvas.keyboardShortcuts.redoShortcutHelpText": "最後の操作をやり直します", + "xpack.canvas.keyboardShortcuts.resizeFromCenterShortcutHelpText": "中央からサイズ変更します", + "xpack.canvas.keyboardShortcuts.runShortcutHelpText": "表現全体を実行します", + "xpack.canvas.keyboardShortcuts.selectBehindShortcutHelpText": "下のエレメントを選択します", + "xpack.canvas.keyboardShortcuts.sendBackwardShortcutHelpText": "後方に送ります", + "xpack.canvas.keyboardShortcuts.sendToBackShortcutHelpText": "後ろに送ります", + "xpack.canvas.keyboardShortcuts.shiftDownShortcutHelpText": "{ELEMENT_SHIFT_OFFSET}px 下に移動させます", + "xpack.canvas.keyboardShortcuts.shiftLeftShortcutHelpText": "{ELEMENT_SHIFT_OFFSET}px 左に移動させます", + "xpack.canvas.keyboardShortcuts.shiftRightShortcutHelpText": "{ELEMENT_SHIFT_OFFSET}px 右に移動させます", + "xpack.canvas.keyboardShortcuts.shiftUpShortcutHelpText": "{ELEMENT_SHIFT_OFFSET}px 上に移動させます", + "xpack.canvas.keyboardShortcuts.ShortcutHelpText": "ワークパッドを更新します", + "xpack.canvas.keyboardShortcuts.undoShortcutHelpText": "最後の操作を元に戻します", + "xpack.canvas.keyboardShortcuts.ungroupShortcutHelpText": "グループ解除", + "xpack.canvas.keyboardShortcuts.zoomInShortcutHelpText": "ズームイン", + "xpack.canvas.keyboardShortcuts.zoomOutShortcutHelpText": "ズームアウト", + "xpack.canvas.keyboardShortcuts.zoomResetShortcutHelpText": "ズームを 100% にリセットします", + "xpack.canvas.keyboardShortcutsDoc.flyout.closeButtonAriaLabel": "キーボードショートカットリファレンスを作成", + "xpack.canvas.keyboardShortcutsDoc.flyoutHeaderTitle": "キーボードショートカット", + "xpack.canvas.keyboardShortcutsDoc.shortcutListSeparator": "または", + "xpack.canvas.link.errorMessage": "リンクエラー: {message}", + "xpack.canvas.pageConfig.backgroundColorDescription": "HEX、RGB、また HTML 色名が使用できます", + "xpack.canvas.pageConfig.backgroundColorLabel": "背景色", + "xpack.canvas.pageConfig.title": "ページ", + "xpack.canvas.pageConfig.transitionLabel": "トランジション", + "xpack.canvas.pageConfig.transitionPreviewLabel": "プレビュー", + "xpack.canvas.pageConfig.transitions.noneDropDownOptionLabel": "なし", + "xpack.canvas.pageManager.pageNumberAriaLabel": "ページ {pageNumber} を読み込む", + "xpack.canvas.pagePreviewPageControls.clonePageAriaLabel": "ページのクローンを作成", + "xpack.canvas.pagePreviewPageControls.clonePageTooltip": "クローンを作成", + "xpack.canvas.pagePreviewPageControls.deletePageAriaLabel": "ページを削除", + "xpack.canvas.pagePreviewPageControls.deletePageTooltip": "削除", + "xpack.canvas.renderer.advancedFilter.applyButtonLabel": "適用", + "xpack.canvas.renderer.advancedFilter.displayName": "高度なフィルター", + "xpack.canvas.renderer.advancedFilter.helpDescription": "Canvas フィルター表現をレンダリングします。", + "xpack.canvas.renderer.advancedFilter.inputPlaceholder": "フィルター表現を入力", + "xpack.canvas.renderer.debug.displayName": "デバッグ", + "xpack.canvas.renderer.debug.helpDescription": "デバッグアウトプットをフォーマットされた {JSON} としてレンダリングします", + "xpack.canvas.renderer.dropdownFilter.displayName": "ドロップダウンフィルター", + "xpack.canvas.renderer.dropdownFilter.helpDescription": "「{exactly}」フィルターの値を選択できるドロップダウンです", + "xpack.canvas.renderer.dropdownFilter.matchAllOptionLabel": "すべて", + "xpack.canvas.renderer.embeddable.displayName": "埋め込み可能", + "xpack.canvas.renderer.embeddable.helpDescription": "Kibana の他の部分から埋め込み可能な保存済みオブジェクトをレンダリングします", + "xpack.canvas.renderer.error.displayName": "エラー情報", + "xpack.canvas.renderer.error.helpDescription": "エラーデータをユーザーにわかるようにレンダリングします", + "xpack.canvas.renderer.image.displayName": "画像", + "xpack.canvas.renderer.image.helpDescription": "画像をレンダリングします", + "xpack.canvas.renderer.markdown.displayName": "マークダウン", + "xpack.canvas.renderer.markdown.helpDescription": "{MARKDOWN} インプットで {HTML} をレンダリングします", + "xpack.canvas.renderer.metric.displayName": "メトリック", + "xpack.canvas.renderer.metric.helpDescription": "ラベルの上に数字をレンダリングします", + "xpack.canvas.renderer.pie.displayName": "円グラフ", + "xpack.canvas.renderer.pie.helpDescription": "データから円グラフをレンダリングします", + "xpack.canvas.renderer.plot.displayName": "座標プロット", + "xpack.canvas.renderer.plot.helpDescription": "データから XY プロットをレンダリングします", + "xpack.canvas.renderer.progress.displayName": "進捗インジケーター", + "xpack.canvas.renderer.progress.helpDescription": "エレメントのパーセンテージを示す進捗インジケーターをレンダリングします", + "xpack.canvas.renderer.repeatImage.displayName": "画像の繰り返し", + "xpack.canvas.renderer.repeatImage.helpDescription": "画像を指定回数繰り返し表示します", + "xpack.canvas.renderer.revealImage.displayName": "画像の部分表示", + "xpack.canvas.renderer.revealImage.helpDescription": "カスタムゲージスタイルチャートを作成するため、画像のパーセンテージを表示します", + "xpack.canvas.renderer.shape.displayName": "図形", + "xpack.canvas.renderer.shape.helpDescription": "基本的な図形をレンダリングします", + "xpack.canvas.renderer.table.displayName": "データテーブル", + "xpack.canvas.renderer.table.helpDescription": "表形式データを {HTML} としてレンダリングします", + "xpack.canvas.renderer.text.displayName": "プレインテキスト", + "xpack.canvas.renderer.text.helpDescription": "アウトプットをプレインテキストとしてレンダリングします", + "xpack.canvas.renderer.timeFilter.displayName": "時間フィルター", + "xpack.canvas.renderer.timeFilter.helpDescription": "時間枠を設定してデータをフィルタリングします", + "xpack.canvas.shareWebsiteFlyout.description": "外部 Web サイトでこのワークパッドの不動バージョンを共有するには、これらの手順に従ってください。現在のワークパッドのビジュアルスナップショットになり、ライブデータにはアクセスできません。", + "xpack.canvas.shareWebsiteFlyout.flyoutCalloutDescription": "共有するには、このワークパッド、{CANVAS} シェアラブルワークパッドランタイム、サンプル {HTML} ファイルを含む {link} を使用します。", + "xpack.canvas.shareWebsiteFlyout.flyoutTitle": "Web サイトで共有", + "xpack.canvas.shareWebsiteFlyout.runtimeStep.description": "共有可能なワークパッドをレンダリングするには、{CANVAS} シェアラブルワークパッドランタイムも含める必要があります。Web サイトに既にランタイムが含まれている場合、この手順をスキップすることができます。", + "xpack.canvas.shareWebsiteFlyout.runtimeStep.downloadLabel": "ダウンロードランタイム", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.addSnippetsTitle": "スナップショットを Web サイトに追加", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.autoplayParameterDescription": "ワークパッドのページ間で’ランタイムを自動的に移動させますか?", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.callRuntimeLabel": "ランタイムを呼び出す", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.description": "ワークパッドは {HTML} プレースホルダーでサイトの {HTML} 内に配置されますランタイムのパラメーターはインラインに含まれます。全パラメーターの一覧は以下をご覧ください。1 ページに複数のワークパッドを含めることができます。", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.downloadRuntimeTitle": "ダウンロードランタイム", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.downloadWorkpadTitle": "ワークパッドのダウンロード", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.heightParameterDescription": "ワークパッドの高さです。デフォルトはワークパッドの高さです。", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.includeRuntimeLabel": "ランタイムを含める", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.intervalParameterDescription": "時間フォーマットでのページが進む間隔です (例: {twoSeconds}、{oneMinute})", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.pageParameterDescription": "表示するページです。デフォルトはワークパッドに指定されたページになります。", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.parametersDescription": "シェアラブルワークパッドを構成するインラインパラメーターが多数あります。", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.parametersLabel": "パラメーター", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.placeholderLabel": "プレースホルダー", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.requiredLabel": "必要", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.shareableParameterDescription": "シェアラブルのタイプです。この場合、{CANVAS} ワークパッドです。", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.toolbarParameterDescription": "ツールバーを非表示にしますか?", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.urlParameterDescription": "シェアラブルワークパッド {JSON} ファイルの {URL} です。", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.widthParameterDescription": "ワークパッドの幅です。デフォルトはワークパッドの幅です。", + "xpack.canvas.shareWebsiteFlyout.workpadStep.description": "ワークパッドは別のサイトで共有できるように 1 つの {JSON} ファイルとしてエクスポートされます。", + "xpack.canvas.shareWebsiteFlyout.workpadStep.downloadLabel": "ワークパッドのダウンロード", + "xpack.canvas.shareWebsiteFlyout.zipDownloadLinkLabel": "サンプル {ZIP} ファイルをダウンロード", + "xpack.canvas.sidebarContent.groupedElementSidebarTitle": "グループ化されたエレメント", + "xpack.canvas.sidebarContent.multiElementSidebarTitle": "複数エレメント", + "xpack.canvas.sidebarContent.singleElementSidebarTitle": "選択されたエレメント", + "xpack.canvas.sidebarHeader.alignmentMenuItemLabel": "アラインメント", + "xpack.canvas.sidebarHeader.bottomAlignMenuItemLabel": "一番下", + "xpack.canvas.sidebarHeader.bringForwardArialLabel": "エレメントを 1 つ上のレイヤーに移動", + "xpack.canvas.sidebarHeader.bringToFrontArialLabel": "エレメントを一番上のレイヤーに移動", + "xpack.canvas.sidebarHeader.centerAlignMenuItemLabel": "中央", + "xpack.canvas.sidebarHeader.contextMenuAriaLabel": "エレメントオプション", + "xpack.canvas.sidebarHeader.createElementModalTitle": "新規エレメントの作成", + "xpack.canvas.sidebarHeader.distributionMenutItemLabel": "分布", + "xpack.canvas.sidebarHeader.groupMenuItemLabel": "グループ", + "xpack.canvas.sidebarHeader.horizontalDistributionMenutItemLabel": "横", + "xpack.canvas.sidebarHeader.leftAlignMenuItemLabel": "左", + "xpack.canvas.sidebarHeader.middleAlignMenuItemLabel": "真ん中", + "xpack.canvas.sidebarHeader.orderMenuItemLabel": "順序", + "xpack.canvas.sidebarHeader.rightAlignMenuItemLabel": "右", + "xpack.canvas.sidebarHeader.savedElementMenuItemLabel": "新規エレメントとして保存", + "xpack.canvas.sidebarHeader.sendBackwardArialLabel": "エレメントを 1 つ下のレイヤーに移動", + "xpack.canvas.sidebarHeader.sendToBackArialLabel": "エレメントを一番下のレイヤーに移動", + "xpack.canvas.sidebarHeader.topAlignMenuItemLabel": "一番上", + "xpack.canvas.sidebarHeader.ungroupMenuItemLabel": "グループ解除", + "xpack.canvas.sidebarHeader.verticalDistributionMenutItemLabel": "縦", + "xpack.canvas.tags.chartTag": "チャート", + "xpack.canvas.tags.filterTag": "フィルター", + "xpack.canvas.tags.graphicTag": "グラフィック", + "xpack.canvas.tags.presentationTag": "プレゼンテーション", + "xpack.canvas.tags.proportionTag": "比率", + "xpack.canvas.tags.reportTag": "レポート", + "xpack.canvas.tags.textTag": "テキスト", + "xpack.canvas.templates.darkHelp": "ダークカラーテーマのプレゼンテーションデッキです", + "xpack.canvas.templates.darkName": "ダーク", + "xpack.canvas.templates.lightHelp": "ライトカラーテーマのプレゼンテーションデッキです", + "xpack.canvas.templates.lightName": "ライト", + "xpack.canvas.templates.pitchHelp": "大きな写真でブランディングされたプレゼンテーションです", + "xpack.canvas.templates.pitchName": "ピッチ", + "xpack.canvas.templates.statusHelp": "ライブチャート付きのドキュメント式のレポートです", + "xpack.canvas.templates.statusName": "ステータス", + "xpack.canvas.templates.summaryDisplayName": "まとめ", + "xpack.canvas.templates.summaryHelp": "ライブチャート付きのインフォグラフィック式のレポートです", + "xpack.canvas.textStylePicker.alignCenterOption": "中央に合わせる", + "xpack.canvas.textStylePicker.alignLeftOption": "左に合わせる", + "xpack.canvas.textStylePicker.alignRightOption": "右に合わせる", + "xpack.canvas.textStylePicker.styleBoldOption": "太字", + "xpack.canvas.textStylePicker.styleItalicOption": "斜体", + "xpack.canvas.textStylePicker.styleUnderlineOption": "下線", + "xpack.canvas.timePicker.applyButtonLabel": "適用", + "xpack.canvas.toolbar.editorButtonLabel": "表現エディター", + "xpack.canvas.toolbar.nextPageAriaLabel": "次のページ", + "xpack.canvas.toolbar.pageButtonLabel": "ページ {pageNum}{rest}", + "xpack.canvas.toolbar.previousPageAriaLabel": "前のページ", + "xpack.canvas.toolbar.workpadManagerCloseButtonLabel": "閉じる", + "xpack.canvas.toolbarTray.closeTrayAriaLabel": "トレイのクローンを作成", + "xpack.canvas.transitions.fade.displayName": "フェード", + "xpack.canvas.transitions.fade.help": "ページからページへフェードします", + "xpack.canvas.transitions.rotate.displayName": "回転", + "xpack.canvas.transitions.rotate.help": "ページからページへ回転します", + "xpack.canvas.transitions.slide.displayName": "スライド", + "xpack.canvas.transitions.slide.help": "ページからページへスライドします", + "xpack.canvas.transitions.zoom.displayName": "ズーム:", + "xpack.canvas.transitions.zoom.help": "ページからページへズームします", + "xpack.canvas.uis.arguments.axisConfig.position.options.bottomDropDown": "一番下", + "xpack.canvas.uis.arguments.axisConfig.position.options.leftDropDown": "左", + "xpack.canvas.uis.arguments.axisConfig.position.options.rightDropDown": "右", + "xpack.canvas.uis.arguments.axisConfig.position.options.topDropDown": "トップ", + "xpack.canvas.uis.arguments.axisConfig.positionLabel": "配置", + "xpack.canvas.uis.arguments.axisConfigLabel": "ビジュアライゼーションの軸の構成", + "xpack.canvas.uis.arguments.axisConfigTitle": "軸の構成", + "xpack.canvas.uis.arguments.dataColumn.options.averageDropDown": "平均", + "xpack.canvas.uis.arguments.dataColumn.options.countDropDown": "カウント", + "xpack.canvas.uis.arguments.dataColumn.options.firstDropDown": "最初", + "xpack.canvas.uis.arguments.dataColumn.options.lastDropDown": "最後", + "xpack.canvas.uis.arguments.dataColumn.options.maxDropDown": "最高", + "xpack.canvas.uis.arguments.dataColumn.options.medianDropDown": "中央", + "xpack.canvas.uis.arguments.dataColumn.options.minDropDown": "最低", + "xpack.canvas.uis.arguments.dataColumn.options.sumDropDown": "合計", + "xpack.canvas.uis.arguments.dataColumn.options.uniqueDropDown": "固有", + "xpack.canvas.uis.arguments.dataColumn.options.valueDropDown": "値", + "xpack.canvas.uis.arguments.dataColumnLabel": "データ列を選択", + "xpack.canvas.uis.arguments.dataColumnTitle": "列", + "xpack.canvas.uis.arguments.dateFormatLabel": "{momentJS} フォーマットを選択または入力", + "xpack.canvas.uis.arguments.dateFormatTitle": "データフォーマット", + "xpack.canvas.uis.arguments.filterGroup.createNewGroupLinkText": "新規グループを作成", + "xpack.canvas.uis.arguments.filterGroupLabel": "フィルターグループを作成または入力", + "xpack.canvas.uis.arguments.filterGroupTitle": "フィルターグループ", + "xpack.canvas.uis.arguments.imageUpload.fileUploadPromptLabel": "画像を選択するかドラッグ &amp; ドロップしてください", + "xpack.canvas.uis.arguments.imageUpload.imageUploadingLabel": "画像をアップロード中", + "xpack.canvas.uis.arguments.imageUpload.urlFieldPlaceholder": "画像 {url}", + "xpack.canvas.uis.arguments.imageUpload.urlTypes.assetDropDown": "アセット", + "xpack.canvas.uis.arguments.imageUpload.urlTypes.fileDropDown": "インポート", + "xpack.canvas.uis.arguments.imageUpload.urlTypes.linkDropDown": "リンク", + "xpack.canvas.uis.arguments.imageUploadLabel": "画像を選択またはアップロード", + "xpack.canvas.uis.arguments.imageUploadTitle": "画像がアップロードされました", + "xpack.canvas.uis.arguments.numberFormat.format.bytesDropDown": "バイト", + "xpack.canvas.uis.arguments.numberFormat.format.currencyDropDown": "通貨", + "xpack.canvas.uis.arguments.numberFormat.format.durationDropDown": "期間", + "xpack.canvas.uis.arguments.numberFormat.format.numberDropDown": "数字", + "xpack.canvas.uis.arguments.numberFormat.format.percentDropDown": "パーセント", + "xpack.canvas.uis.arguments.numberFormatLabel": "有効な {numeralJS} フォーマットを選択または入力", + "xpack.canvas.uis.arguments.numberFormatTitle": "数字フォーマット", + "xpack.canvas.uis.arguments.numberLabel": "数字を入力", + "xpack.canvas.uis.arguments.numberTitle": "数字", + "xpack.canvas.uis.arguments.paletteLabel": "カラーパレットを選択", + "xpack.canvas.uis.arguments.paletteTitle": "カラーパレット", + "xpack.canvas.uis.arguments.percentageLabel": "パーセンテージのスライダー ", + "xpack.canvas.uis.arguments.percentageTitle": "パーセンテージ", + "xpack.canvas.uis.arguments.rangeLabel": "範囲内の値のスライダー", + "xpack.canvas.uis.arguments.rangeTitle": "範囲", + "xpack.canvas.uis.arguments.selectLabel": "ドロップダウンの複数オプションから選択", + "xpack.canvas.uis.arguments.selectTitle": "選択してください", + "xpack.canvas.uis.arguments.shapeLabel": "図形ピッカー", + "xpack.canvas.uis.arguments.shapeTitle": "図形", + "xpack.canvas.uis.arguments.stringLabel": "短い文字列を入力", + "xpack.canvas.uis.arguments.stringTitle": "文字列", + "xpack.canvas.uis.arguments.textareaLabel": "長い文字列を入力", + "xpack.canvas.uis.arguments.textareaTitle": "テキストエリア", + "xpack.canvas.uis.arguments.toggleLabel": "true/false トグルスイッチ", + "xpack.canvas.uis.arguments.toggleTitle": "切り替え", + "xpack.canvas.uis.dataSources.demoData.headingTitle": "デモデータを使用中です", + "xpack.canvas.uis.dataSources.demoDataLabel": "ユーザー名、価格、プロジェクト、国、フェーズを含む模擬データセット", + "xpack.canvas.uis.dataSources.demoDataTitle": "デモデータ", + "xpack.canvas.uis.dataSources.essqlLabel": "{elasticsearch} {sql} でデータ表を取得します", + "xpack.canvas.uis.dataSources.essqlTitle": "{elasticsearch} {sql}", + "xpack.canvas.uis.dataSources.timelion.intervalTitle": "間隔", + "xpack.canvas.uis.dataSources.timelion.queryLabel": "{lucene} クエリ文字列の構文", + "xpack.canvas.uis.dataSources.timelion.queryTitle": "クエリ", + "xpack.canvas.uis.dataSources.timelion.tips.functions": "{functionExample} などの一部 {timelion} 関数は {canvas} データ表に変換できません。データ操作に関する機能は正常に動作するはずです。", + "xpack.canvas.uis.dataSources.timelion.tips.time": "{timelion} には時間範囲が必要です。ページのどこかに時間フィルターを追加するか、コードエディターで時間フィルターを渡す必要があります。", + "xpack.canvas.uis.dataSources.timelionLabel": "{timelion} 構文で時系列を取得します", + "xpack.canvas.uis.models.math.args.valueLabel": "データソースから値を抽出する際に使用する関数と列", + "xpack.canvas.uis.models.math.args.valueTitle": "値", + "xpack.canvas.uis.models.mathTitle": "メジャー", + "xpack.canvas.uis.models.pointSeries.args.colorLabel": "マークまたは数列の色を決定します", + "xpack.canvas.uis.models.pointSeries.args.colorTitle": "色", + "xpack.canvas.uis.models.pointSeries.args.sizeLabel": "マークのサイズを決定します", + "xpack.canvas.uis.models.pointSeries.args.sizeTitle": "サイズ", + "xpack.canvas.uis.models.pointSeries.args.textLabel": "テキストをマークとして、またはマークの周りに使用するように設定", + "xpack.canvas.uis.models.pointSeries.args.textTitle": "テキスト", + "xpack.canvas.uis.models.pointSeries.args.xaxisLabel": "水平軸の周りのデータです。通常は数字、文字列、または日付です。", + "xpack.canvas.uis.models.pointSeries.args.xaxisTitle": "X 軸", + "xpack.canvas.uis.models.pointSeries.args.yaxisLabel": "垂直軸の周りのデータです。通常は数字です。", + "xpack.canvas.uis.models.pointSeries.args.yaxisTitle": "Y 軸", + "xpack.canvas.uis.models.pointSeriesTitle": "ディメンションとメジャー", + "xpack.canvas.uis.transforms.formatDate.args.formatTitle": "フォーマット", + "xpack.canvas.uis.transforms.formatDateTitle": "データフォーマット", + "xpack.canvas.uis.transforms.formatNumber.args.formatTitle": "フォーマット", + "xpack.canvas.uis.transforms.formatNumberTitle": "数字フォーマット", + "xpack.canvas.uis.transforms.roundDate.args.formatLabel": "日付の繰り上げ・繰り下げに使用する {momentJs} フォーマットを選択または入力", + "xpack.canvas.uis.transforms.roundDate.args.formatTitle": "フォーマット", + "xpack.canvas.uis.transforms.roundDateTitle": "日付の繰り上げ・繰り下げ", + "xpack.canvas.uis.transforms.sort.args.reverseToggleSwitch": "降順", + "xpack.canvas.uis.transforms.sort.args.sortFieldTitle": "ソートフィールド", + "xpack.canvas.uis.transforms.sortTitle": "データベースの並べ替え", + "xpack.canvas.uis.views.dropdownControl.args.filterColumnLabel": "ドロップダウンで選択された値を適用する列", + "xpack.canvas.uis.views.dropdownControl.args.filterColumnTitle": "フィルター列", + "xpack.canvas.uis.views.dropdownControl.args.filterGroupLabel": "選択されたグループ名をエレメントのフィルター関数に適用してこのフィルターをターゲットにする", + "xpack.canvas.uis.views.dropdownControl.args.filterGroupTitle": "フィルターグループ", + "xpack.canvas.uis.views.dropdownControl.args.valueColumnLabel": "ドロップダウンに表示する値を抽出する列", + "xpack.canvas.uis.views.dropdownControl.args.valueColumnTitle": "値列", + "xpack.canvas.uis.views.dropdownControlTitle": "ドロップダウンフィルター", + "xpack.canvas.uis.views.getCellLabel": "最初の行と列を使用", + "xpack.canvas.uis.views.getCellTitle": "ドロップダウンフィルター", + "xpack.canvas.uis.views.image.args.mode.containDropDown": "Contain", + "xpack.canvas.uis.views.image.args.mode.coverDropDown": "Cover", + "xpack.canvas.uis.views.image.args.mode.stretchDropDown": "Stretch", + "xpack.canvas.uis.views.image.args.modeLabel": "注:ストレッチ塗りつぶしはベクター画像には使用できない場合があります", + "xpack.canvas.uis.views.image.args.modeTitle": "塗りつぶしモード", + "xpack.canvas.uis.views.imageTitle": "画像", + "xpack.canvas.uis.views.markdown.args.contentLabel": "{markdown} フォーマットのテキスト", + "xpack.canvas.uis.views.markdown.args.contentTitle": "{markdown} コンテンツ", + "xpack.canvas.uis.views.markdownLabel": "{markdown} を使用してマークアップを生成", + "xpack.canvas.uis.views.markdownTitle": "{markdown}", + "xpack.canvas.uis.views.metric.args.labelArgLabel": "メトリックを説明します", + "xpack.canvas.uis.views.metric.args.labelArgTitle": "ラベル", + "xpack.canvas.uis.views.metric.args.labelFontLabel": "フォント、配置、色", + "xpack.canvas.uis.views.metric.args.labelFontTitle": "ラベルテキスト設定", + "xpack.canvas.uis.views.metric.args.metricFontLabel": "フォント、配置、色", + "xpack.canvas.uis.views.metric.args.metricFontTitle": "メトリックテキスト設定", + "xpack.canvas.uis.views.metric.args.metricFormatLabel": "フォント、配置、色", + "xpack.canvas.uis.views.metric.args.metricFormatTitle": "メトリックのフォーマット", + "xpack.canvas.uis.views.metricTitle": "メトリック", + "xpack.canvas.uis.views.numberArgTitle": "数字", + "xpack.canvas.uis.views.pie.args.holeLabel": "穴の半径", + "xpack.canvas.uis.views.pie.args.holeTitle": "内半径", + "xpack.canvas.uis.views.pie.args.labelRadiusLabel": "円グラフの中心からラベルまでの距離です", + "xpack.canvas.uis.views.pie.args.labelRadiusTitle": "ラベル半径", + "xpack.canvas.uis.views.pie.args.labelsTitle": "ラベル", + "xpack.canvas.uis.views.pie.args.labelsToggleSwitch": "ラベルの表示・非表示", + "xpack.canvas.uis.views.pie.args.legendLabel": "凡例の無効化・配置", + "xpack.canvas.uis.views.pie.args.legendTitle": "凡例の配置", + "xpack.canvas.uis.views.pie.args.radiusLabel": "円グラフの半径", + "xpack.canvas.uis.views.pie.args.radiusTitle": "半径", + "xpack.canvas.uis.views.pie.args.tiltLabel": "100 が完全に垂直、0 が完全に水平を表す傾きのパーセンテージです", + "xpack.canvas.uis.views.pie.args.tiltTitle": "傾斜角度", + "xpack.canvas.uis.views.pieTitle": "チャートスタイル", + "xpack.canvas.uis.views.plot.args.defaultStyleLabel": "上書きされない限りすべての数列にデフォルトで使用されるスタイルを設定します", + "xpack.canvas.uis.views.plot.args.defaultStyleTitle": "デフォルトのスタイル", + "xpack.canvas.uis.views.plot.args.legendLabel": "凡例の無効化・配置", + "xpack.canvas.uis.views.plot.args.legendTitle": "凡例の配置", + "xpack.canvas.uis.views.plot.args.xaxisLabel": "X 軸の構成・無効化", + "xpack.canvas.uis.views.plot.args.xaxisTitle": "X 軸", + "xpack.canvas.uis.views.plot.args.yaxisLabel": "Y 軸の構成・無効化", + "xpack.canvas.uis.views.plot.args.yaxisTitle": "Y 軸", + "xpack.canvas.uis.views.plotTitle": "チャートスタイル", + "xpack.canvas.uis.views.progress.args.barColorLabel": "HEX、RGB、または HTML カラーネームが使用できます", + "xpack.canvas.uis.views.progress.args.barColorTitle": "背景色", + "xpack.canvas.uis.views.progress.args.barWeightLabel": "背景バーの太さです", + "xpack.canvas.uis.views.progress.args.barWeightTitle": "背景重量", + "xpack.canvas.uis.views.progress.args.fontLabel": "ラベルのフォント設定です。技術的には他のスタイルを追加することもできます", + "xpack.canvas.uis.views.progress.args.fontTitle": "ラベル設定", + "xpack.canvas.uis.views.progress.args.labelArgLabel": "{true}/{false} でラベルの表示/非表示を設定するか、ラベルとして表示する文字列を指定します", + "xpack.canvas.uis.views.progress.args.labelArgTitle": "ラベル", + "xpack.canvas.uis.views.progress.args.maxLabel": "進捗エレメントの最高値です", + "xpack.canvas.uis.views.progress.args.maxTitle": "最高値", + "xpack.canvas.uis.views.progress.args.shapeLabel": "進捗インジケーターの形", + "xpack.canvas.uis.views.progress.args.shapeTitle": "図形", + "xpack.canvas.uis.views.progress.args.valueColorLabel": "{hex}、{rgb}、または {html} カラーネームが使用できます", + "xpack.canvas.uis.views.progress.args.valueColorTitle": "進捗の色", + "xpack.canvas.uis.views.progress.args.valueWeightLabel": "進捗バーの太さです", + "xpack.canvas.uis.views.progress.args.valueWeightTitle": "進捗の重量", + "xpack.canvas.uis.views.progressTitle": "進捗", + "xpack.canvas.uis.views.render.args.css.applyButtonLabel": "スタイルシートを適用", + "xpack.canvas.uis.views.render.args.cssLabel": "エレメントに合わせた {css} スタイルシート", + "xpack.canvas.uis.views.renderLabel": "エレメントの周りのコンテナーの設定です", + "xpack.canvas.uis.views.renderTitle": "エレメントスタイル", + "xpack.canvas.uis.views.repeatImage.args.emptyImageLabel": "値と最高値の間を埋める画像です", + "xpack.canvas.uis.views.repeatImage.args.emptyImageTitle": "空の部分の画像", + "xpack.canvas.uis.views.repeatImage.args.imageLabel": "繰り返す画像です", + "xpack.canvas.uis.views.repeatImage.args.imageTitle": "画像", + "xpack.canvas.uis.views.repeatImage.args.maxLabel": "画像を繰り返す最高回数", + "xpack.canvas.uis.views.repeatImage.args.maxTitle": "最高カウント", + "xpack.canvas.uis.views.repeatImage.args.sizeLabel": "画像寸法の最大値です。例: 画像が縦長の場合、高さになります", + "xpack.canvas.uis.views.repeatImage.args.sizeTitle": "画像サイズ", + "xpack.canvas.uis.views.repeatImageTitle": "繰り返しの画像", + "xpack.canvas.uis.views.revealImage.args.emptyImageLabel": "背景画像です。例: 空のグラス", + "xpack.canvas.uis.views.revealImage.args.emptyImageTitle": "背景画像", + "xpack.canvas.uis.views.revealImage.args.imageLabel": "関数インプットで徐々に表示される画像です。例: いっぱいのグラス", + "xpack.canvas.uis.views.revealImage.args.imageTitle": "画像", + "xpack.canvas.uis.views.revealImage.args.origin.bottomDropDown": "一番下", + "xpack.canvas.uis.views.revealImage.args.origin.leftDropDown": "左", + "xpack.canvas.uis.views.revealImage.args.origin.rightDropDown": "右", + "xpack.canvas.uis.views.revealImage.args.origin.topDropDown": "一番上", + "xpack.canvas.uis.views.revealImage.args.originLabel": "徐々に表示を開始する方向", + "xpack.canvas.uis.views.revealImage.args.originTitle": "徐々に表示を開始する場所", + "xpack.canvas.uis.views.revealImageTitle": "画像を徐々に表示", + "xpack.canvas.uis.views.shape.args.borderLabel": "HEX、RGB、または HTML カラーネームが使用できます", + "xpack.canvas.uis.views.shape.args.borderTitle": "境界", + "xpack.canvas.uis.views.shape.args.borderWidthLabel": "境界線の幅", + "xpack.canvas.uis.views.shape.args.borderWidthTitle": "境界線の幅", + "xpack.canvas.uis.views.shape.args.fillLabel": "HEX、RGB、または HTML カラーネームが使用できます", + "xpack.canvas.uis.views.shape.args.fillTitle": "塗りつぶし", + "xpack.canvas.uis.views.shape.args.maintainAspectTitle": "アスペクト比を維持", + "xpack.canvas.uis.views.shape.args.shapeTitle": "図形を選択", + "xpack.canvas.uis.views.shapeTitle": "図形", + "xpack.canvas.uis.views.table.args.paginateLabel": "ページ付けコントロールの表示・非表示を切り替えます。無効の場合、初めのページのみが表示されます", + "xpack.canvas.uis.views.table.args.paginateTitle": "ページ付け", + "xpack.canvas.uis.views.table.args.perPageLabel": "各表ページに表示される行数です", + "xpack.canvas.uis.views.table.args.perPageTitle": "ページごとの行数", + "xpack.canvas.uis.views.table.args.showHeaderLabel": "各列のタイトルを含むヘッダー列の表示・非表示を切り替えます", + "xpack.canvas.uis.views.table.args.showHeaderTitle": "ヘッダー", + "xpack.canvas.uis.views.tableLabel": "表エレメントのスタイルを設定します", + "xpack.canvas.uis.views.tableTitle": "表スタイル", + "xpack.canvas.uis.views.timefilter.args.columnConfirmButtonLabel": "設定", + "xpack.canvas.uis.views.timefilter.args.columnLabel": "選択された時間が適用される列です", + "xpack.canvas.uis.views.timefilter.args.columnTitle": "列", + "xpack.canvas.uis.views.timefilter.args.filterGroupLabel": "選択されたグループ名をエレメントのフィルター関数に適用してこのフィルターをターゲットにする", + "xpack.canvas.uis.views.timefilter.args.filterGroupTitle": "フィルターグループ名", + "xpack.canvas.uis.views.timefilterTitle": "時間フィルター", + "xpack.canvas.units.quickRange.dayBeforeYesterday": "一昨日", + "xpack.canvas.units.quickRange.last12Hours": "過去 12 時間", + "xpack.canvas.units.quickRange.last15Minutes": "過去 15 分間", + "xpack.canvas.units.quickRange.last1Hour": "過去 1 時間", + "xpack.canvas.units.quickRange.last1Year": "過去 1 年間", + "xpack.canvas.units.quickRange.last24Hours": "過去 24 時間", + "xpack.canvas.units.quickRange.last2Weeks": "過去 2 週間", + "xpack.canvas.units.quickRange.last2Years": "過去 2 年間", + "xpack.canvas.units.quickRange.last30Days": "過去 30 日間", + "xpack.canvas.units.quickRange.last30Minutes": "過去 30 分間", + "xpack.canvas.units.quickRange.last4Hours": "過去 4 時間", + "xpack.canvas.units.quickRange.last5Years": "過去 5 年間", + "xpack.canvas.units.quickRange.last60Days": "過去 60 日間", + "xpack.canvas.units.quickRange.last6Months": "過去 6 か月間", + "xpack.canvas.units.quickRange.last7Days": "過去 7 日間", + "xpack.canvas.units.quickRange.last90Days": "過去 90 日間", + "xpack.canvas.units.quickRange.monthToDate": "月初めから今日まで", + "xpack.canvas.units.quickRange.previousMonth": "先月", + "xpack.canvas.units.quickRange.previousWeek": "先週", + "xpack.canvas.units.quickRange.previousYear": "昨年", + "xpack.canvas.units.quickRange.theDaySoFar": "本日現在まで", + "xpack.canvas.units.quickRange.thisDayLastWeek": "先週のこの曜日", + "xpack.canvas.units.quickRange.thisMonth": "今月", + "xpack.canvas.units.quickRange.thisWeek": "今週", + "xpack.canvas.units.quickRange.thisYear": "今年", + "xpack.canvas.units.quickRange.today": "今日", + "xpack.canvas.units.quickRange.weekToDate": "週初めから今日まで", + "xpack.canvas.units.quickRange.yearToDate": "年度の頭から今日まで", + "xpack.canvas.units.quickRange.yesterday": "昨日", + "xpack.canvas.units.time.days": "{days, plural, one {# 日} other {# 日}}", + "xpack.canvas.units.time.hours": "{hours, plural, one {# 時間} other {# 時間}}", + "xpack.canvas.units.time.minutes": "{minutes, plural, one {# 分} other {# 分}}", + "xpack.canvas.units.time.seconds": "{seconds, plural, one {# 秒} other {# 秒}}", + "xpack.canvas.workpadConfig.applyStylesheetButtonLabel": "スタイルシートを適用", + "xpack.canvas.workpadConfig.globalCSSLabel": "グローバル CSS オーバーライド", + "xpack.canvas.workpadConfig.globalCSSTooltip": "このワークパッドのすべてのページにスタイルを適用します", + "xpack.canvas.workpadConfig.heightLabel": "高さ", + "xpack.canvas.workpadConfig.nameLabel": "名前", + "xpack.canvas.workpadConfig.pageSizeBadgeAriaLabel": "ページサイズを事前設定: {sizeName}", + "xpack.canvas.workpadConfig.pageSizeBadgeOnClickAriaLabel": "ページサイズを {sizeName} に設定", + "xpack.canvas.workpadConfig.swapDimensionsAriaLabel": "ページの幅と高さを入れ替えます", + "xpack.canvas.workpadConfig.swapDimensionsTooltip": "ページの幅と高さを入れ替える", + "xpack.canvas.workpadConfig.title": "ワークパッド", + "xpack.canvas.workpadConfig.USLetterButtonLabel": "US レター", + "xpack.canvas.workpadConfig.widthLabel": "幅", + "xpack.canvas.workpadCreate.createButtonLabel": "ワークパッドを作成", + "xpack.canvas.workpadHeader.addElementButtonLabel": "エレメントを追加", + "xpack.canvas.workpadHeader.addElementModalCloseButtonLabel": "閉じる", + "xpack.canvas.workpadHeader.cycleIntervalDaysText": "{days} {days, plural, one {日} other {日}}ごと", + "xpack.canvas.workpadHeader.cycleIntervalHoursText": "{hours} {hours, plural, one {時間} other {時間}}ごと", + "xpack.canvas.workpadHeader.cycleIntervalMinutesText": "{minutes} {minutes, plural, one {分} other {分}}ごと", + "xpack.canvas.workpadHeader.cycleIntervalSecondsText": "{seconds} {seconds, plural, one {秒} other {秒}}ごと", + "xpack.canvas.workpadHeader.embedObjectButtonLabel": "オブジェクトを埋め込む", + "xpack.canvas.workpadHeader.fullscreenButtonAriaLabel": "全画面表示", + "xpack.canvas.workpadHeader.fullscreenTooltip": "全画面モードを開始します", + "xpack.canvas.workpadHeader.hideEditControlTooltip": "編集コントロールを非表示にします", + "xpack.canvas.workpadHeader.noWritePermissionTooltip": "このワークパッドを編集するパーミッションがありません", + "xpack.canvas.workpadHeader.showEditControlTooltip": "編集コントロールを表示します", + "xpack.canvas.workpadHeaderAutoRefreshControls.disableTooltip": "自動更新を無効にします", + "xpack.canvas.workpadHeaderAutoRefreshControls.intervalFormLabel": "自動更新間隔を変更します", + "xpack.canvas.workpadHeaderAutoRefreshControls.refreshListDurationManualText": "手動で", + "xpack.canvas.workpadHeaderAutoRefreshControls.refreshListTitle": "エレメントを更新", + "xpack.canvas.workpadHeaderControlSettings.settingsTooltip": "設定をコントロールします", + "xpack.canvas.workpadHeaderCustomInterval.confirmButtonLabel": "設定", + "xpack.canvas.workpadHeaderCustomInterval.formDescription": "{secondsExample}、{minutesExample}、{hoursExample} のような短い表記を使用します", + "xpack.canvas.workpadHeaderCustomInterval.formLabel": "カスタム間隔を設定", + "xpack.canvas.workpadHeaderKioskControl.controlTitle": "全画面ページのサイクル", + "xpack.canvas.workpadHeaderKioskControl.cycleFormLabel": "サイクル間隔を変更", + "xpack.canvas.workpadHeaderKioskControl.cycleToggleSwitch": "スライドを自動的にサイクル", + "xpack.canvas.workpadHeaderRefreshControlSettings.refreshAriaLabel": "エレメントを更新", + "xpack.canvas.workpadHeaderRefreshControlSettings.refreshTooltip": "データを更新", + "xpack.canvas.workpadHeaderWorkpadExport.copyPDFMessage": "{PDF} 生成 {URL} がクリップボードにコピーされました。", + "xpack.canvas.workpadHeaderWorkpadExport.copyReportingConfigMessage": "レポート構成がクリップボードにコピーされました", + "xpack.canvas.workpadHeaderWorkpadExport.copyShareConfigMessage": "共有マークアップがクリップボードにコピーされました", + "xpack.canvas.workpadHeaderWorkpadExport.exportPDFErrorMessage": "「{workpadName}」の {PDF} の作成に失敗しました", + "xpack.canvas.workpadHeaderWorkpadExport.exportPDFMessage": "{PDF} をエクスポート中です。管理で進捗を確認できます。", + "xpack.canvas.workpadHeaderWorkpadExport.exportPDFTitle": "ワークパッド「{workpadName}」の {PDF} エクスポート", + "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyAriaLabel": "この {URL} を使用してスクリプトから、または Watcher で {PDF} を生成することもできます。{URL} をクリップボードにコピーするにはエンターキーを押してください。", + "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyButtonLabel": "{POST} {URL} をコピー", + "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyDescription": "{POST} {URL} をコピーして {KIBANA} 外または ウォッチャー から生成することもできます。", + "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelDisabledDescription": "PDF へのエクスポートは無効になっています。Chromium ブラウザを使用するにはレポートの構成が必要です。これを {fileName} ファイルに追加します。", + "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelGenerateButtonLabel": "{PDF} を生成", + "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelGenerateDescription": "ワークパッドのサイズによって、{PDF} の生成には数分かかる場合があります。", + "xpack.canvas.workpadHeaderWorkpadExport.shareDownloadJSONTitle": "{JSON} をダウンロード", + "xpack.canvas.workpadHeaderWorkpadExport.shareDownloadPDFTitle": "{PDF} レポート", + "xpack.canvas.workpadHeaderWorkpadExport.shareWebsiteErrorTitle": "「{workpadName}」の {ZIP} ファイルの作成に失敗しました。ワークパッドが大きすぎる可能性があります。ファイルを別々にダウンロードする必要があります。", + "xpack.canvas.workpadHeaderWorkpadExport.shareWebsiteTitle": "Web サイトで共有", + "xpack.canvas.workpadHeaderWorkpadExport.shareWorkpadMessage": "このワークパッドを共有", + "xpack.canvas.workpadHeaderWorkpadExport.unknownExportErrorMessage": "未知のエクスポートタイプ: {type}", + "xpack.canvas.workpadHeaderWorkpadExport.unsupportedRendererWarning": "このワークパッドには {CANVAS} シェアラブルワークパッドランタイムがサポートしていないレンダリング関数が含まれています。これらのエレメントはレンダリングされません:", + "xpack.canvas.workpadHeaderWorkpadZoom.zoomControlsAriaLabel": "ズームコントロール", + "xpack.canvas.workpadHeaderWorkpadZoom.zoomControlsTooltip": "ズームコントロール", + "xpack.canvas.workpadHeaderWorkpadZoom.zoomFitToWindowText": "ウィンドウに合わせる", + "xpack.canvas.workpadHeaderWorkpadZoom.zoomInText": "ズームイン", + "xpack.canvas.workpadHeaderWorkpadZoom.zoomOutText": "ズームアウト", + "xpack.canvas.workpadHeaderWorkpadZoom.zoomPanelTitle": "ズーム:", + "xpack.canvas.workpadHeaderWorkpadZoom.zoomPrecentageValue": "リセット", + "xpack.canvas.workpadHeaderWorkpadZoom.zoomResetText": "{scalePercentage}%", + "xpack.canvas.workpadLoader.clonedWorkpadName": "{workpadName} のコピー", + "xpack.canvas.workpadLoader.cloneTooltip": "ワークパッドのクローンを作成します", + "xpack.canvas.workpadLoader.createWorkpadLoadingDescription": "ワークパッドを作成中...", + "xpack.canvas.workpadLoader.deleteButtonAriaLabel": "{numberOfWorkpads} 件のワークパッドを削除", + "xpack.canvas.workpadLoader.deleteButtonLabel": "({numberOfWorkpads}) 件を削除", + "xpack.canvas.workpadLoader.deleteModalConfirmButtonLabel": "削除", + "xpack.canvas.workpadLoader.deleteModalDescription": "削除されたワークパッドは復元できません。", + "xpack.canvas.workpadLoader.deleteMultipleWorkpadsModalTitle": "{numberOfWorkpads} 件のワークパッドを削除しますか?", + "xpack.canvas.workpadLoader.deleteSingleWorkpadModalTitle": "ワークパッド「{workpadName}」を削除しますか?", + "xpack.canvas.workpadLoader.emptyPromptGettingStartedDescription": "新規ワークパッドを作成、テンプレートで開始、またはワークパッド {JSON} ファイルをここにドロップしてインポートします。", + "xpack.canvas.workpadLoader.emptyPromptNewUserDescription": "{CANVAS} は初めてですか?", + "xpack.canvas.workpadLoader.emptyPromptTitle": "初の’ワークパッドを追加しましょう", + "xpack.canvas.workpadLoader.exportButtonAriaLabel": "{numberOfWorkpads} 件のワークパッドをエクスポート", + "xpack.canvas.workpadLoader.exportButtonLabel": "({numberOfWorkpads}) 件をエクスポート:", + "xpack.canvas.workpadLoader.exportTooltip": "ワークパッドをエクスポート", + "xpack.canvas.workpadLoader.fetchLoadingDescription": "ワークパッドを取得中...", + "xpack.canvas.workpadLoader.filePickerPlaceholder": "ワークパッド {JSON} ファイルをインポート", + "xpack.canvas.workpadLoader.loadWorkpadArialLabel": "ワークパッド「{workpadName}」を読み込む", + "xpack.canvas.workpadLoader.noPermissionToCloneToolTip": "ワークパッドのクローンを作成するパーミッションがありません", + "xpack.canvas.workpadLoader.noPermissionToCreateToolTip": "ワークパッドを作成するパーミッションがありません", + "xpack.canvas.workpadLoader.noPermissionToDeleteToolTip": "ワークパッドを削除するパーミッションがありません", + "xpack.canvas.workpadLoader.noPermissionToUploadToolTip": "ワークパッドを更新するパーミッションがありません", + "xpack.canvas.workpadLoader.sampleDataLinkLabel": "初の’ワークパッドを追加しましょう", + "xpack.canvas.workpadLoader.table.createdColumnTitle": "作成済み", + "xpack.canvas.workpadLoader.table.nameColumnTitle": "ワークパッド名", + "xpack.canvas.workpadLoader.table.updatedColumnTitle": "更新しました", + "xpack.canvas.workpadManager.modalTitle": "{CANVAS} ワークパッド", + "xpack.canvas.workpadManager.myWorkpadsTabLabel": "マイワークパッド", + "xpack.canvas.workpadManager.workpadTemplatesTabLabel": "テンプレート", + "xpack.canvas.workpadSearch.searchPlaceholder": "ワークパッドを検索", + "xpack.canvas.workpadTemplate.cloneTemplateLinkAriaLabel": "ワークパッドテンプレート「{templateName}」のクローンを作成", + "xpack.canvas.workpadTemplate.searchPlaceholder": "テンプレートを検索", + "xpack.canvas.workpadTemplates.table.descriptionColumnTitle": "説明", + "xpack.canvas.workpadTemplates.table.nameColumnTitle": "テンプレート名", + "xpack.canvas.workpadTemplates.table.tagsColumnTitle": "タグ", + "xpack.canvas.badge.readOnly.tooltip": "{canvas} ワークパッドを保存できません", "xpack.crossClusterReplication.addAutoFollowPatternButtonLabel": "自動フォローパターンを作成", "xpack.crossClusterReplication.addBreadcrumbTitle": "追加", "xpack.crossClusterReplication.addFollowerButtonLabel": "フォロワーインデックスを作成", @@ -4270,7 +5191,7 @@ "xpack.graph.settings.advancedSettings.maxValuesInputHelpText": "同じ値を含めることのできるサンプルのドキュメントの最大数です", "xpack.graph.settings.advancedSettings.maxValuesInputHelpText.fieldText": "フィールド", "xpack.graph.settings.advancedSettings.maxValuesInputLabel": "フィールドごとの最大ドキュメント数", - "xpack.graph.settings.advancedSettings.sampleSizeInputHelpText": "用語は最も関連性の高いドキュメントのサンプルから認識されます。大きければ良いというものではありません。動作が遅くなり関連性が低くなる可能性があります。", + "xpack.graph.settings.advancedSettings.sampleSizeInputHelpText": "用語は最も関連性の高いドキュメントのサンプルから認識されます。サンプルは大きければ良いというものではありません。動作が遅くなり関連性が低くなる可能性があります。", "xpack.graph.settings.advancedSettings.sampleSizeInputLabel": "サンプルサイズ", "xpack.graph.settings.advancedSettings.significantLinksCheckboxHelpText": "ただ利用頻度が高いだけでなく「重要」な用語を認識します", "xpack.graph.settings.advancedSettings.significantLinksCheckboxLabel": "重要なリンク", @@ -4281,7 +5202,7 @@ "xpack.graph.settings.blacklist.clearButtonLabel": "消去", "xpack.graph.settings.blacklistTitle": "ブラックリスト", "xpack.graph.settings.drillDowns.defaultUrlTemplateTitle": "生ドキュメント", - "xpack.graph.settings.drillDowns.invalidUrlWarningText": "URL には {placeholder} 文字列を含める必要があります", + "xpack.graph.settings.drillDowns.invalidUrlWarningText": "URL には {placeholder} 文字列を含める必要があります。", "xpack.graph.settings.drillDowns.kibanaUrlWarningText": "これは Kibana URL のようです。テンプレートに変換しますか?", "xpack.graph.settings.drillDowns.resetButtonLabel": "リセット", "xpack.graph.settings.drillDowns.toolbarIconPickerLabel": "ツールバーアイコン", @@ -4305,7 +5226,7 @@ "xpack.graph.sidebar.linkSummaryTitle": "リンクの概要", "xpack.graph.sidebar.selections.invertSelectionButtonLabel": "反転", "xpack.graph.sidebar.selections.invertSelectionButtonTooltip": "選択を反転させます", - "xpack.graph.sidebar.selections.noSelectionsHelpText": "選択項目がありません。頂点をクリックして追加します", + "xpack.graph.sidebar.selections.noSelectionsHelpText": "選択項目がありません。頂点をクリックして追加します。", "xpack.graph.sidebar.selections.selectAllButtonLabel": "すべて", "xpack.graph.sidebar.selections.selectAllButtonTooltip": "すべて選択", "xpack.graph.sidebar.selections.selectNeighboursButtonLabel": "リンク", @@ -4340,6 +5261,119 @@ "xpack.graph.topNavMenu.settingsAriaLabel": "設定", "xpack.graph.topNavMenu.settingsLabel": "設定", "xpack.graph.errorToastTitle": "Graph エラー", + "xpack.graph.bar.exploreLabel": "Graph", + "xpack.graph.bar.pickFieldsLabel": "フィールドを追加", + "xpack.graph.bar.pickSourceLabel": "データソースを選択", + "xpack.graph.bar.pickSourceTooltip": "グラフの関係性を開始するデータソースを選択します。", + "xpack.graph.bar.searchFieldPlaceholder": "データを検索してグラフに追加", + "xpack.graph.blacklist.noEntriesDescription": "ブロックされた用語がありません。頂点を選択して、右側のコントロールパネルの {stopSign} をクリックしてブロックします。ブロックされた用語に一致するドキュメントは今後表示されず、関係性が非表示になります。", + "xpack.graph.blacklist.removeButtonAriaLabel": "削除", + "xpack.graph.clearWorkspace.modalTitle": "保存されていない変更", + "xpack.graph.drilldowns.description": "ドリルダウンで他のアプリケーションにリンクします。選択された頂点が URL の一部になります。", + "xpack.graph.fieldManager.cancelLabel": "キャンセル", + "xpack.graph.fieldManager.colorLabel": "色", + "xpack.graph.fieldManager.deleteFieldLabel": "フィールドの選択を解除しました", + "xpack.graph.fieldManager.deleteFieldTooltipContent": "このフィールドの新規頂点は検出されなくなります。 既存の頂点はグラフに残されます。", + "xpack.graph.fieldManager.disabledFieldBadgeDescription": "無効なフィールド {field}:構成するにはクリックしてください。Shift+クリックで有効にします。", + "xpack.graph.fieldManager.disableFieldLabel": "フィールドを無効にする", + "xpack.graph.fieldManager.disableFieldTooltipContent": "このフィールドの頂点の検出をオフにします。フィールドを Shift+クリックしても無効にできます。", + "xpack.graph.fieldManager.enableFieldLabel": "フィールドを有効にする", + "xpack.graph.fieldManager.enableFieldTooltipContent": "このフィールドの頂点の検出をオンにします。フィールドを Shift+クリックしても有効にできます。", + "xpack.graph.fieldManager.fieldBadgeDescription": "フィールド {field}:構成するにはクリックしてください。Shift+クリックで無効にします", + "xpack.graph.fieldManager.fieldLabel": "フィールド", + "xpack.graph.fieldManager.fieldSearchPlaceholder": "フィルタリング条件:", + "xpack.graph.fieldManager.iconLabel": "アイコン", + "xpack.graph.fieldManager.maxTermsPerHopDescription": "各検索ステップで返されるアイテムの最大数をコントロールします。", + "xpack.graph.fieldManager.maxTermsPerHopLabel": "ホップごとの用語数", + "xpack.graph.fieldManager.settingsFormTitle": "編集", + "xpack.graph.fieldManager.settingsLabel": "設定の変更", + "xpack.graph.fieldManager.updateLabel": "変更を保存", + "xpack.graph.fillWorkspaceError": "トップアイテムの取得に失敗しました: {message}", + "xpack.graph.guidancePanel.datasourceItem.indexPatternButtonLabel": "データソースを選択", + "xpack.graph.guidancePanel.fieldsItem.fieldsButtonLabel": "フィールドを追加。", + "xpack.graph.guidancePanel.nodesItem.description": "閲覧を始めるには、検索バーにクエリを入力してください。どこから始めていいかわかりませんか?{topTerms}。", + "xpack.graph.guidancePanel.nodesItem.topTermsButtonLabel": "トップアイテムをグラフ化", + "xpack.graph.guidancePanel.title": "グラフ作成の 3 つのステップ", + "xpack.graph.icon.areaChart": "面グラフ", + "xpack.graph.icon.at": "に", + "xpack.graph.icon.automobile": "自動車", + "xpack.graph.icon.bank": "銀行", + "xpack.graph.icon.barChart": "棒グラフ", + "xpack.graph.icon.bolt": "ボルト", + "xpack.graph.icon.cube": "キューブ", + "xpack.graph.icon.desktop": "デスクトップ", + "xpack.graph.icon.exclamation": "感嘆符", + "xpack.graph.icon.externalLink": "外部リンク", + "xpack.graph.icon.eye": "目", + "xpack.graph.icon.file": "開いているファイル", + "xpack.graph.icon.fileText": "ファイル", + "xpack.graph.icon.flag": "旗", + "xpack.graph.icon.folderOpen": "開いているフォルダ", + "xpack.graph.icon.font": "フォント", + "xpack.graph.icon.globe": "球", + "xpack.graph.icon.google": "Google", + "xpack.graph.icon.heart": "ハート", + "xpack.graph.icon.home": "ホーム", + "xpack.graph.icon.industry": "業界", + "xpack.graph.icon.info": "情報", + "xpack.graph.icon.key": "キー", + "xpack.graph.icon.lineChart": "折れ線グラフ", + "xpack.graph.icon.list": "一覧", + "xpack.graph.icon.mapMarker": "マップマーカー", + "xpack.graph.icon.music": "音楽", + "xpack.graph.icon.phone": "電話", + "xpack.graph.icon.pieChart": "円グラフ", + "xpack.graph.icon.plane": "飛行機", + "xpack.graph.icon.question": "質問", + "xpack.graph.icon.shareAlt": "alt を共有", + "xpack.graph.icon.table": "表", + "xpack.graph.icon.tachometer": "タコメーター", + "xpack.graph.icon.user": "ユーザー", + "xpack.graph.icon.users": "ユーザー", + "xpack.graph.inspect.requestTabTitle": "リクエスト", + "xpack.graph.inspect.responseTabTitle": "応答", + "xpack.graph.inspect.title": "検査", + "xpack.graph.leaveWorkspace.confirmButtonLabel": "それでも移動", + "xpack.graph.leaveWorkspace.confirmText": "今移動すると、保存されていない変更が失われます。", + "xpack.graph.leaveWorkspace.modalTitle": "保存されていない変更", + "xpack.graph.listing.createNewGraph.combineDataViewFromKibanaAppDescription": "Elasticsearch インデックスのパターンと関係性を検出します。", + "xpack.graph.listing.createNewGraph.createButtonLabel": "グラフを作成", + "xpack.graph.listing.createNewGraph.newToKibanaDescription": "Kibana は初めてですか?{sampleDataInstallLink} で始めましょう。", + "xpack.graph.listing.createNewGraph.sampleDataInstallLinkText": "サンプルデータ", + "xpack.graph.listing.createNewGraph.title": "初めてのグラフを作成してみましょう。", + "xpack.graph.listing.graphsTitle": "グラフ", + "xpack.graph.listing.noDataSource.newToKibanaDescription": "Kibana は初めてですか?{sampleDataInstallLink} も使用できます。", + "xpack.graph.listing.noDataSource.sampleDataInstallLinkText": "サンプルデータ", + "xpack.graph.listing.noItemsMessage": "グラフがないようです。", + "xpack.graph.listing.table.descriptionColumnName": "説明", + "xpack.graph.listing.table.entityName": "グラフ", + "xpack.graph.listing.table.entityNamePlural": "グラフ", + "xpack.graph.listing.table.titleColumnName": "タイトル", + "xpack.graph.newGraphTitle": "保存されていないグラフ", + "xpack.graph.outlinkEncoders.kqlLooseDescription": "KQL クエリ、ディスカバリ、可視化、ダッシュボードに対応", + "xpack.graph.outlinkEncoders.kqlLooseTitle": "KQL OR クエリ", + "xpack.graph.outlinkEncoders.kqlTitle": "KQL AND クエリ", + "xpack.graph.saveWorkspace.savingErrorMessage": "ワークスペースの保存に失敗しました: {message}", + "xpack.graph.settings.advancedSettings.timeoutUnit": "ms", + "xpack.graph.settings.closeLabel": "閉じる", + "xpack.graph.settings.drillDowns.cancelButtonLabel": "キャンセル", + "xpack.graph.settings.drillDowns.kibanaUrlWarningConvertOptionLinkText": "変換する。", + "xpack.graph.settings.drillDowns.newSaveButtonLabel": "ドリルダウンを保存", + "xpack.graph.settings.drillDowns.removeButtonLabel": "削除", + "xpack.graph.settings.drillDowns.updateSaveButtonLabel": "ドリルダウンを更新", + "xpack.graph.settings.title": "設定", + "xpack.graph.sourceModal.notFoundLabel": "データソースが見つかりませんでした。", + "xpack.graph.sourceModal.savedObjectType.indexPattern": "インデックスパターン", + "xpack.graph.sourceModal.title": "データソースを選択", + "xpack.graph.templates.addLabel": "新規ドリルダウン", + "xpack.graph.templates.newTemplateFormLabel": "ドリルダウンを追加", + "xpack.graph.topNavMenu.inspectAriaLabel": "検査", + "xpack.graph.topNavMenu.inspectLabel": "検査", + "xpack.graph.topNavMenu.save.objectType": "グラフ", + "xpack.graph.clearWorkspace.confirmButtonLabel": "データソースを変更", + "xpack.graph.clearWorkspace.confirmText": "データソースを変更すると、現在のフィールドと頂点がリセットされます。", + "xpack.graph.loadWorkspace.missingIndexPatternErrorMessage": "インデックスパターンが見つかりませんでした", + "xpack.graph.noDataSourceNotificationMessageText": "データソースが見つかりませんでした。{managementIndexPatternsLink} に移動して Elasticsearch インデックスのインデックスパターンを作成してください。", "xpack.grokDebugger.customPatterns.callOutTitle": "1 行に付き 1 つのカスタムパターンを入力してください例:", "xpack.grokDebugger.customPatternsButtonLabel": "カスタムパターン", "xpack.grokDebugger.displayName": "Grok デバッガー", @@ -4593,6 +5627,13 @@ "xpack.idxMgmt.templateValidation.templateNameRequiredError": "テンプレート名が必要です。", "xpack.idxMgmt.templateValidation.templateNameSpacesError": "テンプレート名にスペースは使用できません。", "xpack.idxMgmt.templateValidation.templateNameUnderscoreError": "テンプレート名はアンダーラインで始めることはできません。", + "xpack.idxMgmt.indexTable.captionText": "以下は {total} 列中 {count, plural, one {# 列} other {# 列}} を含むインデックス表です。", + "xpack.idxMgmt.indexTable.selectAllIndicesAriaLabel": "すべての行を選択", + "xpack.idxMgmt.indexTable.selectIndexAriaLabel": "この行を選択", + "xpack.idxMgmt.validators.string.invalidJSONError": "無効な JSON フォーマット。", + "xpack.idxMgmt.indexActionsMenu.closeIndex.systemIndexLabel": "システムインデックス", + "xpack.idxMgmt.indexActionsMenu.deleteIndex.proceedWithCautionCallOutDescription": "システムインデックスは内部オペレーションに不可欠です。システムインデックスを削除すると、復元することはできません。適切なバックアップがあることを確認してください。", + "xpack.idxMgmt.indexActionsMenu.deleteIndex.systemIndexLabel": "システムインデックス", "xpack.indexLifecycleMgmt.activePhaseMessage": "アクティブ", "xpack.indexLifecycleMgmt.addLifecyclePolicyActionButtonLabel": "ライフサイクルポリシーを追加", "xpack.indexLifecycleMgmt.appTitle": "インデックスライフサイクルポリシー", @@ -4779,6 +5820,8 @@ "xpack.indexLifecycleMgmt.policyJsonFlyout.descriptionText": "この Elasticsearch リクエストは、このインデックスライフサイクルポリシーを作成または更新します。", "xpack.indexLifecycleMgmt.policyJsonFlyout.namedTitle": "「{policyName}」のリクエスト", "xpack.indexLifecycleMgmt.policyJsonFlyout.unnamedTitle": "リクエスト", + "xpack.indexLifecycleMgmt.indexLifecycleMgmtSummary.showPhaseDefinitionDescriptionTitle": "フェーズ検知", + "xpack.indexLifecycleMgmt.policyTable.captionText": "以下は {total} 列中 {count, plural, one {# 列} other {# 列}} を含むインデックスライフサイクルポリシー表です。", "xpack.infra.chartSection.missingMetricDataText": "データが欠けています", "xpack.infra.chartSection.notEnoughDataPointsToRenderText": "チャートのレンダリングに必要なデータポイントが足りません。時間範囲を広げてみてください。", "xpack.infra.configureSourceActionLabel": "ソース構成を変更", @@ -4786,7 +5829,7 @@ "xpack.infra.errorPage.tryAgainButtonLabel": "再試行", "xpack.infra.errorPage.tryAgainDescription ": "戻るボタンをクリックして再試行してください。", "xpack.infra.errorPage.unexpectedErrorTitle": "おっと!", - "xpack.infra.featureRegistry.linkInfrastructureTitle": "インフラ", + "xpack.infra.featureRegistry.linkInfrastructureTitle": "メトリック", "xpack.infra.featureRegistry.linkLogsTitle": "ログ", "xpack.infra.groupByDisplayNames.availabilityZone": "アベイラビリティゾーン", "xpack.infra.groupByDisplayNames.hostName": "ホスト", @@ -4798,8 +5841,8 @@ "xpack.infra.groupByDisplayNames.serviceType": "サービスタイプ", "xpack.infra.header.badge.readOnly.text": "読み込み専用", "xpack.infra.header.badge.readOnly.tooltip": "ソース構成を変更できません", - "xpack.infra.header.infrastructureTitle": "インフラ", - "xpack.infra.homePage.documentTitle": "インフラ", + "xpack.infra.header.infrastructureTitle": "メトリック", + "xpack.infra.homePage.documentTitle": "メトリック", "xpack.infra.homePage.inventoryTabTitle": "インベントリ", "xpack.infra.homePage.metricsExplorerTabTitle": "メトリックエクスプローラー", "xpack.infra.homePage.noMetricsIndicesDescription": "追加しましょう!", @@ -4811,7 +5854,7 @@ "xpack.infra.infrastructureDescription": "インフラストラクチャーを閲覧します", "xpack.infra.infrastructureMetricsExplorerPage.documentTitle": "{previousTitle} | メトリックエクスプローラー", "xpack.infra.infrastructureSnapshotPage.documentTitle": "{previousTitle} | インベントリ", - "xpack.infra.infrastructureTitle": "インフラ", + "xpack.infra.infrastructureTitle": "メトリック", "xpack.infra.kibanaMetrics.invalidInfraMetricErrorMessage": "{id} は有効な InfraMetric ではありません", "xpack.infra.kibanaMetrics.nodeDoesNotExistErrorMessage": "{nodeId} が存在しません。", "xpack.infra.legendControls.applyButton": "適用", @@ -4821,7 +5864,7 @@ "xpack.infra.legendControls.minLabel": "最低", "xpack.infra.legendControls.switchLabel": "自動計算範囲", "xpack.infra.linkInfrastructureDescription": "インフラストラクチャーを閲覧します", - "xpack.infra.linkInfrastructureTitle": "インフラストラクチャー", + "xpack.infra.linkInfrastructureTitle": "メトリック", "xpack.infra.linkLogsDescription": "ログを閲覧します", "xpack.infra.linkLogsTitle": "ログ", "xpack.infra.linkTo.hostWithIp.error": "IP アドレス「{hostIp}」でホストが見つかりません.", @@ -4975,7 +6018,7 @@ "xpack.infra.notFoundPage.noContentFoundErrorTitle": "コンテンツがありません", "xpack.infra.redirectToNodeLogs.loadingNodeLogsMessage": "{nodeType} ログを読み込み中", "xpack.infra.registerFeatures.infraOpsDescription": "共通のサーバー、コンテナー、サービスのインフラストラクチャーメトリックとログを閲覧します。", - "xpack.infra.registerFeatures.infraOpsTitle": "インフラストラクチャー", + "xpack.infra.registerFeatures.infraOpsTitle": "メトリック", "xpack.infra.registerFeatures.logsDescription": "ログをリアルタイムでストリーするか、コンソール式の UI で履歴ビューをスクロールします。", "xpack.infra.registerFeatures.logsTitle": "ログ", "xpack.infra.sourceConfiguration.addLogColumnButtonLabel": "列を追加", @@ -5040,7 +6083,7 @@ "xpack.infra.analysisSetup.endTimeLabel": "終了時刻", "xpack.infra.analysisSetup.startTimeDefaultDescription": "ログインデックスの開始地点です。", "xpack.infra.analysisSetup.startTimeLabel": "開始時刻", - "xpack.infra.analysisSetup.timeRangeDescription": "デフォルトで、機械学習はログインデックスの始めからログメッセージを分析し、永久に継続します。別の開始日、終了日、または両方を指定できます。", + "xpack.infra.analysisSetup.timeRangeDescription": "デフォルトで、機械学習は 4 週間以内のログインデックスのログメッセージを分析し、永久に継続します。別の開始日、終了日、または両方を指定できます。", "xpack.infra.analysisSetup.timeRangeTitle": "時間範囲の選択", "xpack.infra.chartSection.missingMetricDataBody": "このチャートはデータが欠けています。", "xpack.infra.chartSection.notEnoughDataPointsToRenderTitle": "データが不十分です", @@ -5118,6 +6161,111 @@ "xpack.infra.sourceConfiguration.removeLogColumnButtonLabel": "{columnDescription} 列を削除", "xpack.infra.sourceConfiguration.tiebreakerFieldDescription": "同じタイムスタンプの 2 つのエントリーを識別するのに使用されるフィールドです", "xpack.infra.sourceConfiguration.timestampFieldDescription": "ログエントリーの並べ替えに使用されるタイムスタンプです", + "xpack.infra.analysisSetup.deleteAnalysisResultsWarning": "これにより以前検出された異常が削除されます。", + "xpack.infra.analysisSetup.recreateMlJobButton": "ML ジョブを再作成", + "xpack.infra.analysisSetup.steps.setupProcess.loadingText": "ML ジョブを作成中…", + "xpack.infra.analysisSetup.steps.setupProcess.successText": "ML ジョブが正常に設定されました", + "xpack.infra.analysisSetup.steps.setupProcess.tryAgainButton": "再試行", + "xpack.infra.analysisSetup.steps.setupProcess.viewResultsButton": "結果を表示", + "xpack.infra.logs.analysis.analyzeInMlButtonLabel": "ML で分析", + "xpack.infra.logs.analysis.anomaliesExpandedRowNumberOfLogEntriesDescription": "ログエントリーの数です", + "xpack.infra.logs.analysis.anomaliesExpandedRowTopAnomalyScoreDescription": "最高異常スコア", + "xpack.infra.logs.analysis.anomaliesSectionLineSeriesName": "15 分ごとのログエントリー (平均)", + "xpack.infra.logs.analysis.anomaliesSectionLoadingAriaLabel": "異常を読み込み中", + "xpack.infra.logs.analysis.anomaliesSectionTitle": "異常", + "xpack.infra.logs.analysis.anomaliesTableCollapseLabel": "縮小", + "xpack.infra.logs.analysis.anomaliesTableExpandLabel": "拡張", + "xpack.infra.logs.analysis.anomaliesTableMaxAnomalyScoreColumnName": "最高異常スコア", + "xpack.infra.logs.analysis.anomaliesTablePartitionColumnName": "パーティション", + "xpack.infra.logs.analysis.anomalySectionNoAnomaliesTitle": "異常が検出されませんでした。", + "xpack.infra.logs.analysis.anomalySectionNoDataBody": "時間範囲を調整する必要があるかもしれません。", + "xpack.infra.logs.analysis.anomalySectionNoDataTitle": "表示するデータがありません。", + "xpack.infra.logs.analysis.jobConfigurationOutdatedCalloutMessage": "異なるソース構成を使用して ML ジョブが作成されました。現在の構成を適用するにはジョブを再作成してください。これにより以前検出された異常が削除されます。", + "xpack.infra.logs.analysis.jobConfigurationOutdatedCalloutTitle": "古い ML ジョブ構成", + "xpack.infra.logs.analysis.jobDefinitionOutdatedCalloutMessage": "ML ジョブの新しいバージョンが利用可能です。新しいバージョンをデプロイするにはジョブを再作成してください。これにより以前検出された異常が削除されます。", + "xpack.infra.logs.analysis.jobDefinitionOutdatedCalloutTitle": "古い ML ジョブ定義", + "xpack.infra.logs.analysis.jobStoppedCalloutMessage": "ML ジョブが手動またはリソース不足により停止しました。新しいログエントリーはジョブが再起動するまで処理されません。", + "xpack.infra.logs.analysis.jobStoppedCalloutTitle": "ML ジョブが停止しました", + "xpack.infra.logs.analysis.logRateResultsToolbarText": "{startTime} から {endTime} までの {numberOfLogs} 件のログエントリーを分析しました", + "xpack.infra.logs.analysis.logRateSectionBucketSpanLabel": "バケットスパン:", + "xpack.infra.logs.analysis.logRateSectionBucketSpanValue": "15 分", + "xpack.infra.logs.analysis.overallAnomaliesNumberOfLogEntriesDescription": "ログエントリーの数です", + "xpack.infra.logs.analysis.overallAnomaliesTopAnomalyScoreDescription": "最高異常スコア", + "xpack.infra.logs.analysis.overallAnomalyChartMaxScoresLabel": "最高異常スコア", + "xpack.infra.logs.analysis.partitionMaxAnomalyScoreAnnotationLabel": "最高異常スコア: {maxAnomalyScore}", + "xpack.infra.logs.analysis.recreateJobButtonLabel": "ML ジョブを再作成", + "xpack.infra.logs.analysisPage.setupStatusUnknown.title": "ML ジョブのステータスを特定できませんでした。", + "xpack.infra.logs.analysisPage.setupStatusUnknown.tryAgainButton": "再試行", + "xpack.infra.logs.jumpToTailText": "最も新しいエントリーに移動", + "xpack.infra.metricDetailPage.containerMetricsLayout.cpuUsageSection.seriesLabel.cpu": "cpu", + "xpack.infra.metricDetailPage.containerMetricsLayout.memoryUsageSection.seriesLabel.memory": "メモリー", + "xpack.infra.metricDetailPage.containerStates.seriesLabel.paused": "一時停止中", + "xpack.infra.metricDetailPage.containerStates.seriesLabel.running": "実行中", + "xpack.infra.metricDetailPage.containerStates.seriesLabel.stopped": "停止中", + "xpack.infra.metricDetailPage.dockerMetricsLayout.containerStates.sectionLabel": "コンテナーステータス", + "xpack.infra.metricDetailPage.dockerMetricsLayout.overviewSection.pausedLabel": "一時停止中", + "xpack.infra.metricDetailPage.dockerMetricsLayout.overviewSection.runningLabel": "実行中", + "xpack.infra.metricDetailPage.dockerMetricsLayout.overviewSection.sectionLabel": "概要", + "xpack.infra.metricDetailPage.dockerMetricsLayout.overviewSection.stoppedLabel": "停止中", + "xpack.infra.metricDetailPage.dockerMetricsLayout.overviewSection.totalLabel": "合計", + "xpack.infra.metricDetailPage.dockerMetricsLayout.top5Cpu.sectionLabel": "CPU 別のトップ 5 コンテナー", + "xpack.infra.metricDetailPage.dockerMetricsLayout.top5Memory.sectionLabel": "メモリー別のトップ 5 コンテナー", + "xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.iowait": "iowait", + "xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.irq": "irq", + "xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.nice": "nice", + "xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.softirq": "softirq", + "xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.steal": "steal", + "xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.system": "システム", + "xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.user": "ユーザー", + "xpack.infra.metricDetailPage.hostMetricsLayout.memoryUsageSection.seriesLabel.cache": "キャッシュ", + "xpack.infra.metricDetailPage.hostMetricsLayout.memoryUsageSection.seriesLabel.free": "空き", + "xpack.infra.metricDetailPage.hostMetricsLayout.memoryUsageSection.seriesLabel.used": "使用中", + "xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeCpuCapacitySection.seriesLabel.capacity": "容量", + "xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeCpuCapacitySection.seriesLabel.used": "使用中", + "xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeDiskCapacitySection.seriesLabel.capacity": "容量", + "xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeDiskCapacitySection.seriesLabel.used": "使用中", + "xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeMemoryCapacitySection.seriesLabel.capacity": "容量", + "xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeMemoryCapacitySection.seriesLabel.used": "使用中", + "xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodePodCapacitySection.seriesLabel.capacity": "容量", + "xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodePodCapacitySection.seriesLabel.used": "使用中", + "xpack.infra.metricDetailPage.nginxMetricsLayout.activeConnectionsSection.seriesLabel.connections": "接続", + "xpack.infra.metricDetailPage.nginxMetricsLayout.requestRateSection.seriesLabel.rate": "レート", + "xpack.infra.metricDetailPage.podMetricsLayout.cpuUsageSection.seriesLabel.cpu": "cpu", + "xpack.infra.metricDetailPage.podMetricsLayout.memoryUsageSection.seriesLabel.memory": "メモリー", + "xpack.infra.metricsExplorer.chartOptions.barLabel": "バー", + "xpack.infra.nodeDetails.labels.availabilityZone": "アベイラビリティゾーン", + "xpack.infra.nodeDetails.labels.cloudProvider": "クラウドプロバイダー", + "xpack.infra.nodeDetails.labels.containerized": "コンテナー化", + "xpack.infra.nodeDetails.labels.hostname": "ホスト名", + "xpack.infra.nodeDetails.labels.instanceId": "インスタンス ID", + "xpack.infra.nodeDetails.labels.instanceName": "インスタンス名", + "xpack.infra.nodeDetails.labels.kernelVersion": "カーネルバージョン", + "xpack.infra.nodeDetails.labels.machineType": "マシンタイプ", + "xpack.infra.nodeDetails.labels.operatinSystem": "オペレーティングシステム", + "xpack.infra.nodeDetails.labels.projectId": "プロジェクト ID", + "xpack.infra.nodeDetails.labels.showMoreDetails": "他の詳細を表示", + "xpack.infra.nodeDetails.no": "いいえ", + "xpack.infra.nodeDetails.yes": "はい", + "xpack.infra.openView.actionNames.deleteConfirmation": "ビューを削除しますか?", + "xpack.infra.openView.cancelButton": "キャンセル", + "xpack.infra.openView.columnNames.actions": "アクション", + "xpack.infra.openView.columnNames.name": "名前", + "xpack.infra.openView.flyoutHeader": "ビューを読み込む", + "xpack.infra.sampleDataLinkLabel": "ログ", + "xpack.infra.savedView.defaultViewName": "デフォルト", + "xpack.infra.savedView.errorOnCreate.title": "ビューの保存中にエラーが発生しました。", + "xpack.infra.savedView.findError.title": "ビューの読み込み中にエラーが発生しました。", + "xpack.infra.waffle.inventoryButtonLabel": "ビュー: {selectedText}", + "xpack.infra.waffle.metricOptions.countText": "カウント", + "xpack.infra.waffle.savedView.createHeader": "ビューを保存", + "xpack.infra.waffle.savedViews.cancel": "キャンセル", + "xpack.infra.waffle.savedViews.cancelButton": "キャンセル", + "xpack.infra.waffle.savedViews.includeTimeFilterLabel": "ビューに時刻を保存", + "xpack.infra.waffle.savedViews.includeTimeHelpText": "ビューが読み込まれるごとに現在選択された時刻の時間フィルターが変更されます。", + "xpack.infra.waffle.savedViews.loadViewsLabel": "読み込み", + "xpack.infra.waffle.savedViews.saveButton": "保存", + "xpack.infra.waffle.savedViews.saveViewLabel": "保存", + "xpack.infra.waffle.savedViews.viewNamePlaceholder": "名前", "xpack.kueryAutocomplete.andOperatorDescription": "{bothArguments}がtrueであることを条件とする", "xpack.kueryAutocomplete.andOperatorDescription.bothArgumentsText": "両方の引数", "xpack.kueryAutocomplete.equalOperatorDescription": "一部の値に{equals}", @@ -5200,7 +6348,7 @@ "xpack.licenseMgmt.uploadLicense.uploadButtonLabel": "アップロード", "xpack.licenseMgmt.uploadLicense.uploadingButtonLabel": "アップロード中…", "xpack.licenseMgmt.uploadLicense.uploadLicenseTitle": "ライセンスのアップロード", - "xpack.licensing.check.errorExpiredMessage": "{licenseType} ライセンスが期限切れのため {pluginName} を使用できません", + "xpack.licensing.check.errorExpiredMessage": "{licenseType} ライセンスが期限切れのため {pluginName} を使用できません。", "xpack.licensing.check.errorUnavailableMessage": "現在ライセンス情報が利用できないため {pluginName} を使用できません。", "xpack.licensing.check.errorUnsupportedMessage": "ご使用の {licenseType} ライセンスは {pluginName} をサポートしていません。ライセンスをアップグレードしてください。", "xpack.logstash.addRoleAlert.grantAdditionalPrivilegesDescription": "Kibana の管理で、Kibana ユーザーに {role} ロールを割り当ててください。", @@ -5281,8 +6429,8 @@ "xpack.logstash.pipelinesTable.descriptionColumnLabel": "説明", "xpack.logstash.pipelinesTable.filterByIdLabel": "ID でフィルタリング", "xpack.logstash.pipelinesTable.idColumnLabel": "Id", - "xpack.logstash.pipelinesTable.lastModifiedColumnLabel": "最終更新", - "xpack.logstash.pipelinesTable.modifiedByColumnLabel": "変更者", + "xpack.logstash.pipelinesTable.lastModifiedColumnLabel": "最終更新:", + "xpack.logstash.pipelinesTable.modifiedByColumnLabel": "変更者:", "xpack.logstash.pipelinesTable.selectablePipelineMessage": "パイプライン「{id}」を選択します", "xpack.logstash.queueCheckpointWritesTooltip": "永続キューが有効な場合にチェックポイントを強制する前に書き込むイベントの最大数です。無制限にするには 0 を指定します。\n\nデフォルト値:1024", "xpack.logstash.queueMaxBytesTooltip": "バイト単位でのキューの合計容量です。ディスクドライブの容量がここで指定する値よりも大きいことを確認してください。\n\nデフォルト値:1024mb (1g)", @@ -5373,7 +6521,7 @@ "xpack.maps.layerPanel.metricsExpression.joinMustBeSetErrorMessage": "JOIN の設定が必要です", "xpack.maps.layerPanel.metricsExpression.metricsPopoverTitle": "メトリック", "xpack.maps.layerPanel.metricsExpression.useMetricsDescription": "{metricsLength, plural, one {してメトリックを使用します} other {してメトリックを使用します}}", - "xpack.maps.layerPanel.settingsPanel.layerNameLabel": "レイヤー名", + "xpack.maps.layerPanel.settingsPanel.layerNameLabel": "名前", "xpack.maps.layerPanel.settingsPanel.layerTransparencyLabel": "レイヤーの透明度", "xpack.maps.layerPanel.settingsPanel.unableToLoadTitle": "レイヤーを読み込めません", "xpack.maps.layerPanel.settingsPanel.visibleZoomLabel": "レイヤー表示のズーム範囲", @@ -5593,11 +6741,27 @@ "xpack.maps.toolbarOverlay.drawBounds.initialGeometryLabel": "境界", "xpack.maps.toolbarOverlay.drawShape.initialGeometryLabel": "図形", "xpack.maps.tooltip.geometryFilterForm.createFilterButtonLabel": "フィルターを作成", - "xpack.maps.tooltip.pageNumerText": "{total} 個中 {pageNumber} 個の機能", + "xpack.maps.tooltip.pageNumerText": "{pageNumber} / {total}", "xpack.maps.tooltip.showGeometryFilterViewLinkLabel": "ジオメトリでフィルタリング", "xpack.maps.tooltip.toolsControl.cancelDrawButtonLabel": "キャンセル", "xpack.maps.xyztmssource.attributionLink": "属性テキストにはリンクが必要です", "xpack.maps.xyztmssource.attributionText": "属性 URL にはテキストが必要です", + "xpack.maps.layerPanel.settingsPanel.layerGlobalFilterLabel": "グローバルフィルター", + "xpack.maps.layerPanel.settingsPanel.percentageLabel": "%", + "xpack.maps.layerPanel.settingsPanel.visibleZoom": "ズームレベル", + "xpack.maps.source.esSearch.sortFieldSelectPlaceholder": "ソートフィールドを選択", + "xpack.maps.source.esSearch.sortLabel": "並べ替え", + "xpack.maps.toolbarOverlay.drawBoundsLabelShort": "境界を描く", + "xpack.maps.toolbarOverlay.drawShapeLabelShort": "図形を描く", + "xpack.maps.tooltipSelector.addLabelWithCount": "{count} を追加", + "xpack.maps.tooltipSelector.addLabelWithoutCount": "追加", + "xpack.maps.tooltipSelector.grabButtonAriaLabel": "プロパティを並べ替える", + "xpack.maps.tooltipSelector.grabButtonTitle": "プロパティを並べ替える", + "xpack.maps.tooltipSelector.togglePopoverLabel": "追加", + "xpack.maps.tooltipSelector.trashButtonAriaLabel": "プロパティを削除", + "xpack.maps.tooltipSelector.trashButtonTitle": "プロパティを削除", + "xpack.maps.vector.dualSize.unitLabel": "px", + "xpack.maps.vector.size.unitLabel": "px", "xpack.ml.annotationsTable.actionsColumnName": "アクション", "xpack.ml.annotationsTable.annotationColumnName": "注釈", "xpack.ml.annotationsTable.annotationsNotCreatedTitle": "このジョブには注釈が作成されていません", @@ -6271,7 +7435,7 @@ "xpack.ml.models.jobValidation.messages.jobGroupIdInvalidMessage": "ジョブグループ名の 1 つが無効です。アルファベットの小文字 (a-z と 0-9)、ハイフンまたはアンダーラインが使用でき、最初と最後を英数字にする必要があります。", "xpack.ml.models.jobValidation.messages.jobGroupIdValidHeading": "ジョブグループ ID のフォーマットは有効です。", "xpack.ml.models.jobValidation.messages.jobIdEmptyMessage": "ジョブ名フィールドは未入力のままにできません。", - "xpack.ml.models.jobValidation.messages.jobIdInvalidMessage": "ジョブ名が無効です。アルファベットの小文字 (a-z と 0-9)、ハイフンまたはアンダーラインが使用でき、最初と最後を英数字にする必要があります。", + "xpack.ml.models.jobValidation.messages.jobIdInvalidMessage": "ジョブ ID が無効です。アルファベットの小文字 (a-z と 0-9)、ハイフンまたはアンダーラインが使用でき、最初と最後を英数字にする必要があります。", "xpack.ml.models.jobValidation.messages.jobIdValidHeading": "ジョブ ID のフォーマットは有効です。", "xpack.ml.models.jobValidation.messages.mmlGreaterThanMaxMmlMessage": "モデルメモリー制限が、このクラスターに構成された最大モデルメモリー制限を超えています。", "xpack.ml.models.jobValidation.messages.mmlValueInvalidMessage": "{mml} はモデルメモリー制限の有効な値ではありません。この値は最低 1MB で、バイト (例: 10MB) で指定する必要があります。", @@ -6742,6 +7906,251 @@ "xpack.ml.newJob.wizard.validateJob.jobNameAlreadyExists": "ジョブ ID が既に存在しますジョブ ID は既存のジョブやグループと同じにできません。", "xpack.ml.newJob.wizard.validateJob.modelMemoryLimitRangeInvalidErrorMessage": "モデルメモリー制限は最高値の {maxModelMemoryLimit} よりも高くできません", "xpack.ml.newJob.wizard.validateJob.modelMemoryLimitUnitsInvalidErrorMessage": "モデルメモリー制限のデータユニットが認識されません。{str} でなければなりません", + "xpack.ml.accessDenied.description": "ML プラグインへのアクセスパーミッションがありません", + "xpack.ml.accessDenied.label": "パーミッションがありません", + "xpack.ml.anomalyDetection.anomalyExplorerLabel": "異常エクスプローラー", + "xpack.ml.anomalyDetection.jobManagementLabel": "ジョブ管理", + "xpack.ml.anomalyDetection.singleMetricViewerLabel": "シングルメトリックビューアー", + "xpack.ml.anomalyDetectionBreadcrumbLabel": "異常検知", + "xpack.ml.dataframe.analytics.create.advancedEditorMessage.dependentVariableEmpty": "従属変数フィールドは未入力のままにできません。", + "xpack.ml.dataframe.analytics.create.dependentVariableInputAriaLabel": "従属変数として使用するフィールドを入力してください。", + "xpack.ml.dataframe.analytics.create.dependentVariableLabel": "従属変数", + "xpack.ml.dataframe.analytics.create.dependentVariableOptionsFetchError": "フィールドの取得中にエラーが発生しました。ページを更新して再起動してください。", + "xpack.ml.dataframe.analytics.create.dependentVariablePlaceholder": "従属変数", + "xpack.ml.dataframe.analytics.create.enableAdvancedEditorHelpText": "高度なエディターからこのフォームには戻れません。", + "xpack.ml.dataframe.analytics.create.enableAdvancedEditorSwitch": "詳細エディターを有効にする", + "xpack.ml.dataframe.analytics.create.flyoutCancelButton": "キャンセル", + "xpack.ml.dataframe.analytics.create.flyoutCloseButton": "閉じる", + "xpack.ml.dataframe.analytics.create.flyoutCreateButton": "作成", + "xpack.ml.dataframe.analytics.create.flyoutHeaderTitle": "分析ジョブの作成", + "xpack.ml.dataframe.analytics.create.flyoutStartButton": "開始", + "xpack.ml.dataframe.analytics.create.indexPatternAlreadyExistsError": "このタイトルのインデックスパターンが既に存在します。", + "xpack.ml.dataframe.analytics.create.indexPatternExistsError": "このタイトルのインデックスパターンが既に存在します。", + "xpack.ml.dataframe.analytics.create.jobIdInvalidMaxLengthErrorMessage": "ジョブ ID は {maxLength, plural, one {# 文字} other {# 文字}} 以内でなければなりません。", + "xpack.ml.dataframe.analytics.create.outlierDetectionHelpText": "外れ値検出ジョブには表のようなデータストラクチャでマッピングされたソースインデックスが必要で、数字とブールフィールドのみ分析されます。モデルメモリー制限や分析タイプなどのカスタムオプションを適用するには、詳細エディターを使用してください。", + "xpack.ml.dataframe.analytics.create.outlierRegressionHelpText": "リグレッションジョブは数値フィールドのみを分析します。モデルメモリー制限や予測フィールド名などのカスタムオプションを適用するには、詳細エディターを使用してください。", + "xpack.ml.dataframe.analytics.create.trainingPercentLabel": "トレーニングパーセンテージ", + "xpack.ml.dataframe.analytics.regressionExploration.evaluateError": "データの読み込み中にエラーが発生しました。", + "xpack.ml.dataframe.analytics.regressionExploration.generalError": "データの読み込み中にエラーが発生しました。", + "xpack.ml.dataframe.analytics.regressionExploration.generalizationErrorTitle": "一般化エラー", + "xpack.ml.dataframe.analytics.regressionExploration.jobIdTitle": "ジョブ ID {jobId}", + "xpack.ml.dataframe.analytics.regressionExploration.meanSquaredErrorText": "平均二乗エラー", + "xpack.ml.dataframe.analytics.regressionExploration.noDataCalloutBody": "インデックスのクエリが結果を返しませんでした。ジョブが完了済みで、インデックスにドキュメントがあることを確認してください。", + "xpack.ml.dataframe.analytics.regressionExploration.noDataCalloutTitle": "空のインデックスクエリ結果。", + "xpack.ml.dataframe.analytics.regressionExploration.noIndexCalloutBody": "インデックスのクエリが結果を返しませんでした。デスティネーションインデックスが存在し、ドキュメントがあることを確認してください。", + "xpack.ml.dataframe.analytics.regressionExploration.rSquaredText": "R の二乗", + "xpack.ml.dataframe.analytics.regressionExploration.trainingErrorTitle": "トレーニングエラー", + "xpack.ml.dataFrameAnalyticsBreadcrumbs.indexLabel": "インデックス", + "xpack.ml.datavisualizer.actionsPanel.selectKnownConfigurationDescription": "認識されたデータの既知の構成を選択します:", + "xpack.ml.dataVisualizer.fileBasedLabel": "ファイル", + "xpack.ml.datavisualizerBreadcrumbLabel": "データビジュアライザー", + "xpack.ml.explorer.distributionChart.entityLabel": "エンティティ", + "xpack.ml.jobsList.editJobFlyout.leaveAnywayButtonLabel": "それでも移動", + "xpack.ml.jobsList.editJobFlyout.saveChangesButtonLabel": "変更を保存", + "xpack.ml.jobsList.editJobFlyout.unsavedChangesDialogMessage": "保存しないと、変更が失われます。", + "xpack.ml.jobsList.editJobFlyout.unsavedChangesDialogTitle": "閉じる前に変更を保存しますか?", + "xpack.ml.models.jobValidation.messages.jobGroupIdInvalidMaxLengthErrorMessage": "ジョブグループ名は {maxLength, plural, one {# 文字} other {# 文字}} 以内でなければなりません。", + "xpack.ml.models.jobValidation.messages.jobIdInvalidMaxLengthErrorMessage": "ジョブ ID は {maxLength, plural, one {# 文字} other {# 文字}} 以内でなければなりません。", + "xpack.ml.navMenu.anomalyDetectionTabLinkText": "異常検知", + "xpack.ml.navMenu.overviewTabLinkText": "概要", + "xpack.ml.newJi18n(ob.recognize.jobsCreationFailed.resetButtonAriaLabel": "リセット", + "xpack.ml.newJob.recognize.advancedLabel": "高度な設定", + "xpack.ml.newJob.recognize.advancedSettingsAriaLabel": "高度な設定", + "xpack.ml.newJob.recognize.alreadyExistsLabel": "(既に存在します)", + "xpack.ml.newJob.recognize.analysisRunningLabel": "分析を実行中", + "xpack.ml.newJob.recognize.createJobButtonAriaLabel": "ジョブの作成", + "xpack.ml.newJob.recognize.createJobButtonLabel": "{numberOfJobs, plural, zero {Job} one {Job} other {Jobs}} を作成", + "xpack.ml.newJob.recognize.dashboardsLabel": "ダッシュボード", + "xpack.ml.newJob.recognize.datafeed.savedAriaLabel": "保存されました", + "xpack.ml.newJob.recognize.datafeed.saveFailedAriaLabel": "保存に失敗", + "xpack.ml.newJob.recognize.datafeedLabel": "データフィード", + "xpack.ml.newJob.recognize.indexPatternPageTitle": "インデックスパターン {indexPatternTitle}", + "xpack.ml.newJob.recognize.job.savedAriaLabel": "保存されました", + "xpack.ml.newJob.recognize.job.saveFailedAriaLabel": "保存に失敗", + "xpack.ml.newJob.recognize.jobGroupAllowedCharactersDescription": "ジョブグループ名にはアルファベットの小文字 (a-z と 0-9)、ハイフンまたはアンダーラインが使用でき、最初と最後を英数字にする必要があります", + "xpack.ml.newJob.recognize.jobIdPrefixLabel": "ジョブ ID の接頭辞", + "xpack.ml.newJob.recognize.jobLabel": "ジョブ名", + "xpack.ml.newJob.recognize.jobLabelAllowedCharactersDescription": "ジョブラベルにはアルファベットの小文字 (a-z と 0-9)、ハイフンまたはアンダーラインが使用でき、最初と最後を英数字にする必要があります", + "xpack.ml.newJob.recognize.jobPrefixInvalidMaxLengthErrorMessage": "ジョブ ID 接頭辞は {maxLength, plural, one {# 文字} other {# 文字}}以内でなければなりません。", + "xpack.ml.newJob.recognize.jobsCreatedTitle": "ジョブが作成されました", + "xpack.ml.newJob.recognize.jobSettingsTitle": "ジョブ設定", + "xpack.ml.newJob.recognize.jobsTitle": "ジョブ", + "xpack.ml.newJob.recognize.moduleCheckJobsExistWarningDescription": "モジュールのジョブがクラッシュしたか確認する際にエラーが発生しました。", + "xpack.ml.newJob.recognize.moduleCheckJobsExistWarningTitle": "モジュール {moduleId} の確認中にエラーが発生", + "xpack.ml.newJob.recognize.moduleSetupFailedWarningDescription": "モジュールでの{count, plural, one { 件のジョブ} other { 件のジョブ}}の作成中にエラーが発生しました。", + "xpack.ml.newJob.recognize.moduleSetupFailedWarningTitle": "モジュール {moduleId} のセットアップ中にエラーが発生", + "xpack.ml.newJob.recognize.newJobFromTitle": "{pageTitle} からの新規ジョブ", + "xpack.ml.newJob.recognize.results.savedAriaLabel": "保存されました", + "xpack.ml.newJob.recognize.results.saveFailedAriaLabel": "保存に失敗", + "xpack.ml.newJob.recognize.running.startedAriaLabel": "開始済み", + "xpack.ml.newJob.recognize.running.startFailedAriaLabel": "開始に失敗", + "xpack.ml.newJob.recognize.runningLabel": "実行中", + "xpack.ml.newJob.recognize.savedSearchPageTitle": "保存検索 {savedSearchTitle}", + "xpack.ml.newJob.recognize.searchesLabel": "検索", + "xpack.ml.newJob.recognize.searchWillBeOverwrittenLabel": "検索は上書きされます", + "xpack.ml.newJob.recognize.someJobsCreationFailed.resetButtonLabel": "リセット", + "xpack.ml.newJob.recognize.someJobsCreationFailedTitle": "一部のジョブの作成に失敗しました", + "xpack.ml.newJob.recognize.startDatafeedAfterSaveLabel": "保存後データフィードを開始", + "xpack.ml.newJob.recognize.useDedicatedIndexLabel": "専用インデックスを使用", + "xpack.ml.newJob.recognize.useFullDataLabel": "完全な {indexPatternTitle} データを使用", + "xpack.ml.newJob.recognize.usingSavedSearchDescription": "保存検索を使用すると、データフィードで使用されるクエリが、{moduleId} モジュールでデフォルトで提供されるものと異なるものになります。", + "xpack.ml.newJob.recognize.viewResultsAriaLabel": "結果を表示", + "xpack.ml.newJob.recognize.viewResultsLinkText": "結果を表示", + "xpack.ml.newJob.recognize.visualizationsLabel": "ビジュアライゼーション", + "xpack.ml.newJob.wizard.autoSetJobCreatorTimeRange.error": "インデックスの開始時刻と終了時刻の取得中にエラーが発生しました", + "xpack.ml.newJob.wizard.datafeedPreviewFlyout.closeButton": "閉じる", + "xpack.ml.newJob.wizard.datafeedPreviewFlyout.datafeedDoesNotExistLabel": "データフィードが存在しません", + "xpack.ml.newJob.wizard.datafeedPreviewFlyout.showButton": "データフィードのプレビュー", + "xpack.ml.newJob.wizard.datafeedPreviewFlyout.title": "データフィードのプレビュー", + "xpack.ml.newJob.wizard.datafeedStep.frequency.description": "検索の間隔。", + "xpack.ml.newJob.wizard.datafeedStep.frequency.title": "頻度", + "xpack.ml.newJob.wizard.datafeedStep.query.title": "Elasticsearch クエリ", + "xpack.ml.newJob.wizard.datafeedStep.queryDelay.description": "現在の時刻と最新のインプットデータ時刻の間の秒単位での遅延です。", + "xpack.ml.newJob.wizard.datafeedStep.queryDelay.title": "クエリの遅延", + "xpack.ml.newJob.wizard.datafeedStep.scrollSize.description": "検索ごとにリクエストするドキュメントの最高数です。", + "xpack.ml.newJob.wizard.datafeedStep.scrollSize.title": "スクロールサイズ", + "xpack.ml.newJob.wizard.datafeedStep.timeField.description": "インデックスパターンのデフォルトの時間フィールドは自動的に選択されますが、上書きできます。", + "xpack.ml.newJob.wizard.datafeedStep.timeField.title": "時間フィールド", + "xpack.ml.newJob.wizard.editJsonButton": "JSON を編集", + "xpack.ml.newJob.wizard.jsonFlyout.closeButton": "閉じる", + "xpack.ml.newJob.wizard.jsonFlyout.datafeed.title": "データフィード構成 JSON", + "xpack.ml.newJob.wizard.jsonFlyout.job.title": "ジョブ構成 JSON", + "xpack.ml.newJob.wizard.jsonFlyout.saveButton": "保存", + "xpack.ml.newJob.wizard.pickFieldsStep.addDetectorButton": "ディテクターを追加", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorList.deleteButton": "削除", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorList.editButton": "編集", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorList.title": "検知器", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.aggSelect.description": "実行される分析機能です (例: sum、count)。", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.aggSelect.title": "関数", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.byFieldSelect.description": "エンティティ自体の過去の動作と比較し異常が検出された個々の分析に必要です。", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.byFieldSelect.title": "フィールド別", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.cancelButton": "キャンセル", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.description.description": "デフォルトのディテクターの説明で、ディテクターの分析内容を説明します。", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.description.title": "説明", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.excludeFrequent.description": "true の場合、頻繁に発生するエンティティを自動的に認識し除外し、結果の大部分を占めるのを防ぎます。", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.excludeFrequent.title": "頻繁なものを除外", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.fieldSelect.description": "関数 sum、mean、median、max、min、info_content、distinct_count に必要です。", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.fieldSelect.title": "フィールド", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.overFieldSelect.description": "集団の動きと比較して異常が検出された部分の集団分析に必要です。", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.overFieldSelect.title": "オーバーフィールド", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.partitionFieldSelect.description": "モデリングの論理グループへの分裂を可能にします。", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.partitionFieldSelect.title": "パーティションフィールド", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.saveButton": "保存", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.title": "ディテクターの作成", + "xpack.ml.newJob.wizard.pickFieldsStep.categorizationField.description": "オプション。非構造化ログデータの場合使用。テキストデータタイプの使用をお勧めします。", + "xpack.ml.newJob.wizard.pickFieldsStep.categorizationField.title": "カテゴリー分けフィールド", + "xpack.ml.newJob.wizard.pickFieldsStep.noDetectorsCallout.message": "ジョブを作成するには最低 1 つのディテクターが必要です。", + "xpack.ml.newJob.wizard.pickFieldsStep.noDetectorsCallout.title": "ディテクターがありません", + "xpack.ml.newJob.wizard.pickFieldsStep.summaryCountField.description": "オプション。インプットデータが事前にまとめられている場合に使用、例: \\{docCountParam\\}。", + "xpack.ml.newJob.wizard.pickFieldsStep.summaryCountField.title": "サマリーカウントフィールド", + "xpack.ml.newJob.wizard.previewJsonButton": "JSON をプレビュー", + "xpack.ml.newJob.wizard.searchSelection.notFoundLabel": "一致するインデックスまたは保存検索が見つかりませんでした。", + "xpack.ml.newJob.wizard.searchSelection.savedObjectType.indexPattern": "インデックスパターン", + "xpack.ml.newJob.wizard.searchSelection.savedObjectType.search": "保存検索", + "xpack.ml.newJob.wizard.selectIndexPatternOrSavedSearch": "インデックスパターンまたは保存検索を選択してください", + "xpack.ml.newJob.wizard.step.configureDatafeedTitle": "データフィードの構成", + "xpack.ml.newJob.wizard.stepComponentWrapper.configureDatafeedTitle": "データフィードの構成", + "xpack.ml.newJob.wizard.summaryStep.datafeedConfig.title": "データフィードの構成", + "xpack.ml.newJob.wizard.summaryStep.datafeedDetails.frequency.title": "頻度", + "xpack.ml.newJob.wizard.summaryStep.datafeedDetails.query.title": "スクロールサイズ", + "xpack.ml.newJob.wizard.summaryStep.datafeedDetails.queryDelay.title": "クエリの遅延", + "xpack.ml.newJob.wizard.summaryStep.datafeedDetails.scrollSize.title": "スクロールサイズ", + "xpack.ml.newJob.wizard.summaryStep.datafeedDetails.timeField.title": "時間フィールド", + "xpack.ml.newJob.wizard.summaryStep.defaultString": "デフォルト", + "xpack.ml.newJob.wizard.summaryStep.falseLabel": "False", + "xpack.ml.newJob.wizard.summaryStep.jobConfig.title": "ジョブの構成", + "xpack.ml.newJob.wizard.summaryStep.jobDetails.categorizationField.title": "カテゴリー分けフィールド", + "xpack.ml.newJob.wizard.summaryStep.jobDetails.summaryCountField.title": "サマリーカウントフィールド", + "xpack.ml.newJob.wizard.summaryStep.timeRange.end.title": "終了", + "xpack.ml.newJob.wizard.summaryStep.timeRange.start.title": "開始", + "xpack.ml.newJob.wizard.summaryStep.trueLabel": "True", + "xpack.ml.newJob.wizard.validateJob.frequencyInvalidTimeIntervalFormatErrorMessage": "{value} は有効な時間間隔のフォーマット (例: {tenMinutes}、{oneHour}) ではありません。また、0 よりも大きい数字である必要があります。", + "xpack.ml.newJob.wizard.validateJob.jobGroupMaxLengthDescription": "ジョブグループ名は {maxLength, plural, one {# 文字} other {# 文字}} 以内でなければなりません。", + "xpack.ml.newJob.wizard.validateJob.jobIdInvalidMaxLengthErrorMessage": "ジョブ ID は {maxLength, plural, one {# 文字} other {# 文字}} 以内でなければなりません。", + "xpack.ml.newJob.wizard.validateJob.queryCannotBeEmpty": "データフィードクエリは未入力のままにできません。", + "xpack.ml.newJob.wizard.validateJob.queryIsInvalidEsQuery": "データフィードクエリは有効な Elasticsearch クエリでなければなりません。", + "xpack.ml.overview.analyticsList.createFirstJobMessage": "初めての分析ジョブを作ってみましょう。", + "xpack.ml.overview.analyticsList.createJobButtonText": "ジョブを作成", + "xpack.ml.overview.analyticsList.emptyPromptText": "データフレーム分析は、様々なデータ分析を行い結果と共に注釈に追加することができます。分析ジョブは注釈付きデータと共に、ソースデータのコピーを新規インデックスに保存します。", + "xpack.ml.overview.analyticsList.errorPromptTitle": "データフレーム分析リストの取得中にエラーが発生しました。", + "xpack.ml.overview.analyticsList.id": "ID", + "xpack.ml.overview.analyticsList.manageJobsButtonText": "ジョブの管理", + "xpack.ml.overview.analyticsList.PanelTitle": "分析", + "xpack.ml.overview.analyticsList.reatedTimeColumnName": "作成時刻", + "xpack.ml.overview.analyticsList.refreshJobsButtonText": "更新", + "xpack.ml.overview.analyticsList.status": "ステータス", + "xpack.ml.overview.analyticsList.tableActionLabel": "アクション", + "xpack.ml.overview.analyticsList.type": "タイプ", + "xpack.ml.overview.anomalyDetection.createFirstJobMessage": "初めての異常検知ジョブを作成しましょう。", + "xpack.ml.overview.anomalyDetection.createJobButtonText": "ジョブを作成", + "xpack.ml.overview.anomalyDetection.emptyPromptText": "機械学習により、Elasticsearch に格納された時系列データの異常検知が容易になります。1 つのメトリックを 1 台のマシンから確認したり、何百ものメトリックを何千台ものマシンから確認したりできます。データに隠れた異常を自動的に検出して問題をより素早く解決しましょう。", + "xpack.ml.overview.anomalyDetection.errorPromptTitle": "異常検出ジョブリストの取得中にエラーが発生しました。", + "xpack.ml.overview.anomalyDetection.errorWithFetchingAnomalyScoreNotificationErrorMessage": "異常スコアの取得中にエラーが発生しました: {error}", + "xpack.ml.overview.anomalyDetection.exploreActionName": "探索", + "xpack.ml.overview.anomalyDetection.manageJobsButtonText": "ジョブの管理", + "xpack.ml.overview.anomalyDetection.panelTitle": "異常検知", + "xpack.ml.overview.anomalyDetection.refreshJobsButtonText": "更新", + "xpack.ml.overview.anomalyDetection.resultActions.openJobsInAnomalyExplorerText": "{jobsCount, plural, one {{jobId}} other {# 件のジョブ}} を異常エクスプローラーで開く", + "xpack.ml.overview.anomalyDetection.tableActionLabel": "アクション", + "xpack.ml.overview.anomalyDetection.tableDocsProcessed": "処理されたドキュメント", + "xpack.ml.overview.anomalyDetection.tableId": "グループ ID", + "xpack.ml.overview.anomalyDetection.tableLatestTimestamp": "最新タイムスタンプ", + "xpack.ml.overview.anomalyDetection.tableMaxScore": "最高異常スコア", + "xpack.ml.overview.anomalyDetection.tableMaxScoreErrorTooltip": "最高異常スコアの読み込み中に問題が発生しました", + "xpack.ml.overview.anomalyDetection.tableMaxScoreTooltip": "グループ内の 24 時間以内のすべてのジョブの最高スコアです", + "xpack.ml.overview.anomalyDetection.tableNumJobs": "グループのジョブ", + "xpack.ml.overview.feedbackSectionLink": "オンラインでのフィードバック", + "xpack.ml.overview.feedbackSectionText": "機械学習に関するご意見やご提案は、お気軽に {feedbackLink} にお寄せください。", + "xpack.ml.overview.feedbackSectionTitle": "フィードバック", + "xpack.ml.overview.gettingStartedSectionCreateJob": "新規ジョブを作成中", + "xpack.ml.overview.gettingStartedSectionDocs": "ドキュメンテーション", + "xpack.ml.overview.gettingStartedSectionTitle": "はじめて使う", + "xpack.ml.overview.statsBar.failedAnalyticsLabel": "失敗", + "xpack.ml.overview.statsBar.runningAnalyticsLabel": "実行中", + "xpack.ml.overview.statsBar.stoppedAnalyticsLabel": "停止中", + "xpack.ml.overview.statsBar.totalAnalyticsLabel": "分析ジョブ合計", + "xpack.ml.overviewBreadcrumbs.overviewLabel": "概要", + "xpack.ml.overviewJobsList.statsBar.activeMLNodesLabel": "アクティブな ML ノード", + "xpack.ml.overviewJobsList.statsBar.closedJobsLabel": "ジョブを作成", + "xpack.ml.overviewJobsList.statsBar.failedJobsLabel": "失敗したジョブ", + "xpack.ml.overviewJobsList.statsBar.openJobsLabel": "ジョブを開く", + "xpack.ml.overviewJobsList.statsBar.totalJobsLabel": "合計ジョブ数", + "xpack.ml.settingsBreadcrumbLabel": "設定", + "xpack.ml.validateJob.allPassed": "すべてのチェックに合格しました", + "xpack.ml.dataframe.analytics.create.startDataFrameAnalyticsSuccessMessage": "データフレーム分析 {jobId} の開始リクエストが受け付けられました。", + "xpack.ml.dataframe.analyticsList.deleteAnalyticsSuccessMessage": "データフレーム分析 {analyticsId} の削除リクエストが受け付けられました。", + "xpack.ml.dataframe.analyticsList.startAnalyticsSuccessMessage": "データフレーム分析 {analyticsId} の開始リクエストが受け付けられました。", + "xpack.ml.dataframe.analyticsList.stopAnalyticsSuccessMessage": "データフレーム分析 {analyticsId} の停止リクエストが受け付けられました。", + "xpack.ml.dataframe.stepCreateForm.createDataFrameAnalyticsSuccessMessage": "データフレーム分析 {jobId} の作成リクエストが受け付けられました。", + "xpack.ml.explorer.distributionChart.anomalyScoreLabel": "異常スコア", + "xpack.ml.explorer.distributionChart.typicalLabel": "通常", + "xpack.ml.explorer.distributionChart.unusualByFieldValuesLabel": "{ numberOfCauses, plural, one {# 個の異常な {byFieldName} 値 } other {#{plusSign}異常な{byFieldName}値}}", + "xpack.ml.explorer.distributionChart.valueLabel": "値", + "xpack.ml.explorer.distributionChart.valueWithoutAnomalyScoreLabel": "値", + "xpack.ml.explorer.singleMetricChart.actualLabel": "実際", + "xpack.ml.explorer.singleMetricChart.anomalyScoreLabel": "異常スコア", + "xpack.ml.explorer.singleMetricChart.multiBucketImpactLabel": "複数バケットの影響", + "xpack.ml.explorer.singleMetricChart.scheduledEventsLabel": "予定イベント", + "xpack.ml.explorer.singleMetricChart.typicalLabel": "通常", + "xpack.ml.explorer.singleMetricChart.valueLabel": "値", + "xpack.ml.explorer.singleMetricChart.valueWithoutAnomalyScoreLabel": "値", + "xpack.ml.explorer.swimlane.maxAnomalyScoreLabel": "最高異常スコア", + "xpack.ml.models.jobValidation.messages.jobGroupIdValidMessage": "アルファベットの小文字 (a-z と 0-9)、ハイフンまたはアンダーライン、最初と最後を英数字にし、{maxLength, plural, one {# 文字} other {# 文字}}以内にする必要があります。", + "xpack.ml.models.jobValidation.messages.jobIdValidMessage": "アルファベットの小文字 (a-z と 0-9)、ハイフンまたはアンダーライン、最初と最後を英数字にし、{maxLength, plural, one {# 文字} other {# 文字}}以内にする必要があります。", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.actualLabel": "実際", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.anomalyScoreLabel": "異常スコア", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.lowerBoundsLabel": "下の境界", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.upperBoundsLabel": "上の境界", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.valueLabel": "値", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.moreThanOneUnusualByFieldValuesLabel": "{numberOfCauses} 個の {plusSign}異常な{byFieldName}値", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.multiBucketImpactLabel": "複数バケットの影響", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.scheduledEventsLabel": "予定イベント {counter}", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.typicalLabel": "通常", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.valueLabel": "値", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScore.predictionLabel": "予測", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScore.valueLabel": "値", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScoreAndModelPlotEnabled.lowerBoundsLabel": "下の境界", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScoreAndModelPlotEnabled.upperBoundsLabel": "上の境界", "xpack.transform.capability.noPermission.createTransformTooltip": "データフレーム変換を作成するパーミッションがありません。", "xpack.transform.capability.noPermission.deleteTransformTooltip": "データフレーム変換を削除するパーミッションがありません。", "xpack.transform.capability.noPermission.startOrStopTransformTooltip": "データフレーム変換を開始・停止するパーミッションがありません。", @@ -6760,21 +8169,21 @@ "xpack.transform.agg.popoverForm.submitButtonLabel": "適用", "xpack.transform.aggLabelForm.deleteItemAriaLabel": "アイテムを削除", "xpack.transform.aggLabelForm.editAggAriaLabel": "集約を編集", - "xpack.transform.stepDefineForm.aggExistsErrorMessage": "「{aggName}」という名前のアグリゲーション構成は既に存在します。", - "xpack.transform.stepDefineForm.groupByExistsErrorMessage": "「{aggName}」という名前のgroup by構成は既に存在します。", + "xpack.transform.stepDefineForm.aggExistsErrorMessage": "「{aggName}」という名前の集約構成は既に存在します。", + "xpack.transform.stepDefineForm.groupByExistsErrorMessage": "「{aggName}」という名前のグループ分け構成は既に存在します。", "xpack.transform.stepDefineForm.nestedAggListConflictErrorMessage": "「{aggListName}」とネスティングの矛盾があるため、構成「{aggName}」を追加できませんでした。", "xpack.transform.stepDefineForm.nestedConflictErrorMessage": "「{aggNameCheck}」とネスティングの矛盾があるため、構成「{aggName}」を追加できませんでした。", "xpack.transform.stepDefineForm.nestedGroupByListConflictErrorMessage": "「{groupByListName}」とネスティングの矛盾があるため、構成「{aggName}」を追加できませんでした。", "xpack.transform.stepDefineForm.aggregationsLabel": "アグリゲーション(集計)", "xpack.transform.stepDefineForm.aggregationsPlaceholder": "集約を追加…", - "xpack.transform.stepDefineForm.formHelp": "データフレーム変換は、ピボット用のスケーラブルで自動化されたプロセスです。開始するにはグループ分けの条件と集約を少なくとも 1 つ選んでください。", + "xpack.transform.stepDefineForm.formHelp": "変換は、ピボット用のスケーラブルで自動化されたプロセスです。開始するにはグループ分けの条件と集約を少なくとも 1 つ選んでください。", "xpack.transform.stepDefineForm.groupByLabel": "グループ分けの条件", "xpack.transform.stepDefineForm.groupByPlaceholder": "グループ分けの条件フィールドを追加…", "xpack.transform.stepDefineForm.indexPatternHelpText": "このインデックスパターンのオプションのクエリはサポートされていません。サポートされているインデックスフィールドの数は {maxIndexFields} で、このインデックスには {numIndexFields} 個のフィールドがあります。", "xpack.transform.stepDefineForm.indexPatternLabel": "インデックスパターン", "xpack.transform.stepDefineForm.queryHelpText": "クエリ文字列でソースデータをフィルタリングしてください (オプション)。", "xpack.transform.stepDefineForm.queryLabel": "クエリ", - "xpack.transform.stepDefineForm.savedSearchLabel": "保存された検索", + "xpack.transform.stepDefineForm.savedSearchLabel": "保存検索", "xpack.transform.stepDefineSummary.aggregationsLabel": "アグリゲーション(集計)", "xpack.transform.stepDefineSummary.groupByLabel": "グループ分けの条件", "xpack.transform.stepDefineSummary.indexPatternLabel": "インデックスパターン", @@ -6792,7 +8201,7 @@ "xpack.transform.stepCreateForm.copyTransformConfigToClipboardButton": "クリップボードにコピー", "xpack.transform.stepCreateForm.copyTransformConfigToClipboardDescription": "ジョブを作成する Kibana 開発コンソールのコマンドをクリップボードにコピーします。", "xpack.transform.stepCreateForm.createIndexPatternLabel": "インデックスパターンを作成", - "xpack.transform.stepCreateForm.createTransformSuccessMessage": "データフレームジョブ {transformId} が作成されました", + "xpack.transform.stepCreateForm.createTransformSuccessMessage": "変換 {transformId} の作成リクエストが受け付けられました。", "xpack.transform.stepCreateForm.creatingIndexPatternMessage": "Kibana インデックスパターンを作成中…", "xpack.transform.stepCreateForm.discoverCardDescription": "ディスカバリでデータフレームピボットを閲覧します。", "xpack.transform.stepCreateForm.discoverCardTitle": "ディスカバー", @@ -6800,37 +8209,37 @@ "xpack.transform.stepCreateForm.transformListCardTitle": "データフレームジョブ", "xpack.transform.stepCreateForm.progressTitle": "進捗", "xpack.transform.stepCreateForm.createIndexPatternSuccessMessage": "Kibana インデックスパターン {indexPatternName} が作成されました", - "xpack.transform.stepCreateForm.startTransformSuccessMessage": "データフレームジョブ {transformId} が開始しました", + "xpack.transform.stepCreateForm.startTransformSuccessMessage": "変換 {transformId} の開始リクエストが受け付けられました。", "xpack.transform.stepDetailsForm.indexPatternTitleError": "このタイトルのインデックスパターンが既に存在します。", "xpack.transform.stepDetailsForm.transformIdInputAriaLabel": "固有のジョブ ID を選択してください。", "xpack.transform.stepDetailsForm.transformIdLabel": "ジョブ ID", "xpack.transform.stepDetailsSummary.createIndexPatternMessage": "このジョブの Kibana インデックスパターンが作成されます。", "xpack.transform.stepDetailsSummary.transformIdLabel": "ジョブ ID", "xpack.transform.transformList.betaBadgeLabel": "ベータ", - "xpack.transform.transformList.betaBadgeTooltipContent": "データフレームはベータ機能です。フィードバックをお待ちしています。", - "xpack.transform.transformList.completeBatchTransformToolTip": "{transformId} は完了済みのバッチジョブで、再度開始できません。", + "xpack.transform.transformList.betaBadgeTooltipContent": "変換はベータ機能です。フィードバックをお待ちしています。", + "xpack.transform.transformList.completeBatchTransformToolTip": "{transformId} は完了済みの一斉変換で、再度開始できません。", "xpack.transform.transformList.deleteActionDisabledToolTipContent": "削除するにはデータフレームジョブを停止してください。", "xpack.transform.transformList.deleteActionName": "削除", - "xpack.transform.transformList.deleteTransformSuccessMessage": "データフレームジョブ {transformId} が削除されました", - "xpack.transform.transformList.deleteModalBody": "このジョブを削除してよろしいですか?ジョブの最大インデックスとオプションの Kibana インデックスパターンは削除されません。", + "xpack.transform.transformList.deleteTransformSuccessMessage": "変換 {transformId} の削除リクエストが受け付けられました。", + "xpack.transform.transformList.deleteModalBody": "この変換を削除してよろしいですか?変換の送信先インデックスとオプションの Kibana インデックスパターンは削除されません。", "xpack.transform.transformList.deleteModalCancelButton": "キャンセル", "xpack.transform.transformList.deleteModalDeleteButton": "削除", - "xpack.transform.transformList.deleteModalTitle": "{transformId} を削除", + "xpack.transform.transformList.deleteModalTitle": "{transformId} 削除", "xpack.transform.transformList.transformDetails.tabs.transformSettingsLabel": "ジョブの詳細", "xpack.transform.transformList.rowCollapse": "{transformId} の詳細を非表示", "xpack.transform.transformList.rowExpand": "{transformId} の詳細を表示", "xpack.transform.transformList.startActionName": "開始", - "xpack.transform.transformList.startTransformSuccessMessage": "データフレームジョブ {transformId} が開始しました", + "xpack.transform.transformList.startTransformSuccessMessage": "変換 {transformId} の開始リクエストが受け付けられました。", "xpack.transform.transformList.startModalCancelButton": "キャンセル", "xpack.transform.transformList.startModalStartButton": "開始", "xpack.transform.transformList.startModalTitle": "{transformId} を開始", "xpack.transform.transformList.stopActionName": "停止", - "xpack.transform.transformList.stopTransformSuccessMessage": "データフレームジョブ {transformId} が停止しました", + "xpack.transform.transformList.stopTransformSuccessMessage": "データフレーム変換 {transformId} の停止リクエストが受け付けられました。", "xpack.transform.pivotPreview.copyClipboardTooltip": "ピボットプレビューの開発コンソールステートメントをクリップボードにコピーします。", "xpack.transform.progress": "進捗", "xpack.transform.sourceIndex": "ソースインデックス", "xpack.transform.sourceIndexPreview.copyClipboardTooltip": "ソースインデックスプレビューの開発コンソールステートメントをクリップボードにコピーします。", - "xpack.transform.sourceIndexPreview.fieldSelection": "{docFieldsCount, number} 件中 showing {selectedFieldsLength, number} 件の{docFieldsCount, plural, one {フィールド} other {フィールド}}", + "xpack.transform.sourceIndexPreview.fieldSelection": "{docFieldsCount, number} 件中 {selectedFieldsLength, number} 件の {docFieldsCount, plural, one {フィールド} other {フィールド}}を選択済み", "xpack.transform.sourceIndexPreview.rowCollapse": "縮小", "xpack.transform.sourceIndexPreview.rowExpand": "拡張", "xpack.transform.sourceIndexPreview.selectColumnsAriaLabel": "列を選択", @@ -6840,12 +8249,133 @@ "xpack.transform.status": "ステータス", "xpack.transform.tableActionLabel": "アクション", "xpack.transform.transformsWizard.betaBadgeLabel": "ベータ", - "xpack.transform.transformsWizard.betaBadgeTooltipContent": "データフレームはベータ機能です。フィードバックをお待ちしています。", + "xpack.transform.transformsWizard.betaBadgeTooltipContent": "変換はベータ機能です。フィードバックをお待ちしています。", "xpack.transform.transformsWizard.stepCreateTitle": "作成", "xpack.transform.transformsWizard.stepDefineTitle": "ピボットの定義", "xpack.transform.transformsWizard.stepDetailsTitle": "ジョブの詳細", "xpack.transform.wizard.nextStepButton": "次へ", "xpack.transform.wizard.previousStepButton": "前へ", + "xpack.transform.agg.popoverForm.unsupportedAggregationHelpText": "このフォームでは集約名のみを編集できます。詳細エディターを使用して、集約の他の部分を編集してください。", + "xpack.transform.app.checkingPrivilegesDescription": "権限を確認中…", + "xpack.transform.app.checkingPrivilegesErrorMessage": "サーバーからユーザー特権を取得中にエラーが発生。", + "xpack.transform.app.deniedPrivilegeDescription": "Transforms のこのセクションを使用するには、{privilegesCount, plural, one {このクラスター特権} other {これらのクラスター特権}}が必要です: {missingPrivileges}。", + "xpack.transform.app.deniedPrivilegeTitle": "クラスター特権が足りません", + "xpack.transform.appName": "データフレームジョブ", + "xpack.transform.capability.pleaseContactAdministratorTooltip": "{message} 管理者にお問い合わせください。", + "xpack.transform.createTransform.breadcrumbTitle": "変換の作成", + "xpack.transform.description": "説明", + "xpack.transform.destinationIndex": "デスティネーションインデックス", + "xpack.transform.groupBy.popoverForm.unsupportedGroupByHelpText": "このフォームでは group_by 名のみを編集できます。詳細エディターを使用して、group_by 構成の他の部分を編集してください。", + "xpack.transform.home.breadcrumbTitle": "データフレームジョブ", + "xpack.transform.list.emptyPromptButtonText": "初めての変換を作成してみましょう。", + "xpack.transform.list.emptyPromptTitle": "変換が見つかりません", + "xpack.transform.list.errorPromptTitle": "変換リストの取得中にエラーが発生しました。", + "xpack.transform.mode": "モード", + "xpack.transform.modeFilter": "モード", + "xpack.transform.multiTransformActionsMenu.managementActionsAriaLabel": "管理アクション", + "xpack.transform.multiTransformActionsMenu.transformsCount": "{count} 件の{count, plural, one {変換} other {変換}}を選択済み", + "xpack.transform.newTransform.chooseSourceTitle": "ソースの選択", + "xpack.transform.newTransform.newTransformTitle": "新規変換", + "xpack.transform.newTransform.searchSelection.notFoundLabel": "一致するインデックスまたは保存検索が見つかりませんでした。", + "xpack.transform.newTransform.searchSelection.savedObjectType.indexPattern": "インデックスパターン", + "xpack.transform.newTransform.searchSelection.savedObjectType.search": "保存検索", + "xpack.transform.pivotPreview.PivotPreviewError": "ピボットプレビューの読み込み中にエラーが発生しました。", + "xpack.transform.pivotPreview.PivotPreviewIncompleteConfigCalloutBody": "group-by フィールドと集約を 1 つ以上選んでください。", + "xpack.transform.pivotPreview.PivotPreviewNoDataCalloutBody": "プレビューリクエストはデータを返しませんでした。オプションのクエリがデータを返し、グループ分け基準により使用されるフィールドと集約フィールドに値が存在することを確認してください。", + "xpack.transform.pivotPreview.PivotPreviewNoDataCalloutTitle": "ピボットプレビューを利用できません", + "xpack.transform.pivotPreview.PivotPreviewTitle": "ピボットプレビューを変換", + "xpack.transform.sourceIndexPreview.SourceIndexArrayBadgeContent": "配列", + "xpack.transform.sourceIndexPreview.SourceIndexArrayToolTipContent": "この配列に基づく列の完全な内容は、展開された行に表示されます。", + "xpack.transform.sourceIndexPreview.SourceIndexNoDataCalloutBody": "ソースインデックスのクエリが結果を返しませんでした。インデックスにドキュメントが含まれていて、クエリ要件が妥当であることを確認してください。", + "xpack.transform.sourceIndexPreview.SourceIndexNoDataCalloutTitle": "ソースインデックスクエリの結果がありません", + "xpack.transform.sourceIndexPreview.SourceIndexObjectBadgeContent": "オブジェクト", + "xpack.transform.sourceIndexPreview.SourceIndexObjectToolTipContent": "このオブジェクトベースの列の完全な内容は、展開された行に表示されます。", + "xpack.transform.statsBar.batchTransformsLabel": "一斉", + "xpack.transform.statsBar.continuousTransformsLabel": "連続", + "xpack.transform.statsBar.failedTransformsLabel": "失敗", + "xpack.transform.statsBar.startedTransformsLabel": "開始済み", + "xpack.transform.statsBar.totalTransformsLabel": "変換合計", + "xpack.transform.statusFilter": "ステータス", + "xpack.transform.stepCreateForm.continuousModeLabel": "連続モード", + "xpack.transform.stepCreateForm.createAndStartTransformButton": "作成して開始", + "xpack.transform.stepCreateForm.createAndStartTransformDescription": "変換を作成して開始します。変換は、クラスターの検索とインデックスによる負荷を増やします。過剰な負荷が生じた場合は変換を停止してください。変換の開始後、変換の閲覧を続けるオプションが提供されます。", + "xpack.transform.stepCreateForm.createIndexPatternErrorMessage": "Kibana インデックスパターン {indexPatternName} の作成中にエラーが発生しました:", + "xpack.transform.stepCreateForm.createTransformButton": "作成", + "xpack.transform.stepCreateForm.createTransformDescription": "変換を開始せずに作成します。変換は後程変換リストに戻って開始できます。", + "xpack.transform.stepCreateForm.createTransformErrorMessage": "変換 {transformId} の取得中にエラーが発生しました。", + "xpack.transform.stepCreateForm.duplicateIndexPatternErrorMessage": "Kibana インデックスパターン {indexPatternName} の作成中にエラーが発生しました:インデックスパターンが既に存在します。", + "xpack.transform.stepCreateForm.progressErrorMessage": "進捗パーセンテージの取得中にエラーが発生しました:", + "xpack.transform.stepCreateForm.startTransformButton": "開始", + "xpack.transform.stepCreateForm.startTransformDescription": "変換を開始します。変換は、クラスターの検索とインデックスによる負荷を増やします。過剰な負荷が生じた場合は変換を停止してください。変換の開始後、変換の閲覧を続けるオプションが提供されます。", + "xpack.transform.stepCreateForm.startTransformErrorMessage": "変換 {transformId} の開始中にエラーが発生しました。", + "xpack.transform.stepDefineForm.advancedEditorApplyButtonText": "変更を適用", + "xpack.transform.stepDefineForm.advancedEditorAriaLabel": "高度なピボットエディター", + "xpack.transform.stepDefineForm.advancedEditorHelpText": "詳細エディターでは、変換のピボット構成を編集できます。", + "xpack.transform.stepDefineForm.advancedEditorHelpTextLink": "使用可能なオプションの詳細を確認してください。", + "xpack.transform.stepDefineForm.advancedEditorLabel": "ピボット構成オブジェクト", + "xpack.transform.stepDefineForm.advancedEditorSourceConfigSwitchLabel": "クエリの詳細エディター", + "xpack.transform.stepDefineForm.advancedEditorSwitchLabel": "ピボットの詳細エディター", + "xpack.transform.stepDefineForm.advancedEditorSwitchModalBodyText": "詳細エディターの変更は適用されませんでした。詳細エディターを無効にすると、編集内容が失われます。", + "xpack.transform.stepDefineForm.advancedEditorSwitchModalCancelButtonText": "キャンセル", + "xpack.transform.stepDefineForm.advancedEditorSwitchModalConfirmButtonText": "詳細エディターを無効にする", + "xpack.transform.stepDefineForm.advancedEditorSwitchModalTitle": "適用されていない変更", + "xpack.transform.stepDefineForm.advancedSourceEditorApplyButtonText": "変更を適用", + "xpack.transform.stepDefineForm.advancedSourceEditorAriaLabel": "クエリの詳細エディター", + "xpack.transform.stepDefineForm.advancedSourceEditorHelpText": "高度なエディターでは、変換のソースクエリ句を編集できます。", + "xpack.transform.stepDefineForm.advancedSourceEditorLabel": "ソースクエリ句", + "xpack.transform.stepDefineForm.advancedSourceEditorSwitchModalBodyText": "KQL クエリバーに戻すと、編集内容が失われます。", + "xpack.transform.stepDefineForm.advancedSourceEditorSwitchModalConfirmButtonText": "KQL に切り替える", + "xpack.transform.stepDefineForm.advancedSourceEditorSwitchModalTitle": "編集内容は失われます", + "xpack.transform.stepDefineForm.queryPlaceholder": "例: {example}.", + "xpack.transform.stepDefineSummary.queryCodeBlockLabel": "クエリ", + "xpack.transform.stepDefineSummary.savedSearchLabel": "保存検索", + "xpack.transform.stepDetailsForm.continuousModeAriaLabel": "遅延を選択してください。", + "xpack.transform.stepDetailsForm.continuousModeDateFieldHelpText": "新しいドキュメントを特定するために使用できる日付フィールドを選択してください。", + "xpack.transform.stepDetailsForm.continuousModeDateFieldLabel": "日付フィールド", + "xpack.transform.stepDetailsForm.continuousModeDelayError": "無効な遅延フォーマット", + "xpack.transform.stepDetailsForm.continuousModeDelayHelpText": "現在の時刻と最新のインプットデータ時刻の間の遅延です。", + "xpack.transform.stepDetailsForm.continuousModeDelayLabel": "遅延", + "xpack.transform.stepDetailsForm.continuousModeError": "日付フィールドがないインデックスでは、連続モードを使用できません。", + "xpack.transform.stepDetailsForm.destinationIndexHelpText": "この名前のインデックスが既に存在します。この変換を実行すると、デスティネーションインデックスが変更されます。", + "xpack.transform.stepDetailsForm.destinationIndexInputAriaLabel": "固有の宛先インデックス名を選択してください。", + "xpack.transform.stepDetailsForm.destinationIndexInvalidError": "無効なデスティネーションインデックス名。", + "xpack.transform.stepDetailsForm.destinationIndexInvalidErrorLink": "インデックス名の制限に関する詳細。", + "xpack.transform.stepDetailsForm.destinationIndexLabel": "デスティネーションインデックス", + "xpack.transform.stepDetailsForm.errorGettingIndexNames": "既存のインデックス名の取得中にエラーが発生しました:", + "xpack.transform.stepDetailsForm.errorGettingIndexPatternTitles": "既存のインデックスパターンのタイトルの取得中にエラーが発生しました:", + "xpack.transform.stepDetailsForm.errorGettingTransformList": "既存の変換 ID の取得中にエラーが発生しました:", + "xpack.transform.stepDetailsForm.transformDescriptionHelpText": "オプションの説明テキストです。", + "xpack.transform.stepDetailsForm.transformDescriptionInputAriaLabel": "オプションの変換の説明を選択してください。", + "xpack.transform.stepDetailsForm.transformDescriptionLabel": "変換の説明", + "xpack.transform.stepDetailsForm.transformIdExistsError": "この ID の変換が既に存在します。", + "xpack.transform.stepDetailsForm.transformIdInvalidError": "小文字のアルファベットと数字 (a-z と 0-9)、ハイフンまたはアンダーラインのみ使用でき、最初と最後を英数字にする必要があります。", + "xpack.transform.stepDetailsSummary.continuousModeDateFieldLabel": "連続モード日付フィールド", + "xpack.transform.stepDetailsSummary.destinationIndexLabel": "デスティネーションインデックス", + "xpack.transform.stepDetailsSummary.transformDescriptionLabel": "変換の説明", + "xpack.transform.toastText.closeModalButtonText": "閉じる", + "xpack.transform.toastText.modalTitle": "詳細を入力", + "xpack.transform.toastText.openModalButtonText": "詳細を表示", + "xpack.transform.transformForm.sizeNotationPlaceholder": "例: {example1}、{example2}、{example3}、{example4}", + "xpack.transform.transformList.bulkDeleteModalBody": "{count, plural, one {この} other {これらの}} {count} 件の{count, plural, one {変換} other {変換}}を削除してよろしいですか?変換の送信先インデックスとオプションの Kibana インデックスパターンは削除されません。", + "xpack.transform.transformList.bulkDeleteModalTitle": "{count} 件の{count, plural, one {変換} other {変換}}を削除", + "xpack.transform.transformList.bulkStartModalTitle": "{count} 件の{count, plural, one {変換} other {変換}}を開始", + "xpack.transform.transformList.completeBatchTransformBulkActionToolTip": "1 つまたは複数の変換が完了済みの一斉変換で、再度開始できません。", + "xpack.transform.transformList.createTransformButton": "変換の作成", + "xpack.transform.transformList.deleteBulkActionDisabledToolTipContent": "削除するには、選択された変換のうちの 1 つまたは複数を停止する必要があります。", + "xpack.transform.transformList.deleteTransformErrorMessage": "変換 {transformId} の削除中にエラーが発生しました", + "xpack.transform.transformList.deleteTransformGenericErrorMessage": "変換を削除するための API エンドポイントの呼び出し中にエラーが発生しました。", + "xpack.transform.transformList.refreshButtonLabel": "更新", + "xpack.transform.transformList.startedTransformBulkToolTip": "1 つまたは複数の変換が既に開始済みです。", + "xpack.transform.transformList.startedTransformToolTip": "{transformId} は既に開始済みです。", + "xpack.transform.transformList.startModalBody": "変換は、クラスターの検索とインデックスによる負荷を増やします。過剰な負荷が生じた場合は変換を停止してください。{count, plural, one {この} other {これら}} {count} 件の{count, plural, one {変換} other {変換}}を開始してよろしいですか?", + "xpack.transform.transformList.startTransformErrorMessage": "変換 {transformId} の開始中にエラーが発生しました", + "xpack.transform.transformList.stoppedTransformBulkToolTip": "1 つまたは複数の変換が既に開始済みです。", + "xpack.transform.transformList.stoppedTransformToolTip": "{transformId} は既に停止済みです。", + "xpack.transform.transformList.stopTransformErrorMessage": "データフレーム変換 {transformId} の停止中にエラーが発生しました", + "xpack.transform.transformList.transformDetails.tabs.transformMessagesLabel": "メッセージ", + "xpack.transform.transformList.transformDetails.tabs.transformPreviewLabel": "プレビュー", + "xpack.transform.transformList.transformTitle": "データフレームジョブ", + "xpack.transform.transformsWizard.createTransformTitle": "変換の作成", "xpack.monitoring.accessDenied.backToKibanaButtonLabel": "Kibana に戻る", "xpack.monitoring.accessDenied.clusterNotConfiguredDescription": "専用の監視クラスターへのアクセスを試みている場合、監視クラスターで構成されていないユーザーとしてログインしていることが原因である可能性があります。", "xpack.monitoring.accessDenied.notAuthorizedDescription": "監視アクセスが許可されていません。監視を利用するには、「{kibanaUser}」と「{monitoringUser}」の両方のロールからの権限が必要です。", @@ -7339,11 +8869,11 @@ "xpack.monitoring.logstashNavigation.overviewLinkText": "概要", "xpack.monitoring.logstashNavigation.pipelinesLinkText": "パイプライン", "xpack.monitoring.logstashNavigation.pipelineVersionDescription": "バージョンは {relativeLastSeen} 時点でアクティブ、初回検知 {relativeFirstSeen}", - "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.configureMetricbeatDescription": "{file} にこれらの変更を加えます。", + "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.configureMetricbeatDescription": "{file} を変更して接続情報を設定します。:", "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.configureMetricbeatTitle": "Metricbeat を構成して監視クラスターに送ります", - "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.disableInternalCollectionDescription": "Elasticsearch 監視メトリックの内部収集を無効にします。本番クラスターの各サーバーの {monospace} を false に設定します。", + "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.disableInternalCollectionDescription": "Elasticsearch 監視メトリックの自己監視を無効にする本番クラスターの各サーバーの {monospace} を false に設定します。", "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.disableInternalCollectionTitle": "Elasticsearch 監視メトリックの内部収集を無効にする", - "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.enableMetricbeatModuleDescription": "モジュールはデフォルトで {url} から Elasticsearch 監視メトリックを収集します。ローカル Elasticsearch サーバーのアドレスが異なる場合は、{module} ファイルのホスト設定で指定する必要があります。", + "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.enableMetricbeatModuleDescription": "デフォルトで、モジュールは {url} から Elasticsearch メトリックを収集します。ローカルサーバーのアドレスが異なる場合は、{module} のホスト設定に追加します。", "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.enableMetricbeatModuleTitle": "Metricbeat の Elasticsearch X-Pack モジュールの有効化と構成", "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.installMetricbeatLinkText": "こちらの手順に従ってください", "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.installMetricbeatTitle": "Metricbeat を Elasticsearch と同じサーバーにインストール", @@ -7352,12 +8882,12 @@ "xpack.monitoring.metricbeatMigration.flyout.closeButtonLabel": "閉じる", "xpack.monitoring.metricbeatMigration.flyout.doneButtonLabel": "完了", "xpack.monitoring.metricbeatMigration.flyout.nextButtonLabel": "次へ", - "xpack.monitoring.metricbeatMigration.flyout.step1.monitoringUrlHelpText": "これは通常単一のインスタンスですが、複数ある場合は、すべてのインスタンス URL をコンマ区切りで入力します。\n Metricbeat インスタンスの実行には、Elasticsearch サーバーとの通信が必要です。", + "xpack.monitoring.metricbeatMigration.flyout.step1.monitoringUrlHelpText": "通常 1 つの URL です。複数 URL の場合、コンマで区切ります。\n 実行中の Metricbeat インスタンスは、これらの Elasticsearch サーバーとの通信が必要です。", "xpack.monitoring.metricbeatMigration.flyout.step1.monitoringUrlLabel": "監視クラスター URL", "xpack.monitoring.metricbeatMigration.kibanaInstructions.configureMetricbeatDescription": "{file} にこれらの変更を加えます。", "xpack.monitoring.metricbeatMigration.kibanaInstructions.configureMetricbeatTitle": "Metricbeat を構成して監視クラスターに送ります", - "xpack.monitoring.metricbeatMigration.kibanaInstructions.disableInternalCollection.description": "Kibana 構成ファイル ({file}) に次の設定を追加します:", - "xpack.monitoring.metricbeatMigration.kibanaInstructions.disableInternalCollection.note": "{config} をデフォルト値のままにします ({defaultValue})。", + "xpack.monitoring.metricbeatMigration.kibanaInstructions.disableInternalCollection.description": "この設定を {file} に追加します。", + "xpack.monitoring.metricbeatMigration.kibanaInstructions.disableInternalCollection.note": "{config} ではデフォルト値 ({defaultValue}) のままにしてください。", "xpack.monitoring.metricbeatMigration.kibanaInstructions.disableInternalCollection.restartNote": "このステップには Kibana サーバーの再起動が必要です。サーバーの再起動が完了するまでエラーが表示されます。", "xpack.monitoring.metricbeatMigration.kibanaInstructions.disableInternalCollection.restartWarningTitle": "警告", "xpack.monitoring.metricbeatMigration.kibanaInstructions.disableInternalCollection.title": "Kibana 監視メトリックの内部収集を無効にする", @@ -7927,7 +9457,7 @@ "xpack.monitoring.metricbeatMigration.beatsInstructions.configureMetricbeatTitle": "Metricbeat を構成して監視クラスターに送ります", "xpack.monitoring.metricbeatMigration.beatsInstructions.disableInternalCollection.description": "{beatType} の構成ファイル ({file}) に次の設定を追加します:", "xpack.monitoring.metricbeatMigration.beatsInstructions.disableInternalCollection.note": "この変更後、{beatType} の再起動が必要です。", - "xpack.monitoring.metricbeatMigration.beatsInstructions.disableInternalCollection.title": "{beatType} の監視メトリックの内部収集を無効にする", + "xpack.monitoring.metricbeatMigration.beatsInstructions.disableInternalCollection.title": "{beatType} の監視メトリックの自己監視を無効にする", "xpack.monitoring.metricbeatMigration.beatsInstructions.enableMetricbeatModuleDescription": "モジュールはデフォルトで http://localhost:5066 から {beatType} 監視メトリックを収集します。監視されている {beatType} インスタンスのアドレスが異なる場合は、{file} ファイルの {hosts} 設定で指定する必要があります。", "xpack.monitoring.metricbeatMigration.beatsInstructions.enableMetricbeatModuleHttpEnabledDirections": "Metricbeat が実行中の {beatType} からメトリックを収集するには、{link} 必要があります。", "xpack.monitoring.metricbeatMigration.beatsInstructions.enableMetricbeatModuleHttpEnabledDirectionsLinkText": "監視されている {beatType} の HTTP エンドポイントを有効にする", @@ -7952,6 +9482,77 @@ "xpack.monitoring.noData.blurbs.cloudDeploymentDescriptionMore": "Elastic Cloud での監視の詳細は、 ", "xpack.monitoring.noData.blurbs.cloudDeploymentTitle": "監視データはこちらに表示されません。", "xpack.monitoring.noData.explanations.exportersCloudDescription": "Elastic Cloud では、監視データが専用の監視クラスターに格納されます。", + "xpack.monitoring.elasticsearch.nodes.metricbeatMigration.detectedNodeDescription": "次のインデックスは監視されていません。下の「Metricbeat で監視」をクリックして、監視を開始してください。", + "xpack.monitoring.elasticsearch.nodes.metricbeatMigration.detectedNodeTitle": "Elasticsearch ノードが検出されました", + "xpack.monitoring.elasticsearch.nodes.metricbeatMigration.disableInternalCollectionDescription": "移行を完了させるには、自己監視を無効にしてください。", + "xpack.monitoring.elasticsearch.nodes.metricbeatMigration.disableInternalCollectionMigrationButtonLabel": "自己監視を無効にする", + "xpack.monitoring.elasticsearch.nodes.metricbeatMigration.disableInternalCollectionTitle": "Metricbeat による Elasticsearch ノードの監視が開始されました", + "xpack.monitoring.euiSSPTable.setupNewButtonLabel": "新規 {identifier} の監視を設定", + "xpack.monitoring.euiTable.setupNewButtonLabel": "Metricbeat で別の {identifier} を監視", + "xpack.monitoring.kibana.instances.metricbeatMigration.detectedNodeDescription": "次のインスタンスは監視されていません。\n 下の「Metricbeat で監視」をクリックして、監視を開始してください。", + "xpack.monitoring.kibana.instances.metricbeatMigration.detectedNodeTitle": "Kibana インスタンスが検出されました", + "xpack.monitoring.metricbeatMigration.disableInternalCollection.fullyMigratedStatusDescription": "自己監視からのドキュメントがありません。移行完了!", + "xpack.monitoring.metricbeatMigration.disableInternalCollection.fullyMigratedStatusTitle": "おめでとうございます!", + "xpack.monitoring.metricbeatMigration.disableInternalCollection.partiallyMigratedStatusDescription": "最後の自己監視は {secondsSinceLastInternalCollectionLabel} 前です。", + "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.enableMetricbeatModuleInstallDirectory": "インストールディレクトリから次のファイルを実行します:", + "xpack.monitoring.metricbeatMigration.flyout.learnMore": "詳細な理由", + "xpack.monitoring.metricbeatMigration.fullyMigratedStatusDescription": "Metricbeat は監視データを送信しています。", + "xpack.monitoring.metricbeatMigration.fullyMigratedStatusTitle": "おめでとうございます!", + "xpack.monitoring.metricbeatMigration.isInternalCollectorStatusTitle": "監視データは検出されませんでしたが、引き続き確認します。", + "xpack.monitoring.metricbeatMigration.migrationStatus": "移行ステータス", + "xpack.monitoring.metricbeatMigration.monitoringStatus": "監視ステータス", + "xpack.monitoring.metricbeatMigration.partiallyMigratedStatusDescription": "データの検出には最長 {secondsAgo} 秒かかる場合があります。", + "xpack.monitoring.metricbeatMigration.partiallyMigratedStatusTitle": "まだ自己監視からのデータが届いています", + "xpack.monitoring.metricbeatMigration.securitySetup": "セキュリティが有効の場合、{link} が必要な可能性があります。", + "xpack.monitoring.metricbeatMigration.securitySetupLinkText": "追加設定", + "xpack.monitoring.metrics.esNode.ioRateTitle": "I/O オペレーションレート", + "xpack.monitoring.metrics.esNode.totalIoDescription": "I/O 合計。(このメトリックはすべてのプラットフォームでサポートされておらず、I/O が利用できない場合 N/A が表示されることがあります。)", + "xpack.monitoring.metrics.esNode.totalIoLabel": "I/O 合計", + "xpack.monitoring.metrics.esNode.totalIoReadDescription": "読み込み I/O 合計。(このメトリックはすべてのプラットフォームでサポートされておらず、I/O が利用できない場合 N/A が表示されることがあります。)", + "xpack.monitoring.metrics.esNode.totalIoReadLabel": "読み込み I/O 合計", + "xpack.monitoring.metrics.esNode.totalIoWriteDescription": "書き込み I/O 合計。(このメトリックはすべてのプラットフォームでサポートされておらず、I/O が利用できない場合 N/A が表示されることがあります。)", + "xpack.monitoring.metrics.esNode.totalIoWriteLabel": "書き込み I/O 合計", + "xpack.monitoring.noData.collectionInterval.turnOnMonitoringButtonLabel": "Metricbeat で監視を設定", + "xpack.monitoring.noData.defaultLoadingMessage": "読み込み中、お待ちください", + "xpack.monitoring.noData.noMonitoringDataFound": "既に監視を設定済みですか?その場合、右上に選択された期間に監視データが含まれていることを確認してください。", + "xpack.monitoring.noData.noMonitoringDetected": "監視データが見つかりません。", + "xpack.monitoring.noData.routeTitle": "監視の設定", + "xpack.monitoring.noData.setupInternalInstead": "または、自己監視で設定", + "xpack.monitoring.noData.setupMetricbeatInstead": "または、Metricbeat で設定 (推奨)", + "xpack.monitoring.setupMode.clickToDisableInternalCollection": "自己監視を無効にする", + "xpack.monitoring.setupMode.clickToMonitorWithMetricbeat": "Metricbeat で監視", + "xpack.monitoring.setupMode.description": "現在設定モードです。({flagIcon}) アイコンは構成オプションを意味します。", + "xpack.monitoring.setupMode.detectedNodeDescription": "下の「監視を設定」をクリックしてこの {identifier} の監視を開始します。", + "xpack.monitoring.setupMode.detectedNodeTitle": "{product} {identifier} が検出されました", + "xpack.monitoring.setupMode.disableInternalCollectionDescription": "Metricbeat による {product} {identifier} の監視が開始されました。移行を完了させるには、自己監視を無効にしてください。", + "xpack.monitoring.setupMode.disableInternalCollectionTitle": "自己監視を無効にする", + "xpack.monitoring.setupMode.exit": "設定モードを修了", + "xpack.monitoring.setupMode.instance": "インスタンス", + "xpack.monitoring.setupMode.instances": "インスタンス", + "xpack.monitoring.setupMode.metricbeatAllNodes": "Metricbeat がすべての {identifier} を監視しています。", + "xpack.monitoring.setupMode.migrateSomeToMetricbeatDescription": "{product} {identifier} の一部は自己監視で監視されています。Metricbeat での監視に移行してください。", + "xpack.monitoring.setupMode.migrateToMetricbeat": "Metricbeat で監視", + "xpack.monitoring.setupMode.migrateToMetricbeatDescription": "これらの {product} {identifier} は自己監視されています。\n 移行するには「Metricbeat で監視」をクリックしてください。", + "xpack.monitoring.setupMode.monitorAllNodes": "ノードの一部は自己監視のみ使用できます。", + "xpack.monitoring.setupMode.netNewUserDescription": "「監視を設定」をクリックして監視を開始します。", + "xpack.monitoring.setupMode.node": "ノード", + "xpack.monitoring.setupMode.nodes": "ノード", + "xpack.monitoring.setupMode.noMonitoringDataFound": "{product} {identifier} が検出されませんでした", + "xpack.monitoring.setupMode.server": "サーバー", + "xpack.monitoring.setupMode.servers": "サーバー", + "xpack.monitoring.setupMode.tooltip.allSet": "Metricbeat がすべての {identifierPlural} を監視しています。", + "xpack.monitoring.setupMode.tooltip.detected": "監視なし", + "xpack.monitoring.setupMode.tooltip.disableInternal": "Metricbeat がすべての {identifierPlural} を監視しています。クリックして {identifierPlural} を表示し、自己監視を無効にしてください。", + "xpack.monitoring.setupMode.tooltip.mightExist": "この製品の使用が検出されました。クリックして監視を開始してください。", + "xpack.monitoring.setupMode.tooltip.noUsage": "使用なし", + "xpack.monitoring.setupMode.tooltip.noUsageDetected": "使用が検出されませんでした。{identifier} を表示するにはクリックしてください。", + "xpack.monitoring.setupMode.tooltip.oneInternal": "少なくとも 1 つの {identifier} が Metricbeat によって監視されていません。ステータスを表示するにはクリックしてください。", + "xpack.monitoring.setupMode.unknown": "N/A", + "xpack.monitoring.setupMode.usingMetricbeatCollection": "Metricbeat で監視", + "xpack.monitoring.metricbeatMigration.flyout.flyoutTitle": "Metricbeat で「{instanceName}」の {instanceIdentifier} を監視", + "xpack.monitoring.metricbeatMigration.flyout.flyoutTitleNewUser": "Metricbeat で「{instanceName}」の {instanceIdentifier} を監視", + "xpack.monitoring.metricbeatMigration.flyout.noClusterUuidCheckboxLabel": "はい、\n この {productName} {instanceIdentifier} スタンドアロンクラスターを調べる必要があることを理解しています。", + "xpack.monitoring.metricbeatMigration.flyout.noClusterUuidDescription": "この {productName} {instanceIdentifier} は Elasticsearch クラスターに接続されていないため、完全に移行された時点で、この {productName} {instanceIdentifier} はこのクラスターではなくスタンドアロンクラスターに表示されます。 {link}", "xpack.remoteClusters.addAction.clusterNameAlreadyExistsErrorMessage": "「{clusterName}」という名前のクラスターが既に存在します。", "xpack.remoteClusters.addAction.errorTitle": "クラスターの追加中にエラーが発生", "xpack.remoteClusters.addAction.failedDefaultErrorMessage": "{statusCode} エラーでリクエスト失敗: {message}", @@ -8629,6 +10230,48 @@ "xpack.security.users.breadcrumb": "ユーザー", "xpack.security.users.createBreadcrumb": "作成", "xpack.security.management.editRole.featureTable.excludedFromBasePrivilegsTooltip": "アクセスを許可するには、「カスタム」特権を使用します。{featureName} は基本権限の一部ではありません。", + "xpack.security.apiKeys.breadcrumb": "API キー", + "xpack.security.management.apiKeys.deniedPermissionTitle": "API キーを管理するにはパーミッションが必要です", + "xpack.security.management.apiKeys.invalidateApiKey.confirmModal.cancelButtonLabel": "キャンセル", + "xpack.security.management.apiKeys.invalidateApiKey.confirmModal.confirmButtonLabel": "{count, plural, one {API キー} other {API キー}}の無効化", + "xpack.security.management.apiKeys.invalidateApiKey.confirmModal.invalidateMultipleListDescription": "これらの API キーを無効化しようとしています:", + "xpack.security.management.apiKeys.invalidateApiKey.confirmModal.invalidateMultipleTitle": "{count}API キーを無効にしますか?", + "xpack.security.management.apiKeys.invalidateApiKey.confirmModal.invalidateSingleTitle": "API キー「{name}」を無効にしますか?", + "xpack.security.management.apiKeys.invalidateApiKey.errorMultipleNotificationTitle": "{count} 個の API キーの削除中にエラーが発生", + "xpack.security.management.apiKeys.invalidateApiKey.errorSingleNotificationTitle": "API キー「{name}」の削除中にエラーが発生", + "xpack.security.management.apiKeys.invalidateApiKey.successMultipleNotificationTitle": "{count} 個の API キーが無効になりました", + "xpack.security.management.apiKeys.invalidateApiKey.successSingleNotificationTitle": "API キー「{name}」を無効にしますか?", + "xpack.security.management.apiKeys.noPermissionToManageRolesDescription": "システム管理者にお問い合わせください。", + "xpack.security.management.apiKeys.table.actionDeleteAriaLabel": "「{name}」を無効にする", + "xpack.security.management.apiKeys.table.actionDeleteTooltip": "無効にする", + "xpack.security.management.apiKeys.table.actionsColumnName": "アクション", + "xpack.security.management.apiKeys.table.adminText": "あなたは API キー管理者です。", + "xpack.security.management.apiKeys.table.apiKeysAllDescription": "API キーを表示して無効にします。API キーはユーザーの代わりにリクエストを送信します。", + "xpack.security.management.apiKeys.table.apiKeysDisabledErrorDescription": "システム管理者に連絡し、{link} を伝えて API キーを有効にしてください。", + "xpack.security.management.apiKeys.table.apiKeysDisabledErrorLinkText": "ドキュメント", + "xpack.security.management.apiKeys.table.apiKeysDisabledErrorTitle": "Elasticsearch で API キーが有効ではありません", + "xpack.security.management.apiKeys.table.apiKeysOwnDescription": "API キーを表示して無効にします。API キーはユーザーの代わりにリクエストを送信します。", + "xpack.security.management.apiKeys.table.apiKeysTableLoadingMessage": "API キーを読み込み中…", + "xpack.security.management.apiKeys.table.apiKeysTitle": "API キー", + "xpack.security.management.apiKeys.table.creationDateColumnName": "作成済み", + "xpack.security.management.apiKeys.table.emptyPromptAdminTitle": "API キーがありません", + "xpack.security.management.apiKeys.table.emptyPromptConsoleButtonMessage": "コンソールに移動してください", + "xpack.security.management.apiKeys.table.emptyPromptDescription": "コンソールで {link} を作成できます。", + "xpack.security.management.apiKeys.table.emptyPromptDocsLinkMessage": "API キー", + "xpack.security.management.apiKeys.table.emptyPromptNonAdminTitle": "まだ API キーがありません", + "xpack.security.management.apiKeys.table.expirationDateColumnName": "有効期限", + "xpack.security.management.apiKeys.table.expirationDateNeverMessage": "無し", + "xpack.security.management.apiKeys.table.invalidateApiKeyButton": "{count, plural, one {API キー} other {API キー}}を無効にする", + "xpack.security.management.apiKeys.table.loadingApiKeysDescription": "API キーを読み込み中…", + "xpack.security.management.apiKeys.table.loadingApiKeysErrorTitle": "API キーを読み込み中にエラーが発生", + "xpack.security.management.apiKeys.table.nameColumnName": "名前", + "xpack.security.management.apiKeys.table.realmColumnName": "レルム", + "xpack.security.management.apiKeys.table.realmFilterLabel": "レルム", + "xpack.security.management.apiKeys.table.reloadApiKeysButton": "再読み込み", + "xpack.security.management.apiKeys.table.statusColumnName": "ステータス", + "xpack.security.management.apiKeys.table.userFilterLabel": "ユーザー", + "xpack.security.management.apiKeys.table.userNameColumnName": "ユーザー", + "xpack.security.management.apiKeysTitle": "API キー", "xpack.siem.andOrBadge.and": "AND", "xpack.siem.andOrBadge.or": "OR", "xpack.siem.auditd.abortedAuditStartupDescription": "中断された監査のスタートアップ", @@ -8937,7 +10580,7 @@ "xpack.siem.network.ipDetails.usersTable.columns.groupIdTitle": "グループ ID", "xpack.siem.network.ipDetails.usersTable.columns.groupNameTitle": "グループ名", "xpack.siem.network.ipDetails.usersTable.columns.userIdTitle": "ID", - "xpack.siem.network.ipDetails.usersTable.columns.userNameTitle": "名前", + "xpack.siem.network.ipDetails.usersTable.columns.userNameTitle": "ユーザー", "xpack.siem.network.ipDetails.usersTable.rows": "{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}", "xpack.siem.network.ipDetails.usersTable.unit": "{totalCount, plural, =1 {user} other {users}}", "xpack.siem.network.ipDetails.usersTable.usersTitle": "ユーザー", @@ -9124,14 +10767,14 @@ "xpack.siem.timeline.typeTooltip": "タイプ", "xpack.siem.timelines.allTimelines.panelTitle": "すべてのタイムライン", "xpack.siem.timelines.pageTitle": "タイムライン", - "xpack.siem.uiSettings.defaultIndexDescription": "検索するデフォルトの Elasticsearch インデックスです", + "xpack.siem.uiSettings.defaultIndexDescription": "

SIEM アプリがイベントを収集する Elasticsearch インデックスのコンマ区切りのリストです。

", "xpack.siem.uiSettings.defaultIndexLabel": "デフォルトのインデックス", "xpack.siem.uncommonProcessTable.hostsTitle": "すべてのホスト", "xpack.siem.uncommonProcessTable.lastCommandTitle": "前回のコマンド", "xpack.siem.uncommonProcessTable.lastUserTitle": "前回のユーザー", "xpack.siem.uncommonProcessTable.nameTitle": "名前", "xpack.siem.uncommonProcessTable.numberOfHostsTitle": "ホスト数", - "xpack.siem.uncommonProcessTable.numberOfInstances": "インスタンス数", + "xpack.siem.uncommonProcessTable.numberOfInstances": "インスタンス", "xpack.siem.uncommonProcessTable.rows": "{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}", "xpack.siem.uncommonProcessTable.unit": "{totalCount, plural, =1 {process} other {processes}}", "xpack.siem.zeek.othDescription": "SYNが検出されません。ミッドストリームトラフィックのみです", @@ -9228,11 +10871,11 @@ "xpack.siem.paginatedTable.tooManyResultsToastText": "クエリ範囲を縮めて結果をさらにフィルタリングしてください", "xpack.siem.paginatedTable.tooManyResultsToastTitle": " - 結果が多すぎます", "xpack.siem.timeline.callOut.unauthorized.message.description": "SIEM アプリケーションでタイムラインを自動保存するにはパーミッションが必要ですが、引き続きタイムラインを使用してセキュリティイベントの検索とフィルタリングを行うことはできます。", - "xpack.siem.uiSettings.defaultAnomalyScoreDescription": "超えた際に異常を表示するデフォルトの異常スコアしきい値です。有効な値は 0 から 100 までです", + "xpack.siem.uiSettings.defaultAnomalyScoreDescription": "

機械学習ジョブの異常がこの値を超えると SIEM アプリに表示されます。

有効な値:0 ~ 100。

", "xpack.siem.uiSettings.defaultAnomalyScoreLabel": "デフォルトの異常しきい値", - "xpack.siem.uiSettings.defaultRefreshIntervalDescription": "SIEM 時間フィルターのデフォルトの更新間隔です", + "xpack.siem.uiSettings.defaultRefreshIntervalDescription": "

SIEM 時間フィルターのミリ単位のデフォルトの更新間隔です。

", "xpack.siem.uiSettings.defaultRefreshIntervalLabel": "タイムピッカーの更新間隔", - "xpack.siem.uiSettings.defaultTimeRangeDescription": "SIEM 時間フィルダーが選択されずに Kibana が起動した際に使用される SIEM 時間フィルターです", + "xpack.siem.uiSettings.defaultTimeRangeDescription": "

SIEM 時間フィルダーのデフォルトの期間です。

", "xpack.siem.uiSettings.defaultTimeRangeLabel": "デフォルトのタイムピッカー", "xpack.siem.host.details.overview.maxAnomalyScoreByJobTitle": "ジョブ別の最高異常スコア", "xpack.siem.ml.score.anomalyJobTitle": "ジョブ", @@ -9241,6 +10884,93 @@ "xpack.siem.ml.table.influencedByTitle": "影響因子:", "xpack.siem.ml.table.scoreTitle": "異常スコア", "xpack.siem.network.ipDetails.ipOverview.maxAnomalyScoreByJobTitle": "ジョブ別の最高異常スコア", + "xpack.siem.chart.dataAllValuesZerosTitle": "すべての値はゼロを返します", + "xpack.siem.components.embeddables.embeddedMap.destinationLayerLabel": "デスティネーションポイント", + "xpack.siem.components.embeddables.embeddedMap.embeddablePanelTitle": "ソース -> デスティネーション ポイントツーポイントマップ", + "xpack.siem.components.embeddables.embeddedMap.errorConfiguringEmbeddableApiTitle": "埋め込み可能な API の構成中にエラーが発生", + "xpack.siem.components.embeddables.embeddedMap.errorCreatingMapEmbeddableTitle": "マップに’埋め込み可能なアイテムの作成中にエラーが発生", + "xpack.siem.components.embeddables.embeddedMap.lineLayerLabel": "折れ線", + "xpack.siem.components.embeddables.embeddedMap.sourceLayerLabel": "ソースポイント", + "xpack.siem.components.embeddables.mapToolTip.errorTitle": "マップ機能の読み込み中にエラーが発生", + "xpack.siem.components.embeddables.mapToolTip.filterForValueHoverAction": "値でフィルターします", + "xpack.siem.components.embeddables.mapToolTip.footerLabel": "{totalFeatures} 件中 {currentFeature} 件 {totalFeatures, plural, =1 {feature} other {features}}", + "xpack.siem.components.embeddables.mapToolTip.lineContent.destinationLabel": "送信先", + "xpack.siem.components.embeddables.mapToolTip.lineContent.sourceLabel": "送信元", + "xpack.siem.components.embeddables.mapToolTip.pointContent.asnTitle": "ASN", + "xpack.siem.components.embeddables.mapToolTip.pointContent.destinationDomainTitle": "デスティネーションドメイン", + "xpack.siem.components.embeddables.mapToolTip.pointContent.destinationIPTitle": "デスティネーション IP", + "xpack.siem.components.embeddables.mapToolTip.pointContent.hostTitle": "ホスト", + "xpack.siem.components.embeddables.mapToolTip.pointContent.locationTitle": "場所", + "xpack.siem.components.embeddables.mapToolTip.pointContent.sourceDomainTitle": "ソースドメイン", + "xpack.siem.components.embeddables.mapToolTip.pointContent.sourceIPTitle": "ソース IP", + "xpack.siem.components.mlPopover.jobsTable.filters.groupsLabel": "グループ", + "xpack.siem.components.mlPopover.jobsTable.filters.noGroupsAvailableDescription": "利用可能なグループがありません", + "xpack.siem.components.mlPopover.jobsTable.filters.searchFilterPlaceholder": "例: rare_process_linux", + "xpack.siem.components.mlPopover.jobsTable.filters.showAllJobsLabel": "Elastic ジョブ", + "xpack.siem.components.mlPopover.jobsTable.filters.showSiemJobsLabel": "カスタムジョブ", + "xpack.siem.components.mlPopup.cloudLink": "クラウド展開", + "xpack.siem.components.mlPopup.jobsTable.tagsColumn": "グループ", + "xpack.siem.components.mlPopup.licenseButtonLabel": "ライセンスの管理", + "xpack.siem.components.mlPopup.moduleNotCompatibleTitle": "{incompatibleJobCount} 件が {incompatibleJobCount, plural, =1 {job} other {jobs}} 現在利用できません", + "xpack.siem.eventsOverTime.eventCountFrequencyByActionTitle": "アクション別のイベントカウント", + "xpack.siem.eventsOverTime.showing": "表示中", + "xpack.siem.eventsOverTime.unit": "{totalCount, plural, =1 {event} other {events}}", + "xpack.siem.flyout.button.text": "タイムライン", + "xpack.siem.footer.loadingEventsData": "イベントを読み込み中", + "xpack.siem.network.navigation.anomaliesTitle": "異常", + "xpack.siem.network.navigation.dnsTitle": "DNS", + "xpack.siem.network.navigation.flowsTitle": "Flow", + "xpack.siem.network.navigation.tlsTitle": "TLS", + "xpack.siem.networkTopCountriesTable.column.bytesInTitle": "受信バイト", + "xpack.siem.networkTopCountriesTable.column.bytesOutTitle": "送信バイト", + "xpack.siem.networkTopCountriesTable.column.countryTitle": "国", + "xpack.siem.networkTopCountriesTable.column.destinationIps": "デスティネーション IP", + "xpack.siem.networkTopCountriesTable.column.flows": "Flow", + "xpack.siem.networkTopCountriesTable.column.sourceIps": "ソース IP", + "xpack.siem.networkTopCountriesTable.heading.destinationCountries": "デスティネーションの国", + "xpack.siem.networkTopCountriesTable.heading.sourceCountries": "ソースの国", + "xpack.siem.networkTopCountriesTable.heading.unit": "{totalCount, plural, =1 {Country} other {Countries}}", + "xpack.siem.networkTopCountriesTable.rows": "{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}", + "xpack.siem.overview.endgameDnsTitle": "Endgame DNS", + "xpack.siem.overview.endgameFileTitle": "Endgame ファイル", + "xpack.siem.overview.endgameImageLoadTitle": "Endgame 画像の読み込み", + "xpack.siem.overview.endgameNetworkTitle": "Endgame ネットワーク", + "xpack.siem.overview.endgameProcessTitle": "Endgame プロセス", + "xpack.siem.overview.endgameRegistryTitle": "Endgame レジストリ", + "xpack.siem.overview.endgameSecurityTitle": "Endgame セキュリティ", + "xpack.siem.system.acceptedAConnectionViaDescription": "次の手段で接続を受け付けました:", + "xpack.siem.system.createdFileDescription": "ファイルを作成しました", + "xpack.siem.system.deletedFileDescription": "ファイルを削除しました", + "xpack.siem.system.disconnectedViaDescription": "次の手段で接続を解除しました", + "xpack.siem.system.terminatedProcessDescription": "プロセスを中断しました", + "xpack.siem.system.viaDescription": "経由", + "xpack.siem.system.viaParentProcessDescription": "親プロセスで", + "xpack.siem.system.withExitCodeDescription": "終了コードで", + "xpack.siem.timeline.body.renderers.dns.askedForDescription": "要求された", + "xpack.siem.timeline.body.renderers.dns.responseCodeDescription": "応答コード", + "xpack.siem.timeline.body.renderers.dns.viaDescription": "経由", + "xpack.siem.timeline.body.renderers.dns.whichResolvedToDescription": "で解決する", + "xpack.siem.timeline.body.renderers.dns.withQuestionTypeDescription": "質問タイプで", + "xpack.siem.timeline.body.renderers.endgame.aLoginWasAttemptedUsingExplicitCredentialsDescription": "明示認証情報でログインが試みられました", + "xpack.siem.timeline.body.renderers.endgame.asRequestedBySubjectDescription": "サブジェクトにリクエストされた通り", + "xpack.siem.timeline.body.renderers.endgame.loggedOffDescription": "ログオフ", + "xpack.siem.timeline.body.renderers.endgame.logonTypeBatchDescription": "一斉", + "xpack.siem.timeline.body.renderers.endgame.logonTypeCachedInteractiveDescription": "キャッシュインタラクティブ", + "xpack.siem.timeline.body.renderers.endgame.logonTypeInteractiveDescription": "インタラクティブ", + "xpack.siem.timeline.body.renderers.endgame.logonTypeNetworkCleartextDescription": "ネットワーククリアテキスト", + "xpack.siem.timeline.body.renderers.endgame.logonTypeNetworkDescription": "ネットワーク", + "xpack.siem.timeline.body.renderers.endgame.logonTypeNewCredentialsDescription": "新規認証情報", + "xpack.siem.timeline.body.renderers.endgame.logonTypeRemoteInteractiveDescription": "リモートインタラクティブ", + "xpack.siem.timeline.body.renderers.endgame.logonTypeServiceDescription": "サービス", + "xpack.siem.timeline.body.renderers.endgame.logonTypeUnlockDescription": "ロック解除", + "xpack.siem.timeline.body.renderers.endgame.subjectLogonIdDescription": "サブジェクトログオン ID", + "xpack.siem.timeline.body.renderers.endgame.successfullyLoggedInDescription": "正常にログイン", + "xpack.siem.timeline.body.renderers.endgame.targetLogonIdDescription": "ターゲットログオン ID", + "xpack.siem.timeline.body.renderers.endgame.toDescription": "に", + "xpack.siem.timeline.body.renderers.endgame.usingLogonTypeDescription": "ログオンタイプを使用して", + "xpack.siem.timeline.body.renderers.endgame.viaDescription": "経由", + "xpack.siem.timeline.body.renderers.endgame.withSpecialPrivilegesDescription": "割り当てられた特別な権限", + "xpack.siem.components.mlPopup.upgradeDescription": "SIEM の異常検出機能にアクセスするには、ライセンスをプラチナに更新するか、30 日間の無料トライアルを開始するか、AWS、GCP、または Azure で{cloudLink} にサインアップしてください。その後、機械学習ジョブを実行して異常を表示できます。", "xpack.snapshotRestore.addRepository.breadcrumbTitle": "レポジトリの追加", "xpack.snapshotRestore.addRepository.savingRepositoryErrorTitle": "新規レポジトリを登録できません", "xpack.snapshotRestore.addRepositoryButtonLabel": "レポジトリを登録", @@ -9759,7 +11489,7 @@ "xpack.snapshotRestore.policyList.LoadingPoliciesErrorMessage": "ポリシーの読み込み中にエラーが発生しました", "xpack.snapshotRestore.policyList.table.actionDeleteAriaLabel": "ポリシー「{name}」を削除", "xpack.snapshotRestore.policyList.table.actionDeleteTooltip": "削除", - "xpack.snapshotRestore.policyList.table.actionEditAriaLabel": "ポリシー「{name}」を編集", + "xpack.snapshotRestore.policyList.table.actionEditAriaLabel": "ポリシー「{name}」の編集", "xpack.snapshotRestore.policyList.table.actionEditTooltip": "編集", "xpack.snapshotRestore.policyList.table.actionExecuteAriaLabel": "「{name}」を直ちに実行", "xpack.snapshotRestore.policyList.table.actionExecuteDisabledTooltip": "ポリシーを実行中です", @@ -9797,6 +11527,72 @@ "xpack.snapshotRestore.restoreForm.stepLogistics.docsButtonLabel": "スナップショットと復元ドキュメント", "xpack.snapshotRestore.restoreForm.stepLogisticsTitle": "詳細を復元", "xpack.snapshotRestore.restoreSnapshot.executeRestoreErrorTitle": "スナップショットを復元できません", + "xpack.snapshotRestore.executeRetention.confirmModal.cancelButtonLabel": "キャンセル", + "xpack.snapshotRestore.executeRetention.confirmModal.confirmButtonLabel": "保存を実行", + "xpack.snapshotRestore.executeRetention.confirmModal.executeRetentionTitle": "今すぐスナップショットの保存を実行しますか?", + "xpack.snapshotRestore.executeRetention.errorMessage": "保存の実行中にエラーが発生しました", + "xpack.snapshotRestore.executeRetention.successMessage": "保存を実行中です", + "xpack.snapshotRestore.policyDetails.expireAfterLabel": "次の期間後削除:", + "xpack.snapshotRestore.policyDetails.generalTitle": "一般", + "xpack.snapshotRestore.policyDetails.maxCountLabel": "最高カウント", + "xpack.snapshotRestore.policyDetails.minCountLabel": "最低カウント", + "xpack.snapshotRestore.policyDetails.retentionTitle": "保存", + "xpack.snapshotRestore.policyDetails.snapshotDeletionFailuresStat": "削除失敗", + "xpack.snapshotRestore.policyDetails.snapshotsDeletedStat": "削除されました", + "xpack.snapshotRestore.policyDetails.snapshotsFailedStat": "失敗", + "xpack.snapshotRestore.policyDetails.snapshotsTakenStat": "スナップショット", + "xpack.snapshotRestore.policyForm.navigation.stepRetentionName": "スナップショットの保存", + "xpack.snapshotRestore.policyForm.stepRetention.countDescription": "クラスターに格納するスナップショットの最少数と最大数。", + "xpack.snapshotRestore.policyForm.stepRetention.countTitle": "保存するスナップショット", + "xpack.snapshotRestore.policyForm.stepRetention.docsButtonLabel": "スナップショット保存ドキュメント", + "xpack.snapshotRestore.policyForm.stepRetention.expirationDescription": "スナップショットの削除までに待つ時間です。", + "xpack.snapshotRestore.policyForm.stepRetention.expirationTitle": "有効期限", + "xpack.snapshotRestore.policyForm.stepRetention.expireAfterLabel": "次の期間後削除:", + "xpack.snapshotRestore.policyForm.stepRetention.maxCountLabel": "最高カウント", + "xpack.snapshotRestore.policyForm.stepRetention.minCountLabel": "最低カウント", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionAddTitle": "保存スケジュールの追加", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionAdvancedLabel": "Cron expression を作成", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionBasicLabel": "基本間隔を作成", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionCancelButtonLabel": "キャンセル", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionEditButtonLabel": "変更を保存", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionEditTitle": "保存スケジュールの編集", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionErrorTitle": "保存スケジュールの保存中にエラーが発生", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionHelpText": "Cron 式を使用します。{docLink}", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionHelpTextDocLinkText": "さらに詳しく", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionSaveButtonLabel": "スケジュール", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionScheduleFieldErrorMessage": "保存スケジュールが必要です。", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionScheduleLabel": "保存スケジュール", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionSuccessMessage": "保存スケジュールが更新されました", + "xpack.snapshotRestore.policyForm.stepRetentionTitle": "スナップショットの保存 (オプション)", + "xpack.snapshotRestore.policyForm.stepReview.editIconAriaLabel": "ステップを編集", + "xpack.snapshotRestore.policyForm.stepReview.retentionTab.expireAfterLabel": "次の期間後削除:", + "xpack.snapshotRestore.policyForm.stepReview.retentionTab.maxCountLabel": "最高カウント", + "xpack.snapshotRestore.policyForm.stepReview.retentionTab.minCountLabel": "最低カウント", + "xpack.snapshotRestore.policyForm.stepReview.retentionTab.sectionRetentionTitle": "スナップショットの保存", + "xpack.snapshotRestore.policyForm.stepSettings.partialIndicesToggleSwitch": "部分インデックスを許可", + "xpack.snapshotRestore.policyList.table.lastSnapshotFailedTooltip": "前回失敗したスナップショットです", + "xpack.snapshotRestore.policyList.table.retentionColumnAriaLabel": "構成された保存", + "xpack.snapshotRestore.policyList.table.retentionColumnTitle": "保存", + "xpack.snapshotRestore.policyRetentionSchedulePanel.addButtonLabel": "スケジュール", + "xpack.snapshotRestore.policyRetentionSchedulePanel.errorFetchingRetentionScheduleReloadButtonLabel": "再読み込み", + "xpack.snapshotRestore.policyRetentionSchedulePanel.errorFetchingRetentionScheduleTitle": "保存スケジュールの取得中にエラーが発生", + "xpack.snapshotRestore.policyRetentionSchedulePanel.executeButtonLabel": "今すぐ実行", + "xpack.snapshotRestore.policyRetentionSchedulePanel.managePanelTitle": "保存オプション", + "xpack.snapshotRestore.policyRetentionSchedulePanel.manageRetentionButtonLabel": "保存の管理", + "xpack.snapshotRestore.policyRetentionSchedulePanel.noScheduleConfiguredWarningDescription": "1 つまたは複数のポリシーに保存期間がありますが、保存がスケジュールされていません。", + "xpack.snapshotRestore.policyRetentionSchedulePanel.noScheduleConfiguredWarningTitle": "保存がスケジュールされていません", + "xpack.snapshotRestore.policyRetentionSchedulePanel.retentionScheduleDescription": "スナップショットを保存する cron スケジュール は {cronSchedule} です。", + "xpack.snapshotRestore.policyRetentionSchedulePanel.retentionScheduleEditLinkAriaLabel": "保存スケジュールの編集", + "xpack.snapshotRestore.policyRetentionSchedulePanel.retentionScheduleEditLinkTooltip": "保存スケジュールの編集", + "xpack.snapshotRestore.policyRetentionSchedulePanel.retentionScheduleExecuteLinkAriaLabel": "今すぐ保存を実行", + "xpack.snapshotRestore.policyRetentionSchedulePanel.retentionScheduleExecuteLinkTooltip": "今すぐ保存を実行", + "xpack.snapshotRestore.policyValidation.indexPatternRequiredErrorMessage": "インデックスパターンが最低 1 つ必要です。", + "xpack.snapshotRestore.policyValidation.indicesRequiredErrorMessage": "インデックスを最低 1 つ選択する必要があります。", + "xpack.snapshotRestore.policyValidation.invalidMinCountErrorMessage": "最低カウントは最高カウントよりも大きい値にできません。", + "xpack.snapshotRestore.policyValidation.nameRequiredErroMessage": "ポリシー名が必要です。", + "xpack.snapshotRestore.policyValidation.repositoryRequiredErrorMessage": "レポジトリが必要です。", + "xpack.snapshotRestore.policyValidation.scheduleRequiredErrorMessage": "スケジュールが必要です。", + "xpack.snapshotRestore.policyValidation.snapshotNameRequiredErrorMessage": "スナップショット名が必要です。", "xpack.spaces.defaultSpaceDescription": "これはデフォルトのスペースです!", "xpack.spaces.defaultSpaceTitle": "デフォルト", "xpack.spaces.displayName": "スペース", @@ -9902,9 +11698,12 @@ "xpack.spaces.spaceSelector.noSpacesMatchSearchCriteriaDescription": "検索条件に一致するスペースがありません", "xpack.spaces.spaceSelector.selectSpacesTitle": "スペースの選択", "xpack.spaces.spacesTitle": "スペース", + "xpack.spaces.management.customizeSpaceAvatar.imageUrl": "カスタム画像", + "xpack.spaces.management.customizeSpaceAvatar.removeImage": "カスタム画像を削除", + "xpack.spaces.management.customizeSpaceAvatar.selectImageUrl": "画像ファイルを選択", "telemetry.callout.appliesSettingTitle": "この設定は {allOfKibanaText} に適用されます", "telemetry.callout.appliesSettingTitle.allOfKibanaText": "Kibana のすべて", - "telemetry.callout.clusterStatisticsDescription": "これは収集される基本的なクラスター統計の例です。インデックス、チャート、ノードの数が含まれます。監視がオンになっているかどうかなどのハイレベルの使用統計も含まれます。", + "telemetry.callout.clusterStatisticsDescription": "これは収集される基本的なクラスター統計の例です。インデックス、シャード、ノードの数が含まれます。監視がオンになっているかどうかなどのハイレベルの使用統計も含まれます。", "telemetry.callout.clusterStatisticsTitle": "クラスター統計", "telemetry.callout.errorLoadingClusterStatisticsDescription": "クラスター統計の取得中に予期せぬエラーが発生しました。Elasticsearch、Kibana、またはネットワークのエラーが原因の可能性があります。Kibana を確認し、ページを再読み込みして再試行してください。", "telemetry.callout.errorLoadingClusterStatisticsTitle": "クラスター統計の読み込みエラー", @@ -9912,13 +11711,16 @@ "telemetry.callout.errorUnprivilegedUserTitle": "クラスター統計の表示エラー", "telemetry.readOurUsageDataPrivacyStatementLinkText": "使用データのプライバシーステートメントをお読みください", "telemetry.seeExampleOfWhatWeCollectLinkText": "収集されるデータの例を見る", - "telemetry.telemetryConfigDescription": "基本的な機能利用に関する統計情報を提供して Elastic Stack の改善にご協力ください。このデータは Elastic 社外と共有されません。", + "telemetry.telemetryConfigDescription": "基本的な機能の利用状況に関する統計情報を提供して、Elastic Stack の改善にご協力ください。このデータは Elastic 社外と共有されません。", "telemetry.telemetryConfigTitle": "遠隔測定オプトイン", "telemetry.telemetryErrorNotificationMessageDescription.tryAgainText": "Kibana と Elasticsearch が現在も実行中であることを確認し、再試行してください。", "telemetry.telemetryErrorNotificationMessageDescription.unableToSaveTelemetryPreferenceText": "遠隔測定設定を保存できません。", "telemetry.telemetryErrorNotificationMessageTitle": "遠隔測定エラー", "telemetry.usageDataTitle": "使用データ", "telemetry.welcomeBanner.telemetryConfigDetailsDescription.telemetryPrivacyStatementLinkText": "遠隔測定に関するプライバシーステートメント", + "telemetry.optInErrorToastText": "使用状況統計設定の設定中にエラーが発生しました。", + "telemetry.optInErrorToastTitle": "エラー", + "telemetry.welcomeBanner.title": "Elastic Stack の改善にご協力ください", "xpack.upgradeAssistant.appTitle": "{version} アップグレードアシスタント", "xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.calloutDetail": "{snapshotRestoreDocsButton} でデータをバックアップします。", "xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.snapshotRestoreDocsButtonLabel": "API のスナップショットと復元", @@ -10117,6 +11919,30 @@ "xpack.uptime.pingList.expandRow": "拡張", "xpack.uptime.snapshot.pingsOverTimeTitle": "一定時間のピング", "xpack.uptime.snapshotHistogram.yAxis.title": "ピング", + "xpack.uptime.donutChart.ariaLabel": "現在のステータスを表す円グラフ、{total} 個中 {down} 個のモニターがダウンしています。", + "xpack.uptime.donutChart.legend.downRowLabel": "ダウン", + "xpack.uptime.donutChart.legend.upRowLabel": "アップ", + "xpack.uptime.durationChart.emptyPrompt.description": "このモニターは選択された時間範囲で一度も {emphasizedText} していません。", + "xpack.uptime.durationChart.emptyPrompt.title": "利用可能な期間データがありません", + "xpack.uptime.emptyStateError.notAuthorized": "アップタイムデータの表示が承認されていません。システム管理者にお問い合わせください。", + "xpack.uptime.emptyStateError.notFoundPage": "ページが見つかりません", + "xpack.uptime.filterPopout.loadingMessage": "読み込み中…", + "xpack.uptime.filterPopout.searchMessage": "{title} を検索", + "xpack.uptime.kueryBar.indexPatternMissingWarningMessage": "インデックスパターンの取得中にエラーが発生しました。", + "xpack.uptime.kueryBar.searchPlaceholder": "モニター ID、名前、プロトコルタイプなどを検索…", + "xpack.uptime.monitorList.noItemForSelectedFiltersMessage": "選択されたフィルター条件でモニターが見つかりませんでした", + "xpack.uptime.monitorList.table.description": "列にステータス、名前、URL、IP、ダウンタイム履歴、統合が入力されたモニターステータス表です。この表は現在 {length} 項目を表示しています。", + "xpack.uptime.monitorStatusBar.sslCertificateExpiry.ariaLabel": "SSL 証明書の有効期限:", + "xpack.uptime.monitorStatusBar.sslCertificateExpiry.content": "SSL 証明書の有効期限: {certificateValidity}", + "xpack.uptime.notFountPage.homeLinkText": "ホームへ戻る", + "xpack.uptime.overviewPageLink.disabled.ariaLabel": "無効になったページ付けボタンです。モニターリストがこれ以上ナビゲーションできないことを示しています。", + "xpack.uptime.overviewPageLink.next.ariaLabel": "次の結果ページ", + "xpack.uptime.overviewPageLink.prev.ariaLabel": "前の結果ページ", + "xpack.uptime.overviewPageParsingErrorCallout.content": "フィルタークエリの解析中にエラーが発生しました. {content}", + "xpack.uptime.overviewPageParsingErrorCallout.noMessage": "エラーメッセージはありませんでした", + "xpack.uptime.overviewPageParsingErrorCallout.title": "エラーを解析中", + "xpack.uptime.snapshot.downCountsMessage": "{down}/{total} 個のモニターがダウンしています", + "xpack.uptime.snapshotHistogram.description": "{startTime} から {endTime} までの期間のアップタイムステータスを表示する棒グラフです。", "xpack.watcher.constants.actionStates.acknowledgedStateText": "承認済み", "xpack.watcher.constants.actionStates.configErrorStateText": "構成エラー", "xpack.watcher.constants.actionStates.errorStateText": "エラー", @@ -10226,6 +12052,12 @@ "xpack.watcher.thresholdWatchExpression.timeWindow.durationSizeIsRequiredValidationMessage": "ウィンドウ期間サイズが必要です。", "xpack.watcher.watchActions.logging.logTextIsRequiredValidationMessage": "ログテキストが必要です。", "xpack.watcher.watcherDescription": "アラートの作成、管理、監視によりデータへの変更を検知します。", + "xpack.watcher.requestFlyout.closeButtonLabel": "閉じる", + "xpack.watcher.requestFlyout.descriptionText": "この Elasticsearch リクエストは、このウォッチを作成または更新します。", + "xpack.watcher.requestFlyout.namedTitle": "「{id}」のリクエスト", + "xpack.watcher.requestFlyout.unnamedTitle": "リクエスト", + "xpack.watcher.sections.watchEdit.json.hideRequestButtonLabel": "リクエストを非表示", + "xpack.watcher.sections.watchEdit.json.showRequestButtonLabel": "リクエストを表示", "esUi.cronEditor.cronDaily.fieldHour.textAtLabel": "に", "esUi.cronEditor.cronDaily.fieldTimeLabel": "時間", "esUi.cronEditor.cronHourly.fieldMinute.textAtLabel": "に", @@ -10265,12 +12097,26 @@ "esUi.cronEditor.month.october": "10 月", "esUi.cronEditor.month.september": "9 月", "esUi.cronEditor.textEveryLabel": "毎", + "esUi.cronEditor.cronDaily.hourSelectLabel": "時間", + "esUi.cronEditor.cronDaily.minuteSelectLabel": "分", + "esUi.cronEditor.cronMonthly.hourSelectLabel": "時間", + "esUi.cronEditor.cronMonthly.minuteSelectLabel": "分", + "esUi.cronEditor.cronWeekly.hourSelectLabel": "時間", + "esUi.cronEditor.cronWeekly.minuteSelectLabel": "分", + "esUi.cronEditor.cronYearly.hourSelectLabel": "時間", + "esUi.cronEditor.cronYearly.minuteSelectLabel": "分", + "esUi.forms.comboBoxField.placeHolderText": "入力してエンターキーを押してください", + "esUi.forms.fieldValidation.indexNameInvalidCharactersError": "インデックス名に無効な{characterListLength, plural, one {文字} other {文字}} { characterList } が含まれています。", + "esUi.forms.fieldValidation.indexNameSpacesError": "インデックス名にはスペースを使用できません。", + "esUi.forms.fieldValidation.indexNameStartsWithDotError": "インデックス名の始めにピリオド (.) は使用できません。", + "esUi.forms.fieldValidation.indexPatternInvalidCharactersError": "インデックスパターンに無効な{characterListLength, plural, one {文字} other {文字}} { characterList } が含まれています。", + "esUi.forms.fieldValidation.indexPatternSpacesError": "インデックスパターンにはスペースを使用できません。", "visTypeMarkdown.tabs.dataText": "データ", "visTypeMarkdown.tabs.optionsText": "オプション", "visTypeMarkdown.params.fontSizeLabel": "ポイント単位のベースフォントサイズです。", "xpack.actions.actionTypeRegistry.get.missingActionTypeErrorMessage": "アクションタイプ \"{id}\" は登録されていません。", "xpack.actions.actionTypeRegistry.register.duplicateActionTypeErrorMessage": "アクションタイプ \"{id}\" は既に登録されています。", - "xpack.actions.builtin.email.errorSendingErrorMessage": "アクション「{actionId}」メールの送信でエラーが発生: {errorMessage}", + "xpack.actions.builtin.email.errorSendingErrorMessage": "アクション「{actionId}」メールの送信中にエラーが発生: {errorMessage}", "xpack.actions.builtin.esIndex.errorIndexingErrorMessage": "アクション「{actionId}」データのインデックス中にエラーが発生: {errorMessage}", "xpack.actions.builtin.esIndex.indexParamRequiredErrorMessage": "アクション {actionId} の構成で設定されていないため、インデックスパラメーターの設定が必要です", "xpack.actions.builtin.pagerduty.postingErrorMessage": "PagerDuty アクション「{actionId}」イベントの投稿中にエラーが発生: {errorMessage}", @@ -10282,11 +12128,13 @@ "xpack.actions.builtin.slack.errorPostingRetryLaterErrorMessage": "アクション「{id}」Slack メッセージの投稿中にエラーが発生しました。後程再試行してください", "xpack.actions.builtin.slack.unexpectedNullResponseErrorMessage": "Slack から予期せぬ null 応答", "xpack.actions.builtin.slack.unexpectedTextResponseErrorMessage": "Slack から予期せぬテキスト応答", - "xpack.actions.builtin.webhook.invalidResponseErrorMessage": "アクション「{id}」リモートウェブフックの呼び出し中にエラーが発生しました: {message}", - "xpack.actions.builtin.webhook.invalidResponseRetryDateErrorMessage": "アクション「{id}」リモートウェブフックの呼び出し中にエラーが発生しました: {retryString} に再試行してください: {message}", - "xpack.actions.builtin.webhook.invalidResponseRetryLaterErrorMessage": "アクション「{id}」リモートウェブフックの呼び出し中にエラーが発生しました。後程再試行してください", - "xpack.actions.builtin.webhook.unreachableErrorMessage": "アクション「{id}」リモートウェブフックの呼び出し中にエラーが発生しました: {message}", + "xpack.actions.builtin.webhook.invalidResponseErrorMessage": "無効な応答:Web フックアクション「{id}」リモート Web フックの呼び出し中にエラーが発生しました: {message}", + "xpack.actions.builtin.webhook.invalidResponseRetryDateErrorMessage": "無効な応答: Web フックアクション「{id}」リモート Web フックの呼び出し中にエラーが発生しました。{retryString} で後程再試行してください: {message}", + "xpack.actions.builtin.webhook.invalidResponseRetryLaterErrorMessage": "無効な応答: Web フックアクション「{id}」リモート Web フックの呼び出し中にエラーが発生しました。後程再試行してください", + "xpack.actions.builtin.webhook.unreachableErrorMessage": "到達不能な Web フック: Web フックアクション「{id}」リモート Web フックの呼び出し中にエラーが発生しました: {message}", "xpack.actions.builtin.webhook.unreachableRemoteWebhook": "リモートウェブフックにアクセスできません。アドレスが正しいことを確認してください。", + "xpack.actions.builtin.webhook.webhookConfigurationError": "Web フックアクションの構成中にエラーが発生: {message}", + "xpack.actions.urlWhitelistConfigurationError": "ターゲット {field} 「{value}」は Kibana のホワイトリストに登録されていません", "xpack.advancedUiActions.customizePanelTimeRange.modal.addToPanelButtonTitle": "パネルに追加", "xpack.advancedUiActions.customizePanelTimeRange.modal.cancelButtonTitle": "キャンセル", "xpack.advancedUiActions.customizePanelTimeRange.modal.optionsMenuForm.panelTitleFormRowLabel": "時間範囲", @@ -10295,6 +12143,678 @@ "xpack.advancedUiActions.customizeTimeRange.modal.headerTitle": "パネルの時間範囲のカスタマイズ", "xpack.advancedUiActions.customizeTimeRangeMenuItem.displayName": "時間範囲のカスタマイズ", "xpack.fileUpload.fileParser.errorReadingFile": "ファイルの読み込み中にエラーが発生しました", - "xpack.fileUpload.fileParser.noFileProvided": "エラー、ファイルが提供されていません" + "xpack.fileUpload.fileParser.noFileProvided": "エラー、ファイルが提供されていません", + "xpack.fileUpload.fileParser.noFeaturesDetected": "エラー、機能が検出されませんでした", + "xpack.fileUpload.jsonIndexFilePicker.fileParseError": "ファイル解析エラーが検出されました: {error}", + "xpack.fileUpload.jsonIndexFilePicker.fileProcessingError": "ファイル処理エラー: {errorMessage}", + "xpack.fileUpload.jsonIndexFilePicker.fileSizeError": "ファイルサイズエラー: {errorMessage}", + "xpack.fileUpload.jsonUploadAndParse.indexingComplete": "インデックス完了", + "xpack.fileUpload.patternReader.featuresOmitted": "ジオメトリのない一部の機能は省略されました", + "xpack.fileUpload.jsonIndexFilePicker.acceptableFileSize": "ファイルサイズ {fileSize} は最大ファイルサイズの{maxFileSize} を超えています", + "xpack.fileUpload.jsonIndexFilePicker.parsingFile": "{featuresProcessed} 件の機能が解析されました…", + "uiActions.actionPanel.title": "オプション", + "uiActions.errors.incompatibleAction": "操作に互換性がありません", + "visTypeTimeseries.addDeleteButtons.addButtonDefaultTooltip": "追加", + "visTypeTimeseries.addDeleteButtons.cloneButtonDefaultTooltip": "クローンを作成", + "visTypeTimeseries.addDeleteButtons.deleteButtonDefaultTooltip": "削除", + "visTypeTimeseries.addDeleteButtons.reEnableTooltip": "再度有効にする", + "visTypeTimeseries.addDeleteButtons.temporarilyDisableTooltip": "一時的に無効にする", + "visTypeTimeseries.aggLookup.addPipelineAggDescription": "{label} (「+」ボタンでこのパイプライン集約を追加します)", + "visTypeTimeseries.aggLookup.averageLabel": "平均", + "visTypeTimeseries.aggLookup.calculationLabel": "計算", + "visTypeTimeseries.aggLookup.cardinalityLabel": "基数", + "visTypeTimeseries.aggLookup.countLabel": "カウント", + "visTypeTimeseries.aggLookup.cumulativeSumLabel": "累積合計", + "visTypeTimeseries.aggLookup.derivativeLabel": "派生", + "visTypeTimeseries.aggLookup.deviationLabel": "標準偏差", + "visTypeTimeseries.aggLookup.filterRatioLabel": "フィルターレート", + "visTypeTimeseries.aggLookup.mathLabel": "数学処理", + "visTypeTimeseries.aggLookup.maxLabel": "最高", + "visTypeTimeseries.aggLookup.minLabel": "最低", + "visTypeTimeseries.aggLookup.movingAverageLabel": "移動平均", + "visTypeTimeseries.aggLookup.overallAverageLabel": "全体平均", + "visTypeTimeseries.aggLookup.overallMaxLabel": "全体最高", + "visTypeTimeseries.aggLookup.overallMinLabel": "全体最低", + "visTypeTimeseries.aggLookup.overallStdDeviationLabel": "全体標準偏差", + "visTypeTimeseries.aggLookup.overallSumLabel": "全体合計", + "visTypeTimeseries.aggLookup.overallSumOfSqLabel": "全体平方和", + "visTypeTimeseries.aggLookup.overallVarianceLabel": "全体の相異", + "visTypeTimeseries.aggLookup.percentileLabel": "パーセンタイル", + "visTypeTimeseries.aggLookup.percentileRankLabel": "パーセンタイルランク", + "visTypeTimeseries.aggLookup.positiveOnlyLabel": "プラスのみ", + "visTypeTimeseries.aggLookup.serialDifferenceLabel": "連続差", + "visTypeTimeseries.aggLookup.seriesAggLabel": "数列集約", + "visTypeTimeseries.aggLookup.staticValueLabel": "不動値", + "visTypeTimeseries.aggLookup.sumLabel": "合計", + "visTypeTimeseries.aggLookup.sumOfSqLabel": "平方和", + "visTypeTimeseries.aggLookup.topHitLabel": "トップヒット", + "visTypeTimeseries.aggLookup.valueCountLabel": "値カウント", + "visTypeTimeseries.aggLookup.varianceLabel": "相異", + "visTypeTimeseries.aggRow.addMetricButtonTooltip": "メトリックを追加", + "visTypeTimeseries.aggRow.deleteMetricButtonTooltip": "メトリックを削除", + "visTypeTimeseries.aggSelect.aggGroups.metricAggLabel": "メトリック集約", + "visTypeTimeseries.aggSelect.aggGroups.parentPipelineAggLabel": "親パイプライン集約", + "visTypeTimeseries.aggSelect.aggGroups.siblingPipelineAggLabel": "シブリングパイプライン集約", + "visTypeTimeseries.aggSelect.aggGroups.specialAggLabel": "特殊集約", + "visTypeTimeseries.aggSelect.metricsAggs.averageLabel": "平均", + "visTypeTimeseries.aggSelect.metricsAggs.cardinalityLabel": "基数", + "visTypeTimeseries.aggSelect.metricsAggs.countLabel": "カウント", + "visTypeTimeseries.aggSelect.metricsAggs.filterRatioLabel": "フィルターレート", + "visTypeTimeseries.aggSelect.metricsAggs.maxLabel": "最高", + "visTypeTimeseries.aggSelect.metricsAggs.minLabel": "最低", + "visTypeTimeseries.aggSelect.metricsAggs.percentileLabel": "パーセンタイル", + "visTypeTimeseries.aggSelect.metricsAggs.percentileRankLabel": "パーセンタイルランク", + "visTypeTimeseries.aggSelect.metricsAggs.staticValueLabel": "不動値", + "visTypeTimeseries.aggSelect.metricsAggs.stdDeviationLabel": "標準偏差", + "visTypeTimeseries.aggSelect.metricsAggs.sumLabel": "合計", + "visTypeTimeseries.aggSelect.metricsAggs.sumOfSquaresLabel": "平方和", + "visTypeTimeseries.aggSelect.metricsAggs.topHitLabel": "トップヒット", + "visTypeTimeseries.aggSelect.metricsAggs.valueCountLabel": "値カウント", + "visTypeTimeseries.aggSelect.metricsAggs.varianceLabel": "相異", + "visTypeTimeseries.aggSelect.pipelineAggs.bucketScriptLabel": "バケットスクリプト", + "visTypeTimeseries.aggSelect.pipelineAggs.cumulativeSumLabel": "累積合計", + "visTypeTimeseries.aggSelect.pipelineAggs.derivativeLabel": "派生", + "visTypeTimeseries.aggSelect.pipelineAggs.movingAverageLabel": "移動平均", + "visTypeTimeseries.aggSelect.pipelineAggs.positiveOnlyLabel": "プラスのみ", + "visTypeTimeseries.aggSelect.pipelineAggs.serialDifferenceLabel": "連続差", + "visTypeTimeseries.aggSelect.selectAggPlaceholder": "集約を選択", + "visTypeTimeseries.aggSelect.siblingAggs.overallAverageLabel": "全体平均", + "visTypeTimeseries.aggSelect.siblingAggs.overallMaxLabel": "全体最高", + "visTypeTimeseries.aggSelect.siblingAggs.overallMinLabel": "全体最低", + "visTypeTimeseries.aggSelect.siblingAggs.overallStdDeviationLabel": "全体標準偏差", + "visTypeTimeseries.aggSelect.siblingAggs.overallSumLabel": "全体合計", + "visTypeTimeseries.aggSelect.siblingAggs.overallSumOfSquaresLabel": "全体平方和", + "visTypeTimeseries.aggSelect.siblingAggs.overallVarianceLabel": "全体の相異", + "visTypeTimeseries.aggSelect.specialAggs.mathLabel": "数学処理", + "visTypeTimeseries.aggSelect.specialAggs.seriesAggLabel": "数列集約", + "visTypeTimeseries.annotationsEditor.addDataSourceButtonLabel": "データソースを追加", + "visTypeTimeseries.annotationsEditor.dataSourcesLabel": "データソース", + "visTypeTimeseries.annotationsEditor.fieldsLabel": "フィールド (必須 - コンマ区切りのパス)", + "visTypeTimeseries.annotationsEditor.howToCreateAnnotationDataSourceDescription": "下のボタンをクリックして注釈データソースを作成します。", + "visTypeTimeseries.annotationsEditor.iconLabel": "アイコン (必須)", + "visTypeTimeseries.annotationsEditor.ignoreGlobalFiltersLabel": "グローバルフィルターを無視しますか?", + "visTypeTimeseries.annotationsEditor.ignorePanelFiltersLabel": "パネルフィルターを無視しますか?", + "visTypeTimeseries.annotationsEditor.indexPatternLabel": "インデックスパターン (必須)", + "visTypeTimeseries.annotationsEditor.queryStringLabel": "クエリ文字列", + "visTypeTimeseries.annotationsEditor.rowTemplateHelpText": "例: {rowTemplateExample}", + "visTypeTimeseries.annotationsEditor.rowTemplateLabel": "行テンプレート (必須)", + "visTypeTimeseries.annotationsEditor.timeFieldLabel": "時間フィールド (必須)", + "visTypeTimeseries.axisLabelOptions.axisLabel": "{unitValue} {unitString} ごと", + "visTypeTimeseries.calculateLabel.bucketScriptsLabel": "バケットスクリプト", + "visTypeTimeseries.calculateLabel.countLabel": "カウント", + "visTypeTimeseries.calculateLabel.filterRatioLabel": "フィルターレート", + "visTypeTimeseries.calculateLabel.lookupMetricTypeOfMetricFieldRankLabel": "{metricField} 中 {lookupMetricType}", + "visTypeTimeseries.calculateLabel.lookupMetricTypeOfTargetLabel": "{targetLabel} 中 {lookupMetricType}", + "visTypeTimeseries.calculateLabel.lookupMetricTypeOfTargetWithAdditionalLabel": "{targetLabel} ({additionalLabel}) 中 {lookupMetricType}", + "visTypeTimeseries.calculateLabel.mathLabel": "数学処理", + "visTypeTimeseries.calculateLabel.seriesAggLabel": "数列集約 ({metricFunction})", + "visTypeTimeseries.calculateLabel.staticValueLabel": "{metricValue} の不動値", + "visTypeTimeseries.calculateLabel.unknownLabel": "不明", + "visTypeTimeseries.calculation.aggregationLabel": "集約", + "visTypeTimeseries.calculation.painlessScriptDescription": "変数は {params} オブジェクトのキー、すなわち {paramsName} です。バケット間隔 (ミリ秒単位) にアクセスするには {paramsInterval} を使用します。", + "visTypeTimeseries.calculation.painlessScriptLabel": "Painless スクリプト", + "visTypeTimeseries.calculation.variablesLabel": "変数", + "visTypeTimeseries.colorPicker.clearIconLabel": "クリア", + "visTypeTimeseries.colorPicker.notAccessibleAriaLabel": "カラーピッカー、アクセス不可", + "visTypeTimeseries.colorPicker.notAccessibleWithValueAriaLabel": "カラーピッカー ({value})、アクセス不可", + "visTypeTimeseries.colorRules.adjustChartSizeAriaLabel": "上下の矢印を押してチャートサイズを調整します", + "visTypeTimeseries.colorRules.defaultPrimaryNameLabel": "背景", + "visTypeTimeseries.colorRules.defaultSecondaryNameLabel": "テキスト", + "visTypeTimeseries.colorRules.greaterThanLabel": "> greater than", + "visTypeTimeseries.colorRules.greaterThanOrEqualLabel": ">= greater than or equal", + "visTypeTimeseries.colorRules.ifMetricIsLabel": "メトリックが", + "visTypeTimeseries.colorRules.lessThanLabel": "< less than", + "visTypeTimeseries.colorRules.lessThanOrEqualLabel": "<= less than or equal", + "visTypeTimeseries.colorRules.setPrimaryColorLabel": "{primaryName} を", + "visTypeTimeseries.colorRules.setSecondaryColorLabel": "、{secondaryName} を", + "visTypeTimeseries.colorRules.valueAriaLabel": "値", + "visTypeTimeseries.cumulativeSum.aggregationLabel": "集約", + "visTypeTimeseries.cumulativeSum.metricLabel": "メトリック", + "visTypeTimeseries.dataFormatPicker.bytesLabel": "バイト", + "visTypeTimeseries.dataFormatPicker.customLabel": "カスタム", + "visTypeTimeseries.dataFormatPicker.decimalPlacesLabel": "小数部分の桁数", + "visTypeTimeseries.dataFormatPicker.durationLabel": "期間", + "visTypeTimeseries.dataFormatPicker.formatStringHelpText": "{numeralJsLink} をご覧ください", + "visTypeTimeseries.dataFormatPicker.formatStringLabel": "フォーマット文字列", + "visTypeTimeseries.dataFormatPicker.fromLabel": "開始値:", + "visTypeTimeseries.dataFormatPicker.numberLabel": "数字", + "visTypeTimeseries.dataFormatPicker.percentLabel": "パーセント", + "visTypeTimeseries.dataFormatPicker.toLabel": "To", + "visTypeTimeseries.defaultDataFormatterLabel": "データフォーマッター", + "visTypeTimeseries.derivative.aggregationLabel": "集約", + "visTypeTimeseries.derivative.metricLabel": "メトリック", + "visTypeTimeseries.derivative.unitsLabel": "単位 (1s、1m など)", + "visTypeTimeseries.durationOptions.daysLabel": "日", + "visTypeTimeseries.durationOptions.hoursLabel": "時間", + "visTypeTimeseries.durationOptions.humanize": "人間に読解可能", + "visTypeTimeseries.durationOptions.microsecondsLabel": "マイクロ秒", + "visTypeTimeseries.durationOptions.millisecondsLabel": "ミリ秒", + "visTypeTimeseries.durationOptions.minutesLabel": "分", + "visTypeTimeseries.durationOptions.monthsLabel": "か月", + "visTypeTimeseries.durationOptions.nanosecondsLabel": "ナノ秒", + "visTypeTimeseries.durationOptions.picosecondsLabel": "ピコ秒", + "visTypeTimeseries.durationOptions.secondsLabel": "秒", + "visTypeTimeseries.durationOptions.weeksLabel": "週間", + "visTypeTimeseries.durationOptions.yearsLabel": "年", + "visTypeTimeseries.error.requestForPanelFailedErrorMessage": "このパネルのリクエストに失敗しました", + "visTypeTimeseries.fetchFields.loadIndexPatternFieldsErrorMessage": "index_pattern フィールドを読み込めません", + "visTypeTimeseries.fieldSelect.selectFieldPlaceholder": "フィールドを選択してください...", + "visTypeTimeseries.filterRatio.aggregationLabel": "集約", + "visTypeTimeseries.filterRatio.denominatorLabel": "分母", + "visTypeTimeseries.filterRatio.fieldLabel": "フィールド", + "visTypeTimeseries.filterRatio.metricAggregationLabel": "メトリック集約", + "visTypeTimeseries.filterRatio.numeratorLabel": "分子", + "visTypeTimeseries.function.help": "TSVB ビジュアライゼーション", + "visTypeTimeseries.gauge.dataTab.dataButtonLabel": "データ", + "visTypeTimeseries.gauge.dataTab.metricsButtonLabel": "メトリック", + "visTypeTimeseries.gauge.editor.addSeriesTooltip": "数列を追加", + "visTypeTimeseries.gauge.editor.cloneSeriesTooltip": "数列のクローンを作成", + "visTypeTimeseries.gauge.editor.deleteSeriesTooltip": "数列を削除", + "visTypeTimeseries.gauge.editor.labelPlaceholder": "ラベル", + "visTypeTimeseries.gauge.editor.toggleEditorAriaLabel": "数列エディターを切り替える", + "visTypeTimeseries.gauge.optionsTab.backgroundColorLabel": "背景色:", + "visTypeTimeseries.gauge.optionsTab.colorRulesLabel": "カラールール", + "visTypeTimeseries.gauge.optionsTab.dataLabel": "データ", + "visTypeTimeseries.gauge.optionsTab.gaugeLineWidthLabel": "ゲージ線の幅", + "visTypeTimeseries.gauge.optionsTab.gaugeMaxLabel": "ゲージ最大値 (自動は未入力)", + "visTypeTimeseries.gauge.optionsTab.gaugeStyleLabel": "ゲージスタイル", + "visTypeTimeseries.gauge.optionsTab.ignoreGlobalFilterLabel": "グローバルフィルターを無視しますか?", + "visTypeTimeseries.gauge.optionsTab.innerColorLabel": "内側の色:", + "visTypeTimeseries.gauge.optionsTab.innerLineWidthLabel": "内側の線の幅", + "visTypeTimeseries.gauge.optionsTab.optionsButtonLabel": "オプション", + "visTypeTimeseries.gauge.optionsTab.panelFilterLabel": "パネルフィルター", + "visTypeTimeseries.gauge.optionsTab.panelOptionsButtonLabel": "パネルオプション", + "visTypeTimeseries.gauge.optionsTab.styleLabel": "スタイル", + "visTypeTimeseries.gauge.styleOptions.circleLabel": "円", + "visTypeTimeseries.gauge.styleOptions.halfCircleLabel": "半円", + "visTypeTimeseries.getInterval.daysLabel": "日", + "visTypeTimeseries.getInterval.hoursLabel": "時間", + "visTypeTimeseries.getInterval.minutesLabel": "分", + "visTypeTimeseries.getInterval.monthsLabel": "か月", + "visTypeTimeseries.getInterval.secondsLabel": "秒", + "visTypeTimeseries.getInterval.weeksLabel": "週間", + "visTypeTimeseries.getInterval.yearsLabel": "年", + "visTypeTimeseries.iconSelect.asteriskLabel": "アスタリスク", + "visTypeTimeseries.iconSelect.bellLabel": "ベル", + "visTypeTimeseries.iconSelect.boltLabel": "ボルト", + "visTypeTimeseries.iconSelect.bombLabel": "ボム", + "visTypeTimeseries.iconSelect.bugLabel": "バグ", + "visTypeTimeseries.iconSelect.commentLabel": "コメント", + "visTypeTimeseries.iconSelect.exclamationCircleLabel": "マル感嘆符", + "visTypeTimeseries.iconSelect.exclamationTriangleLabel": "注意三角マーク", + "visTypeTimeseries.iconSelect.fireLabel": "炎", + "visTypeTimeseries.iconSelect.flagLabel": "旗", + "visTypeTimeseries.iconSelect.heartLabel": "ハート", + "visTypeTimeseries.iconSelect.mapMarkerLabel": "マップマーカー", + "visTypeTimeseries.iconSelect.mapPinLabel": "マップピン", + "visTypeTimeseries.iconSelect.starLabel": "星", + "visTypeTimeseries.iconSelect.tagLabel": "タグ", + "visTypeTimeseries.indexPattern.dropLastBucketLabel": "最後のバケットをドロップしますか?", + "visTypeTimeseries.indexPattern.intervalHelpText": "例: auto、1m、1d、7d、1y、>=1m", + "visTypeTimeseries.indexPattern.intervalLabel": "間隔", + "visTypeTimeseries.indexPattern.searchByDefaultIndex": "デフォルトのインデックスパターンが使用されています。すべてのインデックスにクエリを実行するには * を使用します", + "visTypeTimeseries.indexPattern.timeFieldLabel": "時間フィールド", + "visTypeTimeseries.indexPattern.timeRange.entireTimeRange": "時間範囲全体", + "visTypeTimeseries.indexPattern.timeRange.hint": "この設定は、一致するドキュメントに使用される期間をコントロールします。\n 「時間範囲全体」は、タイムピッカーで選択されたすべてのドキュメントと照会します。\n 「最終値」は、期間の終了時から指定期間のドキュメントのみと照会します。", + "visTypeTimeseries.indexPattern.timeRange.label": "データ期間モード", + "visTypeTimeseries.indexPattern.timeRange.lastValue": "最終値", + "visTypeTimeseries.indexPattern.timeRange.selectTimeRange": "選択してください", + "visTypeTimeseries.indexPatternLabel": "インデックスパターン", + "visTypeTimeseries.kbnVisTypes.metricsDescription": "ビジュアルパイプラインインターフェースを使用して時系列のチャートを作成します。", + "visTypeTimeseries.kbnVisTypes.metricsTitle": "TSVB", + "visTypeTimeseries.markdown.alignOptions.bottomLabel": "一番下", + "visTypeTimeseries.markdown.alignOptions.middleLabel": "真ん中", + "visTypeTimeseries.markdown.alignOptions.topLabel": "一番上", + "visTypeTimeseries.markdown.dataTab.dataButtonLabel": "データ", + "visTypeTimeseries.markdown.dataTab.metricsButtonLabel": "メトリック", + "visTypeTimeseries.markdown.editor.addSeriesTooltip": "数列を追加", + "visTypeTimeseries.markdown.editor.cloneSeriesTooltip": "数列のクローンを作成", + "visTypeTimeseries.markdown.editor.deleteSeriesTooltip": "数列を削除", + "visTypeTimeseries.markdown.editor.labelPlaceholder": "ラベル", + "visTypeTimeseries.markdown.editor.toggleEditorAriaLabel": "数列エディターを切り替える", + "visTypeTimeseries.markdown.editor.variableNamePlaceholder": "変数名", + "visTypeTimeseries.markdown.optionsTab.backgroundColorLabel": "背景色:", + "visTypeTimeseries.markdown.optionsTab.customCSSLabel": "カスタム CSS (Less をサポート)", + "visTypeTimeseries.markdown.optionsTab.dataLabel": "データ", + "visTypeTimeseries.markdown.optionsTab.ignoreGlobalFilterLabel": "グローバルフィルターを無視しますか?", + "visTypeTimeseries.markdown.optionsTab.openLinksInNewTab": "新規タブでリンクを開きますか?", + "visTypeTimeseries.markdown.optionsTab.optionsButtonLabel": "オプション", + "visTypeTimeseries.markdown.optionsTab.panelFilterLabel": "パネルフィルター", + "visTypeTimeseries.markdown.optionsTab.panelOptionsButtonLabel": "パネルオプション", + "visTypeTimeseries.markdown.optionsTab.showScrollbarsLabel": "スクロールバーを表示しますか?", + "visTypeTimeseries.markdown.optionsTab.styleLabel": "スタイル", + "visTypeTimeseries.markdown.optionsTab.verticalAlignmentLabel": "縦の配列:", + "visTypeTimeseries.markdownEditor.howToAccessEntireTreeDescription": "{all} という特殊な変数もあり、ツリー全体へのアクセスに使用できます。これは group by からデータのリストを作成する際に便利です:", + "visTypeTimeseries.markdownEditor.howToUseVariablesInMarkdownDescription": "次の変数は Markdown で Handlebar (mustache) 構文を使用して使用できます。利用可能な表現は {handlebarLink} をご覧ください。", + "visTypeTimeseries.markdownEditor.howUseVariablesInMarkdownDescription.documentationLinkText": "ドキュメンテーションはここをクリックしてください", + "visTypeTimeseries.markdownEditor.nameLabel": "名前", + "visTypeTimeseries.markdownEditor.noVariablesAvailableDescription": "選択されたデータメトリックに利用可能な変数はありません。", + "visTypeTimeseries.markdownEditor.valueLabel": "値", + "visTypeTimeseries.math.aggregationLabel": "集約", + "visTypeTimeseries.math.expressionDescription": "このフィールドは基本的な数学表現 ({link} を参照) を使用します。つまり、変数は {params} オブジェクトのキーです。{paramsName} すべてのデータにアクセスするには、値の配列には {paramsValues} を使い、タイムスタンプの配列には {paramsTimestamps} を使います。{paramsTimestamp} は現在のバケットのタイムスタンプに使用でき、{paramsIndex} は現在のバケットのインデックスに使用でき、{paramsInterval} はミリ秒単位での間隔に使用できます。", + "visTypeTimeseries.math.expressionDescription.tinyMathLinkText": "TinyMath", + "visTypeTimeseries.math.expressionLabel": "表現", + "visTypeTimeseries.math.variablesLabel": "変数", + "visTypeTimeseries.metric.dataTab.dataButtonLabel": "データ", + "visTypeTimeseries.metric.dataTab.metricsButtonLabel": "メトリック", + "visTypeTimeseries.metric.editor.addSeriesTooltip": "数列を追加", + "visTypeTimeseries.metric.editor.cloneSeriesTooltip": "数列のクローンを作成", + "visTypeTimeseries.metric.editor.deleteSeriesTooltip": "数列を削除", + "visTypeTimeseries.metric.editor.labelPlaceholder": "ラベル", + "visTypeTimeseries.metric.editor.toggleEditorAriaLabel": "数列エディターを切り替える", + "visTypeTimeseries.metric.optionsTab.colorRulesLabel": "カラールール", + "visTypeTimeseries.metric.optionsTab.dataLabel": "データ", + "visTypeTimeseries.metric.optionsTab.ignoreGlobalFilterLabel": "グローバルフィルターを無視しますか?", + "visTypeTimeseries.metric.optionsTab.optionsButtonLabel": "オプション", + "visTypeTimeseries.metric.optionsTab.panelFilterLabel": "パネルフィルター", + "visTypeTimeseries.metric.optionsTab.panelOptionsButtonLabel": "パネルオプション", + "visTypeTimeseries.metricMissingErrorMessage": "メトリックに {field} がありません", + "visTypeTimeseries.metricSelect.selectMetricPlaceholder": "メトリックを選択してください…", + "visTypeTimeseries.missingPanelConfigDescription": "「{modelType}」にパネル構成が欠けています", + "visTypeTimeseries.movingAverage.aggregationLabel": "集約", + "visTypeTimeseries.movingAverage.alpha": "アルファ", + "visTypeTimeseries.movingAverage.beta": "ベータ", + "visTypeTimeseries.movingAverage.gamma": "ガンマ", + "visTypeTimeseries.movingAverage.metricLabel": "メトリック", + "visTypeTimeseries.movingAverage.model.selectPlaceholder": "選択してください", + "visTypeTimeseries.movingAverage.modelLabel": "モデル", + "visTypeTimeseries.movingAverage.modelOptions.exponentiallyWeightedLabel": "指数加重", + "visTypeTimeseries.movingAverage.modelOptions.holtLinearLabel": "Holt-Linear", + "visTypeTimeseries.movingAverage.modelOptions.holtWintersLabel": "Holt-Winters", + "visTypeTimeseries.movingAverage.modelOptions.linearLabel": "直線", + "visTypeTimeseries.movingAverage.modelOptions.simpleLabel": "シンプル", + "visTypeTimeseries.movingAverage.multiplicative": "マルチキャプティブ", + "visTypeTimeseries.movingAverage.multiplicative.selectPlaceholder": "選択してください", + "visTypeTimeseries.movingAverage.multiplicativeOptions.false": "False", + "visTypeTimeseries.movingAverage.multiplicativeOptions.true": "True", + "visTypeTimeseries.movingAverage.period": "期間", + "visTypeTimeseries.movingAverage.windowSizeHint": "ウィンドウは、必ず、期間のサイズの 2 倍以上でなければなりません", + "visTypeTimeseries.movingAverage.windowSizeLabel": "ウィンドウサイズ", + "visTypeTimeseries.multivalueRow.valueLabel": "値:", + "visTypeTimeseries.noButtonLabel": "いいえ", + "visTypeTimeseries.noDataDescription": "選択されたメトリックに表示するデータがありません", + "visTypeTimeseries.percentile.aggregationLabel": "集約", + "visTypeTimeseries.percentile.fieldLabel": "フィールド", + "visTypeTimeseries.percentile.fillToLabel": "次の基準に合わせる:", + "visTypeTimeseries.percentile.modeLabel": "モード:", + "visTypeTimeseries.percentile.modeOptions.bandLabel": "帯", + "visTypeTimeseries.percentile.modeOptions.lineLabel": "折れ線", + "visTypeTimeseries.percentile.percentileAriaLabel": "パーセンタイル", + "visTypeTimeseries.percentile.shadeLabel": "シェイド (0 から 1)", + "visTypeTimeseries.percentileRank.aggregationLabel": "集約", + "visTypeTimeseries.percentileRank.fieldLabel": "フィールド", + "visTypeTimeseries.positiveOnly.aggregationLabel": "集約", + "visTypeTimeseries.positiveOnly.metricLabel": "メトリック", + "visTypeTimeseries.replaceVars.errors.markdownErrorDescription": "Markdown、既知の変数、ビルトイン Handlebars 表現のみが使用されていることを確認してください。", + "visTypeTimeseries.replaceVars.errors.markdownErrorTitle": "Markdown の処理中にエラーが発生", + "visTypeTimeseries.replaceVars.errors.unknownVarDescription": "{badVar} は不明な変数です", + "visTypeTimeseries.replaceVars.errors.unknownVarTitle": "Markdown の処理中にエラーが発生", + "visTypeTimeseries.serialDiff.aggregationLabel": "集約", + "visTypeTimeseries.serialDiff.lagLabel": "ラグ", + "visTypeTimeseries.serialDiff.metricLabel": "メトリック", + "visTypeTimeseries.series.missingAggregationKeyErrorMessage": "返答から集約キーが欠けています。このリクエストのパーミッションを確認してください。", + "visTypeTimeseries.series.shouldOneSeriesPerRequestErrorMessage": "1 つのリクエストに複数の数列を含めることはできません。", + "visTypeTimeseries.seriesAgg.aggregationLabel": "集約", + "visTypeTimeseries.seriesAgg.functionLabel": "関数", + "visTypeTimeseries.seriesAgg.functionOptions.avgLabel": "平均", + "visTypeTimeseries.seriesAgg.functionOptions.cumulativeSumLabel": "累積合計", + "visTypeTimeseries.seriesAgg.functionOptions.maxLabel": "最高", + "visTypeTimeseries.seriesAgg.functionOptions.minLabel": "最低", + "visTypeTimeseries.seriesAgg.functionOptions.overallAvgLabel": "全体平均", + "visTypeTimeseries.seriesAgg.functionOptions.overallMaxLabel": "全体最高", + "visTypeTimeseries.seriesAgg.functionOptions.overallMinLabel": "全体最低", + "visTypeTimeseries.seriesAgg.functionOptions.overallSumLabel": "全体合計", + "visTypeTimeseries.seriesAgg.functionOptions.sumLabel": "合計", + "visTypeTimeseries.seriesAgg.seriesAggIsNotCompatibleLabel": "数列集約は表の可視化に対応していません。", + "visTypeTimeseries.seriesConfig.filterLabel": "フィルター", + "visTypeTimeseries.seriesConfig.missingSeriesComponentDescription": "パネルタイプ {panelType} の数列コンポーネントが欠けています", + "visTypeTimeseries.seriesConfig.offsetSeriesTimeLabel": "数列の時間を (1m, 1h, 1w, 1d) でオフセット", + "visTypeTimeseries.seriesConfig.overrideIndexPatternLabel": "インデックスパターンを上書きしますか?", + "visTypeTimeseries.seriesConfig.templateHelpText": "例: {templateExample}", + "visTypeTimeseries.seriesConfig.templateLabel": "テンプレート", + "visTypeTimeseries.sort.dragToSortAriaLabel": "ドラッグして並べ替えます", + "visTypeTimeseries.sort.dragToSortTooltip": "ドラッグして並べ替えます", + "visTypeTimeseries.splits.everything.groupByLabel": "グループ分けの条件", + "visTypeTimeseries.splits.filter.groupByLabel": "グループ分けの条件", + "visTypeTimeseries.splits.filter.queryStringLabel": "クエリ文字列", + "visTypeTimeseries.splits.filterItems.labelAriaLabel": "ラベル", + "visTypeTimeseries.splits.filterItems.labelPlaceholder": "ラベル", + "visTypeTimeseries.splits.filters.groupByLabel": "グループ分けの条件", + "visTypeTimeseries.splits.groupBySelect.modeOptions.everythingLabel": "すべて", + "visTypeTimeseries.splits.groupBySelect.modeOptions.filterLabel": "フィルター", + "visTypeTimeseries.splits.groupBySelect.modeOptions.filtersLabel": "フィルター", + "visTypeTimeseries.splits.groupBySelect.modeOptions.termsLabel": "用語", + "visTypeTimeseries.splits.terms.byLabel": "グループ基準", + "visTypeTimeseries.splits.terms.defaultCountLabel": "ドキュメントカウント (デフォルト)", + "visTypeTimeseries.splits.terms.directionLabel": "方向", + "visTypeTimeseries.splits.terms.dirOptions.ascendingLabel": "昇順", + "visTypeTimeseries.splits.terms.dirOptions.descendingLabel": "降順", + "visTypeTimeseries.splits.terms.excludeLabel": "除外", + "visTypeTimeseries.splits.terms.groupByLabel": "グループ分けの条件", + "visTypeTimeseries.splits.terms.includeLabel": "含める", + "visTypeTimeseries.splits.terms.orderByLabel": "並び順", + "visTypeTimeseries.splits.terms.sizePlaceholder": "サイズ", + "visTypeTimeseries.splits.terms.termsLabel": "用語", + "visTypeTimeseries.splits.terms.topLabel": "一番上", + "visTypeTimeseries.static.aggregationLabel": "集約", + "visTypeTimeseries.static.staticValuesLabel": "不動値", + "visTypeTimeseries.stdAgg.aggregationLabel": "集約", + "visTypeTimeseries.stdAgg.fieldLabel": "フィールド", + "visTypeTimeseries.stdDeviation.aggregationLabel": "集約", + "visTypeTimeseries.stdDeviation.fieldLabel": "フィールド", + "visTypeTimeseries.stdDeviation.modeLabel": "モード", + "visTypeTimeseries.stdDeviation.modeOptions.boundsBandLabel": "境界バンド", + "visTypeTimeseries.stdDeviation.modeOptions.lowerBoundLabel": "下の境界", + "visTypeTimeseries.stdDeviation.modeOptions.rawLabel": "生", + "visTypeTimeseries.stdDeviation.modeOptions.upperBoundLabel": "上の境界", + "visTypeTimeseries.stdDeviation.sigmaLabel": "シグマ", + "visTypeTimeseries.stdSibling.aggregationLabel": "集約", + "visTypeTimeseries.stdSibling.metricLabel": "メトリック", + "visTypeTimeseries.stdSibling.modeLabel": "モード", + "visTypeTimeseries.stdSibling.modeOptions.boundsBandLabel": "境界バンド", + "visTypeTimeseries.stdSibling.modeOptions.lowerBoundLabel": "下の境界", + "visTypeTimeseries.stdSibling.modeOptions.rawLabel": "生", + "visTypeTimeseries.stdSibling.modeOptions.upperBoundLabel": "上の境界", + "visTypeTimeseries.stdSibling.sigmaLabel": "シグマ", + "visTypeTimeseries.table.addSeriesTooltip": "数列を追加", + "visTypeTimeseries.table.aggregateFunctionLabel": "集約関数", + "visTypeTimeseries.table.avgLabel": "平均", + "visTypeTimeseries.table.cloneSeriesTooltip": "数列のクローンを作成", + "visTypeTimeseries.table.colorRulesLabel": "カラールール", + "visTypeTimeseries.table.columnNotSortableTooltip": "この列は並べ替えできません", + "visTypeTimeseries.table.cumulativeSumLabel": "累積合計", + "visTypeTimeseries.table.dataTab.columnLabel": "列ラベル", + "visTypeTimeseries.table.dataTab.columnsButtonLabel": "フィールド", + "visTypeTimeseries.table.dataTab.defineFieldDescription": "表の可視化は、用語集約でグループ分けの基準となるフィールドを定義する必要があります。", + "visTypeTimeseries.table.dataTab.groupByFieldLabel": "フィールドでグループ分け", + "visTypeTimeseries.table.dataTab.rowsLabel": "行", + "visTypeTimeseries.table.deleteSeriesTooltip": "数列を削除", + "visTypeTimeseries.table.fieldLabel": "フィールド", + "visTypeTimeseries.table.filterLabel": "フィルター", + "visTypeTimeseries.table.labelAriaLabel": "ラベル", + "visTypeTimeseries.table.labelPlaceholder": "ラベル", + "visTypeTimeseries.table.maxLabel": "最高", + "visTypeTimeseries.table.minLabel": "最低", + "visTypeTimeseries.table.noResultsAvailableMessage": "結果がありません。", + "visTypeTimeseries.table.noResultsAvailableWithDescriptionMessage": "結果がありません。このビジュアライゼーションは、フィールドでグループを選択する必要があります。", + "visTypeTimeseries.table.optionsTab.dataLabel": "データ", + "visTypeTimeseries.table.optionsTab.ignoreGlobalFilterLabel": "グローバルフィルターを無視しますか?", + "visTypeTimeseries.table.optionsTab.itemUrlHelpText": "これは mustache テンプレートをサポートしています。{key} が用語に設定されています。", + "visTypeTimeseries.table.optionsTab.itemUrlLabel": "アイテム URL", + "visTypeTimeseries.table.optionsTab.panelFilterLabel": "パネルフィルター", + "visTypeTimeseries.table.optionsTab.panelOptionsButtonLabel": "パネルオプション", + "visTypeTimeseries.table.overallAvgLabel": "全体平均", + "visTypeTimeseries.table.overallMaxLabel": "全体最高", + "visTypeTimeseries.table.overallMinLabel": "全体最低", + "visTypeTimeseries.table.overallSumLabel": "全体合計", + "visTypeTimeseries.table.showTrendArrowsLabel": "傾向矢印を表示しますか?", + "visTypeTimeseries.table.sumLabel": "合計", + "visTypeTimeseries.table.tab.metricsLabel": "メトリック", + "visTypeTimeseries.table.tab.optionsLabel": "オプション", + "visTypeTimeseries.table.templateHelpText": "例: {templateExample}", + "visTypeTimeseries.table.templateLabel": "テンプレート", + "visTypeTimeseries.table.toggleSeriesEditorAriaLabel": "数列エディターを切り替える", + "visTypeTimeseries.timeSeries.addSeriesTooltip": "数列を追加", + "visTypeTimeseries.timeseries.annotationsTab.annotationsButtonLabel": "注釈", + "visTypeTimeseries.timeSeries.axisMaxLabel": "軸最大値", + "visTypeTimeseries.timeSeries.axisMinLabel": "軸最小値", + "visTypeTimeseries.timeSeries.axisPositionLabel": "軸の配置", + "visTypeTimeseries.timeSeries.barLabel": "バー", + "visTypeTimeseries.timeSeries.chartBar.chartTypeLabel": "チャートタイプ", + "visTypeTimeseries.timeSeries.chartBar.fillLabel": "塗りつぶし (0 から 1)", + "visTypeTimeseries.timeSeries.chartBar.lineWidthLabel": "線の幅", + "visTypeTimeseries.timeSeries.chartBar.stackedLabel": "スタック", + "visTypeTimeseries.timeSeries.chartLine.chartTypeLabel": "チャートタイプ", + "visTypeTimeseries.timeSeries.chartLine.fillLabel": "塗りつぶし (0 から 1)", + "visTypeTimeseries.timeSeries.chartLine.lineWidthLabel": "線の幅", + "visTypeTimeseries.timeSeries.chartLine.pointSizeLabel": "点のサイズ", + "visTypeTimeseries.timeSeries.chartLine.stackedLabel": "スタック", + "visTypeTimeseries.timeSeries.chartLine.stepsLabel": "ステップ", + "visTypeTimeseries.timeSeries.cloneSeriesTooltip": "数列のクローンを作成", + "visTypeTimeseries.timeseries.dataTab.dataButtonLabel": "データ", + "visTypeTimeseries.timeSeries.deleteSeriesTooltip": "数列を削除", + "visTypeTimeseries.timeSeries.filterLabel": "フィルター", + "visTypeTimeseries.timeSeries.gradientLabel": "グラデーション", + "visTypeTimeseries.timeSeries.hideInLegendLabel": "凡例で非表示", + "visTypeTimeseries.timeSeries.labelPlaceholder": "ラベル", + "visTypeTimeseries.timeSeries.leftLabel": "左", + "visTypeTimeseries.timeseries.legendPositionOptions.bottomLabel": "一番下", + "visTypeTimeseries.timeseries.legendPositionOptions.leftLabel": "左", + "visTypeTimeseries.timeseries.legendPositionOptions.rightLabel": "右", + "visTypeTimeseries.timeSeries.lineLabel": "折れ線", + "visTypeTimeseries.timeSeries.noneLabel": "なし", + "visTypeTimeseries.timeSeries.offsetSeriesTimeLabel": "数列の時間を (1m, 1h, 1w, 1d) でオフセット", + "visTypeTimeseries.timeseries.optionsTab.axisMaxLabel": "軸最大値", + "visTypeTimeseries.timeseries.optionsTab.axisMinLabel": "軸最小値", + "visTypeTimeseries.timeseries.optionsTab.axisPositionLabel": "軸の配置", + "visTypeTimeseries.timeseries.optionsTab.axisScaleLabel": "軸のスケール", + "visTypeTimeseries.timeseries.optionsTab.backgroundColorLabel": "背景色:", + "visTypeTimeseries.timeseries.optionsTab.dataLabel": "データ", + "visTypeTimeseries.timeseries.optionsTab.displayGridLabel": "グリッドを表示", + "visTypeTimeseries.timeseries.optionsTab.ignoreGlobalFilterLabel": "グローバルフィルターを無視しますか?", + "visTypeTimeseries.timeseries.optionsTab.legendPositionLabel": "凡例の配置", + "visTypeTimeseries.timeseries.optionsTab.panelFilterLabel": "パネルフィルター", + "visTypeTimeseries.timeseries.optionsTab.panelOptionsButtonLabel": "パネルオプション", + "visTypeTimeseries.timeseries.optionsTab.showLegendLabel": "凡例を表示しますか?", + "visTypeTimeseries.timeseries.optionsTab.styleLabel": "スタイル", + "visTypeTimeseries.timeSeries.overrideIndexPatternLabel": "インデックスパターンを上書きしますか?", + "visTypeTimeseries.timeSeries.percentLabel": "パーセント", + "visTypeTimeseries.timeseries.positionOptions.leftLabel": "左", + "visTypeTimeseries.timeseries.positionOptions.rightLabel": "右", + "visTypeTimeseries.timeSeries.rainbowLabel": "虹", + "visTypeTimeseries.timeSeries.rightLabel": "右", + "visTypeTimeseries.timeseries.scaleOptions.logLabel": "ログ", + "visTypeTimeseries.timeseries.scaleOptions.normalLabel": "標準", + "visTypeTimeseries.timeSeries.separateAxisLabel": "軸を分けますか?", + "visTypeTimeseries.timeSeries.splitColorThemeLabel": "カラーテーマを分割", + "visTypeTimeseries.timeSeries.stackedLabel": "スタック", + "visTypeTimeseries.timeSeries.stackedWithinSeriesLabel": "数列内でスタック", + "visTypeTimeseries.timeSeries.tab.metricsLabel": "メトリック", + "visTypeTimeseries.timeSeries.tab.optionsLabel": "オプション", + "visTypeTimeseries.timeSeries.templateHelpText": "例: {templateExample}", + "visTypeTimeseries.timeSeries.templateLabel": "テンプレート", + "visTypeTimeseries.timeSeries.toggleSeriesEditorAriaLabel": "数列エディターを切り替える", + "visTypeTimeseries.topHit.aggregateWith.selectPlaceholder": "選択してください…", + "visTypeTimeseries.topHit.aggregateWithLabel": "アグリゲーション:", + "visTypeTimeseries.topHit.aggregationLabel": "集約", + "visTypeTimeseries.topHit.aggWithOptions.averageLabel": "平均", + "visTypeTimeseries.topHit.aggWithOptions.concatenate": "連結", + "visTypeTimeseries.topHit.aggWithOptions.maxLabel": "最高", + "visTypeTimeseries.topHit.aggWithOptions.minLabel": "最低", + "visTypeTimeseries.topHit.aggWithOptions.sumLabel": "合計", + "visTypeTimeseries.topHit.fieldLabel": "フィールド", + "visTypeTimeseries.topHit.order.selectPlaceholder": "選択してください…", + "visTypeTimeseries.topHit.orderByLabel": "並び順", + "visTypeTimeseries.topHit.orderLabel": "順序", + "visTypeTimeseries.topHit.orderOptions.ascLabel": "昇順", + "visTypeTimeseries.topHit.orderOptions.descLabel": "降順", + "visTypeTimeseries.topHit.sizeLabel": "サイズ", + "visTypeTimeseries.topN.addSeriesTooltip": "数列を追加", + "visTypeTimeseries.topN.cloneSeriesTooltip": "数列のクローンを作成", + "visTypeTimeseries.topN.dataTab.dataButtonLabel": "データ", + "visTypeTimeseries.topN.deleteSeriesTooltip": "数列を削除", + "visTypeTimeseries.topN.labelPlaceholder": "ラベル", + "visTypeTimeseries.topN.optionsTab.backgroundColorLabel": "背景色:", + "visTypeTimeseries.topN.optionsTab.colorRulesLabel": "カラールール", + "visTypeTimeseries.topN.optionsTab.dataLabel": "データ", + "visTypeTimeseries.topN.optionsTab.ignoreGlobalFilterLabel": "グローバルフィルターを無視しますか?", + "visTypeTimeseries.topN.optionsTab.itemUrlDescription": "これは mustache テンプレートをサポートしています。{key} が用語に設定されています。", + "visTypeTimeseries.topN.optionsTab.itemUrlLabel": "アイテム URL", + "visTypeTimeseries.topN.optionsTab.panelFilterLabel": "パネルフィルター", + "visTypeTimeseries.topN.optionsTab.panelOptionsButtonLabel": "パネルオプション", + "visTypeTimeseries.topN.optionsTab.styleLabel": "スタイル", + "visTypeTimeseries.topN.tab.metricsLabel": "メトリック", + "visTypeTimeseries.topN.tab.optionsLabel": "オプション", + "visTypeTimeseries.topN.toggleSeriesEditorAriaLabel": "数列エディターを切り替える", + "visTypeTimeseries.unsupportedAgg.aggIsNotSupportedDescription": "{modelType} 集約はサポートされなくなりました。", + "visTypeTimeseries.unsupportedAgg.aggIsTemporaryUnsupportedDescription": "{modelType} 集約は現在サポートされていません。", + "visTypeTimeseries.unsupportedSplit.splitIsUnsupportedDescription": "{modelType} による分割はサポートされていません。", + "visTypeTimeseries.validateInterval.notifier.maxBucketsExceededErrorMessage": "バケットの最高数を超えました。{buckets} が {maxBuckets} を超えています。パネルオプションでより広い間隔を試してみてください。", + "visTypeTimeseries.vars.variableNameAriaLabel": "変数名", + "visTypeTimeseries.vars.variableNamePlaceholder": "変数名", + "visTypeTimeseries.visEditorVisualization.applyChangesLabel": "変更を適用", + "visTypeTimeseries.visEditorVisualization.autoApplyLabel": "自動適用", + "visTypeTimeseries.visEditorVisualization.changesHaveNotBeenAppliedMessage": "ビジュアライゼーションへの変更が適用されました。", + "visTypeTimeseries.visEditorVisualization.changesSuccessfullyAppliedMessage": "最新の変更が適用されました。", + "visTypeTimeseries.visEditorVisualization.changesWillBeAutomaticallyAppliedMessage": "変更が自動的に適用されます。", + "visTypeTimeseries.visEditorVisualization.panelInterval": "間隔:{panelInterval}", + "visTypeTimeseries.visPicker.gaugeLabel": "ゲージ", + "visTypeTimeseries.visPicker.metricLabel": "メトリック", + "visTypeTimeseries.visPicker.tableLabel": "表", + "visTypeTimeseries.visPicker.timeSeriesLabel": "時系列", + "visTypeTimeseries.visPicker.topNLabel": "トップ N", + "visTypeTimeseries.yesButtonLabel": "はい", + "xpack.alerting.alertsClient.validateActions.invalidGroups": "無効なアクショングループ: {groups}", + "xpack.features.advancedSettingsFeatureName": "高度な設定", + "xpack.features.dashboardFeatureName": "ダッシュボード", + "xpack.features.devToolsFeatureName": "開発ツール", + "xpack.features.devToolsPrivilegesTooltip": "また、ユーザーに適切な Elasticsearch クラスターとインデックスの権限が与えられている必要があります。", + "xpack.features.discoverFeatureName": "ディスカバー", + "xpack.features.indexPatternFeatureName": "インデックスパターン管理", + "xpack.features.savedObjectsManagementFeatureName": "保存されたオブジェクトの管理", + "xpack.features.visualizeFeatureName": "可視化", + "xpack.lens.app.docLoadingError": "保存されたドキュメントの保存中にエラーが発生", + "xpack.lens.app.docSavingError": "ドキュメントの保存中にエラーが発生", + "xpack.lens.app.indexPatternLoadingError": "インデックスパターンの読み込み中にエラーが発生", + "xpack.lens.app.save": "保存", + "xpack.lens.app.saveModalType": "レンズビジュアライゼーション", + "xpack.lens.app404": "404 Not Found", + "xpack.lens.breadcrumbsCreate": "作成", + "xpack.lens.breadcrumbsTitle": "可視化", + "xpack.lens.chartSwitch.dataLossDescription": "このチャートに切り替えると構成の一部が失われます", + "xpack.lens.chartSwitch.dataLossLabel": "データ喪失", + "xpack.lens.configure.addConfig": "構成を追加", + "xpack.lens.configure.editConfig": "構成の編集", + "xpack.lens.configure.emptyConfig": "ここにフィールドをドロップ", + "xpack.lens.dataPanelWrapper.switchDatasource": "データソースに切り替える", + "xpack.lens.datatable.columns": "フィールド", + "xpack.lens.datatable.conjunctionSign": " と ", + "xpack.lens.datatable.expressionHelpLabel": "データベースレンダー", + "xpack.lens.datatable.label": "データテーブル", + "xpack.lens.datatable.suggestionLabel": "表として", + "xpack.lens.datatable.titleLabel": "タイトル", + "xpack.lens.datatable.visualizationName": "データベース", + "xpack.lens.datatable.visualizationOf": "表 {operations}", + "xpack.lens.datatypes.boolean": "ブール", + "xpack.lens.datatypes.date": "日付", + "xpack.lens.datatypes.ipAddress": "IP", + "xpack.lens.datatypes.number": "数字", + "xpack.lens.datatypes.string": "文字列", + "xpack.lens.editorFrame.emptyWorkspace": "開始するにはここにフィールドをドロップしてください", + "xpack.lens.editorFrame.emptyWorkspaceHeading": "レンズはビジュアライゼーションを作成するための新しいツールです", + "xpack.lens.editorFrame.expressionFailure": "表現を正常に実行できませんでした", + "xpack.lens.editorFrame.goToForums": "リクエストとフィードバック", + "xpack.lens.editorFrame.previewErrorLabel": "レンダリングのプレビューに失敗しました", + "xpack.lens.editorFrame.suggestionPanelTitle": "提案", + "xpack.lens.editorFrame.tooltipContent": "レンズはベータ段階で、変更される可能性があります。 デザインとコードはオフィシャル GA 機能よりも完成度が低く、現状のまま保証なしで提供されています。ベータ機能にはオフィシャル GA 機能の SLA が適用されません", + "xpack.lens.embeddable.failure": "ビジュアライゼーションを表示できませんでした", + "xpack.lens.embeddableDisplayName": "レンズ", + "xpack.lens.functions.mergeTables.help": "いくつかの Kibana 表を 1 つの表に結合するのをアシストします", + "xpack.lens.functions.renameColumns.help": "データベースの列の名前の変更をアシストします", + "xpack.lens.functions.renameColumns.idMap.help": "キーが古い列 ID で値が対応する新しい列 ID となるように JSON エンコーディングされたオブジェクトです。他の列 ID はすべてのそのままです。", + "xpack.lens.helpMenu.docLabel": "レンズドキュメンテーション", + "xpack.lens.helpMenu.feedbackLinkText": "レンズアプリケーションに関するフィードバックを提供", + "xpack.lens.indexPattern.avg": "平均", + "xpack.lens.indexPattern.avgOf": "{name} の平均", + "xpack.lens.indexPattern.cardinality": "ユニークカウント", + "xpack.lens.indexPattern.cardinalityOf": "{name} のユニークカウント", + "xpack.lens.indexPattern.columnLabel": "ラベル", + "xpack.lens.indexPattern.count": "カウント", + "xpack.lens.indexPattern.countOf": "ドキュメント数", + "xpack.lens.indexPattern.dateHistogram": "Date histogram", + "xpack.lens.indexPattern.dateHistogram.autoInterval": "時間範囲のカスタマイズ", + "xpack.lens.indexPattern.dateHistogram.restrictedInterval": "集約の制限により間隔は {intervalValue} に固定されています。", + "xpack.lens.indexPattern.fieldDistributionLabel": "分布", + "xpack.lens.indexPattern.fieldlessOperationLabel": "この関数を使用するには、フィールドを選択してください。", + "xpack.lens.indexPattern.fieldPanelEmptyStringValue": "空の文字列", + "xpack.lens.indexPattern.fieldPlaceholder": "フィールド", + "xpack.lens.indexPattern.fieldStatsCountLabel": "カウント", + "xpack.lens.indexPattern.fieldStatsDisplayToggle": "次のどちらかを切り替えます:", + "xpack.lens.indexPattern.fieldStatsNoData": "表示するデータがありません", + "xpack.lens.indexPattern.fieldTimeDistributionLabel": "時間分布", + "xpack.lens.indexPattern.fieldTopValuesLabel": "トップの値", + "xpack.lens.indexPattern.groupByDropdown": "グループ分けの条件", + "xpack.lens.indexPattern.groupingControlLabel": "グループ分け", + "xpack.lens.indexPattern.groupingOverallDateHistogram": "全体の日付", + "xpack.lens.indexPattern.groupingOverallTerms": "全体のトップ {field}", + "xpack.lens.indexPattern.groupingSecondDateHistogram": "各 {target} の日付", + "xpack.lens.indexPattern.groupingSecondTerms": "各 {target} のトップの値", + "xpack.lens.indexPattern.indexPatternLoadError": "インデックスパターンの読み込み中にエラーが発生", + "xpack.lens.indexPattern.individualFieldsLabel": "個々のフィールド", + "xpack.lens.indexPattern.invalidOperationLabel": "この関数を使用するには、別のフィールドを選択してください。", + "xpack.lens.indexPattern.max": "最高", + "xpack.lens.indexPattern.maxOf": "{name} お最高値", + "xpack.lens.indexPattern.min": "最低", + "xpack.lens.indexPattern.minOf": "{name} お最低値", + "xpack.lens.indexPattern.noPatternsDescription": "インデックスパターンを作成するか、別のデータソースに切り替えてください", + "xpack.lens.indexPattern.noPatternsLabel": "インデックスパターンがありません", + "xpack.lens.indexPattern.ofDocumentsLabel": "ドキュメント", + "xpack.lens.indexPattern.otherDocsLabel": "その他", + "xpack.lens.indexPattern.percentageOfLabel": "の {percentage}%", + "xpack.lens.indexPattern.removeColumnLabel": "構成を削除", + "xpack.lens.indexpattern.suggestions.nestingChangeLabel": "各 {outerOperation} の {innerOperation}", + "xpack.lens.indexpattern.suggestions.overallLabel": "全体の {operation}", + "xpack.lens.indexpattern.suggestions.overTimeLabel": "一定時間", + "xpack.lens.indexPattern.sum": "合計", + "xpack.lens.indexPattern.sumOf": "{name} の合計", + "xpack.lens.indexPattern.terms": "トップの値", + "xpack.lens.indexPattern.terms.orderAlphabetical": "アルファベット順", + "xpack.lens.indexPattern.terms.orderAscending": "昇順", + "xpack.lens.indexPattern.terms.orderBy": "並び順", + "xpack.lens.indexPattern.terms.orderDescending": "降順", + "xpack.lens.indexPattern.terms.orderDirection": "全体的な方向", + "xpack.lens.indexPattern.terms.size": "値の数", + "xpack.lens.indexPattern.termsOf": "{name} のトップの値", + "xpack.lens.indexPattern.uniqueLabel": "{label} [{num}]", + "xpack.lens.indexPatterns.clearFiltersLabel": "名前とタイプフィルターを消去", + "xpack.lens.indexPatterns.filterByNameAriaLabel": "検索フィールド", + "xpack.lens.indexPatterns.filterByNameLabel": "フィールドを検索", + "xpack.lens.indexPatterns.filterByTypeLabel": "タイプでフィルタリング", + "xpack.lens.indexPatterns.toggleEmptyFieldsSwitch": "データがあるフィールドだけを表示", + "xpack.lens.indexPatterns.toggleFiltersPopover": "フィルタリングされたフィールド", + "xpack.lens.lensSavedObjectLabel": "レンズビジュアライゼーション", + "xpack.lens.metric.label": "メトリック", + "xpack.lens.metric.valueLabel": "値", + "xpack.lens.suggestions.currentVisLabel": "現在", + "xpack.lens.visTypeAlias.description": "レンズは基本的なビジュアライゼーションを作成するシンプルな方法です", + "xpack.lens.visTypeAlias.promotion.buttonText": "レンズに移動", + "xpack.lens.visTypeAlias.promotion.description": "レンズは直感的に使える新しいビジュアライゼーションの作成方法です。お試しください。", + "xpack.lens.visTypeAlias.title": "レンズビジュアライゼーション", + "xpack.lens.visTypeAlias.type": "レンズ", + "xpack.lens.xyChart.addLayerButton": "レイヤーを追加", + "xpack.lens.xyChart.chartTypeLabel": "チャートタイプ", + "xpack.lens.xyChart.chartTypeLegend": "チャートタイプ", + "xpack.lens.xyChart.deleteLayer": "レイヤーを削除", + "xpack.lens.xyChart.help": "X/Y チャート", + "xpack.lens.xyChart.isVisible.help": "判例の表示・非表示を指定します。", + "xpack.lens.xyChart.layerSettings": "レイヤー設定を編集", + "xpack.lens.xyChart.legend.help": "チャートの凡例を構成します。", + "xpack.lens.xyChart.nestUnderRoot": "データセット全体", + "xpack.lens.xyChart.position.help": "凡例の配置を指定します。", + "xpack.lens.xyChart.renderer.help": "X/Y チャートを再レンダリング", + "xpack.lens.xyChart.splitSeries": "系列を分割", + "xpack.lens.xyChart.title.help": "軸のタイトル", + "xpack.lens.xyChart.xAxisLabel": "X 軸", + "xpack.lens.xyChart.yAxisLabel": "Y 軸", + "xpack.lens.xySuggestions.barChartTitle": "棒グラフ", + "xpack.lens.xySuggestions.dateSuggestion": "{xTitle} の {yTitle}", + "xpack.lens.xySuggestions.flipTitle": "反転", + "xpack.lens.xySuggestions.lineChartTitle": "折れ線グラフ", + "xpack.lens.xySuggestions.nonDateSuggestion": "{xTitle} の {yTitle}", + "xpack.lens.xySuggestions.stackedChartTitle": "スタック", + "xpack.lens.xySuggestions.unstackedChartTitle": "スタックが解除されました", + "xpack.lens.xySuggestions.yAxixConjunctionSign": " と ", + "xpack.lens.xyVisualization.areaLabel": "エリア", + "xpack.lens.xyVisualization.barHorizontalLabel": "横棒", + "xpack.lens.xyVisualization.barLabel": "バー", + "xpack.lens.xyVisualization.chartLabel": "{label} チャート", + "xpack.lens.xyVisualization.lineLabel": "折れ線", + "xpack.lens.xyVisualization.mixedBarHorizontalLabel": "ミックスされた横棒", + "xpack.lens.xyVisualization.mixedLabel": "ミックスされた XY", + "xpack.lens.xyVisualization.noDataLabel": "結果が見つかりませんでした", + "xpack.lens.xyVisualization.stackedAreaLabel": "スタックされたエリア", + "xpack.lens.xyVisualization.stackedBarHorizontalLabel": "スタックされた横棒", + "xpack.lens.xyVisualization.stackedBarLabel": "スタックされたバー", + "xpack.lens.xyVisualization.xyLabel": "XY" } } \ No newline at end of file diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index fefa30b39b392..d542e66b247ec 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -282,7 +282,7 @@ "common.ui.directives.fieldNameIcons.booleanAriaLabel": "布尔字段", "common.ui.directives.fieldNameIcons.conflictFieldAriaLabel": "冲突字段", "common.ui.directives.fieldNameIcons.dateFieldAriaLabel": "日期字段", - "common.ui.directives.fieldNameIcons.geoPointFieldAriaLabel": "地理位置点", + "common.ui.directives.fieldNameIcons.geoPointFieldAriaLabel": "地理位置点字段", "common.ui.directives.fieldNameIcons.ipAddressFieldAriaLabel": "IP 地址字段", "common.ui.directives.fieldNameIcons.murmur3FieldAriaLabel": "Murmur3 字段", "common.ui.directives.fieldNameIcons.numberFieldAriaLabel": "数字字段", @@ -574,9 +574,14 @@ "common.ui.courier.fetch.shardsFailedModal.tableRowCollapse": "折叠 {rowDescription}", "common.ui.courier.fetch.shardsFailedModal.tableRowExpand": "展开 {rowDescription}", "common.ui.courier.fetch.shardsFailedNotificationDescription": "您正在查看的数据可能不完整或有错误。", - "common.ui.directives.fieldNameIcons.geoShapeFieldAriaLabel": "几何形状", + "common.ui.directives.fieldNameIcons.geoShapeFieldAriaLabel": "几何形状字段", "common.ui.vis.editors.agg.errorsAriaLabel": "聚合有错误", "common.ui.vislib.heatmap.maxBucketsText": "定义了过多的序列 ({nr})。配置的最大值为 {max}。", + "common.ui.aggTypes.ranges.greaterThanOrEqualPrepend": "≥", + "common.ui.aggTypes.ranges.lessThanPrepend": "<", + "common.ui.aggTypes.scaleMetricsLabel": "缩放指标值(已弃用)", + "common.ui.aggTypes.scaleMetricsTooltip": "如果选择手动最小时间间隔并将使用较大的时间间隔,则启用此设置将使计数和求和指标缩放到手动选择的时间间隔。", + "common.ui.aggTypes.buckets.ranges.rangesFormatMessage": "{gte} {from} 且 {lt} {to}", "core.ui.overlays.banner.attentionTitle": "注意", "core.ui.overlays.banner.closeButtonLabel": "关闭", "core.ui.chrome.headerGlobalNav.goHomePageIconAriaLabel": "前往主页", @@ -656,6 +661,8 @@ "core.euiSuperUpdateButton.refreshButtonLabel": "刷新", "core.euiSuperUpdateButton.updateButtonLabel": "更新", "core.euiSuperUpdateButton.updatingButtonLabel": "正在更新", + "core.application.appNotFound.pageDescription": "在此 URL 未找到任何应用程序。尝试返回或从菜单中选择应用。", + "core.application.appNotFound.title": "未找到应用程序", "kibana-react.exitFullScreenButton.exitFullScreenModeButtonAreaLabel": "退出全屏模式", "kibana-react.exitFullScreenButton.exitFullScreenModeButtonLabel": "退出全屏", "kibana-react.exitFullScreenButton.fullScreenModeDescription": "在全屏模式下,按 ESC 键可退出。", @@ -673,6 +680,20 @@ "kibana-react.tableListView.listing.table.editActionDescription": "编辑", "kibana-react.tableListView.listing.table.editActionName": "编辑", "kibana-react.tableListView.listing.unableToDeleteDangerMessage": "无法删除{entityName}", + "kibana-react.savedObjects.finder.filterButtonLabel": "类型", + "kibana-react.savedObjects.finder.searchPlaceholder": "搜索……", + "kibana-react.savedObjects.finder.sortAsc": "升序", + "kibana-react.savedObjects.finder.sortAuto": "最佳匹配", + "kibana-react.savedObjects.finder.sortButtonLabel": "排序", + "kibana-react.savedObjects.finder.sortDesc": "降序", + "kibana-react.savedObjects.saveModal.cancelButtonLabel": "取消", + "kibana-react.savedObjects.saveModal.descriptionLabel": "描述", + "kibana-react.savedObjects.saveModal.duplicateTitleDescription": "单击“{confirmSaveLabel}”可覆盖现有 {objectType}。", + "kibana-react.savedObjects.saveModal.duplicateTitleLabel": "具有标题“{title}”的 {objectType} 已存在", + "kibana-react.savedObjects.saveModal.saveAsNewLabel": "另存为新的 {objectType}", + "kibana-react.savedObjects.saveModal.saveButtonLabel": "保存", + "kibana-react.savedObjects.saveModal.saveTitle": "保存 {objectType}", + "kibana-react.savedObjects.saveModal.titleLabel": "标题", "inspector.closeButton": "关闭检查器", "inspector.reqTimestampDescription": "记录请求启动的时间", "inspector.reqTimestampKey": "请求时间戳", @@ -766,6 +787,7 @@ "console.welcomePage.quickTips.useWrenchMenuDescription": "使用扳手菜单执行其他有用的操作。", "console.welcomePage.quickTipsTitle": "有几个需要您注意的有用提示", "console.welcomePage.supportedRequestFormatTitle": "Console 理解紧凑格式的请求,类似于 cURL:", + "console.consoleMenu.copyAsCurlMessage": "请求已复制为 cURL", "data.filter.applyFilters.popupHeader": "选择要应用的筛选", "data.filter.applyFiltersPopup.cancelButtonLabel": "取消", "data.filter.applyFiltersPopup.saveButtonLabel": "应用", @@ -795,7 +817,7 @@ "data.filter.filterEditor.existsOperatorOptionLabel": "存在", "data.filter.filterEditor.falseOptionLabel": "false", "data.filter.filterEditor.fieldSelectLabel": "字段", - "data.filter.filterEditor.fieldSelectPlaceholder": "选择字段", + "data.filter.filterEditor.fieldSelectPlaceholder": "首先选择字段", "data.filter.filterEditor.indexPatternSelectLabel": "索引模式", "data.filter.filterEditor.isBetweenOperatorOptionLabel": "介于", "data.filter.filterEditor.isNotBetweenOperatorOptionLabel": "不介于", @@ -848,7 +870,7 @@ "data.search.searchBar.savedQueryFormTitle": "保存查询", "data.search.searchBar.savedQueryIncludeFiltersLabelText": "包括筛选", "data.search.searchBar.savedQueryIncludeTimeFilterLabelText": "包括时间筛选", - "data.search.searchBar.savedQueryNameHelpText": "名称不能包含前导或尾随空格。名称必须唯一。", + "data.search.searchBar.savedQueryNameHelpText": "“名称”必填。标题不能包含前导或尾随空格。名称必须唯一。", "data.search.searchBar.savedQueryNameLabelText": "名称", "data.search.searchBar.savedQueryNoSavedQueriesText": "没有已保存查询。", "data.search.searchBar.savedQueryPopoverButtonText": "查看已保存查询", @@ -867,6 +889,13 @@ "data.search.searchBar.savedQueryPopoverSavedQueryListItemDescriptionAriaLabel": "{savedQueryName} 描述", "data.search.searchBar.savedQueryPopoverSavedQueryListItemSelectedButtonAriaLabel": "已保存查询按钮已选择 {savedQueryName}。按下可清除任何更改。", "data.search.searchBar.savedQueryPopoverTitleText": "已保存查询", + "data.filter.filterEditor.operatorSelectPlaceholderSelect": "选择", + "data.filter.filterEditor.operatorSelectPlaceholderWaiting": "正在等候", + "data.filter.filterEditor.rangeInputLabel": "范围", + "data.query.queryBar.comboboxAriaLabel": "搜索并筛选 {pageType} 页面", + "data.search.searchBar.savedQueryForm.titleMissingText": "“名称”必填", + "data.query.queryBar.searchInputAriaLabel": "开始键入内容,以搜索并筛选 {pageType} 页面", + "data.search.searchBar.savedQueryPopoverSaveAsNewButtonAriaLabel": "另存为新的已保存查询", "embeddableApi.actions.applyFilterActionTitle": "将筛选应用于当前视图", "embeddableApi.addPanel.createNewDefaultOption": "创建新的......", "embeddableApi.addPanel.displayName": "添加面板", @@ -892,6 +921,9 @@ "embeddableApi.samples.filterableContainer.displayName": "可筛选仪表板", "embeddableApi.samples.filterableEmbeddable.displayName": "可筛选", "embeddableApi.samples.helloworld.displayName": "hello world", + "embeddableApi.panel.enhancedDashboardPanelAriaLabel": "仪表板面板:{title}", + "embeddableApi.panel.optionsMenu.panelOptionsButtonEnhancedAriaLabel": "{title} 的面板选项", + "embeddableApi.panel.dashboardPanelAriaLabel": "仪表板面板", "inputControl.control.noIndexPatternTooltip": "找不到索引模式 ID:{indexPatternId}。", "inputControl.control.notInitializedTooltip": "尚未初始化控件", "inputControl.control.noValuesDisableTooltip": "按 “{fieldName}” 字段进行了筛选,但 “{indexPatternName}” 索引模式的任何文档中都不存在该字段。选择不同的字段或索引包含此字段的值的文档。", @@ -940,6 +972,8 @@ "visualizations.function.visDimension.error.accessor": "提供的列名称无效", "visualizations.function.visDimension.help": "生成 visConfig 维度对象", "interpreter.functions.esaggs.help": "运行 AggConfig 聚合", + "interpreter.functions.esaggs.inspector.dataRequest.description": "此请求将查询 Elasticsearch 以获取用于可视化的数据。", + "interpreter.functions.esaggs.inspector.dataRequest.title": "数据", "expressions_np.functions.kibana_context.help": "更新 kibana 全局上下文", "expressions_np.functions.kibana.help": "获取 kibana 全局上下文", "expressions_np.functions.font.args.alignHelpText": "水平文本对齐。", @@ -1085,12 +1119,12 @@ "kbn.advancedSettings.timepicker.last90Days": "过去 90 天", "kbn.advancedSettings.timepicker.quickRanges.acceptedFormatsLinkText": "接受的格式", "kbn.advancedSettings.timepicker.quickRangesText": "要在时间选取器的“速选”部分中显示的范围列表。这应该是对象数组,每个对象包含“from”、“to”(请参阅“{acceptedFormatsLink}”)和“display”(要显示的标题)。", - "kbn.advancedSettings.timepicker.quickRangesTitle": "时间选取器的速选范围", + "kbn.advancedSettings.timepicker.quickRangesTitle": "时间筛选速选范围", "kbn.advancedSettings.timepicker.refreshIntervalDefaultsText": "时间筛选的默认刷新时间间隔", - "kbn.advancedSettings.timepicker.refreshIntervalDefaultsTitle": "时间选取器刷新时间间隔", + "kbn.advancedSettings.timepicker.refreshIntervalDefaultsTitle": "时间筛选刷新时间间隔", "kbn.advancedSettings.timepicker.thisWeek": "本周", "kbn.advancedSettings.timepicker.timeDefaultsText": "未使用时间筛选启动 Kibana 时要使用的时间筛选选择", - "kbn.advancedSettings.timepicker.timeDefaultsTitle": "时间选取器默认值", + "kbn.advancedSettings.timepicker.timeDefaultsTitle": "时间筛选默认值", "kbn.advancedSettings.timepicker.today": "今日", "kbn.advancedSettings.visualization.colorMappingText": "将值映射到可视化内的指定颜色", "kbn.advancedSettings.visualization.colorMappingTitle": "颜色映射", @@ -2467,6 +2501,38 @@ "kbn.discover.reloadSavedSearchButton": "重置搜索", "kbn.server.tutorials.apm.dotNetClient.download.textPre": "将代理软件包从 [NuGet]({allNuGetPackagesLink}) 添加到 .NET 应用程序。有多个 NuGet 软件包可用于不同的用例。\n\n对于具有 Entity Framework Core 的 ASP.NET Core 应用程序,请下载 [Elastic.Apm.NetCoreAll]({netCoreAllApmPackageLink}) 软件包。此软件包将自动将每个 代理组件添加到您的应用程序。\n\n 如果您希望最大程度减少依存关系,您可以将 [Elastic.Apm.AspNetCore]({aspNetCorePackageLink}) 软件包仅用于 ASP.NET Core 监测,或将 [Elastic.Apm.EfCore]({efCorePackageLink}) 软件包仅用于 Entity Framework Core 监测。\n\n 如果仅希望将公共代理 API 用于手动检测,请使用 [Elastic.Apm]({elasticApmPackageLink}) 软件包。", "kbn.server.tutorials.apm.elasticCloud.textPre": "要启用 APM Server,请前往 [Elastic Cloud 控制台](https://cloud.elastic.co/deployments?q={cloudId}) 并在部署设置中启用 APM。启用后,请刷新此页面。", + "kbn.advancedSettings.defaultRoute.defaultRouteText": "此设置指定打开 Kibana 时的默认路由。您可以使用此设置修改打开 Kibana 时的登陆页面。路由必须以正斜杠(“/”)开头。", + "kbn.advancedSettings.defaultRoute.defaultRouteTitle": "默认路由", + "kbn.advancedSettings.defaultRoute.defaultRouteValidationMessage": "路由必须以正斜杠(“/”)开头", + "kbn.context.loadButtonLabel": "加载", + "kbn.context.newerDocumentsAriaLabel": "较新文档数目", + "kbn.context.newerDocumentsWarning": "仅可以找到 {docCount} 个比定位标记新的文档。", + "kbn.context.newerDocumentsWarningZero": "找不到比定位标记新的文档。", + "kbn.context.olderDocumentsAriaLabel": "较旧文档数目", + "kbn.context.olderDocumentsWarning": "仅可以找到 {docCount} 个比定位标记旧的文档。", + "kbn.context.olderDocumentsWarningZero": "找不到比定位标记旧的文档。", + "kbn.discover.fieldChooser.fieldFilterFacetButtonLabel": "已筛选字段", + "kbn.discover.fieldChooser.indexPattern.changeLinkLabel": "更改", + "kbn.discover.fieldChooser.indexPattern.changeLinkTooltip": "更改当前索引模式", + "kbn.discover.fieldChooser.searchPlaceHolder": "搜索字段", + "kbn.discover.histogram.partialData.bucketTooltipText": "选定的时间范围不包括此整个存储桶,其可能包含部分数据。", + "kbn.doc.failedToLocateIndexPattern": "无索引模式匹配 ID {indexPatternId}", + "kbn.doc.somethingWentWrongDescriptionAddon": "请确保索引存在。", + "kbn.management.objects.objectsTable.export.successWithMissingRefsNotification": "您的文件正在后台下载。找不到某些相关对象。有关缺失对象列表,请查看导出文件的最后一行。", + "kbn.management.settings.categoryNames.siemLabel": "SIEM", + "kbn.management.settings.field.imageTooLargeErrorMessage": "图像过大,最大大小为 {maxSizeDescription}", + "kbn.server.tutorials.couchdbMetrics.artifacts.dashboards.linkLabel": "CouchDB 指标仪表板", + "kbn.server.tutorials.couchdbMetrics.longDescription": "`couchdb` Metricbeat 模块从 CouchDB 提取监测指标。[了解详情]({learnMoreLink})。", + "kbn.server.tutorials.couchdbMetrics.nameTitle": "CouchDB 指标", + "kbn.server.tutorials.couchdbMetrics.shortDescription": "从 CouchdB 服务器提取监测指标。", + "kbn.visualize.listing.betaTitle": "公测版", + "kbn.visualize.listing.betaTooltip": "此可视化为公测版,可能会进行更改。设计和代码相对于正式发行版功能还不够成熟,将按原样提供,且不提供任何保证。公测版功能不受正式发行版功能支持 SLA 的约束", + "kbn.visualize.newVisWizard.betaDescription": "此可视化为公测版,可能会进行更改。设计和代码相对于正式发行版功能还不够成熟,将按原样提供,且不提供任何保证。公测版功能不受正式发行版功能支持 SLA 的约束", + "kbn.visualize.newVisWizard.betaTitle": "公测版", + "kbn.visualize.pageHeading": "{chartName} {chartType}可视化", + "kbn.advancedSettings.courier.batchSearchesText": "禁用时,仪表板面板将分别加载,用户离开时或更新查询时,\n 搜索请求将终止。启用时,仪表板面板将一起加载并加载所有数据,\n 搜索将不会终止。", + "kbn.doc.couldNotFindDocumentsDescription": "无文档匹配该 ID。", + "kbn.doc.somethingWentWrongDescription": "{indexName} 缺失。", "kbnDocViews.table.fieldNamesBeginningWithUnderscoreUnsupportedTooltip": "不支持以 {underscoreSign} 开头的字段名称", "kbnDocViews.table.filterForFieldPresentButtonAriaLabel": "筛留存在的字段", "kbnDocViews.table.filterForFieldPresentButtonTooltip": "筛留存在的字段", @@ -2528,7 +2594,7 @@ "kbnVislibVisTypes.controls.pointSeries.series.valueAxisLabel": "值轴", "kbnVislibVisTypes.controls.pointSeries.valueAxes.maxLabel": "最大值", "kbnVislibVisTypes.controls.pointSeries.valueAxes.minLabel": "最小值", - "kbnVislibVisTypes.controls.pointSeries.valueAxes.minNeededScaleText": "如果选择了对数刻度,最小值必须大于 0", + "kbnVislibVisTypes.controls.pointSeries.valueAxes.minNeededScaleText": "如果选择了对数刻度,最小值必须大于 0。", "kbnVislibVisTypes.controls.pointSeries.valueAxes.modeLabel": "模式", "kbnVislibVisTypes.controls.pointSeries.valueAxes.positionLabel": "位置", "kbnVislibVisTypes.controls.pointSeries.valueAxes.scaleToDataBoundsLabel": "缩放到数据边界", @@ -2614,6 +2680,59 @@ "kbnVislibVisTypes.editors.pie.showLabelsLabel": "显示标签", "kbnVislibVisTypes.editors.pie.showTopLevelOnlyLabel": "仅显示顶级", "kbnVislibVisTypes.editors.pie.showValuesLabel": "显示值", + "kbnVislibVisTypes.area.countText": "计数", + "kbnVislibVisTypes.area.tabs.metricsAxesTitle": "指标和轴", + "kbnVislibVisTypes.area.tabs.panelSettingsTitle": "面板设置", + "kbnVislibVisTypes.axisModes.normalText": "正常", + "kbnVislibVisTypes.axisModes.percentageText": "百分比", + "kbnVislibVisTypes.axisModes.silhouetteText": "剪影", + "kbnVislibVisTypes.axisModes.wiggleText": "扭动", + "kbnVislibVisTypes.categoryAxis.rotate.angledText": "带角度", + "kbnVislibVisTypes.categoryAxis.rotate.horizontalText": "水平", + "kbnVislibVisTypes.categoryAxis.rotate.verticalText": "垂直", + "kbnVislibVisTypes.chartModes.normalText": "正常", + "kbnVislibVisTypes.chartModes.stackedText": "堆叠", + "kbnVislibVisTypes.chartTypes.areaText": "面积图", + "kbnVislibVisTypes.chartTypes.barText": "条形图", + "kbnVislibVisTypes.chartTypes.lineText": "折线图", + "kbnVislibVisTypes.controls.colorRanges.errorText": "每个范围应大于前一范围。", + "kbnVislibVisTypes.controls.colorSchema.colorSchemaLabel": "颜色模式", + "kbnVislibVisTypes.controls.colorSchema.howToChangeColorsDescription": "可以更改图例中的各个颜色。", + "kbnVislibVisTypes.controls.colorSchema.resetColorsButtonLabel": "重置颜色", + "kbnVislibVisTypes.controls.colorSchema.reverseColorSchemaLabel": "反转模式", + "kbnVislibVisTypes.controls.heatmapOptions.labelsTitle": "标签", + "kbnVislibVisTypes.controls.heatmapOptions.useCustomRangesLabel": "使用定制范围", + "kbnVislibVisTypes.controls.pointSeries.categoryAxis.alignLabel": "对齐", + "kbnVislibVisTypes.controls.pointSeries.series.showDotsLabel": "显示点线", + "kbnVislibVisTypes.controls.pointSeries.seriesAccordionAriaLabel": "切换 {agg} 选项", + "kbnVislibVisTypes.controls.pointSeries.valueAxes.addButtonTooltip": "添加 Y 轴", + "kbnVislibVisTypes.controls.pointSeries.valueAxes.customExtentsLabel": "定制范围", + "kbnVislibVisTypes.controls.pointSeries.valueAxes.minErrorMessage": "最小值应小于最大值。", + "kbnVislibVisTypes.controls.pointSeries.valueAxes.removeButtonTooltip": "移除 Y 轴", + "kbnVislibVisTypes.controls.pointSeries.valueAxes.toggleCustomExtendsAriaLabel": "切换定制范围", + "kbnVislibVisTypes.controls.pointSeries.valueAxes.toggleOptionsAriaLabel": "切换 {axisName} 选项", + "kbnVislibVisTypes.editors.heatmap.highlightLabelTooltip": "高亮显示图表中鼠标悬停的范围以及图例中对应的标签。", + "kbnVislibVisTypes.editors.pointSeries.thresholdLine.colorLabel": "线条颜色", + "kbnVislibVisTypes.editors.pointSeries.thresholdLine.showLabel": "显示阈值线条", + "kbnVislibVisTypes.editors.pointSeries.thresholdLine.styleLabel": "线条样式", + "kbnVislibVisTypes.editors.pointSeries.thresholdLineSettingsTitle": "阈值线条", + "kbnVislibVisTypes.interpolationModes.smoothedText": "平滑", + "kbnVislibVisTypes.interpolationModes.steppedText": "渐变", + "kbnVislibVisTypes.interpolationModes.straightText": "直线", + "kbnVislibVisTypes.scaleTypes.linearText": "线性", + "kbnVislibVisTypes.scaleTypes.logText": "对数", + "kbnVislibVisTypes.scaleTypes.squareRootText": "平方根", + "kbnVislibVisTypes.thresholdLine.style.dashedText": "虚线", + "kbnVislibVisTypes.thresholdLine.style.dotdashedText": "点虚线", + "kbnVislibVisTypes.thresholdLine.style.fullText": "实线", + "kbnVislibVisTypes.controls.heatmapOptions.colorScaleLabel": "色阶", + "kbnVislibVisTypes.controls.heatmapOptions.percentageModeLabel": "百分比模式", + "kbnVislibVisTypes.controls.heatmapOptions.scaleToDataBoundsLabel": "缩放到数据边界", + "kbnVislibVisTypes.controls.pointSeries.valueAxes.scaleToDataBounds.boundsMargin": "边界边距", + "kbnVislibVisTypes.controls.pointSeries.valueAxes.scaleToDataBounds.minNeededBoundsMargin": "边界边距必须大于或等于 0。", + "kbnVislibVisTypes.editors.heatmap.basicSettingsTitle": "基本设置", + "kbnVislibVisTypes.editors.heatmap.heatmapSettingsTitle": "热图设置", + "kbnVislibVisTypes.editors.heatmap.highlightLabel": "高亮范围", "visTypeMetric.colorModes.backgroundOptionLabel": "背景", "visTypeMetric.colorModes.labelsOptionLabel": "标签", "visTypeMetric.colorModes.noneOptionLabel": "无", @@ -2636,6 +2755,11 @@ "visTypeMetric.function.showLabels.help": "在指标值下显示标签。", "visTypeMetric.function.subText.help": "要在指标下显示的定制文本", "visTypeMetric.function.useRanges.help": "已启用颜色范围。", + "visTypeMetric.params.settingsTitle": "设置", + "visTypeMetric.params.showTitleLabel": "显示标题", + "visTypeMetric.params.color.useForLabel": "将颜色用于", + "visTypeMetric.params.percentageModeLabel": "百分比模式", + "visTypeMetric.params.style.fontSizeLabel": "指标字体大小(磅)", "regionMap.choroplethLayer.downloadingVectorData404ErrorMessage": "尝试提取 {name} 时,服务器响应状态为“404”。请确保目标文件位于该位置。", "regionMap.choroplethLayer.downloadingVectorDataErrorMessage": "无法下载 {name} 文件。请确保服务器的 CORS 配置允许来自此主机上的 Kibana 应用程序的请求。", "regionMap.choroplethLayer.downloadingVectorDataErrorMessageTitle": "下载矢量数据时出错", @@ -2752,6 +2876,13 @@ "visTypeTable.vis.noResultsFoundTitle": "找不到结果", "visTypeTable.params.PercentageColLabel": "百分比列", "visTypeTable.params.percentageTableColumnName": "{title} 百分比", + "visTypeTable.params.defaultPercentageCol": "不显示", + "visTypeTable.totalAggregations.averageText": "平均值", + "visTypeTable.totalAggregations.countText": "计数", + "visTypeTable.totalAggregations.maxText": "最大值", + "visTypeTable.totalAggregations.minText": "最小值", + "visTypeTable.totalAggregations.sumText": "和", + "visTypeTable.params.perPageLabel": "每页行数", "visTypeTagCloud.feedbackMessage.tooSmallContainerDescription": "容器太小,无法显示整个云。标记可能被裁剪或省略。", "visTypeTagCloud.feedbackMessage.truncatedTagsDescription": "标记数量已截断,以避免绘制时间过长。", "visTypeTagCloud.function.bucket.help": "存储桶维度配置", @@ -3139,6 +3270,7 @@ "timelion.vis.expressionLabel": "Timelion 表达式", "timelion.vis.intervalLabel": "时间间隔", "timelion.help.functions.notAllowedGraphiteUrl": "在 kibana.yml 文件中未配置此 Graphite URL。\n 请在 kibana.yml 文件中“timelion.graphiteUrls”下配置 Graphite 服务器列表,并\n 从 Kibana 的高级设置中选择一个", + "timelion.emptyExpressionErrorMessage": "Timelion 错误:未提供表达式", "visTypeVega.editor.formatError": "格式化规范时出错", "visTypeVega.editor.reformatAsHJSONButtonLabel": "重新格式化为 HJSON", "visTypeVega.editor.reformatAsJSONButtonLabel": "重新格式化为 JSON,删除注释", @@ -3225,7 +3357,7 @@ "xpack.apm.errorGroupDetails.occurrencesLongLabel": "{occCount} 次发生", "xpack.apm.errorGroupDetails.occurrencesShortLabel": "{occCount} 次发生", "xpack.apm.errorGroupDetails.unhandledLabel": "未处理", - "xpack.apm.errorGroupDetails.viewOccurrencesInDiscoverButtonLabel": "在 Discover 中查看 {occurrencesCount} 次发生", + "xpack.apm.errorGroupDetails.viewOccurrencesInDiscoverButtonLabel": "在 Discover 查看 {occurrencesCount} 个 {occurrencesCount, plural, one {匹配项} other {匹配项}}。", "xpack.apm.errorsTable.errorMessageAndCulpritColumnLabel": "错误消息和原因", "xpack.apm.errorsTable.groupIdColumnLabel": "组 ID", "xpack.apm.errorsTable.latestOccurrenceColumnLabel": "最新一次发生", @@ -3449,12 +3581,96 @@ "xpack.apm.settings.agentConf.configTable.lastUpdatedColumnLabel": "最后更新时间", "xpack.apm.settings.agentConf.configTable.sampleRateColumnLabel": "采样速率", "xpack.apm.settings.agentConf.configTable.serviceNameColumnLabel": "服务名称", - "xpack.apm.settings.agentConf.configurationsPanelTitle": "配置", + "xpack.apm.settings.agentConf.configurationsPanelTitle": "代理远程配置", "xpack.apm.settings.agentConf.createConfigButtonLabel": "创建配置", "xpack.apm.transactionDetails.traceNotFound": "找不到所选跟踪", "xpack.apm.transactionDetails.traceSampleTitle": "跟踪样例", "xpack.apm.transactionsTable.notFoundLabel": "未找到任何事务。", "xpack.apm.waterfall.exceedsMax": "此跟踪中的项目数超过显示的项目数", + "xpack.apm.agentMetrics.java.gcRate": "GC 速率", + "xpack.apm.agentMetrics.java.gcRateChartTitle": "每分钟垃圾回收率", + "xpack.apm.agentMetrics.java.gcTime": "GC 时间", + "xpack.apm.agentMetrics.java.gcTimeChartTitle": "每分钟花费的垃圾回收时间", + "xpack.apm.breadcrumb.nodesTitle": "JVM", + "xpack.apm.breadcrumb.serviceMapTitle": "服务地图", + "xpack.apm.errorGroupDetails.relatedTransactionSample": "相关的事务样本", + "xpack.apm.home.serviceMapTabLabel": "服务地图", + "xpack.apm.jvmsTable.cpuColumnLabel": "CPU 平均值", + "xpack.apm.jvmsTable.explainServiceNodeNameMissing": "无法识别这些指标属于哪些 JVM。这可能因为运行的 APM Server 版本低于 7.5。如果升级到 APM Server 7.5 或更高版本,应可解决此问题。", + "xpack.apm.jvmsTable.heapMemoryColumnLabel": "堆内存平均值", + "xpack.apm.jvmsTable.nameColumnLabel": "名称", + "xpack.apm.jvmsTable.nameExplanation": "默认情况下,JVM 名称是容器 ID(如果适用)或主机名,但其可以通过代理的“service_node_name”配置手动进行配置。", + "xpack.apm.jvmsTable.noJvmsLabel": "未找到任何 JVM", + "xpack.apm.jvmsTable.nonHeapMemoryColumnLabel": "非堆内存平均值", + "xpack.apm.jvmsTable.threadCountColumnLabel": "线程计数最大值", + "xpack.apm.metadataTable.section.errorLabel": "错误", + "xpack.apm.metadataTable.section.pageLabel": "页", + "xpack.apm.metadataTable.section.spanLabel": "范围", + "xpack.apm.metadataTable.section.traceLabel": "跟踪", + "xpack.apm.metadataTable.section.transactionLabel": "事务", + "xpack.apm.metadataTable.section.userAgentLabel": "用户代理", + "xpack.apm.percentOfParent": "({value} 的 {parentType, select, transaction {事务} trace {trace} })", + "xpack.apm.serviceDetails.nodesTabLabel": "JVM", + "xpack.apm.serviceMap.fullscreen": "全屏", + "xpack.apm.serviceMap.zoomIn": "放大", + "xpack.apm.serviceMap.zoomOut": "缩小", + "xpack.apm.serviceNodeMetrics.containerId": "容器 ID", + "xpack.apm.serviceNodeMetrics.host": "主机", + "xpack.apm.serviceNodeMetrics.unidentifiedServiceNodesWarningDocumentationLink": "APM Server 的文档", + "xpack.apm.serviceNodeMetrics.unidentifiedServiceNodesWarningText": "无法识别这些指标属于哪些 JVM。这可能因为运行的 APM Server 版本低于 7.5。如果升级到 APM Server 7.5 或更高版本,应可解决此问题。有关升级的详细信息,请参阅{link}。或者,也可以使用 Kibana 查询栏按主机名、容器 ID 或其他字段筛选。", + "xpack.apm.serviceNodeMetrics.unidentifiedServiceNodesWarningTitle": "找不到 JVM", + "xpack.apm.serviceNodeNameMissing": "(空)", + "xpack.apm.settings.agentConf.allOptionLabel": "全部", + "xpack.apm.settings.agentConf.cancelButtonLabel": "取消", + "xpack.apm.settings.agentConf.configTable.appliedTooltipMessage": "已至少由一个代理应用", + "xpack.apm.settings.agentConf.configTable.captureBodyColumnLabel": "捕获正文", + "xpack.apm.settings.agentConf.configTable.configTable.failurePromptText": "无法获取代理配置列表。您的用户可能没有足够的权限。", + "xpack.apm.settings.agentConf.configTable.createConfigButtonLabel": "创建配置", + "xpack.apm.settings.agentConf.configTable.notAppliedTooltipMessage": "尚未由任何代理应用", + "xpack.apm.settings.agentConf.configTable.transactionMaxSpansColumnLabel": "事务最大跨度数", + "xpack.apm.settings.agentConf.createConfigTitle": "创建配置", + "xpack.apm.settings.agentConf.editConfigTitle": "编辑配置", + "xpack.apm.settings.agentConf.flyout.deleteSection.buttonLabel": "删除", + "xpack.apm.settings.agentConf.flyout.deleteSection.deleteConfigFailedText": "为“{serviceName}”删除配置时出现问题。错误:“{errorMessage}”", + "xpack.apm.settings.agentConf.flyout.deleteSection.deleteConfigFailedTitle": "配置无法删除", + "xpack.apm.settings.agentConf.flyout.deleteSection.deleteConfigSucceededText": "您已成功为“{serviceName}”删除配置。将需要一些时间才能传播到代理。", + "xpack.apm.settings.agentConf.flyout.deleteSection.deleteConfigSucceededTitle": "配置已删除", + "xpack.apm.settings.agentConf.flyOut.serviceSection.alreadyConfiguredOption": "已配置", + "xpack.apm.settings.agentConf.flyOut.serviceSection.selectPlaceholder": "选择", + "xpack.apm.settings.agentConf.flyOut.serviceSection.serviceEnvironmentSelectHelpText": "每个配置仅支持单个环境。", + "xpack.apm.settings.agentConf.flyOut.serviceSection.serviceEnvironmentSelectLabel": "环境", + "xpack.apm.settings.agentConf.flyOut.serviceSection.serviceNameSelectHelpText": "选择要配置的服务。", + "xpack.apm.settings.agentConf.flyOut.serviceSection.serviceNameSelectLabel": "名称", + "xpack.apm.settings.agentConf.flyOut.serviceSection.title": "服务", + "xpack.apm.settings.agentConf.flyOut.settingsSection.captureBodyInputHelpText": "有关属于 HTTP 请求的事务,代理可以选择性地捕获请求正文(例如 POST 变量)。默认为“off”。", + "xpack.apm.settings.agentConf.flyOut.settingsSection.captureBodyInputLabel": "捕获正文", + "xpack.apm.settings.agentConf.flyOut.settingsSection.captureBodyInputPlaceholderText": "选择选项", + "xpack.apm.settings.agentConf.flyOut.settingsSection.sampleRateConfigurationInputErrorText": "采样速率必须介于 0.000 和 1 之间", + "xpack.apm.settings.agentConf.flyOut.settingsSection.sampleRateConfigurationInputHelpText": "选择 0.000 和 1.0 之间的速率。默认为 1.0(100% 的跟踪)。", + "xpack.apm.settings.agentConf.flyOut.settingsSection.sampleRateConfigurationInputLabel": "事务采样速率", + "xpack.apm.settings.agentConf.flyOut.settingsSection.sampleRateConfigurationInputPlaceholderText": "设置采样速率", + "xpack.apm.settings.agentConf.flyOut.settingsSection.title": "选项", + "xpack.apm.settings.agentConf.flyOut.settingsSection.transactionMaxSpansConfigInputErrorText": "必须介于 0 和 32000 之间", + "xpack.apm.settings.agentConf.flyOut.settingsSection.transactionMaxSpansConfigInputHelpText": "限制每个事务记录的跨度数量。默认值为 500。", + "xpack.apm.settings.agentConf.flyOut.settingsSection.transactionMaxSpansConfigInputLabel": "事务最大跨度数", + "xpack.apm.settings.agentConf.flyOut.settingsSection.transactionMaxSpansConfigInputPlaceholderText": "设置事务最大跨度数", + "xpack.apm.settings.agentConf.saveConfig.failed.text": "编辑“{serviceName}”的配置时出现问题。错误:“{errorMessage}”", + "xpack.apm.settings.agentConf.saveConfig.failed.title": "配置无法编辑", + "xpack.apm.settings.agentConf.saveConfig.succeeded.text": "“{serviceName}”的配置已保存。将需要一些时间才能传播到代理。", + "xpack.apm.settings.agentConf.saveConfig.succeeded.title": "配置已保存", + "xpack.apm.settings.agentConf.saveConfigurationButtonLabel": "保存", + "xpack.apm.tracesTable.impactColumnDescription": "服务中最常用的和最慢的终端节点。其计算方法是相对平均持续时间乘以每分钟事务数。", + "xpack.apm.transactionBreakdown.noData": "此时间范围内没有数据。", + "xpack.apm.transactionDetails.errorCount": "{errorCount, number} 个 {errorCount, plural, one {错误} other {错误}}", + "xpack.apm.transactionDetails.requestMethodLabel": "请求方法", + "xpack.apm.transactionDetails.spanFlyout.spanAction": "操作", + "xpack.apm.transactionDetails.spanFlyout.spanSubtype": "子类型", + "xpack.apm.transactionDetails.spanFlyout.spanType": "类型", + "xpack.apm.transactionDetails.statusCode": "状态代码", + "xpack.apm.transactionDetails.userAgentAndVersionLabel": "用户代理和版本", + "xpack.apm.transactionDurationLabel": "持续时间", + "xpack.apm.transactionsTable.impactColumnDescription": "服务中最常用的和最慢的终端节点。其计算方法是相对平均持续时间乘以每分钟事务数。", + "xpack.apm.transactionDetails.percentOfTraceLabelExplanation": "{parentType, select, transaction {事务} trace {trace} } 的 % 超过 100%,因为此{childType, select, span {跨度} transaction {transaction} }比根事务花费更长时间。", "xpack.beatsManagement.beat.actionSectionTypeLabel": "类型:{beatType}。", "xpack.beatsManagement.beat.actionSectionVersionLabel": "版本:{beatVersion}。", "xpack.beatsManagement.beat.beatNameAndIdTitle": "Beat:{nameOrNoName}(ID:{id})", @@ -3937,6 +4153,712 @@ "xpack.canvas.functions.urlparam.args.defaultHelpText": "未指定 {URL} 参数时返回的值。", "xpack.canvas.functions.urlparam.args.paramHelpText": "要检索的 {URL} 哈希参数。", "xpack.canvas.functions.urlparamHelpText": "检索要在表达式中使用的 {URL} 参数。{urlparamFn} 函数始终返回 {TYPE_STRING}。例如,从 {URL} 的参数 {myVar} 检索值 {value} ({example})。", + "xpack.canvas.app.loadErrorMessage": "消息:{error}", + "xpack.canvas.app.loadErrorTitle": "Canvas 加载失败", + "xpack.canvas.app.loadingMessage": "Canvas 正在加载", + "xpack.canvas.argAddPopover.addAriaLabel": "添加参数", + "xpack.canvas.argFormAdvancedFailure.applyButtonLabel": "应用", + "xpack.canvas.argFormAdvancedFailure.resetButtonLabel": "重置", + "xpack.canvas.argFormAdvancedFailure.rowErrorMessage": "表达式无效", + "xpack.canvas.argFormArgSimpleForm.removeAriaLabel": "删除", + "xpack.canvas.argFormArgSimpleForm.requiredTooltip": "此参数为必需,应指定值。", + "xpack.canvas.argFormPendingArgValue.loadingMessage": "正在加载", + "xpack.canvas.argFormSimpleFailure.failureTooltip": "此参数的接口无法解析该值,因此将使用回退输入", + "xpack.canvas.asset.copyAssetTooltip": "将 ID 复制到剪贴板", + "xpack.canvas.asset.createImageTooltip": "创建图像元素", + "xpack.canvas.asset.deleteAssetTooltip": "删除", + "xpack.canvas.asset.downloadAssetTooltip": "下载", + "xpack.canvas.asset.thumbnailAltText": "资产缩略图", + "xpack.canvas.assetManager.confirmModalButtonLabel": "删除", + "xpack.canvas.assetManager.confirmModalDetail": "确定要删除此资产?", + "xpack.canvas.assetManager.confirmModalTitle": "删除资产", + "xpack.canvas.assetManager.manageButtonLabel": "管理资产", + "xpack.canvas.assetModal.emptyAssetsDescription": "导入您的资产以开始", + "xpack.canvas.assetModal.filePickerPromptText": "选择或拖放图像", + "xpack.canvas.assetModal.loadingText": "正在上传图像", + "xpack.canvas.assetModal.modalCloseButtonLabel": "关闭", + "xpack.canvas.assetModal.modalDescription": "以下为此 Workpad 中的图像资产。此时无法确定当前在用的任何资产。要回收空间,请删除资产。", + "xpack.canvas.assetModal.modalTitle": "管理 Workpad 资产", + "xpack.canvas.assetModal.spacedUsedText": "{percentageUsed}% 空间已用", + "xpack.canvas.assetpicker.assetAltText": "资产缩略图", + "xpack.canvas.colorManager.addAriaLabel": "添加颜色", + "xpack.canvas.colorManager.codePlaceholder": "颜色代码", + "xpack.canvas.colorManager.removeAriaLabel": "删除颜色", + "xpack.canvas.customElementModal.cancelButtonLabel": "取消", + "xpack.canvas.customElementModal.descriptionInputLabel": "描述", + "xpack.canvas.customElementModal.elementPreviewTitle": "元素预览", + "xpack.canvas.customElementModal.imageFilePickerPlaceholder": "选择或拖放图像", + "xpack.canvas.customElementModal.imageInputDescription": "对您的元素进行截屏并将截图上传到此处。也可以在保存之后执行此操作。", + "xpack.canvas.customElementModal.imageInputLabel": "缩略图", + "xpack.canvas.customElementModal.nameInputLabel": "名称", + "xpack.canvas.customElementModal.remainingCharactersDescription": "剩余 {numberOfRemainingCharacter} 个字符", + "xpack.canvas.customElementModal.saveButtonLabel": "保存", + "xpack.canvas.datasourceDatasourceComponent.changeButtonLabel": "更改您的数据源", + "xpack.canvas.datasourceDatasourceComponent.previewButtonLabel": "预览", + "xpack.canvas.datasourceDatasourceComponent.saveButtonLabel": "保存", + "xpack.canvas.datasourceDatasourcePreview.emptyFirstLineDescription": "找不到与您的搜索条件匹配的任何文档。", + "xpack.canvas.datasourceDatasourcePreview.emptySecondLineDescription": "请检查您的数据源设置并重试。", + "xpack.canvas.datasourceDatasourcePreview.emptyTitle": "找不到文档", + "xpack.canvas.datasourceDatasourcePreview.modalDescription": "单击侧栏中“{saveLabel}”以保存此数据。", + "xpack.canvas.datasourceDatasourcePreview.modalTitle": "数据源预览", + "xpack.canvas.datasourceNoDatasource.panelDescription": "此元素未附加数据源。通常这是因为该元素为图像或其他静态资产。否则,您可能需要检查表达式,以确保其格式正确。", + "xpack.canvas.datasourceNoDatasource.panelTitle": "数据源不存在", + "xpack.canvas.elementConfig.failedLabel": "失败", + "xpack.canvas.elementConfig.loadedLabel": "已加载", + "xpack.canvas.elementConfig.progressLabel": "进度", + "xpack.canvas.elementConfig.title": "元素", + "xpack.canvas.elementConfig.totalLabel": "合计", + "xpack.canvas.elementControls.deleteAriaLabel": "删除元素", + "xpack.canvas.elementControls.deleteToolTip": "删除", + "xpack.canvas.elementControls.editAriaLabel": "编辑元素", + "xpack.canvas.elementControls.editToolTip": "编辑", + "xpack.canvas.elementSettings.dataTabLabel": "数据", + "xpack.canvas.elementSettings.displayTabLabel": "显示", + "xpack.canvas.elementTypes.addNewElementDescription": "分组并保存 Workpad 元素以创建新元素", + "xpack.canvas.elementTypes.addNewElementTitle": "添加新元素", + "xpack.canvas.elementTypes.cancelButtonLabel": "取消", + "xpack.canvas.elementTypes.deleteButtonLabel": "删除", + "xpack.canvas.elementTypes.deleteElementDescription": "确定要删除此元素?", + "xpack.canvas.elementTypes.deleteElementTitle": "删除元素“{elementName}”?", + "xpack.canvas.elementTypes.editElementTitle": "编辑元素", + "xpack.canvas.elementTypes.elementsTitle": "元素", + "xpack.canvas.elementTypes.findElementPlaceholder": "查找元素", + "xpack.canvas.elementTypes.myElementsTitle": "我的元素", + "xpack.canvas.embedObject.noMatchingObjectsMessage": "未找到任何匹配对象。", + "xpack.canvas.embedObject.titleText": "嵌入对象", + "xpack.canvas.error.actionsElements.invaludArgIndexErrorMessage": "无效的参数索引:{index}", + "xpack.canvas.error.downloadWorkpad.downloadFailureErrorMessage": "无法下载 Workpad", + "xpack.canvas.error.downloadWorkpad.downloadRenderedWorkpadFailureErrorMessage": "无法下载已呈现 Workpad", + "xpack.canvas.error.downloadWorkpad.downloadRuntimeFailureErrorMessage": "无法下载 Shareable Runtime", + "xpack.canvas.error.downloadWorkpad.downloadZippedRuntimeFailureErrorMessage": "无法下载 ZIP 文件", + "xpack.canvas.error.esPersist.saveFailureTitle": "无法将您的更改保存到 Elasticsearch", + "xpack.canvas.error.esPersist.tooLargeErrorMessage": "服务器响应 Workpad 数据过大。这通常表示上传的图像资产对于 Kibana 或代理过大。请尝试移除资产管理器中的一些资产。", + "xpack.canvas.error.esPersist.updateFailureTitle": "无法更新 Workpad", + "xpack.canvas.error.esService.defaultIndexFetchErrorMessage": "无法提取默认索引", + "xpack.canvas.error.esService.fieldsFetchErrorMessage": "无法为“{index}”提取 Elasticsearch 字段", + "xpack.canvas.error.esService.indicesFetchErrorMessage": "无法提取 Elasticsearch 索引", + "xpack.canvas.error.RenderWithFn.renderErrorMessage": "呈现“{functionName}”失败", + "xpack.canvas.error.repeatImage.missingMaxArgument": "如果提供 {emptyImageArgument},则必须设置 {maxArgument}", + "xpack.canvas.error.workpadLoader.cloneFailureErrorMessage": "无法克隆 Workpad", + "xpack.canvas.error.workpadLoader.deleteFailureErrorMessage": "无法删除所有 Workpad", + "xpack.canvas.error.workpadLoader.findFailureErrorMessage": "无法查找 Workpad", + "xpack.canvas.error.workpadLoader.uploadFailureErrorMessage": "无法上传 Workpad", + "xpack.canvas.error.workpadRoutes.createFailureErrorMessage": "无法创建 Workpad", + "xpack.canvas.error.workpadRoutes.loadFailureErrorMessage": "无法加载具有以下 ID 的 Workpad", + "xpack.canvas.error.workpadUpload.acceptJSONOnlyErrorMessage": "仅接受 {JSON} 文件", + "xpack.canvas.error.workpadUpload.fileUploadFailureWithoutFileNameErrorMessage": "无法上传文件", + "xpack.canvas.error.workpadUpload.missingPropertiesErrorMessage": "{CANVAS} Workpad 所需的某些属性缺失。 编辑 {JSON} 文件以提供正确的属性值,然后重试。", + "xpack.canvas.errorComponent.description": "表达式失败,并显示消息:", + "xpack.canvas.errorComponent.title": "哎哟!表达式失败", + "xpack.canvas.errors.workpadUpload.fileUploadFileWithFileNameErrorMessage": "无法上传“{fileName}”", + "xpack.canvas.expression.cancelButtonLabel": "取消", + "xpack.canvas.expression.closeButtonLabel": "关闭", + "xpack.canvas.expression.learnLinkText": "学习表达式语法", + "xpack.canvas.expression.maximizeButtonLabel": "最大化编辑器", + "xpack.canvas.expression.minimizeButtonLabel": "最小化编辑器", + "xpack.canvas.expression.runButtonLabel": "运行", + "xpack.canvas.expression.runTooltip": "运行表达式", + "xpack.canvas.expressionElementNotSelected.closeButtonLabel": "关闭", + "xpack.canvas.expressionElementNotSelected.selectDescription": "选择元素以显示表达式输入", + "xpack.canvas.expressionInput.argReferenceAliasesDetail": "{BOLD_MD_TOKEN}别名{BOLD_MD_TOKEN}:{aliases}", + "xpack.canvas.expressionInput.argReferenceDefaultDetail": "{BOLD_MD_TOKEN}默认{BOLD_MD_TOKEN}:{defaultVal}", + "xpack.canvas.expressionInput.argReferenceRequiredDetail": "{BOLD_MD_TOKEN}必需{BOLD_MD_TOKEN}:{required}", + "xpack.canvas.expressionInput.argReferenceTypesDetail": "{BOLD_MD_TOKEN}类型{BOLD_MD_TOKEN}:{types}", + "xpack.canvas.expressionInput.functionReferenceAccepts": "{BOLD_MD_TOKEN}接受{BOLD_MD_TOKEN}:{acceptTypes}", + "xpack.canvas.expressionInput.functionReferenceReturns": "{BOLD_MD_TOKEN}返回{BOLD_MD_TOKEN}:{returnType}", + "xpack.canvas.expressionTypes.argTypes.colorDisplayName": "颜色", + "xpack.canvas.expressionTypes.argTypes.colorHelp": "颜色选取器", + "xpack.canvas.expressionTypes.argTypes.containerStyle.appearanceTitle": "外观", + "xpack.canvas.expressionTypes.argTypes.containerStyle.borderTitle": "边框", + "xpack.canvas.expressionTypes.argTypes.containerStyle.colorLabel": "颜色", + "xpack.canvas.expressionTypes.argTypes.containerStyle.opacityLabel": "图层透明度", + "xpack.canvas.expressionTypes.argTypes.containerStyle.overflowHiddenDropDown": "隐藏", + "xpack.canvas.expressionTypes.argTypes.containerStyle.overflowLabel": "溢出", + "xpack.canvas.expressionTypes.argTypes.containerStyle.overflowVisibleDropDown": "可见", + "xpack.canvas.expressionTypes.argTypes.containerStyle.paddingLabel": "填充", + "xpack.canvas.expressionTypes.argTypes.containerStyle.radiusLabel": "半径", + "xpack.canvas.expressionTypes.argTypes.containerStyle.styleLabel": "样式", + "xpack.canvas.expressionTypes.argTypes.containerStyle.thicknessLabel": "厚度", + "xpack.canvas.expressionTypes.argTypes.containerStyleLabel": "调整元素容器的外观", + "xpack.canvas.expressionTypes.argTypes.containerStyleTitle": "容器样式", + "xpack.canvas.expressionTypes.argTypes.fontHelpLabel": "设置字体、大小和颜色", + "xpack.canvas.expressionTypes.argTypes.fontTitle": "文本设置", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.barLabel": "条形图", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.colorLabel": "颜色", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.lineLabel": "折线图", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.noneDropDown": "无", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.noSeriesTooltip": "数据没有要应用样式的序列,请添加颜色维度", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.pointLabel": "点", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.removeAriaLabel": "移除序列颜色", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.selectSeriesDropDown": "选择序列", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.seriesIdentifierLabel": "序列标识符", + "xpack.canvas.expressionTypes.argTypes.seriesStyle.styleLabel": "样式", + "xpack.canvas.expressionTypes.argTypes.seriesStyleLabel": "设置选定已命名序列的样式", + "xpack.canvas.expressionTypes.argTypes.seriesStyleTitle": "序列样式", + "xpack.canvas.expressionTypes.datasources.esdocs.ascendingDropDown": "升序", + "xpack.canvas.expressionTypes.datasources.esdocs.descendingDropDown": "降序", + "xpack.canvas.expressionTypes.datasources.esdocs.fieldsLabel": "要提取的字段。Kibana 脚本字段当前不可用", + "xpack.canvas.expressionTypes.datasources.esdocs.fieldsTitle": "字段", + "xpack.canvas.expressionTypes.datasources.esdocs.fieldsWarningLabel": "字段不超过 10 个时,此数据源性能最佳", + "xpack.canvas.expressionTypes.datasources.esdocs.indexLabel": "输入索引名称或选择索引模式", + "xpack.canvas.expressionTypes.datasources.esdocs.indexTitle": "索引", + "xpack.canvas.expressionTypes.datasources.esdocs.queryLabel": "{lucene} 查询字符串语法", + "xpack.canvas.expressionTypes.datasources.esdocs.queryTitle": "查询", + "xpack.canvas.expressionTypes.datasources.esdocs.sortFieldLabel": "文档排序字段", + "xpack.canvas.expressionTypes.datasources.esdocs.sortFieldTitle": "排序字段", + "xpack.canvas.expressionTypes.datasources.esdocs.sortOrderLabel": "文档排序顺序", + "xpack.canvas.expressionTypes.datasources.esdocs.sortOrderTitle": "排序顺序", + "xpack.canvas.expressionTypes.datasources.esdocs.warningTitle": "务必谨慎操作", + "xpack.canvas.expressionTypes.datasources.esdocsLabel": "从 Elasticsearch 拉取原始文档", + "xpack.canvas.expressionTypes.datasources.esdocsTitle": "Elasticsearch 原始文档", + "xpack.canvas.functionForm.contextError": "错误:{errorMessage}", + "xpack.canvas.functionForm.functionUnknown.unknownArgumentTypeError": "表达式类型“{expressionType}”未知", + "xpack.canvas.functions.asset.args.id": "要检索的资产的 ID。", + "xpack.canvas.functions.asset.invalidAssetId": "无法通过以下 ID 获取资产:“{assetId}”", + "xpack.canvas.functions.assetHelpText": "检索要作为参数值来提供的 Canvas Workpad 资产对象。通常为图像。", + "xpack.canvas.functions.filters.args.group": "要使用的筛选组的名称。", + "xpack.canvas.functions.filters.args.ungrouped": "排除属于筛选组的筛选?", + "xpack.canvas.functions.filtersHelpText": "聚合 Workpad 的元素筛选以用于他处,通常用于数据源。", + "xpack.canvas.functions.savedMapHelpText": "为已保存地图对象返回可嵌入对象", + "xpack.canvas.functions.savedSearchHelpText": "为已保存搜索对象返回可嵌入对象", + "xpack.canvas.functions.savedVisualizationHelpText": "为已保存可视化对象返回可嵌入对象", + "xpack.canvas.functions.timelion.args.from": "表示时间范围起始的 {ELASTICSEARCH} {DATEMATH} 字符串。", + "xpack.canvas.functions.timelion.args.interval": "时间序列的存储桶间隔。", + "xpack.canvas.functions.timelion.args.query": "Timelion 查询", + "xpack.canvas.functions.timelion.args.timezone": "时间范围的时区。请参阅 {MOMENTJS_TIMEZONE_URL}。", + "xpack.canvas.functions.timelion.args.to": "表示时间范围结束的 {ELASTICSEARCH} {DATEMATH} 字符串。", + "xpack.canvas.functions.timelionHelpText": "使用 Timelion 从多个源中提取一个或多个时间序列。", + "xpack.canvas.functions.to.args.type": "表达式语言中的已知数据类型。", + "xpack.canvas.functions.to.missingType": "必须指定转换类型", + "xpack.canvas.functions.toHelpText": "将 {CONTEXT} 的类型显式转换为指定类型。", + "xpack.canvas.groupSettings.multipleElementsActionsDescription": "取消选择这些元素以编辑各自的设置,按 ({gKey}) 以对它们进行分组,或将此选择另存为新元素,以在整个 Workpad 中重复使用。", + "xpack.canvas.groupSettings.multipleElementsDescription": "当前选择了多个元素。", + "xpack.canvas.groupSettings.saveGroupDescription": "将此组另存为新元素,以在整个 Workpad 重复使用。", + "xpack.canvas.groupSettings.ungroupDescription": "取消分组 ({uKey}) 以编辑各个元素设置。", + "xpack.canvas.helpMenu.description": "有关 {CANVAS} 特定信息", + "xpack.canvas.helpMenu.documentationLinkLabel": "{CANVAS} 文档", + "xpack.canvas.helpMenu.keyboardShortcutsLinkLabel": "快捷键", + "xpack.canvas.keyboardShortcuts.bringFowardShortcutHelpText": "置前", + "xpack.canvas.keyboardShortcuts.bringToFrontShortcutHelpText": "前移", + "xpack.canvas.keyboardShortcuts.cloneShortcutHelpText": "克隆", + "xpack.canvas.keyboardShortcuts.copyShortcutHelpText": "复制", + "xpack.canvas.keyboardShortcuts.cutShortcutHelpText": "剪切", + "xpack.canvas.keyboardShortcuts.deleteShortcutHelpText": "删除", + "xpack.canvas.keyboardShortcuts.editingShortcutHelpText": "切换编辑模式", + "xpack.canvas.keyboardShortcuts.fullscreenExitShortcutHelpText": "退出演示模式", + "xpack.canvas.keyboardShortcuts.fullscreenShortcutHelpText": "进入演示模式", + "xpack.canvas.keyboardShortcuts.gridShortcutHelpText": "显示网格", + "xpack.canvas.keyboardShortcuts.groupShortcutHelpText": "组", + "xpack.canvas.keyboardShortcuts.ignoreSnapShortcutHelpText": "移动、调整大小及旋转时不对齐", + "xpack.canvas.keyboardShortcuts.multiselectShortcutHelpText": "选择多个元素", + "xpack.canvas.keyboardShortcuts.namespace.editorDisplayName": "编辑器控件", + "xpack.canvas.keyboardShortcuts.namespace.elementDisplayName": "元素控件", + "xpack.canvas.keyboardShortcuts.namespace.expressionDisplayName": "表达式控件", + "xpack.canvas.keyboardShortcuts.namespace.presentationDisplayName": "演示控件", + "xpack.canvas.keyboardShortcuts.nextShortcutHelpText": "前往下一页", + "xpack.canvas.keyboardShortcuts.nudgeDownShortcutHelpText": "下移 {ELEMENT_NUDGE_OFFSET}px", + "xpack.canvas.keyboardShortcuts.nudgeLeftShortcutHelpText": "左移 {ELEMENT_NUDGE_OFFSET}px", + "xpack.canvas.keyboardShortcuts.nudgeRightShortcutHelpText": "右移 {ELEMENT_NUDGE_OFFSET}px", + "xpack.canvas.keyboardShortcuts.nudgeUpShortcutHelpText": "上移 {ELEMENT_NUDGE_OFFSET}px", + "xpack.canvas.keyboardShortcuts.pageCycleToggleShortcutHelpText": "切换页面循环播放", + "xpack.canvas.keyboardShortcuts.pasteShortcutHelpText": "粘贴", + "xpack.canvas.keyboardShortcuts.prevShortcutHelpText": "前往上一页", + "xpack.canvas.keyboardShortcuts.redoShortcutHelpText": "恢复上一操作", + "xpack.canvas.keyboardShortcuts.resizeFromCenterShortcutHelpText": "从中心调整大小", + "xpack.canvas.keyboardShortcuts.runShortcutHelpText": "运行整个表达式", + "xpack.canvas.keyboardShortcuts.selectBehindShortcutHelpText": "在下面选择元素", + "xpack.canvas.keyboardShortcuts.sendBackwardShortcutHelpText": "后移", + "xpack.canvas.keyboardShortcuts.sendToBackShortcutHelpText": "置后", + "xpack.canvas.keyboardShortcuts.shiftDownShortcutHelpText": "下移 {ELEMENT_SHIFT_OFFSET}px", + "xpack.canvas.keyboardShortcuts.shiftLeftShortcutHelpText": "左移 {ELEMENT_SHIFT_OFFSET}px", + "xpack.canvas.keyboardShortcuts.shiftRightShortcutHelpText": "右移 {ELEMENT_SHIFT_OFFSET}px", + "xpack.canvas.keyboardShortcuts.shiftUpShortcutHelpText": "上移 {ELEMENT_SHIFT_OFFSET}px", + "xpack.canvas.keyboardShortcuts.ShortcutHelpText": "刷新 Workpad", + "xpack.canvas.keyboardShortcuts.undoShortcutHelpText": "撤消上一操作", + "xpack.canvas.keyboardShortcuts.ungroupShortcutHelpText": "取消分组", + "xpack.canvas.keyboardShortcuts.zoomInShortcutHelpText": "放大", + "xpack.canvas.keyboardShortcuts.zoomOutShortcutHelpText": "缩小", + "xpack.canvas.keyboardShortcuts.zoomResetShortcutHelpText": "将缩放比例重置为 100%", + "xpack.canvas.keyboardShortcutsDoc.flyout.closeButtonAriaLabel": "关闭快捷键参考", + "xpack.canvas.keyboardShortcutsDoc.flyoutHeaderTitle": "快捷键", + "xpack.canvas.keyboardShortcutsDoc.shortcutListSeparator": "或", + "xpack.canvas.link.errorMessage": "链接错误:{message}", + "xpack.canvas.pageConfig.backgroundColorDescription": "接受 HEX、RGB 或 HTML 颜色名称", + "xpack.canvas.pageConfig.backgroundColorLabel": "背景色", + "xpack.canvas.pageConfig.title": "页", + "xpack.canvas.pageConfig.transitionLabel": "切换", + "xpack.canvas.pageConfig.transitionPreviewLabel": "预览", + "xpack.canvas.pageConfig.transitions.noneDropDownOptionLabel": "无", + "xpack.canvas.pageManager.pageNumberAriaLabel": "加载页码 {pageNumber}", + "xpack.canvas.pagePreviewPageControls.clonePageAriaLabel": "克隆页面", + "xpack.canvas.pagePreviewPageControls.clonePageTooltip": "克隆", + "xpack.canvas.pagePreviewPageControls.deletePageAriaLabel": "删除页面", + "xpack.canvas.pagePreviewPageControls.deletePageTooltip": "删除", + "xpack.canvas.renderer.advancedFilter.applyButtonLabel": "应用", + "xpack.canvas.renderer.advancedFilter.displayName": "高级筛选", + "xpack.canvas.renderer.advancedFilter.helpDescription": "呈现 Canvas 筛选表达式", + "xpack.canvas.renderer.advancedFilter.inputPlaceholder": "输入筛选表达式", + "xpack.canvas.renderer.debug.displayName": "故障排查", + "xpack.canvas.renderer.debug.helpDescription": "将故障排查输出呈现为带格式的 {JSON}", + "xpack.canvas.renderer.dropdownFilter.displayName": "下拉列表筛选", + "xpack.canvas.renderer.dropdownFilter.helpDescription": "可以从其中为“{exactly}”筛选选择值的下拉列表", + "xpack.canvas.renderer.dropdownFilter.matchAllOptionLabel": "任意", + "xpack.canvas.renderer.embeddable.displayName": "可嵌入", + "xpack.canvas.renderer.embeddable.helpDescription": "从 Kibana 的其他部分呈现可嵌入的已保存对象", + "xpack.canvas.renderer.error.displayName": "错误信息", + "xpack.canvas.renderer.error.helpDescription": "以用户友好的方式呈现错误数据", + "xpack.canvas.renderer.image.displayName": "图像", + "xpack.canvas.renderer.image.helpDescription": "呈现图像", + "xpack.canvas.renderer.markdown.displayName": "Markdown", + "xpack.canvas.renderer.markdown.helpDescription": "使用 {MARKDOWN} 输入呈现 {HTML}", + "xpack.canvas.renderer.metric.displayName": "指标", + "xpack.canvas.renderer.metric.helpDescription": "在标签上呈现数字", + "xpack.canvas.renderer.pie.displayName": "饼图", + "xpack.canvas.renderer.pie.helpDescription": "根据您的数据呈现饼图", + "xpack.canvas.renderer.plot.displayName": "坐标图", + "xpack.canvas.renderer.plot.helpDescription": "根据您的数据呈现 XY 坐标图", + "xpack.canvas.renderer.progress.displayName": "进度指示", + "xpack.canvas.renderer.progress.helpDescription": "呈现显示元素百分比的进度指示", + "xpack.canvas.renderer.repeatImage.displayName": "图像重复", + "xpack.canvas.renderer.repeatImage.helpDescription": "重复图像给定次数", + "xpack.canvas.renderer.revealImage.displayName": "图像显示", + "xpack.canvas.renderer.revealImage.helpDescription": "显示一定百分比的图像,以制作定制的仪表样式图表", + "xpack.canvas.renderer.shape.displayName": "形状", + "xpack.canvas.renderer.shape.helpDescription": "呈现基本形状", + "xpack.canvas.renderer.table.displayName": "数据表", + "xpack.canvas.renderer.table.helpDescription": "将表格数据呈现为 {HTML}", + "xpack.canvas.renderer.text.displayName": "纯文本", + "xpack.canvas.renderer.text.helpDescription": "将输出呈现为纯文本", + "xpack.canvas.renderer.timeFilter.displayName": "时间筛选", + "xpack.canvas.renderer.timeFilter.helpDescription": "设置时间窗口以筛选数据", + "xpack.canvas.shareWebsiteFlyout.description": "按照以下步骤在外部网站上共享此 Workpad 的静态版本。其将是当前 Workpad 的可视化快照,对实时数据没有访问权限。", + "xpack.canvas.shareWebsiteFlyout.flyoutCalloutDescription": "要尝试共享,可以{link},其包含此 Workpad、{CANVAS} Shareable Workpad Runtime 及示例 {HTML} 文件。", + "xpack.canvas.shareWebsiteFlyout.flyoutTitle": "在网站上共享", + "xpack.canvas.shareWebsiteFlyout.runtimeStep.description": "要呈现可共享 Workpad,还需要加入 {CANVAS} Shareable Workpad Runtime。如果您的网站已包含该运行时,则可以跳过此步骤。", + "xpack.canvas.shareWebsiteFlyout.runtimeStep.downloadLabel": "下载运行时", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.addSnippetsTitle": "将代码段添加到网站", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.autoplayParameterDescription": "该运行时是否应自动播放 Workpad 的所有页面?", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.callRuntimeLabel": "调用运行时", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.description": "通过使用 {HTML} 占位符,Workpad 将置于站点的 {HTML} 内。将内联包含运行时的参数。请在下面参阅参数的完整列表。可以在页面上包含多个 Workpad。", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.downloadRuntimeTitle": "下载运行时", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.downloadWorkpadTitle": "下载 Workpad", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.heightParameterDescription": "Workpad 的高度。默认为 Workpad 高度。", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.includeRuntimeLabel": "包含运行时", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.intervalParameterDescription": "页面前进的间隔,时间格式(例如 {twoSeconds}、{oneMinute}", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.pageParameterDescription": "要显示的页面。默认为 Workpad 指定的页面。", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.parametersDescription": "有很多可用于配置可共享 Workpad 的内联参数。", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.parametersLabel": "参数", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.placeholderLabel": "占位符", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.requiredLabel": "必需", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.shareableParameterDescription": "可共享对象的类型。在这种情况下,为 {CANVAS} Workpad。", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.toolbarParameterDescription": "工具栏是否应隐藏?", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.urlParameterDescription": "可共享 Workpad {JSON} 文件的 {URL}", + "xpack.canvas.shareWebsiteFlyout.snippetsStep.widthParameterDescription": "Workdpad 的宽度。默认为 Workpad 宽度。", + "xpack.canvas.shareWebsiteFlyout.workpadStep.description": "Workpad 将导出为单个 {JSON} 文件,以在其他站点上共享。", + "xpack.canvas.shareWebsiteFlyout.workpadStep.downloadLabel": "下载 Workpad", + "xpack.canvas.shareWebsiteFlyout.zipDownloadLinkLabel": "下载示例 {ZIP} 文件", + "xpack.canvas.sidebarContent.groupedElementSidebarTitle": "已分组元素", + "xpack.canvas.sidebarContent.multiElementSidebarTitle": "多个元素", + "xpack.canvas.sidebarContent.singleElementSidebarTitle": "选定元素", + "xpack.canvas.sidebarHeader.alignmentMenuItemLabel": "对齐方式", + "xpack.canvas.sidebarHeader.bottomAlignMenuItemLabel": "下", + "xpack.canvas.sidebarHeader.bringForwardArialLabel": "将元素上移一层", + "xpack.canvas.sidebarHeader.bringToFrontArialLabel": "将元素移到顶层", + "xpack.canvas.sidebarHeader.centerAlignMenuItemLabel": "中", + "xpack.canvas.sidebarHeader.contextMenuAriaLabel": "元素选项", + "xpack.canvas.sidebarHeader.createElementModalTitle": "创建新元素", + "xpack.canvas.sidebarHeader.distributionMenutItemLabel": "分布", + "xpack.canvas.sidebarHeader.groupMenuItemLabel": "分组", + "xpack.canvas.sidebarHeader.horizontalDistributionMenutItemLabel": "水平", + "xpack.canvas.sidebarHeader.leftAlignMenuItemLabel": "左", + "xpack.canvas.sidebarHeader.middleAlignMenuItemLabel": "中", + "xpack.canvas.sidebarHeader.orderMenuItemLabel": "顺序", + "xpack.canvas.sidebarHeader.rightAlignMenuItemLabel": "右", + "xpack.canvas.sidebarHeader.savedElementMenuItemLabel": "另存为新元素", + "xpack.canvas.sidebarHeader.sendBackwardArialLabel": "将元素下移一层", + "xpack.canvas.sidebarHeader.sendToBackArialLabel": "将元素移到底层", + "xpack.canvas.sidebarHeader.topAlignMenuItemLabel": "上", + "xpack.canvas.sidebarHeader.ungroupMenuItemLabel": "取消分组", + "xpack.canvas.sidebarHeader.verticalDistributionMenutItemLabel": "垂直", + "xpack.canvas.tags.chartTag": "图表", + "xpack.canvas.tags.filterTag": "筛选", + "xpack.canvas.tags.graphicTag": "图形", + "xpack.canvas.tags.presentationTag": "演示", + "xpack.canvas.tags.proportionTag": "比例", + "xpack.canvas.tags.reportTag": "报告", + "xpack.canvas.tags.textTag": "文本", + "xpack.canvas.templates.darkHelp": "深色主题的演示幻灯片", + "xpack.canvas.templates.darkName": "深色", + "xpack.canvas.templates.lightHelp": "浅色主题的演示幻灯片", + "xpack.canvas.templates.lightName": "浅色", + "xpack.canvas.templates.pitchHelp": "具有大尺寸照片的冠名演示", + "xpack.canvas.templates.pitchName": "推销演示", + "xpack.canvas.templates.statusHelp": "具有动态图表的文档式报告", + "xpack.canvas.templates.statusName": "状态", + "xpack.canvas.templates.summaryDisplayName": "总结", + "xpack.canvas.templates.summaryHelp": "具有动态图表的信息图式报告", + "xpack.canvas.textStylePicker.alignCenterOption": "中间对齐", + "xpack.canvas.textStylePicker.alignLeftOption": "左对齐", + "xpack.canvas.textStylePicker.alignRightOption": "右对齐", + "xpack.canvas.textStylePicker.styleBoldOption": "粗体", + "xpack.canvas.textStylePicker.styleItalicOption": "斜体", + "xpack.canvas.textStylePicker.styleUnderlineOption": "下划线", + "xpack.canvas.timePicker.applyButtonLabel": "应用", + "xpack.canvas.toolbar.editorButtonLabel": "表达式编辑器", + "xpack.canvas.toolbar.nextPageAriaLabel": "下一页", + "xpack.canvas.toolbar.pageButtonLabel": "页 {pageNum}{rest}", + "xpack.canvas.toolbar.previousPageAriaLabel": "上一页", + "xpack.canvas.toolbar.workpadManagerCloseButtonLabel": "关闭", + "xpack.canvas.toolbarTray.closeTrayAriaLabel": "关闭托盘", + "xpack.canvas.transitions.fade.displayName": "淡化", + "xpack.canvas.transitions.fade.help": "从一页淡入到下一页", + "xpack.canvas.transitions.rotate.displayName": "旋转", + "xpack.canvas.transitions.rotate.help": "从一页旋转到下一页", + "xpack.canvas.transitions.slide.displayName": "滑动", + "xpack.canvas.transitions.slide.help": "从一页滑到下一页", + "xpack.canvas.transitions.zoom.displayName": "缩放", + "xpack.canvas.transitions.zoom.help": "从一页缩放到下一页", + "xpack.canvas.uis.arguments.axisConfig.position.options.bottomDropDown": "下", + "xpack.canvas.uis.arguments.axisConfig.position.options.leftDropDown": "左", + "xpack.canvas.uis.arguments.axisConfig.position.options.rightDropDown": "右", + "xpack.canvas.uis.arguments.axisConfig.position.options.topDropDown": "上", + "xpack.canvas.uis.arguments.axisConfig.positionLabel": "位置", + "xpack.canvas.uis.arguments.axisConfigLabel": "可视化轴配置", + "xpack.canvas.uis.arguments.axisConfigTitle": "轴配置", + "xpack.canvas.uis.arguments.dataColumn.options.averageDropDown": "平均值", + "xpack.canvas.uis.arguments.dataColumn.options.countDropDown": "计数", + "xpack.canvas.uis.arguments.dataColumn.options.firstDropDown": "第一", + "xpack.canvas.uis.arguments.dataColumn.options.lastDropDown": "最后", + "xpack.canvas.uis.arguments.dataColumn.options.maxDropDown": "最大值", + "xpack.canvas.uis.arguments.dataColumn.options.medianDropDown": "中值", + "xpack.canvas.uis.arguments.dataColumn.options.minDropDown": "最小值", + "xpack.canvas.uis.arguments.dataColumn.options.sumDropDown": "和", + "xpack.canvas.uis.arguments.dataColumn.options.uniqueDropDown": "唯一", + "xpack.canvas.uis.arguments.dataColumn.options.valueDropDown": "值", + "xpack.canvas.uis.arguments.dataColumnLabel": "选择数据列", + "xpack.canvas.uis.arguments.dataColumnTitle": "列", + "xpack.canvas.uis.arguments.dateFormatLabel": "选择或输入 {momentJS} 格式", + "xpack.canvas.uis.arguments.dateFormatTitle": "日期格式", + "xpack.canvas.uis.arguments.filterGroup.createNewGroupLinkText": "创建新组", + "xpack.canvas.uis.arguments.filterGroupLabel": "创建或选择筛选组", + "xpack.canvas.uis.arguments.filterGroupTitle": "筛选组", + "xpack.canvas.uis.arguments.imageUpload.fileUploadPromptLabel": "选择或拖放图像", + "xpack.canvas.uis.arguments.imageUpload.imageUploadingLabel": "图像上传", + "xpack.canvas.uis.arguments.imageUpload.urlFieldPlaceholder": "图像 {url}", + "xpack.canvas.uis.arguments.imageUpload.urlTypes.assetDropDown": "资产", + "xpack.canvas.uis.arguments.imageUpload.urlTypes.fileDropDown": "导入", + "xpack.canvas.uis.arguments.imageUpload.urlTypes.linkDropDown": "链接", + "xpack.canvas.uis.arguments.imageUploadLabel": "选择或上传图像", + "xpack.canvas.uis.arguments.imageUploadTitle": "图像上传", + "xpack.canvas.uis.arguments.numberFormat.format.bytesDropDown": "字节", + "xpack.canvas.uis.arguments.numberFormat.format.currencyDropDown": "货币", + "xpack.canvas.uis.arguments.numberFormat.format.durationDropDown": "持续时间", + "xpack.canvas.uis.arguments.numberFormat.format.numberDropDown": "数字", + "xpack.canvas.uis.arguments.numberFormat.format.percentDropDown": "百分比", + "xpack.canvas.uis.arguments.numberFormatLabel": "选择或输入有效的 {numeralJS} 格式", + "xpack.canvas.uis.arguments.numberFormatTitle": "数字格式", + "xpack.canvas.uis.arguments.numberLabel": "输入数字", + "xpack.canvas.uis.arguments.numberTitle": "数字", + "xpack.canvas.uis.arguments.paletteLabel": "选择调色板", + "xpack.canvas.uis.arguments.paletteTitle": "调色板", + "xpack.canvas.uis.arguments.percentageLabel": "百分比滑块 ", + "xpack.canvas.uis.arguments.percentageTitle": "百分比", + "xpack.canvas.uis.arguments.rangeLabel": "范围内的值滑块", + "xpack.canvas.uis.arguments.rangeTitle": "范围", + "xpack.canvas.uis.arguments.selectLabel": "从具有多个选项的下拉列表中选择", + "xpack.canvas.uis.arguments.selectTitle": "选择", + "xpack.canvas.uis.arguments.shapeLabel": "形状选取器", + "xpack.canvas.uis.arguments.shapeTitle": "形状", + "xpack.canvas.uis.arguments.stringLabel": "输入短字符串", + "xpack.canvas.uis.arguments.stringTitle": "字符串", + "xpack.canvas.uis.arguments.textareaLabel": "输入长字符串", + "xpack.canvas.uis.arguments.textareaTitle": "文本区域", + "xpack.canvas.uis.arguments.toggleLabel": "True/False 切换开关", + "xpack.canvas.uis.arguments.toggleTitle": "切换", + "xpack.canvas.uis.dataSources.demoData.headingTitle": "您正在使用演示数据", + "xpack.canvas.uis.dataSources.demoDataLabel": "使用用户名、价格、项目、国家/地区和阶段模拟数据集", + "xpack.canvas.uis.dataSources.demoDataTitle": "演示数据", + "xpack.canvas.uis.dataSources.essqlLabel": "使用 {elasticsearch} {sql} 以获取数据表", + "xpack.canvas.uis.dataSources.essqlTitle": "{elasticsearch} {sql}", + "xpack.canvas.uis.dataSources.timelion.intervalTitle": "时间间隔", + "xpack.canvas.uis.dataSources.timelion.queryLabel": "{lucene} 查询字符串语法", + "xpack.canvas.uis.dataSources.timelion.queryTitle": "查询", + "xpack.canvas.uis.dataSources.timelion.tips.functions": "一些 {timelion} 函数(如 {functionExample})不转换成 {canvas} 数据表。任何与数据操作有关的内容都适用。", + "xpack.canvas.uis.dataSources.timelion.tips.time": "{timelion} 需要时间范围,您应将时间筛选元素添加到页面上的某个位置,或使用代码编辑器传入时间筛选。", + "xpack.canvas.uis.dataSources.timelionLabel": "使用 {timelion} 语法检索时间序列", + "xpack.canvas.uis.models.math.args.valueLabel": "要用于从数据源提取值的函数和列", + "xpack.canvas.uis.models.math.args.valueTitle": "值", + "xpack.canvas.uis.models.mathTitle": "度量", + "xpack.canvas.uis.models.pointSeries.args.colorLabel": "确定标记或序列的颜色", + "xpack.canvas.uis.models.pointSeries.args.colorTitle": "颜色", + "xpack.canvas.uis.models.pointSeries.args.sizeLabel": "确定标记的大小", + "xpack.canvas.uis.models.pointSeries.args.sizeTitle": "大小", + "xpack.canvas.uis.models.pointSeries.args.textLabel": "设置要用作标记或用在标记旁的文本", + "xpack.canvas.uis.models.pointSeries.args.textTitle": "文本", + "xpack.canvas.uis.models.pointSeries.args.xaxisLabel": "横轴上的数据。通常为数字、字符串或日期", + "xpack.canvas.uis.models.pointSeries.args.xaxisTitle": "X 轴", + "xpack.canvas.uis.models.pointSeries.args.yaxisLabel": "竖轴上的数据。通常为数字", + "xpack.canvas.uis.models.pointSeries.args.yaxisTitle": "Y 轴", + "xpack.canvas.uis.models.pointSeriesTitle": "维度和度量", + "xpack.canvas.uis.transforms.formatDate.args.formatTitle": "格式", + "xpack.canvas.uis.transforms.formatDateTitle": "日期格式", + "xpack.canvas.uis.transforms.formatNumber.args.formatTitle": "格式", + "xpack.canvas.uis.transforms.formatNumberTitle": "数字格式", + "xpack.canvas.uis.transforms.roundDate.args.formatLabel": "选择或输入 {momentJs} 格式以舍入日期", + "xpack.canvas.uis.transforms.roundDate.args.formatTitle": "格式", + "xpack.canvas.uis.transforms.roundDateTitle": "舍入日期", + "xpack.canvas.uis.transforms.sort.args.reverseToggleSwitch": "降序", + "xpack.canvas.uis.transforms.sort.args.sortFieldTitle": "排序字段", + "xpack.canvas.uis.transforms.sortTitle": "数据表排序", + "xpack.canvas.uis.views.dropdownControl.args.filterColumnLabel": "从下拉列表中选择的值应用到的列", + "xpack.canvas.uis.views.dropdownControl.args.filterColumnTitle": "筛选列", + "xpack.canvas.uis.views.dropdownControl.args.filterGroupLabel": "将选定组名称应用到元素的筛选函数,以定位该筛选", + "xpack.canvas.uis.views.dropdownControl.args.filterGroupTitle": "筛选组", + "xpack.canvas.uis.views.dropdownControl.args.valueColumnLabel": "从其中提取下拉列表可用值的列", + "xpack.canvas.uis.views.dropdownControl.args.valueColumnTitle": "值列", + "xpack.canvas.uis.views.dropdownControlTitle": "下拉列表筛选", + "xpack.canvas.uis.views.getCellLabel": "获取第一行和第一列", + "xpack.canvas.uis.views.getCellTitle": "下拉列表筛选", + "xpack.canvas.uis.views.image.args.mode.containDropDown": "包含", + "xpack.canvas.uis.views.image.args.mode.coverDropDown": "覆盖", + "xpack.canvas.uis.views.image.args.mode.stretchDropDown": "拉伸", + "xpack.canvas.uis.views.image.args.modeLabel": "注意:拉伸填充可能不适用于矢量图。", + "xpack.canvas.uis.views.image.args.modeTitle": "填充模式", + "xpack.canvas.uis.views.imageTitle": "图像", + "xpack.canvas.uis.views.markdown.args.contentLabel": "{markdown} 格式文本", + "xpack.canvas.uis.views.markdown.args.contentTitle": "{markdown} 内容", + "xpack.canvas.uis.views.markdownLabel": "使用 {markdown} 生成标记", + "xpack.canvas.uis.views.markdownTitle": "{markdown}", + "xpack.canvas.uis.views.metric.args.labelArgLabel": "描述指标", + "xpack.canvas.uis.views.metric.args.labelArgTitle": "标签", + "xpack.canvas.uis.views.metric.args.labelFontLabel": "字体、对齐和颜色", + "xpack.canvas.uis.views.metric.args.labelFontTitle": "标签文本设置", + "xpack.canvas.uis.views.metric.args.metricFontLabel": "字体、对齐和颜色", + "xpack.canvas.uis.views.metric.args.metricFontTitle": "指标文本设置", + "xpack.canvas.uis.views.metric.args.metricFormatLabel": "字体、对齐和颜色", + "xpack.canvas.uis.views.metric.args.metricFormatTitle": "指标格式", + "xpack.canvas.uis.views.metricTitle": "指标", + "xpack.canvas.uis.views.numberArgTitle": "数字", + "xpack.canvas.uis.views.pie.args.holeLabel": "孔洞半径", + "xpack.canvas.uis.views.pie.args.holeTitle": "内半径", + "xpack.canvas.uis.views.pie.args.labelRadiusLabel": "标签到饼图中心的距离", + "xpack.canvas.uis.views.pie.args.labelRadiusTitle": "标签半径", + "xpack.canvas.uis.views.pie.args.labelsTitle": "标签", + "xpack.canvas.uis.views.pie.args.labelsToggleSwitch": "显示/隐藏标签", + "xpack.canvas.uis.views.pie.args.legendLabel": "禁用或定位图例", + "xpack.canvas.uis.views.pie.args.legendTitle": "图例位置", + "xpack.canvas.uis.views.pie.args.radiusLabel": "饼图半径", + "xpack.canvas.uis.views.pie.args.radiusTitle": "半径", + "xpack.canvas.uis.views.pie.args.tiltLabel": "倾斜百分比,其中 100 为完全垂直,0 为完全水平", + "xpack.canvas.uis.views.pie.args.tiltTitle": "倾斜角度", + "xpack.canvas.uis.views.pieTitle": "图表样式", + "xpack.canvas.uis.views.plot.args.defaultStyleLabel": "设置每个序列默认使用的样式,除非被覆盖", + "xpack.canvas.uis.views.plot.args.defaultStyleTitle": "默认样式", + "xpack.canvas.uis.views.plot.args.legendLabel": "禁用或定位图例", + "xpack.canvas.uis.views.plot.args.legendTitle": "图例位置", + "xpack.canvas.uis.views.plot.args.xaxisLabel": "配置或禁用 X 轴", + "xpack.canvas.uis.views.plot.args.xaxisTitle": "X 轴", + "xpack.canvas.uis.views.plot.args.yaxisLabel": "配置或禁用 Y 轴", + "xpack.canvas.uis.views.plot.args.yaxisTitle": "Y 轴", + "xpack.canvas.uis.views.plotTitle": "图表样式", + "xpack.canvas.uis.views.progress.args.barColorLabel": "接受 HEX、RGB 或 HTML 颜色名称", + "xpack.canvas.uis.views.progress.args.barColorTitle": "背景色", + "xpack.canvas.uis.views.progress.args.barWeightLabel": "背景条形的粗细", + "xpack.canvas.uis.views.progress.args.barWeightTitle": "背景权重", + "xpack.canvas.uis.views.progress.args.fontLabel": "标签的字体设置。通常,也可以添加其他样式", + "xpack.canvas.uis.views.progress.args.fontTitle": "标签设置", + "xpack.canvas.uis.views.progress.args.labelArgLabel": "设置 {true}/{false} 以显示/隐藏标签或提供显示为标签的字符串", + "xpack.canvas.uis.views.progress.args.labelArgTitle": "标签", + "xpack.canvas.uis.views.progress.args.maxLabel": "进度元素的最大值", + "xpack.canvas.uis.views.progress.args.maxTitle": "最大值", + "xpack.canvas.uis.views.progress.args.shapeLabel": "进度指示的形状", + "xpack.canvas.uis.views.progress.args.shapeTitle": "形状", + "xpack.canvas.uis.views.progress.args.valueColorLabel": "接受 {hex}、{rgb} 或 {html} 颜色名称", + "xpack.canvas.uis.views.progress.args.valueColorTitle": "进度颜色", + "xpack.canvas.uis.views.progress.args.valueWeightLabel": "进度条的粗细", + "xpack.canvas.uis.views.progress.args.valueWeightTitle": "进度权重", + "xpack.canvas.uis.views.progressTitle": "进度", + "xpack.canvas.uis.views.render.args.css.applyButtonLabel": "应用样式表", + "xpack.canvas.uis.views.render.args.cssLabel": "适用于您的元素的 {css} 样式表", + "xpack.canvas.uis.views.renderLabel": "您的元素的容器设置", + "xpack.canvas.uis.views.renderTitle": "元素样式", + "xpack.canvas.uis.views.repeatImage.args.emptyImageLabel": "填补值与最大计数之间差异的图像", + "xpack.canvas.uis.views.repeatImage.args.emptyImageTitle": "空图像", + "xpack.canvas.uis.views.repeatImage.args.imageLabel": "要重复的图像", + "xpack.canvas.uis.views.repeatImage.args.imageTitle": "图像", + "xpack.canvas.uis.views.repeatImage.args.maxLabel": "重复图像的最大数目", + "xpack.canvas.uis.views.repeatImage.args.maxTitle": "最大计数", + "xpack.canvas.uis.views.repeatImage.args.sizeLabel": "图像最大维度的大小。例如,如果图像高而不宽,则其为高度", + "xpack.canvas.uis.views.repeatImage.args.sizeTitle": "图像大小", + "xpack.canvas.uis.views.repeatImageTitle": "重复图像", + "xpack.canvas.uis.views.revealImage.args.emptyImageLabel": "背景图像。例如,空杯子", + "xpack.canvas.uis.views.revealImage.args.emptyImageTitle": "背景图像", + "xpack.canvas.uis.views.revealImage.args.imageLabel": "显示给定函数输入的图像。例如,满杯子", + "xpack.canvas.uis.views.revealImage.args.imageTitle": "图像", + "xpack.canvas.uis.views.revealImage.args.origin.bottomDropDown": "下", + "xpack.canvas.uis.views.revealImage.args.origin.leftDropDown": "左", + "xpack.canvas.uis.views.revealImage.args.origin.rightDropDown": "右", + "xpack.canvas.uis.views.revealImage.args.origin.topDropDown": "上", + "xpack.canvas.uis.views.revealImage.args.originLabel": "开始显示的方向", + "xpack.canvas.uis.views.revealImage.args.originTitle": "显示自", + "xpack.canvas.uis.views.revealImageTitle": "显示图像", + "xpack.canvas.uis.views.shape.args.borderLabel": "接受 HEX、RGB 或 HTML 颜色名称", + "xpack.canvas.uis.views.shape.args.borderTitle": "边框", + "xpack.canvas.uis.views.shape.args.borderWidthLabel": "边框宽度", + "xpack.canvas.uis.views.shape.args.borderWidthTitle": "边框宽度", + "xpack.canvas.uis.views.shape.args.fillLabel": "接受 HEX、RGB 或 HTML 颜色名称", + "xpack.canvas.uis.views.shape.args.fillTitle": "填充", + "xpack.canvas.uis.views.shape.args.maintainAspectTitle": "保持纵横比", + "xpack.canvas.uis.views.shape.args.shapeTitle": "选择形状", + "xpack.canvas.uis.views.shapeTitle": "形状", + "xpack.canvas.uis.views.table.args.paginateLabel": "显示或隐藏分页控制。如果禁用,仅第一页显示", + "xpack.canvas.uis.views.table.args.paginateTitle": "分页", + "xpack.canvas.uis.views.table.args.perPageLabel": "每个表页面要显示的行数", + "xpack.canvas.uis.views.table.args.perPageTitle": "每页行数", + "xpack.canvas.uis.views.table.args.showHeaderLabel": "显示或隐藏具有每列标题的标题行", + "xpack.canvas.uis.views.table.args.showHeaderTitle": "标题", + "xpack.canvas.uis.views.tableLabel": "设置表元素的样式", + "xpack.canvas.uis.views.tableTitle": "表样式", + "xpack.canvas.uis.views.timefilter.args.columnConfirmButtonLabel": "设置", + "xpack.canvas.uis.views.timefilter.args.columnLabel": "应用选定时间的列", + "xpack.canvas.uis.views.timefilter.args.columnTitle": "列", + "xpack.canvas.uis.views.timefilter.args.filterGroupLabel": "将选定组名称应用到元素的筛选函数,以定位该筛选", + "xpack.canvas.uis.views.timefilter.args.filterGroupTitle": "筛选组名称", + "xpack.canvas.uis.views.timefilterTitle": "时间筛选", + "xpack.canvas.units.quickRange.dayBeforeYesterday": "前天", + "xpack.canvas.units.quickRange.last12Hours": "过去 12 小时", + "xpack.canvas.units.quickRange.last15Minutes": "过去 15 分钟", + "xpack.canvas.units.quickRange.last1Hour": "过去 1 小时", + "xpack.canvas.units.quickRange.last1Year": "过去 1 年", + "xpack.canvas.units.quickRange.last24Hours": "过去 24 小时", + "xpack.canvas.units.quickRange.last2Weeks": "过去 2 周", + "xpack.canvas.units.quickRange.last2Years": "过去 2 年", + "xpack.canvas.units.quickRange.last30Days": "过去 30 天", + "xpack.canvas.units.quickRange.last30Minutes": "过去 30 分钟", + "xpack.canvas.units.quickRange.last4Hours": "过去 4 小时", + "xpack.canvas.units.quickRange.last5Years": "过去 5 年", + "xpack.canvas.units.quickRange.last60Days": "过去 60 天", + "xpack.canvas.units.quickRange.last6Months": "过去 6 个月", + "xpack.canvas.units.quickRange.last7Days": "过去 7 天", + "xpack.canvas.units.quickRange.last90Days": "过去 90 天", + "xpack.canvas.units.quickRange.monthToDate": "本月迄今为止", + "xpack.canvas.units.quickRange.previousMonth": "上一月", + "xpack.canvas.units.quickRange.previousWeek": "上一周", + "xpack.canvas.units.quickRange.previousYear": "上一年", + "xpack.canvas.units.quickRange.theDaySoFar": "今天迄今为止", + "xpack.canvas.units.quickRange.thisDayLastWeek": "上周本日", + "xpack.canvas.units.quickRange.thisMonth": "本月", + "xpack.canvas.units.quickRange.thisWeek": "本周", + "xpack.canvas.units.quickRange.thisYear": "本年", + "xpack.canvas.units.quickRange.today": "今日", + "xpack.canvas.units.quickRange.weekToDate": "本周迄今为止", + "xpack.canvas.units.quickRange.yearToDate": "本年迄今为止", + "xpack.canvas.units.quickRange.yesterday": "昨天", + "xpack.canvas.units.time.days": "{days, plural, one {# 天} other {# 天}}", + "xpack.canvas.units.time.hours": "{hours, plural, one {# 小时} other {# 小时}}", + "xpack.canvas.units.time.minutes": "{minutes, plural, one {# 分钟} other {# 分钟}}", + "xpack.canvas.units.time.seconds": "{seconds, plural, one {# 秒} other {# 秒}}", + "xpack.canvas.workpadConfig.applyStylesheetButtonLabel": "应用样式表", + "xpack.canvas.workpadConfig.globalCSSLabel": "全局 CSS 覆盖", + "xpack.canvas.workpadConfig.globalCSSTooltip": "将样式应用到此 Workpad 中的所有页面", + "xpack.canvas.workpadConfig.heightLabel": "高", + "xpack.canvas.workpadConfig.nameLabel": "名称", + "xpack.canvas.workpadConfig.pageSizeBadgeAriaLabel": "预设页面大小:{sizeName}", + "xpack.canvas.workpadConfig.pageSizeBadgeOnClickAriaLabel": "将页面大小设置为 {sizeName}", + "xpack.canvas.workpadConfig.swapDimensionsAriaLabel": "交换页面的宽和高", + "xpack.canvas.workpadConfig.swapDimensionsTooltip": "交换宽高", + "xpack.canvas.workpadConfig.title": "Workpad", + "xpack.canvas.workpadConfig.USLetterButtonLabel": "美国信函", + "xpack.canvas.workpadConfig.widthLabel": "宽", + "xpack.canvas.workpadCreate.createButtonLabel": "创建 Workpad", + "xpack.canvas.workpadHeader.addElementButtonLabel": "添加元素", + "xpack.canvas.workpadHeader.addElementModalCloseButtonLabel": "关闭", + "xpack.canvas.workpadHeader.cycleIntervalDaysText": "每 {days} {days, plural, one {天} other {天}}", + "xpack.canvas.workpadHeader.cycleIntervalHoursText": "每 {hours} {hours, plural, one {小时} other {小时}}", + "xpack.canvas.workpadHeader.cycleIntervalMinutesText": "每 {minutes} {minutes, plural, one {分钟} other {分钟}}", + "xpack.canvas.workpadHeader.cycleIntervalSecondsText": "每 {seconds} {seconds, plural, one {秒} other {秒}}", + "xpack.canvas.workpadHeader.embedObjectButtonLabel": "嵌入对象", + "xpack.canvas.workpadHeader.fullscreenButtonAriaLabel": "全屏查看", + "xpack.canvas.workpadHeader.fullscreenTooltip": "进入全屏模式", + "xpack.canvas.workpadHeader.hideEditControlTooltip": "隐藏编辑控件", + "xpack.canvas.workpadHeader.noWritePermissionTooltip": "您无权编辑此 Workpad", + "xpack.canvas.workpadHeader.showEditControlTooltip": "显示编辑控件", + "xpack.canvas.workpadHeaderAutoRefreshControls.disableTooltip": "禁用自动刷新", + "xpack.canvas.workpadHeaderAutoRefreshControls.intervalFormLabel": "更改自动刷新时间间隔", + "xpack.canvas.workpadHeaderAutoRefreshControls.refreshListDurationManualText": "手动", + "xpack.canvas.workpadHeaderAutoRefreshControls.refreshListTitle": "刷新元素", + "xpack.canvas.workpadHeaderControlSettings.settingsTooltip": "控制设置", + "xpack.canvas.workpadHeaderCustomInterval.confirmButtonLabel": "设置", + "xpack.canvas.workpadHeaderCustomInterval.formDescription": "使用速记表示法,如 {secondsExample}、{minutesExample} 或 {hoursExample}", + "xpack.canvas.workpadHeaderCustomInterval.formLabel": "设置定制时间间隔", + "xpack.canvas.workpadHeaderKioskControl.controlTitle": "循环播放全屏页面", + "xpack.canvas.workpadHeaderKioskControl.cycleFormLabel": "更改循环播放时间间隔", + "xpack.canvas.workpadHeaderKioskControl.cycleToggleSwitch": "自动循环播放幻灯片", + "xpack.canvas.workpadHeaderRefreshControlSettings.refreshAriaLabel": "刷新元素", + "xpack.canvas.workpadHeaderRefreshControlSettings.refreshTooltip": "刷新数据", + "xpack.canvas.workpadHeaderWorkpadExport.copyPDFMessage": "{PDF} 生成 {URL} 已复制到剪贴板", + "xpack.canvas.workpadHeaderWorkpadExport.copyReportingConfigMessage": "已将报告配置复制到剪贴板", + "xpack.canvas.workpadHeaderWorkpadExport.copyShareConfigMessage": "已将共享标记复制到剪贴板", + "xpack.canvas.workpadHeaderWorkpadExport.exportPDFErrorMessage": "无法为“{workpadName}”创建 {PDF}", + "xpack.canvas.workpadHeaderWorkpadExport.exportPDFMessage": "正在导出 {PDF}。可以在“管理”中跟踪进度。", + "xpack.canvas.workpadHeaderWorkpadExport.exportPDFTitle": "Workpad“{workpadName}”的 {PDF} 导出", + "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyAriaLabel": "或者,也可以从脚本或使用 {URL} 通过 Watcher 生成 {PDF}。按 Enter 键可将 {URL} 复制到剪贴板。", + "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyButtonLabel": "复制 {POST} {URL}", + "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelCopyDescription": "或者,复制此 {POST} {URL} 以从 {KIBANA} 外部或从 Watcher 调用生成。", + "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelDisabledDescription": "导出到 PDF 已禁用。必须配置报告,才能使用 Chromium 浏览器。将其添加到您的 {fileName} 文件中。", + "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelGenerateButtonLabel": "生成 {PDF}", + "xpack.canvas.workpadHeaderWorkpadExport.pdfPanelGenerateDescription": "{PDF} 可能会花费 1 或 2 分钟生成,取决于 Workpad 的大小。", + "xpack.canvas.workpadHeaderWorkpadExport.shareDownloadJSONTitle": "下载为 {JSON}", + "xpack.canvas.workpadHeaderWorkpadExport.shareDownloadPDFTitle": "{PDF} 报告", + "xpack.canvas.workpadHeaderWorkpadExport.shareWebsiteErrorTitle": "无法为“{workpadName}”创建 {ZIP} 文件。Workpad 可能过大。您将需要分别下载文件。", + "xpack.canvas.workpadHeaderWorkpadExport.shareWebsiteTitle": "在网站上共享", + "xpack.canvas.workpadHeaderWorkpadExport.shareWorkpadMessage": "共享此 Workpad", + "xpack.canvas.workpadHeaderWorkpadExport.unknownExportErrorMessage": "未知导出类型:{type}", + "xpack.canvas.workpadHeaderWorkpadExport.unsupportedRendererWarning": "此 Workpad 包含 {CANVAS} Shareable Workpad Runtime 不支持的呈现函数。将不会呈现以下元素:", + "xpack.canvas.workpadHeaderWorkpadZoom.zoomControlsAriaLabel": "缩放控制", + "xpack.canvas.workpadHeaderWorkpadZoom.zoomControlsTooltip": "缩放控制", + "xpack.canvas.workpadHeaderWorkpadZoom.zoomFitToWindowText": "适应窗口大小", + "xpack.canvas.workpadHeaderWorkpadZoom.zoomInText": "放大", + "xpack.canvas.workpadHeaderWorkpadZoom.zoomOutText": "缩小", + "xpack.canvas.workpadHeaderWorkpadZoom.zoomPanelTitle": "缩放", + "xpack.canvas.workpadHeaderWorkpadZoom.zoomPrecentageValue": "重置", + "xpack.canvas.workpadHeaderWorkpadZoom.zoomResetText": "{scalePercentage}%", + "xpack.canvas.workpadLoader.clonedWorkpadName": "{workpadName} 的副本", + "xpack.canvas.workpadLoader.cloneTooltip": "克隆 Workpad", + "xpack.canvas.workpadLoader.createWorkpadLoadingDescription": "正在创建 Workpad......", + "xpack.canvas.workpadLoader.deleteButtonAriaLabel": "删除 {numberOfWorkpads} 个 Workpad", + "xpack.canvas.workpadLoader.deleteButtonLabel": "删除 ({numberOfWorkpads})", + "xpack.canvas.workpadLoader.deleteModalConfirmButtonLabel": "删除", + "xpack.canvas.workpadLoader.deleteModalDescription": "您无法恢复删除的 Workpad。", + "xpack.canvas.workpadLoader.deleteMultipleWorkpadsModalTitle": "删除 {numberOfWorkpads} 个 Workpad?", + "xpack.canvas.workpadLoader.deleteSingleWorkpadModalTitle": "删除 Workpad“{workpadName}”?", + "xpack.canvas.workpadLoader.emptyPromptGettingStartedDescription": "创建新的 Workpad、从模板入手或通过将 Workpad {JSON} 文件拖放到此处来导入。", + "xpack.canvas.workpadLoader.emptyPromptNewUserDescription": "{CANVAS} 新手?", + "xpack.canvas.workpadLoader.emptyPromptTitle": "添加您的首个 Workpad", + "xpack.canvas.workpadLoader.exportButtonAriaLabel": "导出 {numberOfWorkpads} 个 Workpad", + "xpack.canvas.workpadLoader.exportButtonLabel": "导出 ({numberOfWorkpads})", + "xpack.canvas.workpadLoader.exportTooltip": "导出 Workpad", + "xpack.canvas.workpadLoader.fetchLoadingDescription": "正在获取 Workpad......", + "xpack.canvas.workpadLoader.filePickerPlaceholder": "导入 Workpad {JSON} 文件", + "xpack.canvas.workpadLoader.loadWorkpadArialLabel": "加载 Workpad“{workpadName}”", + "xpack.canvas.workpadLoader.noPermissionToCloneToolTip": "您无权克隆 Workpad", + "xpack.canvas.workpadLoader.noPermissionToCreateToolTip": "您无权创建 Workpad", + "xpack.canvas.workpadLoader.noPermissionToDeleteToolTip": "您无权删除 Workpad", + "xpack.canvas.workpadLoader.noPermissionToUploadToolTip": "您无权上传 Workpad", + "xpack.canvas.workpadLoader.sampleDataLinkLabel": "添加您的首个 Workpad", + "xpack.canvas.workpadLoader.table.createdColumnTitle": "创建时间", + "xpack.canvas.workpadLoader.table.nameColumnTitle": "Workpad 名称", + "xpack.canvas.workpadLoader.table.updatedColumnTitle": "更新时间", + "xpack.canvas.workpadManager.modalTitle": "{CANVAS} Workpad", + "xpack.canvas.workpadManager.myWorkpadsTabLabel": "我的 Workpad", + "xpack.canvas.workpadManager.workpadTemplatesTabLabel": "模板", + "xpack.canvas.workpadSearch.searchPlaceholder": "查找 Workpad", + "xpack.canvas.workpadTemplate.cloneTemplateLinkAriaLabel": "克隆 Workpad 模板“{templateName}”", + "xpack.canvas.workpadTemplate.searchPlaceholder": "查找模板", + "xpack.canvas.workpadTemplates.table.descriptionColumnTitle": "描述", + "xpack.canvas.workpadTemplates.table.nameColumnTitle": "模板名称", + "xpack.canvas.workpadTemplates.table.tagsColumnTitle": "标记", + "xpack.canvas.badge.readOnly.tooltip": "无法保存 {canvas} Workpad", "xpack.crossClusterReplication.addAutoFollowPatternButtonLabel": "创建自动跟随模式", "xpack.crossClusterReplication.addBreadcrumbTitle": "添加", "xpack.crossClusterReplication.addFollowerButtonLabel": "创建 Follower 索引", @@ -4262,37 +5184,37 @@ "xpack.graph.serverSideErrors.unavailableGraphErrorMessage": "Graph 不可用", "xpack.graph.serverSideErrors.unavailableLicenseInformationErrorMessage": "Graph 不可用 - 许可信息当前不可用。", "xpack.graph.serverSideErrors.wrongLicenseTypeErrorMessage": "当前{licenseType}许可的 Graph 不可用。请升级您的许可。", - "xpack.graph.settings.advancedSettings.certaintyInputHelpText": "在引入相关字词之前作为证据所需的最小文档数量", + "xpack.graph.settings.advancedSettings.certaintyInputHelpText": "在引入相关字词之前作为证据所需的最小文档数量。", "xpack.graph.settings.advancedSettings.certaintyInputLabel": "确定性", "xpack.graph.settings.advancedSettings.diversityFieldInputHelpText1": "为避免文档示例过于雷同,请选取有助于识别偏差来源的字段。", - "xpack.graph.settings.advancedSettings.diversityFieldInputHelpText2": "此字段必须为单字字段,否则会拒绝搜索,并发生错误", + "xpack.graph.settings.advancedSettings.diversityFieldInputHelpText2": "此字段必须为单字字段,否则会拒绝搜索,并发生错误。", "xpack.graph.settings.advancedSettings.diversityFieldInputLabel": "多元化字段", "xpack.graph.settings.advancedSettings.diversityFieldInputOptionLabel": "[没有多元化]", "xpack.graph.settings.advancedSettings.maxValuesInputHelpText": "示例中可以包含相同", "xpack.graph.settings.advancedSettings.maxValuesInputHelpText.fieldText": "字段", "xpack.graph.settings.advancedSettings.maxValuesInputLabel": "每个字段的最大文档数量", - "xpack.graph.settings.advancedSettings.sampleSizeInputHelpText": "字词从最相关的文档示例中识别。并非越大越好 - 速度可能会更慢,相关性更低。", + "xpack.graph.settings.advancedSettings.sampleSizeInputHelpText": "字词从最相关的文档样本中进行识别。较大样本不一定更好—因为较大的样本会更慢且相关性更差。", "xpack.graph.settings.advancedSettings.sampleSizeInputLabel": "示例大小", - "xpack.graph.settings.advancedSettings.significantLinksCheckboxHelpText": "识别“重要”而不只是常用的字词", + "xpack.graph.settings.advancedSettings.significantLinksCheckboxHelpText": "识别“重要”而不只是常用的字词。", "xpack.graph.settings.advancedSettings.significantLinksCheckboxLabel": "重要链接", - "xpack.graph.settings.advancedSettings.timeoutInputHelpText": "请求可以运行的最大时间(以毫秒为单位)", + "xpack.graph.settings.advancedSettings.timeoutInputHelpText": "请求可以运行的最大时间(以毫秒为单位)。", "xpack.graph.settings.advancedSettings.timeoutInputLabel": "超时 (ms)", "xpack.graph.settings.advancedSettingsTitle": "高级设置", - "xpack.graph.settings.blacklist.blacklistHelpText": "这些字词当前已列入黑名单,不允许重新显示在工作空间中", + "xpack.graph.settings.blacklist.blacklistHelpText": "这些字词当前已列入黑名单,不允许重新显示在工作空间中。", "xpack.graph.settings.blacklist.clearButtonLabel": "清除", "xpack.graph.settings.blacklistTitle": "黑名单", "xpack.graph.settings.drillDowns.defaultUrlTemplateTitle": "原始文档", - "xpack.graph.settings.drillDowns.invalidUrlWarningText": "URL 必须包含 {placeholder} 字符串", + "xpack.graph.settings.drillDowns.invalidUrlWarningText": "URL 必须包含 {placeholder} 字符串。", "xpack.graph.settings.drillDowns.kibanaUrlWarningText": "这会类似于 Kibana URL。是否要将其转换为模板?", "xpack.graph.settings.drillDowns.resetButtonLabel": "重置", "xpack.graph.settings.drillDowns.toolbarIconPickerLabel": "工具栏图标", "xpack.graph.settings.drillDowns.urlDescriptionInputLabel": "标题", "xpack.graph.settings.drillDowns.urlDescriptionInputPlaceholder": "在 Google 上搜索", "xpack.graph.settings.drillDowns.urlEncoderInputLabel": "URL 参数类型", - "xpack.graph.settings.drillDowns.urlInputHelpText": "使用 {gquery} 定义模板 URL,在其中插入选择的顶点字词", + "xpack.graph.settings.drillDowns.urlInputHelpText": "使用插入选定顶点字词的 {gquery} 定义模板 URL。", "xpack.graph.settings.drillDowns.urlInputLabel": "URL", "xpack.graph.settings.drillDownsTitle": "向下钻取", - "xpack.graph.sidebar.displayLabelHelpText": "更改此顶点的标签", + "xpack.graph.sidebar.displayLabelHelpText": "更改此顶点的标签。", "xpack.graph.sidebar.displayLabelLabel": "显示标签", "xpack.graph.sidebar.drillDowns.noDrillDownsHelpText": "从设置菜单配置向下钻取", "xpack.graph.sidebar.drillDownsTitle": "向下钻取", @@ -4306,7 +5228,7 @@ "xpack.graph.sidebar.linkSummaryTitle": "链接摘要", "xpack.graph.sidebar.selections.invertSelectionButtonLabel": "反向", "xpack.graph.sidebar.selections.invertSelectionButtonTooltip": "反向选择", - "xpack.graph.sidebar.selections.noSelectionsHelpText": "不选择。点击顶点以添加", + "xpack.graph.sidebar.selections.noSelectionsHelpText": "无选择。点击顶点以添加。", "xpack.graph.sidebar.selections.selectAllButtonLabel": "全部", "xpack.graph.sidebar.selections.selectAllButtonTooltip": "全选", "xpack.graph.sidebar.selections.selectNeighboursButtonLabel": "已链接", @@ -4331,8 +5253,8 @@ "xpack.graph.topNavMenu.newWorkspaceLabel": "新建", "xpack.graph.topNavMenu.newWorkspaceTooltip": "新建工作空间", "xpack.graph.topNavMenu.save.descriptionInputLabel": "描述", - "xpack.graph.topNavMenu.save.saveConfigurationOnlyText": "将清除此工作空间的数据,仅保存配置", - "xpack.graph.topNavMenu.save.saveConfigurationOnlyWarning": "将清除此工作空间的数据,仅保存配置", + "xpack.graph.topNavMenu.save.saveConfigurationOnlyText": "将清除此工作空间的数据,仅保存配置。", + "xpack.graph.topNavMenu.save.saveConfigurationOnlyWarning": "将清除此工作空间的数据,仅保存配置。", "xpack.graph.topNavMenu.save.saveGraphContentCheckboxLabel": "保存 Graph 内容", "xpack.graph.topNavMenu.saveWorkspace.disabledTooltip": "当前保存策略不允许对已保存的工作空间做任何更改", "xpack.graph.topNavMenu.saveWorkspace.enabledAriaLabel": "保存工作空间", @@ -4341,6 +5263,119 @@ "xpack.graph.topNavMenu.settingsAriaLabel": "设置", "xpack.graph.topNavMenu.settingsLabel": "设置", "xpack.graph.errorToastTitle": "Graph 错误", + "xpack.graph.bar.exploreLabel": "Graph", + "xpack.graph.bar.pickFieldsLabel": "添加字段", + "xpack.graph.bar.pickSourceLabel": "选择数据源", + "xpack.graph.bar.pickSourceTooltip": "选择数据源以开始绘制关系图。", + "xpack.graph.bar.searchFieldPlaceholder": "搜索数据并将其添加到图表", + "xpack.graph.blacklist.noEntriesDescription": "您没有任何已阻止字词。选择顶点并单击右侧控制面板上的 {stopSign} 以阻止它们。匹配已阻止字词的文档将不再被浏览,与它们的关系将隐藏。", + "xpack.graph.blacklist.removeButtonAriaLabel": "删除", + "xpack.graph.clearWorkspace.modalTitle": "未保存更改", + "xpack.graph.drilldowns.description": "使用向下钻取以链接到其他应用程序。选定的顶点成为 URL 的一部分。", + "xpack.graph.fieldManager.cancelLabel": "取消", + "xpack.graph.fieldManager.colorLabel": "颜色", + "xpack.graph.fieldManager.deleteFieldLabel": "取消选择字段", + "xpack.graph.fieldManager.deleteFieldTooltipContent": "此字段的新顶点将不会发现。 现有顶点仍在图表中。", + "xpack.graph.fieldManager.disabledFieldBadgeDescription": "已禁用字段 {field}:单击以配置。按 Shift 键并单击可启用", + "xpack.graph.fieldManager.disableFieldLabel": "禁用字段", + "xpack.graph.fieldManager.disableFieldTooltipContent": "关闭此字段顶点的发现。还可以按 Shift 键并单击字段可将其禁用。", + "xpack.graph.fieldManager.enableFieldLabel": "启用字段", + "xpack.graph.fieldManager.enableFieldTooltipContent": "打开此字段顶点的发现。还可以按 Shift 键并单击字段可将其启用。", + "xpack.graph.fieldManager.fieldBadgeDescription": "字段 {field}:单击以配置。按 Shift 键并单击可禁用", + "xpack.graph.fieldManager.fieldLabel": "字段", + "xpack.graph.fieldManager.fieldSearchPlaceholder": "筛选依据", + "xpack.graph.fieldManager.iconLabel": "图标", + "xpack.graph.fieldManager.maxTermsPerHopDescription": "控制要为每个搜索步长返回的字词最大数目。", + "xpack.graph.fieldManager.maxTermsPerHopLabel": "每跃点字词数", + "xpack.graph.fieldManager.settingsFormTitle": "编辑", + "xpack.graph.fieldManager.settingsLabel": "编辑设置", + "xpack.graph.fieldManager.updateLabel": "保存更改", + "xpack.graph.fillWorkspaceError": "获取排名最前字词失败:{message}", + "xpack.graph.guidancePanel.datasourceItem.indexPatternButtonLabel": "选择数据源。", + "xpack.graph.guidancePanel.fieldsItem.fieldsButtonLabel": "添加字段。", + "xpack.graph.guidancePanel.nodesItem.description": "在搜索栏中输入查询以开始浏览。不知道如何入手?{topTerms}。", + "xpack.graph.guidancePanel.nodesItem.topTermsButtonLabel": "将排名最前字词绘入图表", + "xpack.graph.guidancePanel.title": "绘制图表的三个步骤", + "xpack.graph.icon.areaChart": "面积图", + "xpack.graph.icon.at": "@ 符号", + "xpack.graph.icon.automobile": "汽车", + "xpack.graph.icon.bank": "银行", + "xpack.graph.icon.barChart": "条形图", + "xpack.graph.icon.bolt": "闪电", + "xpack.graph.icon.cube": "立方", + "xpack.graph.icon.desktop": "台式机", + "xpack.graph.icon.exclamation": "惊叹号", + "xpack.graph.icon.externalLink": "外部链接", + "xpack.graph.icon.eye": "眼睛", + "xpack.graph.icon.file": "文件打开", + "xpack.graph.icon.fileText": "文件", + "xpack.graph.icon.flag": "旗帜", + "xpack.graph.icon.folderOpen": "文件夹打开", + "xpack.graph.icon.font": "字体", + "xpack.graph.icon.globe": "地球", + "xpack.graph.icon.google": "Google", + "xpack.graph.icon.heart": "心形", + "xpack.graph.icon.home": "主页", + "xpack.graph.icon.industry": "工业", + "xpack.graph.icon.info": "信息", + "xpack.graph.icon.key": "钥匙", + "xpack.graph.icon.lineChart": "折线图", + "xpack.graph.icon.list": "列表", + "xpack.graph.icon.mapMarker": "地图标记", + "xpack.graph.icon.music": "音乐", + "xpack.graph.icon.phone": "电话", + "xpack.graph.icon.pieChart": "饼图", + "xpack.graph.icon.plane": "飞机", + "xpack.graph.icon.question": "问号", + "xpack.graph.icon.shareAlt": "Share alt", + "xpack.graph.icon.table": "桌子", + "xpack.graph.icon.tachometer": "转速表", + "xpack.graph.icon.user": "用户", + "xpack.graph.icon.users": "用户", + "xpack.graph.inspect.requestTabTitle": "请求", + "xpack.graph.inspect.responseTabTitle": "响应", + "xpack.graph.inspect.title": "检查", + "xpack.graph.leaveWorkspace.confirmButtonLabel": "离开", + "xpack.graph.leaveWorkspace.confirmText": "如果现在离开,将丢失未保存的更改。", + "xpack.graph.leaveWorkspace.modalTitle": "未保存更改", + "xpack.graph.listing.createNewGraph.combineDataViewFromKibanaAppDescription": "发现 Elasticsearch 索引中的模式和关系。", + "xpack.graph.listing.createNewGraph.createButtonLabel": "创建图表", + "xpack.graph.listing.createNewGraph.newToKibanaDescription": "Kibana 新手?从 {sampleDataInstallLink} 入手。", + "xpack.graph.listing.createNewGraph.sampleDataInstallLinkText": "样例数据", + "xpack.graph.listing.createNewGraph.title": "创建您的首个图表", + "xpack.graph.listing.graphsTitle": "图表", + "xpack.graph.listing.noDataSource.newToKibanaDescription": "Kibana 新手?还可以使用我们的{sampleDataInstallLink}。", + "xpack.graph.listing.noDataSource.sampleDataInstallLinkText": "样例数据", + "xpack.graph.listing.noItemsMessage": "似乎您没有任何图表。", + "xpack.graph.listing.table.descriptionColumnName": "描述", + "xpack.graph.listing.table.entityName": "图表", + "xpack.graph.listing.table.entityNamePlural": "图表", + "xpack.graph.listing.table.titleColumnName": "标题", + "xpack.graph.newGraphTitle": "未保存图表", + "xpack.graph.outlinkEncoders.kqlLooseDescription": "KQL 查询,与 Discover、Visualize 和仪表板兼容", + "xpack.graph.outlinkEncoders.kqlLooseTitle": "KQL OR 查询", + "xpack.graph.outlinkEncoders.kqlTitle": "KQL AND 查询", + "xpack.graph.saveWorkspace.savingErrorMessage": "无法保存工作空间:{message}", + "xpack.graph.settings.advancedSettings.timeoutUnit": "ms", + "xpack.graph.settings.closeLabel": "关闭", + "xpack.graph.settings.drillDowns.cancelButtonLabel": "取消", + "xpack.graph.settings.drillDowns.kibanaUrlWarningConvertOptionLinkText": "转换它。", + "xpack.graph.settings.drillDowns.newSaveButtonLabel": "保存向下钻取", + "xpack.graph.settings.drillDowns.removeButtonLabel": "删除", + "xpack.graph.settings.drillDowns.updateSaveButtonLabel": "更新向下钻取", + "xpack.graph.settings.title": "设置", + "xpack.graph.sourceModal.notFoundLabel": "未找到数据源。", + "xpack.graph.sourceModal.savedObjectType.indexPattern": "索引模式", + "xpack.graph.sourceModal.title": "选择数据源", + "xpack.graph.templates.addLabel": "新向下钻取", + "xpack.graph.templates.newTemplateFormLabel": "添加向下钻取", + "xpack.graph.topNavMenu.inspectAriaLabel": "检查", + "xpack.graph.topNavMenu.inspectLabel": "检查", + "xpack.graph.topNavMenu.save.objectType": "图表", + "xpack.graph.clearWorkspace.confirmButtonLabel": "更改数据源", + "xpack.graph.clearWorkspace.confirmText": "如果更改数据源,您当前的字段和顶点将会重置。", + "xpack.graph.loadWorkspace.missingIndexPatternErrorMessage": "未找到索引模式", + "xpack.graph.noDataSourceNotificationMessageText": "未找到数据源。前往 {managementIndexPatternsLink},为您的 Elasticsearch 索引创建索引模式。", "xpack.grokDebugger.customPatterns.callOutTitle": "每行输入一个自定义模式。例如:", "xpack.grokDebugger.customPatternsButtonLabel": "自定义模式", "xpack.grokDebugger.displayName": "Grok Debugger", @@ -4594,6 +5629,13 @@ "xpack.idxMgmt.templateValidation.templateNameRequiredError": "模板名称必填。", "xpack.idxMgmt.templateValidation.templateNameSpacesError": "模板名称不允许包含空格。", "xpack.idxMgmt.templateValidation.templateNameUnderscoreError": "模板名称不得以下划线开头。", + "xpack.idxMgmt.indexTable.captionText": "下面是包含 {count, plural, one {# 行} other {# 行}}(共 {total} 行)的索引表。", + "xpack.idxMgmt.indexTable.selectAllIndicesAriaLabel": "选择所有行", + "xpack.idxMgmt.indexTable.selectIndexAriaLabel": "选择此行", + "xpack.idxMgmt.validators.string.invalidJSONError": "JSON 格式无效。", + "xpack.idxMgmt.indexActionsMenu.closeIndex.systemIndexLabel": "系统索引", + "xpack.idxMgmt.indexActionsMenu.deleteIndex.proceedWithCautionCallOutDescription": "系统索引对内部操作至关重要。如果删除系统索引,将无法恢复。确保您有适当的备份。", + "xpack.idxMgmt.indexActionsMenu.deleteIndex.systemIndexLabel": "系统索引", "xpack.indexLifecycleMgmt.activePhaseMessage": "有效", "xpack.indexLifecycleMgmt.addLifecyclePolicyActionButtonLabel": "添加生命周期策略", "xpack.indexLifecycleMgmt.appTitle": "索引生命周期策略", @@ -4780,6 +5822,8 @@ "xpack.indexLifecycleMgmt.policyJsonFlyout.descriptionText": "此 Elasticsearch 请求将创建或更新此索引生命周期策略。", "xpack.indexLifecycleMgmt.policyJsonFlyout.namedTitle": "对“{policyName}”的请求", "xpack.indexLifecycleMgmt.policyJsonFlyout.unnamedTitle": "请求", + "xpack.indexLifecycleMgmt.indexLifecycleMgmtSummary.showPhaseDefinitionDescriptionTitle": "阶段定义", + "xpack.indexLifecycleMgmt.policyTable.captionText": "下面是包含 {count, plural, one {# 行} other {# 行}}(共 {total} 行)的索引生命周期策略表。", "xpack.infra.chartSection.missingMetricDataText": "缺失数据", "xpack.infra.chartSection.notEnoughDataPointsToRenderText": "没有足够的数据点来呈现图表,请尝试增大时间范围。", "xpack.infra.configureSourceActionLabel": "更改源配置", @@ -4787,7 +5831,7 @@ "xpack.infra.errorPage.tryAgainButtonLabel": "重试", "xpack.infra.errorPage.tryAgainDescription ": "请点击后退按钮,然后重试。", "xpack.infra.errorPage.unexpectedErrorTitle": "糟糕!", - "xpack.infra.featureRegistry.linkInfrastructureTitle": "Infrastructure", + "xpack.infra.featureRegistry.linkInfrastructureTitle": "指标", "xpack.infra.featureRegistry.linkLogsTitle": "Logs", "xpack.infra.groupByDisplayNames.availabilityZone": "可用区", "xpack.infra.groupByDisplayNames.hostName": "主机", @@ -4799,8 +5843,8 @@ "xpack.infra.groupByDisplayNames.serviceType": "服务类型", "xpack.infra.header.badge.readOnly.text": "只读", "xpack.infra.header.badge.readOnly.tooltip": "无法更改源配置", - "xpack.infra.header.infrastructureTitle": "Infrastructure", - "xpack.infra.homePage.documentTitle": "Infrastructure", + "xpack.infra.header.infrastructureTitle": "指标", + "xpack.infra.homePage.documentTitle": "指标", "xpack.infra.homePage.inventoryTabTitle": "库存", "xpack.infra.homePage.metricsExplorerTabTitle": "指标浏览器", "xpack.infra.homePage.noMetricsIndicesDescription": "让我们添加一些!", @@ -4812,7 +5856,7 @@ "xpack.infra.infrastructureDescription": "浏览您的基础设施", "xpack.infra.infrastructureMetricsExplorerPage.documentTitle": "{previousTitle} | 指标浏览器", "xpack.infra.infrastructureSnapshotPage.documentTitle": "{previousTitle} | 库存", - "xpack.infra.infrastructureTitle": "Infrastructure", + "xpack.infra.infrastructureTitle": "指标", "xpack.infra.kibanaMetrics.invalidInfraMetricErrorMessage": "{id} 不是有效的 InfraMetric", "xpack.infra.kibanaMetrics.nodeDoesNotExistErrorMessage": "{nodeId} 不存在。", "xpack.infra.legendControls.applyButton": "应用", @@ -4822,7 +5866,7 @@ "xpack.infra.legendControls.minLabel": "最小值", "xpack.infra.legendControls.switchLabel": "自动计算范围", "xpack.infra.linkInfrastructureDescription": "浏览您的基础设施", - "xpack.infra.linkInfrastructureTitle": "Infrastructure", + "xpack.infra.linkInfrastructureTitle": "指标", "xpack.infra.linkLogsDescription": "浏览您的日志", "xpack.infra.linkLogsTitle": "Logs", "xpack.infra.linkTo.hostWithIp.error": "未找到 IP 地址为“{hostIp}”的主机。", @@ -4976,7 +6020,7 @@ "xpack.infra.notFoundPage.noContentFoundErrorTitle": "未找到任何内容", "xpack.infra.redirectToNodeLogs.loadingNodeLogsMessage": "正在加载 {nodeType} 日志", "xpack.infra.registerFeatures.infraOpsDescription": "浏览常用服务器、容器和服务的基础设施指标和日志。", - "xpack.infra.registerFeatures.infraOpsTitle": "Infrastructure", + "xpack.infra.registerFeatures.infraOpsTitle": "指标", "xpack.infra.registerFeatures.logsDescription": "实时流式传输日志或在类似控制台的工具中滚动浏览历史视图。", "xpack.infra.registerFeatures.logsTitle": "Logs", "xpack.infra.sourceConfiguration.addLogColumnButtonLabel": "添加列", @@ -5041,7 +6085,7 @@ "xpack.infra.analysisSetup.endTimeLabel": "结束时间", "xpack.infra.analysisSetup.startTimeDefaultDescription": "日志索引的开始时间", "xpack.infra.analysisSetup.startTimeLabel": "开始时间", - "xpack.infra.analysisSetup.timeRangeDescription": "默认情况下,Machine Learning 分析自日志索引开始时间起的日志消息并无限期继续下去。您可以指定不同的开始日期或/和结束日期。", + "xpack.infra.analysisSetup.timeRangeDescription": "默认情况下,Machine Learning 分析日志索引中 4 周前的日志消息,并无限持续下去。您可以指定不同的开始日期或/和结束日期。", "xpack.infra.analysisSetup.timeRangeTitle": "选择时间范围", "xpack.infra.chartSection.missingMetricDataBody": "此图表的数据缺失。", "xpack.infra.chartSection.notEnoughDataPointsToRenderTitle": "没有足够的数据", @@ -5119,6 +6163,111 @@ "xpack.infra.sourceConfiguration.removeLogColumnButtonLabel": "删除“{columnDescription}”列", "xpack.infra.sourceConfiguration.tiebreakerFieldDescription": "用于时间戳相同的两个条目间决胜的字段", "xpack.infra.sourceConfiguration.timestampFieldDescription": "用于排序日志条目的时间戳", + "xpack.infra.analysisSetup.deleteAnalysisResultsWarning": "这将移除以前检测到的异常。", + "xpack.infra.analysisSetup.recreateMlJobButton": "重新创建 ML 作业", + "xpack.infra.analysisSetup.steps.setupProcess.loadingText": "正在创建 ML 作业......", + "xpack.infra.analysisSetup.steps.setupProcess.successText": "ML 作业已设置成功", + "xpack.infra.analysisSetup.steps.setupProcess.tryAgainButton": "重试", + "xpack.infra.analysisSetup.steps.setupProcess.viewResultsButton": "查看结果", + "xpack.infra.logs.analysis.analyzeInMlButtonLabel": "在 ML 中分析", + "xpack.infra.logs.analysis.anomaliesExpandedRowNumberOfLogEntriesDescription": "日志条目数", + "xpack.infra.logs.analysis.anomaliesExpandedRowTopAnomalyScoreDescription": "最大异常分数", + "xpack.infra.logs.analysis.anomaliesSectionLineSeriesName": "每 15 分钟日志条目数(平均值)", + "xpack.infra.logs.analysis.anomaliesSectionLoadingAriaLabel": "正在加载异常", + "xpack.infra.logs.analysis.anomaliesSectionTitle": "异常", + "xpack.infra.logs.analysis.anomaliesTableCollapseLabel": "折叠", + "xpack.infra.logs.analysis.anomaliesTableExpandLabel": "展开", + "xpack.infra.logs.analysis.anomaliesTableMaxAnomalyScoreColumnName": "最大异常分数", + "xpack.infra.logs.analysis.anomaliesTablePartitionColumnName": "分区", + "xpack.infra.logs.analysis.anomalySectionNoAnomaliesTitle": "未检测到任何异常。", + "xpack.infra.logs.analysis.anomalySectionNoDataBody": "您可能想调整时间范围。", + "xpack.infra.logs.analysis.anomalySectionNoDataTitle": "没有可显示的数据。", + "xpack.infra.logs.analysis.jobConfigurationOutdatedCalloutMessage": "创建 ML 作业时所使用的源配置不同。重新创建作业以应用当前配置。这将移除以前检测到的异常。", + "xpack.infra.logs.analysis.jobConfigurationOutdatedCalloutTitle": "ML 作业配置已过期", + "xpack.infra.logs.analysis.jobDefinitionOutdatedCalloutMessage": "ML 作业有更新的版本可用。重新创建作业以部署更新的版本。这将移除以前检测到的异常。", + "xpack.infra.logs.analysis.jobDefinitionOutdatedCalloutTitle": "ML 作业定义已过期", + "xpack.infra.logs.analysis.jobStoppedCalloutMessage": "ML 作业已手动停止或由于缺乏资源而停止。作业重新启动后,才会处理新的日志条目。", + "xpack.infra.logs.analysis.jobStoppedCalloutTitle": "ML 作业已停止", + "xpack.infra.logs.analysis.logRateResultsToolbarText": "从 {startTime} 到 {endTime} 已分析 {numberOfLogs} 个日志条目", + "xpack.infra.logs.analysis.logRateSectionBucketSpanLabel": "存储桶跨度:", + "xpack.infra.logs.analysis.logRateSectionBucketSpanValue": "15 分钟", + "xpack.infra.logs.analysis.overallAnomaliesNumberOfLogEntriesDescription": "日志条目数", + "xpack.infra.logs.analysis.overallAnomaliesTopAnomalyScoreDescription": "最大异常分数", + "xpack.infra.logs.analysis.overallAnomalyChartMaxScoresLabel": "最大异常分数:", + "xpack.infra.logs.analysis.partitionMaxAnomalyScoreAnnotationLabel": "最大异常分数:{maxAnomalyScore}", + "xpack.infra.logs.analysis.recreateJobButtonLabel": "重新创建 ML 作业", + "xpack.infra.logs.analysisPage.setupStatusUnknown.title": "我们无法确定您的 ML 作业的状态。", + "xpack.infra.logs.analysisPage.setupStatusUnknown.tryAgainButton": "请重试", + "xpack.infra.logs.jumpToTailText": "跳到最近的条目", + "xpack.infra.metricDetailPage.containerMetricsLayout.cpuUsageSection.seriesLabel.cpu": "cpu", + "xpack.infra.metricDetailPage.containerMetricsLayout.memoryUsageSection.seriesLabel.memory": "memory", + "xpack.infra.metricDetailPage.containerStates.seriesLabel.paused": "已暂停", + "xpack.infra.metricDetailPage.containerStates.seriesLabel.running": "正在运行", + "xpack.infra.metricDetailPage.containerStates.seriesLabel.stopped": "已停止", + "xpack.infra.metricDetailPage.dockerMetricsLayout.containerStates.sectionLabel": "容器状态", + "xpack.infra.metricDetailPage.dockerMetricsLayout.overviewSection.pausedLabel": "已暂停", + "xpack.infra.metricDetailPage.dockerMetricsLayout.overviewSection.runningLabel": "正在运行", + "xpack.infra.metricDetailPage.dockerMetricsLayout.overviewSection.sectionLabel": "概览", + "xpack.infra.metricDetailPage.dockerMetricsLayout.overviewSection.stoppedLabel": "已停止", + "xpack.infra.metricDetailPage.dockerMetricsLayout.overviewSection.totalLabel": "合计", + "xpack.infra.metricDetailPage.dockerMetricsLayout.top5Cpu.sectionLabel": "排名前 5 容器 - 按 CPU", + "xpack.infra.metricDetailPage.dockerMetricsLayout.top5Memory.sectionLabel": "排名前 5 容器 - 按内存", + "xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.iowait": "iowait", + "xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.irq": "irq", + "xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.nice": "nice", + "xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.softirq": "softirq", + "xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.steal": "steal", + "xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.system": "system", + "xpack.infra.metricDetailPage.hostMetricsLayout.cpuUsageSection.seriesLabel.user": "user", + "xpack.infra.metricDetailPage.hostMetricsLayout.memoryUsageSection.seriesLabel.cache": "缓存", + "xpack.infra.metricDetailPage.hostMetricsLayout.memoryUsageSection.seriesLabel.free": "可用", + "xpack.infra.metricDetailPage.hostMetricsLayout.memoryUsageSection.seriesLabel.used": "已使用", + "xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeCpuCapacitySection.seriesLabel.capacity": "容量", + "xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeCpuCapacitySection.seriesLabel.used": "已使用", + "xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeDiskCapacitySection.seriesLabel.capacity": "容量", + "xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeDiskCapacitySection.seriesLabel.used": "已使用", + "xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeMemoryCapacitySection.seriesLabel.capacity": "容量", + "xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodeMemoryCapacitySection.seriesLabel.used": "已使用", + "xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodePodCapacitySection.seriesLabel.capacity": "容量", + "xpack.infra.metricDetailPage.kubernetesMetricsLayout.nodePodCapacitySection.seriesLabel.used": "已使用", + "xpack.infra.metricDetailPage.nginxMetricsLayout.activeConnectionsSection.seriesLabel.connections": "连接", + "xpack.infra.metricDetailPage.nginxMetricsLayout.requestRateSection.seriesLabel.rate": "速率", + "xpack.infra.metricDetailPage.podMetricsLayout.cpuUsageSection.seriesLabel.cpu": "cpu", + "xpack.infra.metricDetailPage.podMetricsLayout.memoryUsageSection.seriesLabel.memory": "memory", + "xpack.infra.metricsExplorer.chartOptions.barLabel": "条形图", + "xpack.infra.nodeDetails.labels.availabilityZone": "可用区", + "xpack.infra.nodeDetails.labels.cloudProvider": "云服务提供商", + "xpack.infra.nodeDetails.labels.containerized": "容器化", + "xpack.infra.nodeDetails.labels.hostname": "主机名", + "xpack.infra.nodeDetails.labels.instanceId": "实例 ID", + "xpack.infra.nodeDetails.labels.instanceName": "实例名称", + "xpack.infra.nodeDetails.labels.kernelVersion": "内核版本", + "xpack.infra.nodeDetails.labels.machineType": "机器类型", + "xpack.infra.nodeDetails.labels.operatinSystem": "操作系统", + "xpack.infra.nodeDetails.labels.projectId": "项目 ID", + "xpack.infra.nodeDetails.labels.showMoreDetails": "显示更多详情", + "xpack.infra.nodeDetails.no": "否", + "xpack.infra.nodeDetails.yes": "是", + "xpack.infra.openView.actionNames.deleteConfirmation": "删除视图?", + "xpack.infra.openView.cancelButton": "取消", + "xpack.infra.openView.columnNames.actions": "操作", + "xpack.infra.openView.columnNames.name": "名称", + "xpack.infra.openView.flyoutHeader": "加载视图", + "xpack.infra.sampleDataLinkLabel": "日志", + "xpack.infra.savedView.defaultViewName": "默认值", + "xpack.infra.savedView.errorOnCreate.title": "保存视图时出错。", + "xpack.infra.savedView.findError.title": "加载视图时出错。", + "xpack.infra.waffle.inventoryButtonLabel": "视图:{selectedText}", + "xpack.infra.waffle.metricOptions.countText": "计数", + "xpack.infra.waffle.savedView.createHeader": "保存视图", + "xpack.infra.waffle.savedViews.cancel": "取消", + "xpack.infra.waffle.savedViews.cancelButton": "取消", + "xpack.infra.waffle.savedViews.includeTimeFilterLabel": "将时间与视图一起存储", + "xpack.infra.waffle.savedViews.includeTimeHelpText": "每次加载此仪表板时,这都会将时间筛选更改为当前选定的时间", + "xpack.infra.waffle.savedViews.loadViewsLabel": "负载", + "xpack.infra.waffle.savedViews.saveButton": "保存", + "xpack.infra.waffle.savedViews.saveViewLabel": "保存", + "xpack.infra.waffle.savedViews.viewNamePlaceholder": "名称", "xpack.kueryAutocomplete.andOperatorDescription": "需要{bothArguments}为真", "xpack.kueryAutocomplete.andOperatorDescription.bothArgumentsText": "两个参数都", "xpack.kueryAutocomplete.equalOperatorDescription": "{equals}某一值", @@ -5201,9 +6350,9 @@ "xpack.licenseMgmt.uploadLicense.uploadButtonLabel": "上传", "xpack.licenseMgmt.uploadLicense.uploadingButtonLabel": "正在上传……", "xpack.licenseMgmt.uploadLicense.uploadLicenseTitle": "上传您的许可", - "xpack.licensing.check.errorExpiredMessage": "您不能使用 {pluginName},因为您的 {licenseType} 许可证已过期", + "xpack.licensing.check.errorExpiredMessage": "您不能使用 {pluginName},因为您的{licenseType}许可证已过期。", "xpack.licensing.check.errorUnavailableMessage": "您不能使用 {pluginName},因为许可证信息当前不可用。", - "xpack.licensing.check.errorUnsupportedMessage": "您的 {licenseType} 许可证不支持 {pluginName}。请升级您的许可。", + "xpack.licensing.check.errorUnsupportedMessage": "您的{licenseType}许可证不支持 {pluginName}。请升级您的许可证。", "xpack.logstash.addRoleAlert.grantAdditionalPrivilegesDescription": "在 Kibana“管理”中,将 {role} 角色分配给您的 Kibana 用户。", "xpack.logstash.addRoleAlert.grantAdditionalPrivilegesTitle": "授予其他权限。", "xpack.logstash.alertCallOut.howToSeeAdditionalPipelinesDescription": "我如何可以看到其他管道?", @@ -5374,7 +6523,7 @@ "xpack.maps.layerPanel.metricsExpression.joinMustBeSetErrorMessage": "必须设置联接", "xpack.maps.layerPanel.metricsExpression.metricsPopoverTitle": "指标", "xpack.maps.layerPanel.metricsExpression.useMetricsDescription": "{metricsLength, plural, one {并使用指标} other {并使用指标}}", - "xpack.maps.layerPanel.settingsPanel.layerNameLabel": "图层名称", + "xpack.maps.layerPanel.settingsPanel.layerNameLabel": "名称", "xpack.maps.layerPanel.settingsPanel.layerTransparencyLabel": "图层透明度", "xpack.maps.layerPanel.settingsPanel.unableToLoadTitle": "无法加载图层", "xpack.maps.layerPanel.settingsPanel.visibleZoomLabel": "图层可见性的缩放范围", @@ -5586,7 +6735,7 @@ "xpack.maps.source.pewPew.noSourceAndDestDetails": "选定的索引模式不包含源和目标字段。", "xpack.maps.source.pewPew.sourceGeoFieldLabel": "源", "xpack.maps.source.pewPew.sourceGeoFieldPlaceholder": "选择源地理位置字段", - "xpack.maps.source.pewPewDescription": "源和目标之间的聚合数据路径。", + "xpack.maps.source.pewPewDescription": "源和目标之间的聚合数据路径", "xpack.maps.source.pewPewTitle": "源-目标连接", "xpack.maps.source.wms.attributionLink": "属性文本必须附带链接", "xpack.maps.source.wms.attributionText": "属性 url 必须附带文本", @@ -5594,11 +6743,27 @@ "xpack.maps.toolbarOverlay.drawBounds.initialGeometryLabel": "边界", "xpack.maps.toolbarOverlay.drawShape.initialGeometryLabel": "形状", "xpack.maps.tooltip.geometryFilterForm.createFilterButtonLabel": "创建筛选", - "xpack.maps.tooltip.pageNumerText": "第 {pageNumber} 项功能,总计 {total} 项", + "xpack.maps.tooltip.pageNumerText": "{total} 的 {pageNumber}", "xpack.maps.tooltip.showGeometryFilterViewLinkLabel": "按几何筛选", "xpack.maps.tooltip.toolsControl.cancelDrawButtonLabel": "取消", "xpack.maps.xyztmssource.attributionLink": "属性文本必须附带链接", "xpack.maps.xyztmssource.attributionText": "属性 url 必须附带文本", + "xpack.maps.layerPanel.settingsPanel.layerGlobalFilterLabel": "全局筛选", + "xpack.maps.layerPanel.settingsPanel.percentageLabel": "%", + "xpack.maps.layerPanel.settingsPanel.visibleZoom": "缩放级别", + "xpack.maps.source.esSearch.sortFieldSelectPlaceholder": "选择排序字段", + "xpack.maps.source.esSearch.sortLabel": "排序", + "xpack.maps.toolbarOverlay.drawBoundsLabelShort": "绘制边界", + "xpack.maps.toolbarOverlay.drawShapeLabelShort": "绘制形状", + "xpack.maps.tooltipSelector.addLabelWithCount": "添加 {count} 个", + "xpack.maps.tooltipSelector.addLabelWithoutCount": "添加", + "xpack.maps.tooltipSelector.grabButtonAriaLabel": "重新排序属性", + "xpack.maps.tooltipSelector.grabButtonTitle": "重新排序属性", + "xpack.maps.tooltipSelector.togglePopoverLabel": "添加", + "xpack.maps.tooltipSelector.trashButtonAriaLabel": "移除属性", + "xpack.maps.tooltipSelector.trashButtonTitle": "移除属性", + "xpack.maps.vector.dualSize.unitLabel": "px", + "xpack.maps.vector.size.unitLabel": "px", "xpack.ml.annotationsTable.actionsColumnName": "操作", "xpack.ml.annotationsTable.annotationColumnName": "注释", "xpack.ml.annotationsTable.annotationsNotCreatedTitle": "没有为此作业创建注释", @@ -6272,8 +7437,8 @@ "xpack.ml.models.jobValidation.messages.jobGroupIdInvalidMessage": "有一个作业组名称无效。它们可以包含小写字母数字(a-z 和 0-9)字符、连字符或下划线,必须以字母数字字符开头和结尾", "xpack.ml.models.jobValidation.messages.jobGroupIdValidHeading": "作业 ID 格式有效。", "xpack.ml.models.jobValidation.messages.jobIdEmptyMessage": "作业名称字段不得为空。", - "xpack.ml.models.jobValidation.messages.jobIdInvalidMessage": "作业名称无效。其可以包含小写字母数字(a-z 和 0-9)字符、连字符或下划线,必须以字母数字字符开头和结尾", - "xpack.ml.models.jobValidation.messages.jobIdValidHeading": "作业 ID 格式有效。", + "xpack.ml.models.jobValidation.messages.jobIdInvalidMessage": "作业 ID 无效.其可以包含小写字母数字(a-z 和 0-9)字符、连字符或下划线,且必须以字母数字字符开头和结尾。", + "xpack.ml.models.jobValidation.messages.jobIdValidHeading": "作业 ID 格式有效", "xpack.ml.models.jobValidation.messages.mmlGreaterThanMaxMmlMessage": "模型内存限制大于为此集群配置的最大模型内存限制。", "xpack.ml.models.jobValidation.messages.mmlValueInvalidMessage": "{mml} 不是有效的模型内存限制值。该值需要至少 1MB,且应以字节为单位(例如 10MB)指定。", "xpack.ml.models.jobValidation.messages.skippedExtendedTestsMessage": "已跳过其他检查,因为未满足作业配置的基本要求。", @@ -6628,7 +7793,7 @@ "xpack.ml.dataframe.analytics.create.sourceIndexInvalidError": "源索引名称无效,其不能包含空格或以下字符:{characterList}", "xpack.ml.dataframe.analytics.create.sourceIndexLabel": "源索引", "xpack.ml.dataframe.analytics.create.sourceIndexPlaceholder": "选择源索引模式或已保存搜索。", - "xpack.ml.dataframe.analytics.create.startDataFrameAnalyticsSuccessMessage": "分析作业 {jobId} 已启动。", + "xpack.ml.dataframe.analytics.create.startDataFrameAnalyticsSuccessMessage": "数据帧分析 {jobId} 启动请求已确认。", "xpack.ml.dataframe.analytics.exploration.experimentalBadgeLabel": "实验性", "xpack.ml.dataframe.analytics.exploration.experimentalBadgeTooltipContent": "数据帧分析为实验功能。我们很乐意听取您的反馈意见。", "xpack.ml.dataframe.analytics.exploration.fieldSelection": "已选择 {selectedFieldsLength, number} 个{docFieldsCount, plural, one {字段} other {字段}},共 {docFieldsCount, number} 个", @@ -6835,10 +8000,251 @@ "xpack.ml.newJob.wizard.validateJob.jobNameAlreadyExists": "作业 ID 已存在。作业 ID 不能与现有作业或组相同。", "xpack.ml.newJob.wizard.validateJob.modelMemoryLimitRangeInvalidErrorMessage": "模型内存限制不能高于最大值 {maxModelMemoryLimit}", "xpack.ml.newJob.wizard.validateJob.modelMemoryLimitUnitsInvalidErrorMessage": "无法识别模型内存限制数据单元。必须为 {str}", + "xpack.ml.accessDenied.description": "您无权访问 ML 插件", + "xpack.ml.accessDenied.label": "权限不足", + "xpack.ml.anomalyDetection.anomalyExplorerLabel": "Anomaly Explorer", + "xpack.ml.anomalyDetection.jobManagementLabel": "作业管理", + "xpack.ml.anomalyDetection.singleMetricViewerLabel": "Single Metric Viewer", + "xpack.ml.anomalyDetectionBreadcrumbLabel": "异常检测", + "xpack.ml.dataframe.analytics.create.advancedEditorMessage.dependentVariableEmpty": "因变量字段不得为空。", + "xpack.ml.dataframe.analytics.create.dependentVariableInputAriaLabel": "输入要用作因变量的字段。", + "xpack.ml.dataframe.analytics.create.dependentVariableLabel": "因变量", + "xpack.ml.dataframe.analytics.create.dependentVariableOptionsFetchError": "获取字段时出现问题。请刷新页面并重试。", + "xpack.ml.dataframe.analytics.create.dependentVariablePlaceholder": "因变量", + "xpack.ml.dataframe.analytics.create.enableAdvancedEditorHelpText": "您不能从高级编辑器切回到此表单。", + "xpack.ml.dataframe.analytics.create.enableAdvancedEditorSwitch": "启用高级编辑器", + "xpack.ml.dataframe.analytics.create.flyoutCancelButton": "取消", + "xpack.ml.dataframe.analytics.create.flyoutCloseButton": "关闭", + "xpack.ml.dataframe.analytics.create.flyoutCreateButton": "创建", + "xpack.ml.dataframe.analytics.create.flyoutHeaderTitle": "创建分析作业", + "xpack.ml.dataframe.analytics.create.flyoutStartButton": "开始", + "xpack.ml.dataframe.analytics.create.indexPatternAlreadyExistsError": "具有此名称的索引模式已存在。", + "xpack.ml.dataframe.analytics.create.indexPatternExistsError": "具有此名称的索引模式已存在。", + "xpack.ml.dataframe.analytics.create.jobIdInvalidMaxLengthErrorMessage": "作业 ID 的长度不得超过 {maxLength, plural, one {# 个字符} other {# 个字符}}。", + "xpack.ml.dataframe.analytics.create.outlierDetectionHelpText": "离群值检测作业需要映射为类表数据结构的源索引,将仅分析数值和布尔值字段。请使用高级编辑器应用定制选项,如模型内存限制和分析类型。", + "xpack.ml.dataframe.analytics.create.outlierRegressionHelpText": "回归作业将仅分析数值字段。请使用高级编辑器来应用定制选项,如模型内存限制和预测字段名称。", + "xpack.ml.dataframe.analytics.create.trainingPercentLabel": "训练百分比", + "xpack.ml.dataframe.analytics.regressionExploration.evaluateError": "加载数据时出错。", + "xpack.ml.dataframe.analytics.regressionExploration.generalError": "加载数据时出错。", + "xpack.ml.dataframe.analytics.regressionExploration.generalizationErrorTitle": "泛化误差", + "xpack.ml.dataframe.analytics.regressionExploration.jobIdTitle": "作业 ID {jobId}", + "xpack.ml.dataframe.analytics.regressionExploration.meanSquaredErrorText": "均方误差", + "xpack.ml.dataframe.analytics.regressionExploration.noDataCalloutBody": "该索引的查询未返回结果。请确保作业已完成且索引包含文档。", + "xpack.ml.dataframe.analytics.regressionExploration.noDataCalloutTitle": "空的索引查询结果。", + "xpack.ml.dataframe.analytics.regressionExploration.noIndexCalloutBody": "该索引的查询未返回结果。请确保目标索引存在且包含文档。", + "xpack.ml.dataframe.analytics.regressionExploration.rSquaredText": "R 平方", + "xpack.ml.dataframe.analytics.regressionExploration.trainingErrorTitle": "训练误差", + "xpack.ml.dataFrameAnalyticsBreadcrumbs.indexLabel": "索引", + "xpack.ml.datavisualizer.actionsPanel.selectKnownConfigurationDescription": "选择已识别数据的已知配置:", + "xpack.ml.dataVisualizer.fileBasedLabel": "文件", + "xpack.ml.datavisualizerBreadcrumbLabel": "数据可视化工具", + "xpack.ml.explorer.distributionChart.entityLabel": "实体", + "xpack.ml.jobsList.editJobFlyout.leaveAnywayButtonLabel": "离开", + "xpack.ml.jobsList.editJobFlyout.saveChangesButtonLabel": "保存更改", + "xpack.ml.jobsList.editJobFlyout.unsavedChangesDialogMessage": "如果未保存,您的更改将会丢失。", + "xpack.ml.jobsList.editJobFlyout.unsavedChangesDialogTitle": "离开前保存更改?", + "xpack.ml.models.jobValidation.messages.jobGroupIdInvalidMaxLengthErrorMessage": "作业组名称的长度不得超过 {maxLength, plural, one {# 个字符} other {# 个字符}}。", + "xpack.ml.models.jobValidation.messages.jobIdInvalidMaxLengthErrorMessage": "作业 ID 的长度不得超过 {maxLength, plural, one {# 个字符} other {# 个字符}}。", + "xpack.ml.navMenu.anomalyDetectionTabLinkText": "异常检测", + "xpack.ml.navMenu.overviewTabLinkText": "概览", + "xpack.ml.newJi18n(ob.recognize.jobsCreationFailed.resetButtonAriaLabel": "重置", + "xpack.ml.newJob.recognize.advancedLabel": "高级", + "xpack.ml.newJob.recognize.advancedSettingsAriaLabel": "高级设置", + "xpack.ml.newJob.recognize.alreadyExistsLabel": "(已存在)", + "xpack.ml.newJob.recognize.analysisRunningLabel": "分析正在运行", + "xpack.ml.newJob.recognize.createJobButtonAriaLabel": "创建作业", + "xpack.ml.newJob.recognize.createJobButtonLabel": "创建{numberOfJobs, plural, zero {作业} one {Job} other {Jobs}}", + "xpack.ml.newJob.recognize.dashboardsLabel": "仪表板", + "xpack.ml.newJob.recognize.datafeed.savedAriaLabel": "已保存", + "xpack.ml.newJob.recognize.datafeed.saveFailedAriaLabel": "保存失败", + "xpack.ml.newJob.recognize.datafeedLabel": "数据馈送", + "xpack.ml.newJob.recognize.indexPatternPageTitle": "索引模式 {indexPatternTitle}", + "xpack.ml.newJob.recognize.job.savedAriaLabel": "已保存", + "xpack.ml.newJob.recognize.job.saveFailedAriaLabel": "保存失败", + "xpack.ml.newJob.recognize.jobGroupAllowedCharactersDescription": "作业组名称可以包含小写字母数字(a-z 和 0-9)、连字符或下划线;必须以字母数字字符开头和结尾", + "xpack.ml.newJob.recognize.jobIdPrefixLabel": "作业 ID 前缀", + "xpack.ml.newJob.recognize.jobLabel": "作业名称", + "xpack.ml.newJob.recognize.jobLabelAllowedCharactersDescription": "作业标签可以包含小写字母数字(a-z 和 0-9)、连字符或下划线;必须以字母数字字符开头和结尾", + "xpack.ml.newJob.recognize.jobPrefixInvalidMaxLengthErrorMessage": "作业 ID 前缀的长度不得超过 {maxLength, plural, one {# 个字符} other {# 个字符}}。", + "xpack.ml.newJob.recognize.jobsCreatedTitle": "已创建作业", + "xpack.ml.newJob.recognize.jobSettingsTitle": "作业设置", + "xpack.ml.newJob.recognize.jobsTitle": "作业", + "xpack.ml.newJob.recognize.moduleCheckJobsExistWarningDescription": "尝试检查模块中的作业是否已创建时出错。", + "xpack.ml.newJob.recognize.moduleCheckJobsExistWarningTitle": "检查模块 {moduleId} 时出错", + "xpack.ml.newJob.recognize.moduleSetupFailedWarningDescription": "尝试创建模块中的{count, plural, one {作业} other {作业}}时出错。", + "xpack.ml.newJob.recognize.moduleSetupFailedWarningTitle": "设置模块 {moduleId} 时出错", + "xpack.ml.newJob.recognize.newJobFromTitle": "来自 {pageTitle} 的新作业", + "xpack.ml.newJob.recognize.results.savedAriaLabel": "已保存", + "xpack.ml.newJob.recognize.results.saveFailedAriaLabel": "保存失败", + "xpack.ml.newJob.recognize.running.startedAriaLabel": "已启动", + "xpack.ml.newJob.recognize.running.startFailedAriaLabel": "启动失败", + "xpack.ml.newJob.recognize.runningLabel": "正在运行", + "xpack.ml.newJob.recognize.savedSearchPageTitle": "已保存搜索 {savedSearchTitle}", + "xpack.ml.newJob.recognize.searchesLabel": "搜索", + "xpack.ml.newJob.recognize.searchWillBeOverwrittenLabel": "搜索将被覆盖", + "xpack.ml.newJob.recognize.someJobsCreationFailed.resetButtonLabel": "重置", + "xpack.ml.newJob.recognize.someJobsCreationFailedTitle": "部分作业未能创建", + "xpack.ml.newJob.recognize.startDatafeedAfterSaveLabel": "保存后启动数据馈送", + "xpack.ml.newJob.recognize.useDedicatedIndexLabel": "使用专用索引", + "xpack.ml.newJob.recognize.useFullDataLabel": "使用完整的 {indexPatternTitle} 数据", + "xpack.ml.newJob.recognize.usingSavedSearchDescription": "使用已保存搜索意味着在数据馈送中使用的查询会与我们在 {moduleId} 模块中提供的默认查询不同。", + "xpack.ml.newJob.recognize.viewResultsAriaLabel": "查看结果", + "xpack.ml.newJob.recognize.viewResultsLinkText": "查看结果", + "xpack.ml.newJob.recognize.visualizationsLabel": "可视化", + "xpack.ml.newJob.wizard.autoSetJobCreatorTimeRange.error": "检索索引的开始和结束时间", + "xpack.ml.newJob.wizard.datafeedPreviewFlyout.closeButton": "关闭", + "xpack.ml.newJob.wizard.datafeedPreviewFlyout.datafeedDoesNotExistLabel": "数据馈送不存在", + "xpack.ml.newJob.wizard.datafeedPreviewFlyout.showButton": "数据馈送预览", + "xpack.ml.newJob.wizard.datafeedPreviewFlyout.title": "数据馈送预览", + "xpack.ml.newJob.wizard.datafeedStep.frequency.description": "搜索的时间间隔。", + "xpack.ml.newJob.wizard.datafeedStep.frequency.title": "频率", + "xpack.ml.newJob.wizard.datafeedStep.query.title": "Elasticsearch 查询", + "xpack.ml.newJob.wizard.datafeedStep.queryDelay.description": "当前时间和最新输入数据时间之间的时间延迟(秒)。", + "xpack.ml.newJob.wizard.datafeedStep.queryDelay.title": "查询延迟", + "xpack.ml.newJob.wizard.datafeedStep.scrollSize.description": "为搜索请求的最大文档数目。", + "xpack.ml.newJob.wizard.datafeedStep.scrollSize.title": "滚动条大小", + "xpack.ml.newJob.wizard.datafeedStep.timeField.description": "索引模式的默认时间字段将被自动选择,但可以覆盖。", + "xpack.ml.newJob.wizard.datafeedStep.timeField.title": "时间字段", + "xpack.ml.newJob.wizard.editJsonButton": "编辑 JSON", + "xpack.ml.newJob.wizard.jsonFlyout.closeButton": "关闭", + "xpack.ml.newJob.wizard.jsonFlyout.datafeed.title": "数据馈送配置 JSON", + "xpack.ml.newJob.wizard.jsonFlyout.job.title": "作业配置 JSON", + "xpack.ml.newJob.wizard.jsonFlyout.saveButton": "保存", + "xpack.ml.newJob.wizard.pickFieldsStep.addDetectorButton": "添加检测工具", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorList.deleteButton": "删除", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorList.editButton": "编辑", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorList.title": "检测工具", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.aggSelect.description": "要执行的分析函数,例如 sum、count。", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.aggSelect.title": "函数", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.byFieldSelect.description": "通过与实体自身过去行为对比来检测异常的单个分析所必需。", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.byFieldSelect.title": "按字段", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.cancelButton": "取消", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.description.description": "使用检测工具分析内容的有意义描述覆盖默认检测工具描述。", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.description.title": "描述", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.excludeFrequent.description": "如果为 true,将自动识别并排除经常出现的实体,否则其可能影响结果。", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.excludeFrequent.title": "排除频繁", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.fieldSelect.description": "以下函数所必需:sum、mean、median、max、min、info_content、distinct_count。", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.fieldSelect.title": "字段", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.overFieldSelect.description": "通过与人口行为对比来检测异常的人口分析所必需。", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.overFieldSelect.title": "基于字段", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.partitionFieldSelect.description": "允许将建模分割成逻辑组。", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.partitionFieldSelect.title": "分区字段", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.saveButton": "保存", + "xpack.ml.newJob.wizard.pickFieldsStep.advancedDetectorModal.title": "创建检测工具", + "xpack.ml.newJob.wizard.pickFieldsStep.categorizationField.description": "可选,用于分析非结构化日志数据。建议使用文本数据类型。", + "xpack.ml.newJob.wizard.pickFieldsStep.categorizationField.title": "归类字段", + "xpack.ml.newJob.wizard.pickFieldsStep.noDetectorsCallout.message": "至少需要一个检测工具,才能创建作业。", + "xpack.ml.newJob.wizard.pickFieldsStep.noDetectorsCallout.title": "无检测工具", + "xpack.ml.newJob.wizard.pickFieldsStep.summaryCountField.description": "可选,用于输入数据已预汇总时,例如 \\{docCountParam\\}。", + "xpack.ml.newJob.wizard.pickFieldsStep.summaryCountField.title": "汇总计数字段", + "xpack.ml.newJob.wizard.previewJsonButton": "预览 JSON", + "xpack.ml.newJob.wizard.searchSelection.notFoundLabel": "未找到匹配的索引或已保存搜索。", + "xpack.ml.newJob.wizard.searchSelection.savedObjectType.indexPattern": "索引模式", + "xpack.ml.newJob.wizard.searchSelection.savedObjectType.search": "已保存搜索", + "xpack.ml.newJob.wizard.selectIndexPatternOrSavedSearch": "选择索引模式或已保存搜索", + "xpack.ml.newJob.wizard.step.configureDatafeedTitle": "配置数据馈送", + "xpack.ml.newJob.wizard.stepComponentWrapper.configureDatafeedTitle": "配置数据馈送", + "xpack.ml.newJob.wizard.summaryStep.datafeedConfig.title": "数据馈送配置", + "xpack.ml.newJob.wizard.summaryStep.datafeedDetails.frequency.title": "频率", + "xpack.ml.newJob.wizard.summaryStep.datafeedDetails.query.title": "滚动条大小", + "xpack.ml.newJob.wizard.summaryStep.datafeedDetails.queryDelay.title": "查询延迟", + "xpack.ml.newJob.wizard.summaryStep.datafeedDetails.scrollSize.title": "滚动条大小", + "xpack.ml.newJob.wizard.summaryStep.datafeedDetails.timeField.title": "时间字段", + "xpack.ml.newJob.wizard.summaryStep.defaultString": "默认值", + "xpack.ml.newJob.wizard.summaryStep.falseLabel": "False", + "xpack.ml.newJob.wizard.summaryStep.jobConfig.title": "作业配置", + "xpack.ml.newJob.wizard.summaryStep.jobDetails.categorizationField.title": "归类字段", + "xpack.ml.newJob.wizard.summaryStep.jobDetails.summaryCountField.title": "汇总计数字段", + "xpack.ml.newJob.wizard.summaryStep.timeRange.end.title": "结束", + "xpack.ml.newJob.wizard.summaryStep.timeRange.start.title": "开始", + "xpack.ml.newJob.wizard.summaryStep.trueLabel": "True", + "xpack.ml.newJob.wizard.validateJob.frequencyInvalidTimeIntervalFormatErrorMessage": "{value} 不是有效的时间间隔格式,例如,{tenMinutes}、{oneHour}。还需要大于零。", + "xpack.ml.newJob.wizard.validateJob.jobGroupMaxLengthDescription": "作业组名称的长度不得超过 {maxLength, plural, one {# 个字符} other {# 个字符}}。", + "xpack.ml.newJob.wizard.validateJob.jobIdInvalidMaxLengthErrorMessage": "作业 ID 的长度不得超过 {maxLength, plural, one {# 个字符} other {# 个字符}}。", + "xpack.ml.newJob.wizard.validateJob.queryCannotBeEmpty": "数据馈送查询不能为空。", + "xpack.ml.newJob.wizard.validateJob.queryIsInvalidEsQuery": "数据馈送查询必须是有效的 Elasticsearch 查询。", + "xpack.ml.overview.analyticsList.createFirstJobMessage": "创建您的首个分析作业。", + "xpack.ml.overview.analyticsList.createJobButtonText": "创建作业", + "xpack.ml.overview.analyticsList.emptyPromptText": "数据帧分析允许您对数据执行不同的分析,并使用结果标注数据。分析作业将标注的数据以及源数据的副本存储在新的索引中。", + "xpack.ml.overview.analyticsList.errorPromptTitle": "获取数据帧分析列表时发生错误。", + "xpack.ml.overview.analyticsList.id": "ID", + "xpack.ml.overview.analyticsList.manageJobsButtonText": "管理作业", + "xpack.ml.overview.analyticsList.PanelTitle": "分析", + "xpack.ml.overview.analyticsList.reatedTimeColumnName": "创建时间", + "xpack.ml.overview.analyticsList.refreshJobsButtonText": "刷新", + "xpack.ml.overview.analyticsList.status": "状态", + "xpack.ml.overview.analyticsList.tableActionLabel": "操作", + "xpack.ml.overview.analyticsList.type": "类型", + "xpack.ml.overview.anomalyDetection.createFirstJobMessage": "创建您的首个异常检测作业。", + "xpack.ml.overview.anomalyDetection.createJobButtonText": "创建作业", + "xpack.ml.overview.anomalyDetection.emptyPromptText": "通过 Machine Learning,可以轻松检测 Elasticsearch 中存储的时序数据中的异常。跟踪单个机器的一个指标或数千个机器的数百个指标。开始自动发现数据中隐藏的异常并更快捷地解决问题。", + "xpack.ml.overview.anomalyDetection.errorPromptTitle": "获取异常检测作业列表时出错。", + "xpack.ml.overview.anomalyDetection.errorWithFetchingAnomalyScoreNotificationErrorMessage": "获取异常分数时出错:{error}", + "xpack.ml.overview.anomalyDetection.exploreActionName": "浏览", + "xpack.ml.overview.anomalyDetection.manageJobsButtonText": "管理作业", + "xpack.ml.overview.anomalyDetection.panelTitle": "异常检测", + "xpack.ml.overview.anomalyDetection.refreshJobsButtonText": "刷新", + "xpack.ml.overview.anomalyDetection.resultActions.openJobsInAnomalyExplorerText": "在 Anomaly Explorer 中打开 {jobsCount, plural, one {{jobId}} other {# 个作业}}", + "xpack.ml.overview.anomalyDetection.tableActionLabel": "操作", + "xpack.ml.overview.anomalyDetection.tableDocsProcessed": "已处理文档", + "xpack.ml.overview.anomalyDetection.tableId": "组 ID", + "xpack.ml.overview.anomalyDetection.tableLatestTimestamp": "最新时间戳", + "xpack.ml.overview.anomalyDetection.tableMaxScore": "最大异常分数", + "xpack.ml.overview.anomalyDetection.tableMaxScoreErrorTooltip": "加载最大异常分数时出现问题", + "xpack.ml.overview.anomalyDetection.tableMaxScoreTooltip": "最近 24 小时期间组中所有作业的最大分数", + "xpack.ml.overview.anomalyDetection.tableNumJobs": "组中的作业", + "xpack.ml.overview.feedbackSectionLink": "在线反馈", + "xpack.ml.overview.feedbackSectionText": "如果您对 Machine Learning 体验有任何建议,请随时{feedbackLink}。", + "xpack.ml.overview.feedbackSectionTitle": "反馈", + "xpack.ml.overview.gettingStartedSectionCreateJob": "创建新作业", + "xpack.ml.overview.gettingStartedSectionDocs": "文档", + "xpack.ml.overview.gettingStartedSectionTitle": "入门", + "xpack.ml.overview.statsBar.failedAnalyticsLabel": "失败", + "xpack.ml.overview.statsBar.runningAnalyticsLabel": "正在运行", + "xpack.ml.overview.statsBar.stoppedAnalyticsLabel": "已停止", + "xpack.ml.overview.statsBar.totalAnalyticsLabel": "分析作业总数", + "xpack.ml.overviewBreadcrumbs.overviewLabel": "概览", + "xpack.ml.overviewJobsList.statsBar.activeMLNodesLabel": "活动 ML 节点", + "xpack.ml.overviewJobsList.statsBar.closedJobsLabel": "已关闭的作业", + "xpack.ml.overviewJobsList.statsBar.failedJobsLabel": "失败的作业", + "xpack.ml.overviewJobsList.statsBar.openJobsLabel": "打开的作业", + "xpack.ml.overviewJobsList.statsBar.totalJobsLabel": "总计作业数", + "xpack.ml.settingsBreadcrumbLabel": "设置", + "xpack.ml.validateJob.allPassed": "所有验证检查都成功通过", + "xpack.ml.dataframe.stepCreateForm.createDataFrameAnalyticsSuccessMessage": "数据帧分析 {jobId} 创建请求已确认。", + "xpack.ml.explorer.distributionChart.anomalyScoreLabel": "异常分数", + "xpack.ml.explorer.distributionChart.typicalLabel": "典型", + "xpack.ml.explorer.distributionChart.unusualByFieldValuesLabel": "{ numberOfCauses, plural, one {# 个异常 {byFieldName} 值} other {#{plusSign} 个异常 {byFieldName} 值}}", + "xpack.ml.explorer.distributionChart.valueLabel": "值", + "xpack.ml.explorer.distributionChart.valueWithoutAnomalyScoreLabel": "值", + "xpack.ml.explorer.singleMetricChart.actualLabel": "实际", + "xpack.ml.explorer.singleMetricChart.anomalyScoreLabel": "异常分数", + "xpack.ml.explorer.singleMetricChart.multiBucketImpactLabel": "多存储桶影响", + "xpack.ml.explorer.singleMetricChart.scheduledEventsLabel": "已计划事件", + "xpack.ml.explorer.singleMetricChart.typicalLabel": "典型", + "xpack.ml.explorer.singleMetricChart.valueLabel": "值", + "xpack.ml.explorer.singleMetricChart.valueWithoutAnomalyScoreLabel": "值", + "xpack.ml.explorer.swimlane.maxAnomalyScoreLabel": "最大异常分数", + "xpack.ml.models.jobValidation.messages.jobGroupIdValidMessage": "小写字母数字(a-z 和 0-9)字符、连字符或下划线,以字母数字字符开头和结尾,且长度不超过 {maxLength, plural, one {# 个字符} other {# 个字符}}。", + "xpack.ml.models.jobValidation.messages.jobIdValidMessage": "小写字母数字(a-z 和 0-9)字符、连字符或下划线,以字母数字字符开头和结尾,且长度不超过 {maxLength, plural, one {# 个字符} other {# 个字符}}。", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.actualLabel": "实际", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.anomalyScoreLabel": "异常分数", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.lowerBoundsLabel": "下边界", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.upperBoundsLabel": "上边界", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.modelPlotEnabled.valueLabel": "值", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.moreThanOneUnusualByFieldValuesLabel": "{numberOfCauses}{plusSign} 异常 {byFieldName} 值", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.multiBucketImpactLabel": "多存储桶影响", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.scheduledEventsLabel": "已计划事件{counter}", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.typicalLabel": "典型", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.valueLabel": "值", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScore.predictionLabel": "预测", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScore.valueLabel": "值", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScoreAndModelPlotEnabled.lowerBoundsLabel": "下边界", + "xpack.ml.timeSeriesExplorer.timeSeriesChart.withoutAnomalyScoreAndModelPlotEnabled.upperBoundsLabel": "上边界", "xpack.transform.capability.noPermission.createTransformTooltip": "您无权创建数据帧转换。", "xpack.transform.capability.noPermission.deleteTransformTooltip": "您无权删除数据帧转换。", "xpack.transform.models.transformService.allOtherRequestsCancelledDescription": "所有其他请求已取消。", - "xpack.transform.models.transformService.requestToActionTimedOutErrorMessage": "对 {action}“{id}” 的请求超时。{extra}", + "xpack.transform.models.transformService.requestToActionTimedOutErrorMessage": "对 {action}“{id}”的请求超时。{extra}", "xpack.transform.list.errorPromptTitle": "获取数据帧转换列表时发生错误。", "xpack.transform.modeFilter": "模式", "xpack.transform.multiTransformActionsMenu.managementActionsAriaLabel": "管理操作", @@ -6846,11 +8252,11 @@ "xpack.transform.statsBar.batchTransformsLabel": "批量", "xpack.transform.statsBar.continuousTransformsLabel": "连续", "xpack.transform.statsBar.failedTransformsLabel": "失败", - "xpack.transform.statsBar.startedTransformsLabel": "已开始", + "xpack.transform.statsBar.startedTransformsLabel": "已启动", "xpack.transform.statsBar.totalTransformsLabel": "转换总数", "xpack.transform.statusFilter": "状态", "xpack.transform.stepCreateForm.continuousModeLabel": "连续模式", - "xpack.transform.stepCreateForm.duplicateIndexPatternErrorMessage": "创建 Kibana 索引模式 {indexPatternName} 时发生错误:该索引模式已存在。", + "xpack.transform.stepCreateForm.duplicateIndexPatternErrorMessage": "创建 Kibana 索引模式时发生错误 {indexPatternName}:该索引模式已存在。", "xpack.transform.stepDefineForm.advancedEditorApplyButtonText": "应用更改", "xpack.transform.stepDefineForm.advancedEditorAriaLabel": "高级数据透视表编辑器", "xpack.transform.stepDefineForm.advancedEditorHelpText": "高级编辑器允许您编辑数据帧转换的数据透视表配置。", @@ -6895,14 +8301,14 @@ "xpack.transform.transformList.bulkDeleteModalBody": "是否确定要删除{count, plural, one {这} other {这}} {count} 个 {count, plural, one {转换} other {转换}}?转换的目标索引和可选 Kibana 索引模式将不会删除。", "xpack.transform.transformList.bulkDeleteModalTitle": "删除 {count} 个 {count, plural, one {转换} other {转换}}?", "xpack.transform.transformList.bulkStartModalTitle": "启动 {count} 个 {count, plural, one {转换} other {转换}}?", - "xpack.transform.transformList.completeBatchTransformBulkActionToolTip": "一个或多个选定数据帧转换是已完成的批量转换,无法重新启动。", + "xpack.transform.transformList.completeBatchTransformBulkActionToolTip": "一个或多个转换为已完成批量转换,无法重新启动。", "xpack.transform.transformList.deleteBulkActionDisabledToolTipContent": "一个或多个选定数据帧转换必须停止,才能删除。", - "xpack.transform.transformList.deleteTransformErrorMessage": "删除数据帧转换 {transformId} 时发生错误", + "xpack.transform.transformList.deleteTransformErrorMessage": "删除转换 {transformId} 时发生错误", "xpack.transform.transformList.refreshButtonLabel": "刷新", "xpack.transform.transformList.startedTransformBulkToolTip": "一个或多个选定数据帧转换已启动。", "xpack.transform.transformList.startedTransformToolTip": "{transformId} 已启动。", - "xpack.transform.transformList.startModalBody": "数据帧转换将增加集群的搜索和索引负荷。如果负荷超载,请停止转换。是否确定要启动{count, plural, one {这} other {这}} {count} 个 {count, plural, one {转换} other {转换}}?", - "xpack.transform.transformList.startTransformErrorMessage": "启动数据帧转换 {transformId} 时发生错误", + "xpack.transform.transformList.startModalBody": "转换将增加集群的搜索和索引负荷。如果负荷超载,请停止转换。是否确定要启动{count, plural, one {这} other {这}} {count} 个 {count, plural, one {转换} other {转换}}?", + "xpack.transform.transformList.startTransformErrorMessage": "启动转换 {transformId} 时发生错误", "xpack.transform.transformList.stoppedTransformBulkToolTip": "一个或多个选定数据帧转换已停止。", "xpack.transform.transformList.stoppedTransformToolTip": "{transformId} 已停止。", "xpack.transform.transformList.stopTransformErrorMessage": "停止数据帧转换 {transformId} 时发生错误", @@ -6910,7 +8316,7 @@ "xpack.transform.transformList.transformDetails.tabs.transformPreviewLabel": "预览", "xpack.transform.agg.popoverForm.aggLabel": "聚合", "xpack.transform.agg.popoverForm.aggNameAlreadyUsedError": "其他聚合已使用该名称。", - "xpack.transform.agg.popoverForm.aggNameInvalidCharError": "名称无效。不允许使用字符“[”、“]” 和“>”且名称不得以空格字符开头或结束。", + "xpack.transform.agg.popoverForm.aggNameInvalidCharError": "名称无效。不允许使用字符“[”、“]”和“>”,且名称不得以空格字符开头或结束。", "xpack.transform.agg.popoverForm.fieldLabel": "字段", "xpack.transform.agg.popoverForm.nameLabel": "聚合名称", "xpack.transform.agg.popoverForm.submitButtonLabel": "应用", @@ -6923,7 +8329,7 @@ "xpack.transform.stepDefineForm.nestedGroupByListConflictErrorMessage": "无法添加配置“{aggName}”,因为与“{groupByListName}”有嵌套冲突。", "xpack.transform.stepDefineForm.aggregationsLabel": "聚合", "xpack.transform.stepDefineForm.aggregationsPlaceholder": "添加聚合……", - "xpack.transform.stepDefineForm.formHelp": "数据帧转换是用于数据透视的可伸缩和自动化流程。至少选择一个分组依据和聚合,才能开始。", + "xpack.transform.stepDefineForm.formHelp": "转换是用于数据透视的可伸缩和自动化流程。至少选择一个分组依据和聚合,才能开始。", "xpack.transform.stepDefineForm.groupByLabel": "分组依据", "xpack.transform.stepDefineForm.groupByPlaceholder": "添加分组依据字段……", "xpack.transform.stepDefineForm.indexPatternHelpText": "不支持此索引模式的可选查询。受支持索引字段数目为 {maxIndexFields},而此索引有 {numIndexFields} 个字段。", @@ -6937,7 +8343,7 @@ "xpack.transform.stepDefineSummary.queryLabel": "查询", "xpack.transform.groupby.popoverForm.aggLabel": "聚合", "xpack.transform.groupBy.popoverForm.aggNameAlreadyUsedError": "其他分组依据配置已使用该名称。", - "xpack.transform.groupBy.popoverForm.aggNameInvalidCharError": "名称无效。不允许使用字符“[”、“]” 和“>”且名称不得以空格字符开头或结束。", + "xpack.transform.groupBy.popoverForm.aggNameInvalidCharError": "名称无效。不允许使用字符“[”、“]”和“>”,且名称不得以空格字符开头或结束。", "xpack.transform.groupBy.popoverForm.fieldLabel": "字段", "xpack.transform.groupBy.popoverForm.intervalError": "时间间隔无效。", "xpack.transform.groupBy.popoverForm.intervalLabel": "时间间隔", @@ -6948,7 +8354,7 @@ "xpack.transform.stepCreateForm.copyTransformConfigToClipboardButton": "复制到剪贴板", "xpack.transform.stepCreateForm.copyTransformConfigToClipboardDescription": "将用于创建作业的 Kibana 开发控制台命令复制到剪贴板。", "xpack.transform.stepCreateForm.createIndexPatternLabel": "创建索引模式", - "xpack.transform.stepCreateForm.createTransformSuccessMessage": "数据帧作业 {transformId} 创建成功。", + "xpack.transform.stepCreateForm.createTransformSuccessMessage": "创建转换 {transformId} 的请求已确认。", "xpack.transform.stepCreateForm.creatingIndexPatternMessage": "正在创建 Kibana 索引模式......", "xpack.transform.stepCreateForm.discoverCardDescription": "使用 Discover 浏览数据帧透视表。", "xpack.transform.stepCreateForm.discoverCardTitle": "Discover", @@ -6956,19 +8362,19 @@ "xpack.transform.stepCreateForm.transformListCardTitle": "数据帧作业", "xpack.transform.stepCreateForm.progressTitle": "进度", "xpack.transform.stepCreateForm.createIndexPatternSuccessMessage": "Kibana 索引模式 {indexPatternName} 成功创建。", - "xpack.transform.stepCreateForm.startTransformSuccessMessage": "数据帧作业 {transformId} 启动成功。", + "xpack.transform.stepCreateForm.startTransformSuccessMessage": "启动转换 {transformId} 的请求已确认。", "xpack.transform.stepDetailsForm.indexPatternTitleError": "具有此名称的索引模式已存在。", "xpack.transform.stepDetailsForm.transformIdInputAriaLabel": "选择唯一的作业 ID。", "xpack.transform.stepDetailsForm.transformIdLabel": "作业 ID", "xpack.transform.stepDetailsSummary.createIndexPatternMessage": "将为此作业创建 Kibana 索引模式。", "xpack.transform.stepDetailsSummary.transformIdLabel": "作业 ID", "xpack.transform.transformList.betaBadgeLabel": "公测版", - "xpack.transform.transformList.betaBadgeTooltipContent": "数据帧是公测版功能。我们很乐意听取您的反馈意见。", - "xpack.transform.transformList.completeBatchTransformToolTip": "{transformId} 为已完成的批处理作业,无法重新启动。", + "xpack.transform.transformList.betaBadgeTooltipContent": "转换为公测版功能。我们很乐意听取您的反馈意见。", + "xpack.transform.transformList.completeBatchTransformToolTip": "{transformId} 为已完成批量转换,无法重新启动。", "xpack.transform.transformList.deleteActionDisabledToolTipContent": "停止数据帧作业,以便将其删除。", "xpack.transform.transformList.deleteActionName": "删除", - "xpack.transform.transformList.deleteTransformSuccessMessage": "数据帧作业 {transformId} 删除成功。", - "xpack.transform.transformList.deleteModalBody": "是否确定要删除此作业?作业的目标索引和可选 Kibana 索引模式将不会删除。", + "xpack.transform.transformList.deleteTransformSuccessMessage": "删除转换 {transformId} 的请求已确认。", + "xpack.transform.transformList.deleteModalBody": "是否确定要删除此转换?转换的目标索引和可选 Kibana 索引模式将不会删除。", "xpack.transform.transformList.deleteModalCancelButton": "取消", "xpack.transform.transformList.deleteModalDeleteButton": "删除", "xpack.transform.transformList.deleteModalTitle": "删除 {transformId}", @@ -6976,17 +8382,17 @@ "xpack.transform.transformList.rowCollapse": "隐藏 {transformId} 的详情", "xpack.transform.transformList.rowExpand": "显示 {transformId} 的详情", "xpack.transform.transformList.startActionName": "开始", - "xpack.transform.transformList.startTransformSuccessMessage": "数据帧作业 {transformId} 启动成功。", + "xpack.transform.transformList.startTransformSuccessMessage": "启动转换 {transformId} 的请求已确认。", "xpack.transform.transformList.startModalCancelButton": "取消", - "xpack.transform.transformList.startModalStartButton": "开始", + "xpack.transform.transformList.startModalStartButton": "启动", "xpack.transform.transformList.startModalTitle": "启动 {transformId}", "xpack.transform.transformList.stopActionName": "停止", - "xpack.transform.transformList.stopTransformSuccessMessage": "数据帧作业 {transformId} 停止成功。", + "xpack.transform.transformList.stopTransformSuccessMessage": "停止数据帧转换 {transformId} 的请求已确认。", "xpack.transform.pivotPreview.copyClipboardTooltip": "将透视预览的开发控制台语句复制到剪贴板。", "xpack.transform.progress": "进度", "xpack.transform.sourceIndex": "源索引", "xpack.transform.sourceIndexPreview.copyClipboardTooltip": "将源索引预览的开发控制台语句复制到剪贴板。", - "xpack.transform.sourceIndexPreview.fieldSelection": "显示 {selectedFieldsLength, number} 个{docFieldsCount, plural, one {字段} other {字段}},共 {docFieldsCount, number} 个", + "xpack.transform.sourceIndexPreview.fieldSelection": "已选择 {selectedFieldsLength, number} 个{docFieldsCount, plural, one {字段} other {字段}},共 {docFieldsCount, number} 个", "xpack.transform.sourceIndexPreview.rowCollapse": "折叠", "xpack.transform.sourceIndexPreview.rowExpand": "展开", "xpack.transform.sourceIndexPreview.selectColumnsAriaLabel": "选择列", @@ -7001,6 +8407,65 @@ "xpack.transform.transformsWizard.stepDetailsTitle": "作业详情", "xpack.transform.wizard.nextStepButton": "下一个", "xpack.transform.wizard.previousStepButton": "上一页", + "xpack.transform.agg.popoverForm.unsupportedAggregationHelpText": "在此表单中仅可以编辑聚合名称。请使用高级编辑器编辑聚合的其他部分。", + "xpack.transform.app.checkingPrivilegesDescription": "正在检查权限……", + "xpack.transform.app.checkingPrivilegesErrorMessage": "从服务器获取用户权限时出错。", + "xpack.transform.app.deniedPrivilegeDescription": "要使用“转换”的此部分,必须具有{privilegesCount, plural, one {以下集群权限} other {以下集群权限}}:{missingPrivileges}。", + "xpack.transform.app.deniedPrivilegeTitle": "您缺少集群权限", + "xpack.transform.appName": "数据帧作业", + "xpack.transform.capability.noPermission.startOrStopTransformTooltip": "您无权启动或停止转换。", + "xpack.transform.capability.pleaseContactAdministratorTooltip": "{message}请联系您的管理员。", + "xpack.transform.createTransform.breadcrumbTitle": "创建转换", + "xpack.transform.description": "描述", + "xpack.transform.destinationIndex": "目标 IP", + "xpack.transform.groupBy.popoverForm.unsupportedGroupByHelpText": "在此表单中仅可以编辑 group_by 名称。请使用高级编辑器编辑 group_by 配置的其他部分。", + "xpack.transform.home.breadcrumbTitle": "数据帧作业", + "xpack.transform.list.emptyPromptButtonText": "创建您的首个转换", + "xpack.transform.list.emptyPromptTitle": "找不到转换", + "xpack.transform.mode": "模式", + "xpack.transform.newTransform.chooseSourceTitle": "选择源", + "xpack.transform.newTransform.newTransformTitle": "新转换", + "xpack.transform.newTransform.searchSelection.notFoundLabel": "未找到匹配的索引或已保存搜索。", + "xpack.transform.newTransform.searchSelection.savedObjectType.indexPattern": "索引模式", + "xpack.transform.newTransform.searchSelection.savedObjectType.search": "已保存搜索", + "xpack.transform.pivotPreview.PivotPreviewError": "加载数据透视表预览时出错。", + "xpack.transform.pivotPreview.PivotPreviewIncompleteConfigCalloutBody": "请至少选择一个分组依据字段和聚合。", + "xpack.transform.pivotPreview.PivotPreviewNoDataCalloutBody": "预览请求未返回任何数据。请确保可选查询返回数据且存在分组依据和聚合字段使用的字段的值。", + "xpack.transform.pivotPreview.PivotPreviewNoDataCalloutTitle": "数据透视表预览不可用", + "xpack.transform.pivotPreview.PivotPreviewTitle": "转换数据透视表预览", + "xpack.transform.sourceIndexPreview.SourceIndexArrayBadgeContent": "数组", + "xpack.transform.sourceIndexPreview.SourceIndexArrayToolTipContent": "此基于数组的列的完整内容在展开的行中。", + "xpack.transform.sourceIndexPreview.SourceIndexNoDataCalloutBody": "源索引的查询未返回结果。请确保索引包含文档且您的查询限制不过于严格。", + "xpack.transform.sourceIndexPreview.SourceIndexNoDataCalloutTitle": "源索引查询结果为空。", + "xpack.transform.sourceIndexPreview.SourceIndexObjectBadgeContent": "对象", + "xpack.transform.sourceIndexPreview.SourceIndexObjectToolTipContent": "此基于对象的列的完整内容在展开的行中。", + "xpack.transform.stepCreateForm.createAndStartTransformButton": "创建并启动", + "xpack.transform.stepCreateForm.createAndStartTransformDescription": "创建并启动转换。转换将增加集群的搜索和索引负荷。如果负荷超载,请停止转换。转换启动后,系统将为您提供继续浏览转换的选项。", + "xpack.transform.stepCreateForm.createIndexPatternErrorMessage": "创建 Kibana 索引模式时发生错误 {indexPatternName}:", + "xpack.transform.stepCreateForm.createTransformButton": "创建", + "xpack.transform.stepCreateForm.createTransformDescription": "在不启动转换的情况下创建转换。您之后能够通过返回到转换列表,来启动转换。", + "xpack.transform.stepCreateForm.createTransformErrorMessage": "创建转换 {transformId} 时出错:", + "xpack.transform.stepCreateForm.progressErrorMessage": "获取进度百分比时出错:", + "xpack.transform.stepCreateForm.startTransformButton": "开始", + "xpack.transform.stepCreateForm.startTransformDescription": "启动转换。转换将增加集群的搜索和索引负荷。如果负荷超载,请停止转换。转换启动后,系统将为您提供继续浏览转换的选项。", + "xpack.transform.stepCreateForm.startTransformErrorMessage": "启动转换 {transformId} 时发生错误:", + "xpack.transform.stepDetailsForm.errorGettingIndexNames": "获取现有索引名称时发生错误:", + "xpack.transform.stepDetailsForm.errorGettingIndexPatternTitles": "获取现有索引模式标题时发生错误:", + "xpack.transform.stepDetailsForm.errorGettingTransformList": "获取现有转换 ID 时发生错误:", + "xpack.transform.toastText.closeModalButtonText": "关闭", + "xpack.transform.toastText.modalTitle": "错误详细信息", + "xpack.transform.toastText.openModalButtonText": "查看详情", + "xpack.transform.transformForm.sizeNotationPlaceholder": "示例:{example1}、{example2}、{example3}、{example4}", + "xpack.transform.transformList.createTransformButton": "创建转换", + "xpack.transform.transformList.deleteTransformGenericErrorMessage": "调用用于删除转换的 API 终端节点时发生错误。", + "xpack.transform.transformList.stepDetails.previewPane.errorMessage": "无法加载预览", + "xpack.transform.transformList.transformDetails.messagesPane.errorMessage": "无法加载消息", + "xpack.transform.transformList.transformDetails.messagesPane.messageLabel": "消息", + "xpack.transform.transformList.transformDetails.messagesPane.nodeLabel": "节点", + "xpack.transform.transformList.transformDetails.messagesPane.timeLabel": "时间", + "xpack.transform.transformList.transformTitle": "数据帧作业", + "xpack.transform.transformsWizard.betaBadgeTooltipContent": "转换为公测版功能。我们很乐意听取您的反馈意见。", + "xpack.transform.transformsWizard.createTransformTitle": "创建转换", "xpack.monitoring.accessDenied.backToKibanaButtonLabel": "返回 Kibana", "xpack.monitoring.accessDenied.clusterNotConfiguredDescription": "如果您尝试访问专用监测集群,则这可能是因为该监测集群上未配置您登录时所用的用户帐户。", "xpack.monitoring.accessDenied.notAuthorizedDescription": "您无权访问 Monitoring。要使用 Monitoring,您同时需要 `{kibanaUser}` 和 `{monitoringUser}` 角色授予的权限。", @@ -7374,7 +8839,7 @@ "xpack.monitoring.esNavigation.overviewLinkText": "概览", "xpack.monitoring.euiTable.isFullyMigratedLabel": "Metricbeat 收集", "xpack.monitoring.euiTable.isInternalCollectorLabel": "内部收集", - "xpack.monitoring.euiTable.isPartiallyMigratedLabel": "内部收集和 Metricbeat 收集", + "xpack.monitoring.euiTable.isPartiallyMigratedLabel": "内部收集开启", "xpack.monitoring.feature.reserved.description": "要向用户授予访问权限,还应分配 monitoring_user 角色。", "xpack.monitoring.featureRegistry.monitoringFeatureName": "堆栈监测", "xpack.monitoring.formatNumbers.notAvailableLabel": "不适用", @@ -7494,33 +8959,33 @@ "xpack.monitoring.logstashNavigation.overviewLinkText": "概览", "xpack.monitoring.logstashNavigation.pipelinesLinkText": "管道", "xpack.monitoring.logstashNavigation.pipelineVersionDescription": "活动版本 {relativeLastSeen} 和首次看到 {relativeFirstSeen}", - "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.configureMetricbeatDescription": "在 {file} 文件中进行这些更改。", + "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.configureMetricbeatDescription": "修改 {file} 以设置连接信息。", "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.configureMetricbeatTitle": "配置 Metricbeat 以发送至监测集群", "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.disableInternalCollectionDescription": "禁用 Elasticsearch 监测指标的内部收集。在生产集群中的每个服务器上将 {monospace} 设置为 false。", "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.disableInternalCollectionTitle": "禁用 Elasticsearch 监测指标的内部收集", - "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.enableMetricbeatModuleDescription": "默认情况下,该模块将从 {url} 收集 Elasticsearch 监测指标。如果本地 Elasticsearch 服务器有不同的地址,则必须通过 {module} 文件中的 hosts 设置来进行指定。", + "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.enableMetricbeatModuleDescription": "默认情况下,模块从 {url} 收集 Elasticsearch 指标。如果本地服务器有不同的地址,请在 {module} 中将其添加到主机设置。", "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.enableMetricbeatModuleTitle": "在 Metricbeat 中启用并配置 Elasticsearch x-pack 模块", - "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.installMetricbeatLinkText": "按照此处的说明执行操作", + "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.installMetricbeatLinkText": "按照此处的说明执行操作。", "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.installMetricbeatTitle": "在安装 Elasticsearch 的同一台服务器上安装 Metricbeat", - "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.startMetricbeatLinkText": "按照此处的说明执行操作", + "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.startMetricbeatLinkText": "按照此处的说明执行操作。", "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.startMetricbeatTitle": "启动 Metricbeat", "xpack.monitoring.metricbeatMigration.flyout.closeButtonLabel": "关闭", "xpack.monitoring.metricbeatMigration.flyout.doneButtonLabel": "完成", "xpack.monitoring.metricbeatMigration.flyout.nextButtonLabel": "下一个", - "xpack.monitoring.metricbeatMigration.flyout.step1.monitoringUrlHelpText": "这通常是单个实例,但如果您有多个,请输入所有实例 url,以逗号分隔。\n 切记运行的 Metricbeat 实例需要能够与这些 Elasticsearch 实例通信。", + "xpack.monitoring.metricbeatMigration.flyout.step1.monitoringUrlHelpText": "通常为单个 URL。如果有多个 URL,请使用逗号分隔。\n 正在运行的 Metricbeat 实例必须能够与这些 Elasticsearch 服务器通信。", "xpack.monitoring.metricbeatMigration.flyout.step1.monitoringUrlLabel": "监测集群 URL", "xpack.monitoring.metricbeatMigration.kibanaInstructions.configureMetricbeatDescription": "在 {file} 文件中进行这些更改。", "xpack.monitoring.metricbeatMigration.kibanaInstructions.configureMetricbeatTitle": "配置 Metricbeat 以发送至监测集群", - "xpack.monitoring.metricbeatMigration.kibanaInstructions.disableInternalCollection.description": "在 Kibana 配置文件 ({file}) 中添加以下设置:", - "xpack.monitoring.metricbeatMigration.kibanaInstructions.disableInternalCollection.note": "将 {config} 设置为其默认值 ({defaultValue})。", + "xpack.monitoring.metricbeatMigration.kibanaInstructions.disableInternalCollection.description": "将此设置添加到 {file}。", + "xpack.monitoring.metricbeatMigration.kibanaInstructions.disableInternalCollection.note": "对于 {config},请保留默认值 ({defaultValue})。", "xpack.monitoring.metricbeatMigration.kibanaInstructions.disableInternalCollection.restartNote": "此步骤需要您重新启动 Kibana 服务器。在服务器再次运行之前应会看到错误。", "xpack.monitoring.metricbeatMigration.kibanaInstructions.disableInternalCollection.restartWarningTitle": "警告", "xpack.monitoring.metricbeatMigration.kibanaInstructions.disableInternalCollection.title": "禁用 Kibana 监测指标的内部收集", "xpack.monitoring.metricbeatMigration.kibanaInstructions.enableMetricbeatModuleDescription": "该模块将默认从 http://localhost:5601 收集 Kibana 监测指标。如果本地 Kibana 实例有不同的地址,则必须通过 {file} 文件中的 {hosts} 设置进行指定。", "xpack.monitoring.metricbeatMigration.kibanaInstructions.enableMetricbeatModuleTitle": "在 Metricbeat 中启用并配置 Kibana x-pack 模块", - "xpack.monitoring.metricbeatMigration.kibanaInstructions.installMetricbeatLinkText": "按照此处的说明执行操作", + "xpack.monitoring.metricbeatMigration.kibanaInstructions.installMetricbeatLinkText": "按照此处的说明执行操作。", "xpack.monitoring.metricbeatMigration.kibanaInstructions.installMetricbeatTitle": "在安装 Kibana 的同一台服务器上安装 Metricbeat", - "xpack.monitoring.metricbeatMigration.kibanaInstructions.startMetricbeatLinkText": "按照此处的说明执行操作", + "xpack.monitoring.metricbeatMigration.kibanaInstructions.startMetricbeatLinkText": "按照此处的说明执行操作。", "xpack.monitoring.metricbeatMigration.kibanaInstructions.startMetricbeatTitle": "启动 Metricbeat", "xpack.monitoring.metrics.apm.outputAckedEventsRate.ackedDescription": "输出处理的事件(包括重试)", "xpack.monitoring.metrics.apm.outputAckedEventsRate.ackedLabel": "已确认", @@ -8074,9 +9539,9 @@ "xpack.monitoring.metricbeatMigration.apmInstructions.disableInternalCollection.title": "禁用 APM Server 监测指标的内部收集", "xpack.monitoring.metricbeatMigration.apmInstructions.enableMetricbeatModuleDescription": "该模块将默认从 http://localhost:5066 收集 APM Server 监测指标。如果本地 APM Server 有不同的地址,则必须通过 {file} 文件中的 {hosts} 设置进行指定。", "xpack.monitoring.metricbeatMigration.apmInstructions.enableMetricbeatModuleTitle": "在 Metricbeat 中启用并配置 Beat x-pack 模块", - "xpack.monitoring.metricbeatMigration.apmInstructions.installMetricbeatLinkText": "按照此处的说明执行操作", + "xpack.monitoring.metricbeatMigration.apmInstructions.installMetricbeatLinkText": "按照此处的说明执行操作。", "xpack.monitoring.metricbeatMigration.apmInstructions.installMetricbeatTitle": "在安装 APM Server 的同一台服务器上安装 Metricbeat", - "xpack.monitoring.metricbeatMigration.apmInstructions.startMetricbeatLinkText": "按照此处的说明执行操作", + "xpack.monitoring.metricbeatMigration.apmInstructions.startMetricbeatLinkText": "按照此处的说明执行操作。", "xpack.monitoring.metricbeatMigration.apmInstructions.startMetricbeatTitle": "启动 Metricbeat", "xpack.monitoring.metricbeatMigration.beatsInstructions.configureMetricbeatDescription": "在 {file} 文件中进行这些更改。", "xpack.monitoring.metricbeatMigration.beatsInstructions.configureMetricbeatTitle": "配置 Metricbeat 以发送至监测集群", @@ -8087,9 +9552,9 @@ "xpack.monitoring.metricbeatMigration.beatsInstructions.enableMetricbeatModuleHttpEnabledDirections": "要使 Metricbeat 从正在运行的 {beatType} 收集指标,需要{link}。", "xpack.monitoring.metricbeatMigration.beatsInstructions.enableMetricbeatModuleHttpEnabledDirectionsLinkText": "为正在监测的 {beatType} 实例启用 HTTP 终端节点", "xpack.monitoring.metricbeatMigration.beatsInstructions.enableMetricbeatModuleTitle": "在 Metricbeat 中启用并配置 Beat x-pack 模块", - "xpack.monitoring.metricbeatMigration.beatsInstructions.installMetricbeatLinkText": "按照此处的说明执行操作", + "xpack.monitoring.metricbeatMigration.beatsInstructions.installMetricbeatLinkText": "按照此处的说明执行操作。", "xpack.monitoring.metricbeatMigration.beatsInstructions.installMetricbeatTitle": "在安装此 {beatType} 的同一台服务器上安装 Metricbeat", - "xpack.monitoring.metricbeatMigration.beatsInstructions.startMetricbeatLinkText": "按照此处的说明执行操作", + "xpack.monitoring.metricbeatMigration.beatsInstructions.startMetricbeatLinkText": "按照此处的说明执行操作。", "xpack.monitoring.metricbeatMigration.beatsInstructions.startMetricbeatTitle": "启动 Metricbeat", "xpack.monitoring.metricbeatMigration.flyout.noClusterUuidTitle": "未检测到集群", "xpack.monitoring.metricbeatMigration.logstashInstructions.configureMetricbeatDescription": "在 {file} 文件中进行这些更改。", @@ -8099,14 +9564,85 @@ "xpack.monitoring.metricbeatMigration.logstashInstructions.disableInternalCollection.title": "禁用 Logstash 监测指标的内部收集", "xpack.monitoring.metricbeatMigration.logstashInstructions.enableMetricbeatModuleDescription": "该模块将默认从 http://localhost:9600 收集 Logstash 监测指标。如果本地 Logstash 实例有不同的地址,则必须通过 {file} 文件中的 {hosts} 设置进行指定。", "xpack.monitoring.metricbeatMigration.logstashInstructions.enableMetricbeatModuleTitle": "在 Metricbeat 中启用并配置 Logstash x-pack 模块", - "xpack.monitoring.metricbeatMigration.logstashInstructions.installMetricbeatLinkText": "按照此处的说明执行操作", + "xpack.monitoring.metricbeatMigration.logstashInstructions.installMetricbeatLinkText": "按照此处的说明执行操作。", "xpack.monitoring.metricbeatMigration.logstashInstructions.installMetricbeatTitle": "在安装 Logstash 的同一台服务器上安装 Metricbeat", - "xpack.monitoring.metricbeatMigration.logstashInstructions.startMetricbeatLinkText": "按照此处的说明执行操作", + "xpack.monitoring.metricbeatMigration.logstashInstructions.startMetricbeatLinkText": "按照此处的说明执行操作。", "xpack.monitoring.metricbeatMigration.logstashInstructions.startMetricbeatTitle": "启动 Metricbeat", "xpack.monitoring.noData.blurbs.cloudDeploymentDescription": "请返回到您的 ", "xpack.monitoring.noData.blurbs.cloudDeploymentDescriptionMore": "有关在 Elastic Cloud 中监测的详情,请参阅 ", "xpack.monitoring.noData.blurbs.cloudDeploymentTitle": "此处没有您的监测数据。", "xpack.monitoring.noData.explanations.exportersCloudDescription": "在 Elastic Cloud 中,您的监测数据将存储在专用监测集群中。", + "xpack.monitoring.elasticsearch.nodes.metricbeatMigration.detectedNodeDescription": "以下节点未受监测。单击下面的“使用 Metricbeat 监测”以开始监测。", + "xpack.monitoring.elasticsearch.nodes.metricbeatMigration.detectedNodeTitle": "检测到 Elasticsearch 节点", + "xpack.monitoring.elasticsearch.nodes.metricbeatMigration.disableInternalCollectionDescription": "禁用内部收集以完成迁移。", + "xpack.monitoring.elasticsearch.nodes.metricbeatMigration.disableInternalCollectionMigrationButtonLabel": "禁用内部收集", + "xpack.monitoring.elasticsearch.nodes.metricbeatMigration.disableInternalCollectionTitle": "Metricbeat 现在正监测您的 Elasticsearch 节点", + "xpack.monitoring.euiSSPTable.setupNewButtonLabel": "为新的 {identifier} 设置监测", + "xpack.monitoring.euiTable.setupNewButtonLabel": "使用 Metricbeat 监测其他 {identifier}", + "xpack.monitoring.kibana.instances.metricbeatMigration.detectedNodeDescription": "以下实例未受监测。\n 单击下面的“使用 Metricbeat 监测”以开始监测。", + "xpack.monitoring.kibana.instances.metricbeatMigration.detectedNodeTitle": "检测到 Kibana 实例", + "xpack.monitoring.metricbeatMigration.disableInternalCollection.fullyMigratedStatusDescription": "我们未看到来自内部收集的任何文档。迁移完成!", + "xpack.monitoring.metricbeatMigration.disableInternalCollection.fullyMigratedStatusTitle": "恭喜您!", + "xpack.monitoring.metricbeatMigration.disableInternalCollection.partiallyMigratedStatusDescription": "上次内部收集是在 {secondsSinceLastInternalCollectionLabel}前。", + "xpack.monitoring.metricbeatMigration.elasticsearchInstructions.enableMetricbeatModuleInstallDirectory": "从安装目录中,运行:", + "xpack.monitoring.metricbeatMigration.flyout.learnMore": "了解原因。", + "xpack.monitoring.metricbeatMigration.fullyMigratedStatusDescription": "Metricbeat 正在发送监测数据。", + "xpack.monitoring.metricbeatMigration.fullyMigratedStatusTitle": "恭喜您!", + "xpack.monitoring.metricbeatMigration.isInternalCollectorStatusTitle": "未检测到任何监测数据,但我们将继续检查。", + "xpack.monitoring.metricbeatMigration.migrationStatus": "迁移状态", + "xpack.monitoring.metricbeatMigration.monitoringStatus": "监测状态", + "xpack.monitoring.metricbeatMigration.partiallyMigratedStatusDescription": "最多需要 {secondsAgo} 秒钟检测到数据。", + "xpack.monitoring.metricbeatMigration.partiallyMigratedStatusTitle": "数据仍来自于内部收集", + "xpack.monitoring.metricbeatMigration.securitySetup": "如果启用了安全,可能需要{link}。", + "xpack.monitoring.metricbeatMigration.securitySetupLinkText": "其他设置", + "xpack.monitoring.metrics.esNode.ioRateTitle": "I/O 操作速率", + "xpack.monitoring.metrics.esNode.totalIoDescription": "I/O 总计。(此指标不支持所有平台,如果 I/O 数据不可用,可能显示“不可用”)", + "xpack.monitoring.metrics.esNode.totalIoLabel": "I/O 总计", + "xpack.monitoring.metrics.esNode.totalIoReadDescription": "读取 I/O 总计。(此指标不支持所有平台,如果 I/O 数据不可用,可能显示“不可用”)", + "xpack.monitoring.metrics.esNode.totalIoReadLabel": "读取 I/O 总计", + "xpack.monitoring.metrics.esNode.totalIoWriteDescription": "写入 I/O 总计。(此指标不支持所有平台,如果 I/O 数据不可用,可能显示“不可用”)", + "xpack.monitoring.metrics.esNode.totalIoWriteLabel": "写入 I/O 总计", + "xpack.monitoring.noData.collectionInterval.turnOnMonitoringButtonLabel": "使用 Metricbeat 设置监测", + "xpack.monitoring.noData.defaultLoadingMessage": "正在加载,请稍候", + "xpack.monitoring.noData.noMonitoringDataFound": "是否已设置监测?如果已设置,确保右上角所选的时间段包含监测数据。", + "xpack.monitoring.noData.noMonitoringDetected": "找不到任何监测数据", + "xpack.monitoring.noData.routeTitle": "设置监测", + "xpack.monitoring.noData.setupInternalInstead": "或,使用内部收集设置", + "xpack.monitoring.noData.setupMetricbeatInstead": "或,使用 Metricbeat 设置(推荐)", + "xpack.monitoring.setupMode.clickToDisableInternalCollection": "禁用内部收集(self monitoring)", + "xpack.monitoring.setupMode.clickToMonitorWithMetricbeat": "使用 Metricbeat 监测", + "xpack.monitoring.setupMode.description": "您处于设置模式。图标 ({flagIcon}) 表示配置选项。", + "xpack.monitoring.setupMode.detectedNodeDescription": "单击下面的“设置监测”以开始监测此 {identifier}。", + "xpack.monitoring.setupMode.detectedNodeTitle": "检测到 {product} {identifier}", + "xpack.monitoring.setupMode.disableInternalCollectionDescription": "Metricbeat 现在正监测您的 {product} {identifier}。禁用内部收集以完成迁移。", + "xpack.monitoring.setupMode.disableInternalCollectionTitle": "禁用内部收集(self monitoring)", + "xpack.monitoring.setupMode.exit": "退出设置模式", + "xpack.monitoring.setupMode.instance": "实例", + "xpack.monitoring.setupMode.instances": "实例", + "xpack.monitoring.setupMode.metricbeatAllNodes": "Metricbeat 正在监测所有 {identifier}。", + "xpack.monitoring.setupMode.migrateSomeToMetricbeatDescription": "某些 {product} {identifier} 通过内部收集进行监测。迁移到使用 Metricbeat 监测。", + "xpack.monitoring.setupMode.migrateToMetricbeat": "使用 Metricbeat 监测", + "xpack.monitoring.setupMode.migrateToMetricbeatDescription": "这些 {product} {identifier} 自我监测。\n 单击“使用 Metricbeat 监测”以迁移。", + "xpack.monitoring.setupMode.monitorAllNodes": "一些节点仅使用内部收集", + "xpack.monitoring.setupMode.netNewUserDescription": "单击“设置监测”以开始使用 Metricbeat 监测。", + "xpack.monitoring.setupMode.node": "节点", + "xpack.monitoring.setupMode.nodes": "节点", + "xpack.monitoring.setupMode.noMonitoringDataFound": "未检测到 {product} {identifier}", + "xpack.monitoring.setupMode.server": "服务器", + "xpack.monitoring.setupMode.servers": "服务器", + "xpack.monitoring.setupMode.tooltip.allSet": "Metricbeat 正在监测所有 {identifierPlural}。", + "xpack.monitoring.setupMode.tooltip.detected": "无监测", + "xpack.monitoring.setupMode.tooltip.disableInternal": "Metricbeat 正在监测所有 {identifierPlural}。单击以查看 {identifierPlural} 并禁用内部收集。", + "xpack.monitoring.setupMode.tooltip.mightExist": "我们检测到此产品的使用。单击以开始监测。", + "xpack.monitoring.setupMode.tooltip.noUsage": "无使用", + "xpack.monitoring.setupMode.tooltip.noUsageDetected": "我们未检测到任何使用。单击以查看 {identifier}。", + "xpack.monitoring.setupMode.tooltip.oneInternal": "至少一个 {identifier} 未使用 Metricbeat 进行监测。单击以查看状态。", + "xpack.monitoring.setupMode.unknown": "不可用", + "xpack.monitoring.setupMode.usingMetricbeatCollection": "已使用 Metricbeat 监测", + "xpack.monitoring.metricbeatMigration.flyout.flyoutTitle": "使用 Metricbeat 监测 `{instanceName}` {instanceIdentifier}", + "xpack.monitoring.metricbeatMigration.flyout.flyoutTitleNewUser": "使用 Metricbeat 监测 {instanceName} {instanceIdentifier}", + "xpack.monitoring.metricbeatMigration.flyout.noClusterUuidCheckboxLabel": "是的,我明白我将需要在独立集群中寻找\n 此 {productName} {instanceIdentifier}。", + "xpack.monitoring.metricbeatMigration.flyout.noClusterUuidDescription": "此 {productName} {instanceIdentifier} 未连接到 Elasticsearch 集群,因此完全迁移后,此 {productName} {instanceIdentifier} 将显示在独立集群中,而非此集群中。{link}", "xpack.remoteClusters.addAction.clusterNameAlreadyExistsErrorMessage": "名为 “{clusterName}” 的集群已存在。", "xpack.remoteClusters.addAction.errorTitle": "添加集群时出错", "xpack.remoteClusters.addAction.failedDefaultErrorMessage": "请求失败,显示 {statusCode} 错误。{message}", @@ -8784,6 +10320,48 @@ "xpack.security.users.breadcrumb": "用户", "xpack.security.users.createBreadcrumb": "创建", "xpack.security.management.editRole.featureTable.excludedFromBasePrivilegsTooltip": "使用“定制”权限来授予权限。{featureName} 不属于基础权限。", + "xpack.security.apiKeys.breadcrumb": "API 密钥", + "xpack.security.management.apiKeys.deniedPermissionTitle": "您需要管理 API 密钥的权限", + "xpack.security.management.apiKeys.invalidateApiKey.confirmModal.cancelButtonLabel": "取消", + "xpack.security.management.apiKeys.invalidateApiKey.confirmModal.confirmButtonLabel": "作废 {count, plural, one {API 密钥} other {API 密钥}}", + "xpack.security.management.apiKeys.invalidateApiKey.confirmModal.invalidateMultipleListDescription": "您即将作废以下 API 密钥:", + "xpack.security.management.apiKeys.invalidateApiKey.confirmModal.invalidateMultipleTitle": "作废 {count} API 密钥?", + "xpack.security.management.apiKeys.invalidateApiKey.confirmModal.invalidateSingleTitle": "作废 API 密钥“{name}”?", + "xpack.security.management.apiKeys.invalidateApiKey.errorMultipleNotificationTitle": "删除 {count} 个 api 密钥时出错", + "xpack.security.management.apiKeys.invalidateApiKey.errorSingleNotificationTitle": "删除 API 密钥“{name}”时出错", + "xpack.security.management.apiKeys.invalidateApiKey.successMultipleNotificationTitle": "已作废 {count} 个 API 密钥", + "xpack.security.management.apiKeys.invalidateApiKey.successSingleNotificationTitle": "已作废 API 密钥“{name}”", + "xpack.security.management.apiKeys.noPermissionToManageRolesDescription": "请联系您的管理员。", + "xpack.security.management.apiKeys.table.actionDeleteAriaLabel": "作废“{name}”", + "xpack.security.management.apiKeys.table.actionDeleteTooltip": "作废", + "xpack.security.management.apiKeys.table.actionsColumnName": "操作", + "xpack.security.management.apiKeys.table.adminText": "您是 API 密钥管理员。", + "xpack.security.management.apiKeys.table.apiKeysAllDescription": "查看并作废 API 密钥。API 密钥代表用户发送请求。", + "xpack.security.management.apiKeys.table.apiKeysDisabledErrorDescription": "请联系您的系统管理员并参阅{link}以启用 API 密钥。", + "xpack.security.management.apiKeys.table.apiKeysDisabledErrorLinkText": "文档", + "xpack.security.management.apiKeys.table.apiKeysDisabledErrorTitle": "Elasticsearch 中未启用 API 密钥", + "xpack.security.management.apiKeys.table.apiKeysOwnDescription": "查看并作废您的 API 密钥。API 密钥代表您发送请求。", + "xpack.security.management.apiKeys.table.apiKeysTableLoadingMessage": "正在加载 API 密钥……", + "xpack.security.management.apiKeys.table.apiKeysTitle": "API 密钥", + "xpack.security.management.apiKeys.table.creationDateColumnName": "创建时间", + "xpack.security.management.apiKeys.table.emptyPromptAdminTitle": "无 API 密钥", + "xpack.security.management.apiKeys.table.emptyPromptConsoleButtonMessage": "前往 Console", + "xpack.security.management.apiKeys.table.emptyPromptDescription": "您可以从 Console 创建 {link}。", + "xpack.security.management.apiKeys.table.emptyPromptDocsLinkMessage": "API 密钥", + "xpack.security.management.apiKeys.table.emptyPromptNonAdminTitle": "您未有任何 API 密钥", + "xpack.security.management.apiKeys.table.expirationDateColumnName": "过期", + "xpack.security.management.apiKeys.table.expirationDateNeverMessage": "永远不", + "xpack.security.management.apiKeys.table.invalidateApiKeyButton": "作废 {count, plural, one {API 密钥} other {API 密钥}}", + "xpack.security.management.apiKeys.table.loadingApiKeysDescription": "正在加载 API 密钥……", + "xpack.security.management.apiKeys.table.loadingApiKeysErrorTitle": "加载 API 密钥时出错", + "xpack.security.management.apiKeys.table.nameColumnName": "名称", + "xpack.security.management.apiKeys.table.realmColumnName": "Realm", + "xpack.security.management.apiKeys.table.realmFilterLabel": "Realm", + "xpack.security.management.apiKeys.table.reloadApiKeysButton": "重新加载", + "xpack.security.management.apiKeys.table.statusColumnName": "状态", + "xpack.security.management.apiKeys.table.userFilterLabel": "用户", + "xpack.security.management.apiKeys.table.userNameColumnName": "用户", + "xpack.security.management.apiKeysTitle": "API 密钥", "xpack.siem.andOrBadge.and": "AND", "xpack.siem.andOrBadge.or": "OR", "xpack.siem.auditd.abortedAuditStartupDescription": "已中止审计启动", @@ -9092,7 +10670,7 @@ "xpack.siem.network.ipDetails.usersTable.columns.groupIdTitle": "组 ID", "xpack.siem.network.ipDetails.usersTable.columns.groupNameTitle": "组名称", "xpack.siem.network.ipDetails.usersTable.columns.userIdTitle": "ID", - "xpack.siem.network.ipDetails.usersTable.columns.userNameTitle": "名称", + "xpack.siem.network.ipDetails.usersTable.columns.userNameTitle": "用户", "xpack.siem.network.ipDetails.usersTable.rows": "{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}", "xpack.siem.network.ipDetails.usersTable.unit": "{totalCount, plural, =1 {user} other {users}}", "xpack.siem.network.ipDetails.usersTable.usersTitle": "用户", @@ -9279,14 +10857,14 @@ "xpack.siem.timeline.typeTooltip": "类型", "xpack.siem.timelines.allTimelines.panelTitle": "所有时间线", "xpack.siem.timelines.pageTitle": "时间线", - "xpack.siem.uiSettings.defaultIndexDescription": "要搜索的默认 Elasticsearch 索引", + "xpack.siem.uiSettings.defaultIndexDescription": "

SIEM 应用要从其中搜索事件的 Elasticsearch 索引逗号分隔列表。

", "xpack.siem.uiSettings.defaultIndexLabel": "默认索引", "xpack.siem.uncommonProcessTable.hostsTitle": "主机", "xpack.siem.uncommonProcessTable.lastCommandTitle": "上一命令", "xpack.siem.uncommonProcessTable.lastUserTitle": "上一用户", "xpack.siem.uncommonProcessTable.nameTitle": "名称", "xpack.siem.uncommonProcessTable.numberOfHostsTitle": "主机数目", - "xpack.siem.uncommonProcessTable.numberOfInstances": "实例数目", + "xpack.siem.uncommonProcessTable.numberOfInstances": "实例", "xpack.siem.uncommonProcessTable.rows": "{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}", "xpack.siem.uncommonProcessTable.unit": "{totalCount, plural, =1 {process} other {processes}}", "xpack.siem.zeek.othDescription": "未看到 SYN,仅中游流量", @@ -9383,12 +10961,12 @@ "xpack.siem.paginatedTable.tooManyResultsToastText": "缩减您的查询范围,以更好地筛选结果", "xpack.siem.paginatedTable.tooManyResultsToastTitle": " - 结果过多", "xpack.siem.timeline.callOut.unauthorized.message.description": "您需要在 SIEM 内自动保存时间线的权限,但您可以继续使用该时间线搜索和筛选安全事件", - "xpack.siem.uiSettings.defaultAnomalyScoreDescription": "在显示异常之前要超过的默认异常分数阈值。有效值介于 0 和 100 之间", + "xpack.siem.uiSettings.defaultAnomalyScoreDescription": "

在显示异常之前要超过的默认异常分数阈值。

有效值:0 到 100。

", "xpack.siem.uiSettings.defaultAnomalyScoreLabel": "默认异常阈值", - "xpack.siem.uiSettings.defaultRefreshIntervalDescription": "SIEM 时间筛选的默认刷新时间间隔", - "xpack.siem.uiSettings.defaultRefreshIntervalLabel": "时间选取器刷新时间间隔", - "xpack.siem.uiSettings.defaultTimeRangeDescription": "未使用时间筛选启动 Kibana 时要使用的 SIEM 时间筛选选择", - "xpack.siem.uiSettings.defaultTimeRangeLabel": "时间选取器默认值", + "xpack.siem.uiSettings.defaultRefreshIntervalDescription": "

SIEM 时间筛选的默认刷新时间间隔(毫秒)。

", + "xpack.siem.uiSettings.defaultRefreshIntervalLabel": "时间筛选刷新时间间隔", + "xpack.siem.uiSettings.defaultTimeRangeDescription": "

SIEM 时间筛选中的默认时间期间。

", + "xpack.siem.uiSettings.defaultTimeRangeLabel": "时间筛选默认值", "xpack.siem.host.details.overview.maxAnomalyScoreByJobTitle": "最大异常分数(按作业)", "xpack.siem.ml.score.anomalyJobTitle": "作业", "xpack.siem.ml.table.detectorTitle": "作业名称", @@ -9396,6 +10974,93 @@ "xpack.siem.ml.table.influencedByTitle": "影响因素", "xpack.siem.ml.table.scoreTitle": "异常分数", "xpack.siem.network.ipDetails.ipOverview.maxAnomalyScoreByJobTitle": "最大异常分数(按作业)", + "xpack.siem.chart.dataAllValuesZerosTitle": "所有值返回零", + "xpack.siem.components.embeddables.embeddedMap.destinationLayerLabel": "目标点", + "xpack.siem.components.embeddables.embeddedMap.embeddablePanelTitle": "源 -> 目标点对点地图", + "xpack.siem.components.embeddables.embeddedMap.errorConfiguringEmbeddableApiTitle": "配置可嵌入 API 时出错", + "xpack.siem.components.embeddables.embeddedMap.errorCreatingMapEmbeddableTitle": "创建地图可嵌入对象时出错", + "xpack.siem.components.embeddables.embeddedMap.lineLayerLabel": "折线图", + "xpack.siem.components.embeddables.embeddedMap.sourceLayerLabel": "源点", + "xpack.siem.components.embeddables.mapToolTip.errorTitle": "加载地图特征时出错", + "xpack.siem.components.embeddables.mapToolTip.filterForValueHoverAction": "筛留值", + "xpack.siem.components.embeddables.mapToolTip.footerLabel": "{currentFeature} / {totalFeatures} {totalFeatures, plural, =1 {feature} other {features}}", + "xpack.siem.components.embeddables.mapToolTip.lineContent.destinationLabel": "目标", + "xpack.siem.components.embeddables.mapToolTip.lineContent.sourceLabel": "源", + "xpack.siem.components.embeddables.mapToolTip.pointContent.asnTitle": "ASN", + "xpack.siem.components.embeddables.mapToolTip.pointContent.destinationDomainTitle": "目标域", + "xpack.siem.components.embeddables.mapToolTip.pointContent.destinationIPTitle": "目标 IP", + "xpack.siem.components.embeddables.mapToolTip.pointContent.hostTitle": "主机", + "xpack.siem.components.embeddables.mapToolTip.pointContent.locationTitle": "位置", + "xpack.siem.components.embeddables.mapToolTip.pointContent.sourceDomainTitle": "源域", + "xpack.siem.components.embeddables.mapToolTip.pointContent.sourceIPTitle": "源 IP", + "xpack.siem.components.mlPopover.jobsTable.filters.groupsLabel": "组", + "xpack.siem.components.mlPopover.jobsTable.filters.noGroupsAvailableDescription": "没有可用的组", + "xpack.siem.components.mlPopover.jobsTable.filters.searchFilterPlaceholder": "例如 rare_process_linux", + "xpack.siem.components.mlPopover.jobsTable.filters.showAllJobsLabel": "Elastic 作业", + "xpack.siem.components.mlPopover.jobsTable.filters.showSiemJobsLabel": "定制作业", + "xpack.siem.components.mlPopup.cloudLink": "云部署", + "xpack.siem.components.mlPopup.jobsTable.tagsColumn": "组", + "xpack.siem.components.mlPopup.licenseButtonLabel": "管理许可", + "xpack.siem.components.mlPopup.moduleNotCompatibleTitle": "{incompatibleJobCount} {incompatibleJobCount, plural, =1 {job} other {jobs}}当前不可用。", + "xpack.siem.eventsOverTime.eventCountFrequencyByActionTitle": "事件计数 - 按操作", + "xpack.siem.eventsOverTime.showing": "显示", + "xpack.siem.eventsOverTime.unit": "{totalCount, plural, =1 {event} other {events}}", + "xpack.siem.flyout.button.text": "时间线", + "xpack.siem.footer.loadingEventsData": "正在加载事件", + "xpack.siem.network.navigation.anomaliesTitle": "异常", + "xpack.siem.network.navigation.dnsTitle": "DNS", + "xpack.siem.network.navigation.flowsTitle": "Flows", + "xpack.siem.network.navigation.tlsTitle": "TLS", + "xpack.siem.networkTopCountriesTable.column.bytesInTitle": "传入字节", + "xpack.siem.networkTopCountriesTable.column.bytesOutTitle": "传出字节", + "xpack.siem.networkTopCountriesTable.column.countryTitle": "国家/地区", + "xpack.siem.networkTopCountriesTable.column.destinationIps": "目标 IP", + "xpack.siem.networkTopCountriesTable.column.flows": "Flows", + "xpack.siem.networkTopCountriesTable.column.sourceIps": "源 IP", + "xpack.siem.networkTopCountriesTable.heading.destinationCountries": "目标国家/地区", + "xpack.siem.networkTopCountriesTable.heading.sourceCountries": "源国家/地区", + "xpack.siem.networkTopCountriesTable.heading.unit": "{totalCount, plural, =1 {Country} other {Countries}}", + "xpack.siem.networkTopCountriesTable.rows": "{numRows} {numRows, plural, =0 {rows} =1 {row} other {rows}}", + "xpack.siem.overview.endgameDnsTitle": "Endgame DNS", + "xpack.siem.overview.endgameFileTitle": "Endgame 文件", + "xpack.siem.overview.endgameImageLoadTitle": "Endgame 图像加载", + "xpack.siem.overview.endgameNetworkTitle": "Endgame 网络", + "xpack.siem.overview.endgameProcessTitle": "Endgame 进程", + "xpack.siem.overview.endgameRegistryTitle": "Endgame 注册表", + "xpack.siem.overview.endgameSecurityTitle": "Endgame 安全", + "xpack.siem.system.acceptedAConnectionViaDescription": "已接受连接,通过", + "xpack.siem.system.createdFileDescription": "已创建文件", + "xpack.siem.system.deletedFileDescription": "已删除文件", + "xpack.siem.system.disconnectedViaDescription": "已断开连接,通过", + "xpack.siem.system.terminatedProcessDescription": "已终止进程", + "xpack.siem.system.viaDescription": "通过", + "xpack.siem.system.viaParentProcessDescription": "通过父进程", + "xpack.siem.system.withExitCodeDescription": "退出代码为", + "xpack.siem.timeline.body.renderers.dns.askedForDescription": "请求过", + "xpack.siem.timeline.body.renderers.dns.responseCodeDescription": "响应代码:", + "xpack.siem.timeline.body.renderers.dns.viaDescription": "通过", + "xpack.siem.timeline.body.renderers.dns.whichResolvedToDescription": ",其已解析为", + "xpack.siem.timeline.body.renderers.dns.withQuestionTypeDescription": ",问题类型为", + "xpack.siem.timeline.body.renderers.endgame.aLoginWasAttemptedUsingExplicitCredentialsDescription": "已使用显式凭据尝试登录", + "xpack.siem.timeline.body.renderers.endgame.asRequestedBySubjectDescription": ",如主语所请求", + "xpack.siem.timeline.body.renderers.endgame.loggedOffDescription": "已注销", + "xpack.siem.timeline.body.renderers.endgame.logonTypeBatchDescription": "批量", + "xpack.siem.timeline.body.renderers.endgame.logonTypeCachedInteractiveDescription": "缓存交互", + "xpack.siem.timeline.body.renderers.endgame.logonTypeInteractiveDescription": "交互", + "xpack.siem.timeline.body.renderers.endgame.logonTypeNetworkCleartextDescription": "网络明文", + "xpack.siem.timeline.body.renderers.endgame.logonTypeNetworkDescription": "网络", + "xpack.siem.timeline.body.renderers.endgame.logonTypeNewCredentialsDescription": "新凭据", + "xpack.siem.timeline.body.renderers.endgame.logonTypeRemoteInteractiveDescription": "远程交互", + "xpack.siem.timeline.body.renderers.endgame.logonTypeServiceDescription": "服务", + "xpack.siem.timeline.body.renderers.endgame.logonTypeUnlockDescription": "解锁", + "xpack.siem.timeline.body.renderers.endgame.subjectLogonIdDescription": "主语登录 ID", + "xpack.siem.timeline.body.renderers.endgame.successfullyLoggedInDescription": "已成功登录", + "xpack.siem.timeline.body.renderers.endgame.targetLogonIdDescription": "目标登录 ID", + "xpack.siem.timeline.body.renderers.endgame.toDescription": "到", + "xpack.siem.timeline.body.renderers.endgame.usingLogonTypeDescription": "使用登录类型", + "xpack.siem.timeline.body.renderers.endgame.viaDescription": "通过", + "xpack.siem.timeline.body.renderers.endgame.withSpecialPrivilegesDescription": "使用特殊权限,", + "xpack.siem.components.mlPopup.upgradeDescription": "要访问 SIEM 的异常检测功能,必须将您的许可更新到白金级、开始 30 天免费试用或在 AWS、GCP 或 Azurein 实施{cloudLink}。然后便可以运行 Machine Learning 作业并查看异常。", "xpack.snapshotRestore.addRepository.breadcrumbTitle": "添加存储库", "xpack.snapshotRestore.addRepository.savingRepositoryErrorTitle": "无法注册新存储库", "xpack.snapshotRestore.addRepositoryButtonLabel": "注册存储库", @@ -9849,10 +11514,10 @@ "xpack.snapshotRestore.policyForm.stepLogistics.policyScheduleButtonAdvancedLabel": "创建 Cron 表达式", "xpack.snapshotRestore.policyForm.stepLogistics.policyScheduleButtonBasicLabel": "创建基本间隔", "xpack.snapshotRestore.policyForm.stepLogistics.policyScheduleHelpText": "使用 Cron 表达式。{docLink}", - "xpack.snapshotRestore.policyForm.stepLogistics.policyScheduleHelpTextDocLink": "了解详情", + "xpack.snapshotRestore.policyForm.stepLogistics.policyScheduleHelpTextDocLink": "了解详情。", "xpack.snapshotRestore.policyForm.stepLogistics.policyScheduleLabel": "计划", "xpack.snapshotRestore.policyForm.stepLogistics.policySnapshotNameHelpText": "支持日期匹配表达式。{docLink}", - "xpack.snapshotRestore.policyForm.stepLogistics.policySnapshotNameHelpTextDocLink": "了解详情", + "xpack.snapshotRestore.policyForm.stepLogistics.policySnapshotNameHelpTextDocLink": "了解详情。", "xpack.snapshotRestore.policyForm.stepLogistics.policySnapshotNameLabel": "快照名称", "xpack.snapshotRestore.policyForm.stepLogistics.policySnapshotNamePlaceholder": "<daily-snap-\\{now \\}>", "xpack.snapshotRestore.policyForm.stepLogistics.repositoryDescription": "要用于存储快照的存储库。", @@ -9952,6 +11617,72 @@ "xpack.snapshotRestore.restoreForm.stepLogistics.docsButtonLabel": "快照和还原文档", "xpack.snapshotRestore.restoreForm.stepLogisticsTitle": "还原详情", "xpack.snapshotRestore.restoreSnapshot.executeRestoreErrorTitle": "无法还原快照", + "xpack.snapshotRestore.executeRetention.confirmModal.cancelButtonLabel": "取消", + "xpack.snapshotRestore.executeRetention.confirmModal.confirmButtonLabel": "运行保留", + "xpack.snapshotRestore.executeRetention.confirmModal.executeRetentionTitle": "立即运行快照保留?", + "xpack.snapshotRestore.executeRetention.errorMessage": "运行保留时出错", + "xpack.snapshotRestore.executeRetention.successMessage": "保留正在运行", + "xpack.snapshotRestore.policyDetails.expireAfterLabel": "在指定时间后删除:", + "xpack.snapshotRestore.policyDetails.generalTitle": "常规", + "xpack.snapshotRestore.policyDetails.maxCountLabel": "最大计数", + "xpack.snapshotRestore.policyDetails.minCountLabel": "最小计数", + "xpack.snapshotRestore.policyDetails.retentionTitle": "保留", + "xpack.snapshotRestore.policyDetails.snapshotDeletionFailuresStat": "删除失败", + "xpack.snapshotRestore.policyDetails.snapshotsDeletedStat": "已删除", + "xpack.snapshotRestore.policyDetails.snapshotsFailedStat": "失败", + "xpack.snapshotRestore.policyDetails.snapshotsTakenStat": "快照", + "xpack.snapshotRestore.policyForm.navigation.stepRetentionName": "快照保留", + "xpack.snapshotRestore.policyForm.stepRetention.countDescription": "在您的集群中要存储的最小和最大快照数目。", + "xpack.snapshotRestore.policyForm.stepRetention.countTitle": "要保留的快照", + "xpack.snapshotRestore.policyForm.stepRetention.docsButtonLabel": "快照保留文档", + "xpack.snapshotRestore.policyForm.stepRetention.expirationDescription": "删除快照前要等候的时间。", + "xpack.snapshotRestore.policyForm.stepRetention.expirationTitle": "到期", + "xpack.snapshotRestore.policyForm.stepRetention.expireAfterLabel": "在指定时间后删除:", + "xpack.snapshotRestore.policyForm.stepRetention.maxCountLabel": "最大计数", + "xpack.snapshotRestore.policyForm.stepRetention.minCountLabel": "最小计数", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionAddTitle": "添加保留计划", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionAdvancedLabel": "创建 Cron 表达式", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionBasicLabel": "创建基本间隔", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionCancelButtonLabel": "取消", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionEditButtonLabel": "保存更改", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionEditTitle": "编辑保留计划", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionErrorTitle": "保存保留计划时出错", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionHelpText": "使用 Cron 表达式。{docLink}", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionHelpTextDocLinkText": "了解详情。", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionSaveButtonLabel": "计划", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionScheduleFieldErrorMessage": "保留计划必填。", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionScheduleLabel": "保留计划", + "xpack.snapshotRestore.policyForm.stepRetention.policyUpdateRetentionSuccessMessage": "保留计划已更新", + "xpack.snapshotRestore.policyForm.stepRetentionTitle": "快照保留(可选)", + "xpack.snapshotRestore.policyForm.stepReview.editIconAriaLabel": "编辑步长", + "xpack.snapshotRestore.policyForm.stepReview.retentionTab.expireAfterLabel": "在指定时间后删除:", + "xpack.snapshotRestore.policyForm.stepReview.retentionTab.maxCountLabel": "最大计数", + "xpack.snapshotRestore.policyForm.stepReview.retentionTab.minCountLabel": "最小计数", + "xpack.snapshotRestore.policyForm.stepReview.retentionTab.sectionRetentionTitle": "快照保留", + "xpack.snapshotRestore.policyForm.stepSettings.partialIndicesToggleSwitch": "允许部分索引", + "xpack.snapshotRestore.policyList.table.lastSnapshotFailedTooltip": "上次快照失败", + "xpack.snapshotRestore.policyList.table.retentionColumnAriaLabel": "已配置保留", + "xpack.snapshotRestore.policyList.table.retentionColumnTitle": "保留", + "xpack.snapshotRestore.policyRetentionSchedulePanel.addButtonLabel": "计划", + "xpack.snapshotRestore.policyRetentionSchedulePanel.errorFetchingRetentionScheduleReloadButtonLabel": "重新加载", + "xpack.snapshotRestore.policyRetentionSchedulePanel.errorFetchingRetentionScheduleTitle": "获取保留计划时出错", + "xpack.snapshotRestore.policyRetentionSchedulePanel.executeButtonLabel": "立即运行", + "xpack.snapshotRestore.policyRetentionSchedulePanel.managePanelTitle": "保留选项", + "xpack.snapshotRestore.policyRetentionSchedulePanel.manageRetentionButtonLabel": "管理保留", + "xpack.snapshotRestore.policyRetentionSchedulePanel.noScheduleConfiguredWarningDescription": "一个或多个策略具有保留期间,但未计划保留。", + "xpack.snapshotRestore.policyRetentionSchedulePanel.noScheduleConfiguredWarningTitle": "保留未计划", + "xpack.snapshotRestore.policyRetentionSchedulePanel.retentionScheduleDescription": "保留快照的 cron 计划是:{cronSchedule}。", + "xpack.snapshotRestore.policyRetentionSchedulePanel.retentionScheduleEditLinkAriaLabel": "编辑保留计划", + "xpack.snapshotRestore.policyRetentionSchedulePanel.retentionScheduleEditLinkTooltip": "编辑保留计划", + "xpack.snapshotRestore.policyRetentionSchedulePanel.retentionScheduleExecuteLinkAriaLabel": "立即运行保留", + "xpack.snapshotRestore.policyRetentionSchedulePanel.retentionScheduleExecuteLinkTooltip": "立即运行保留", + "xpack.snapshotRestore.policyValidation.indexPatternRequiredErrorMessage": "至少需要一个索引模式。", + "xpack.snapshotRestore.policyValidation.indicesRequiredErrorMessage": "必须至少选择一个索引。", + "xpack.snapshotRestore.policyValidation.invalidMinCountErrorMessage": "最小计数不能大于最大计数。", + "xpack.snapshotRestore.policyValidation.nameRequiredErroMessage": "策略名称必填。", + "xpack.snapshotRestore.policyValidation.repositoryRequiredErrorMessage": "存储库必填。", + "xpack.snapshotRestore.policyValidation.scheduleRequiredErrorMessage": "计划必填。", + "xpack.snapshotRestore.policyValidation.snapshotNameRequiredErrorMessage": "快照名称必填。", "xpack.spaces.defaultSpaceDescription": "这是您的默认工作区!", "xpack.spaces.defaultSpaceTitle": "默认值", "xpack.spaces.displayName": "工作区", @@ -10057,11 +11788,14 @@ "xpack.spaces.spaceSelector.noSpacesMatchSearchCriteriaDescription": "没有匹配搜索条件的空间", "xpack.spaces.spaceSelector.selectSpacesTitle": "选择您的空间", "xpack.spaces.spacesTitle": "工作区", + "xpack.spaces.management.customizeSpaceAvatar.imageUrl": "定制图像", + "xpack.spaces.management.customizeSpaceAvatar.removeImage": "删除定制图像", + "xpack.spaces.management.customizeSpaceAvatar.selectImageUrl": "选择图像文件", "telemetry.callout.appliesSettingTitle": "此设置适用于{allOfKibanaText}", "telemetry.callout.appliesSettingTitle.allOfKibanaText": "所有 Kibana。", - "telemetry.callout.clusterStatisticsDescription": "这是我们将收集的基本集群统计信息的示例。其包括索引、分片和节点的数目。还包括高级使用情况统计信息,例如监测是否打开。", + "telemetry.callout.clusterStatisticsDescription": "这是我们将收集的基本集群统计信息的示例。其包括索引、分片和节点的数目。还包括概括性的使用情况统计信息,例如监测是否打开。", "telemetry.callout.clusterStatisticsTitle": "集群统计信息", - "telemetry.callout.errorLoadingClusterStatisticsDescription": "尝试提取集群统计信息时发生意外错误。发生此问题的原因可能是 Elasticsearch 出故障、Kibana 出故障,或者有网络错误。检查 Kibana,然后重新加载页面并重试。", + "telemetry.callout.errorLoadingClusterStatisticsDescription": "尝试提取集群统计信息时发生意外错误。发生此问题的原因可能是 Elasticsearch 出故障、Kibana 出故障或者有网络错误。检查 Kibana,然后重新加载页面并重试。", "telemetry.callout.errorLoadingClusterStatisticsTitle": "加载集群统计信息时出错", "telemetry.callout.errorUnprivilegedUserDescription": "您无权查看未加密的集群统计信息。", "telemetry.callout.errorUnprivilegedUserTitle": "显示集群统计信息时出错", @@ -10074,6 +11808,9 @@ "telemetry.telemetryErrorNotificationMessageTitle": "遥测错误", "telemetry.usageDataTitle": "使用情况数据", "telemetry.welcomeBanner.telemetryConfigDetailsDescription.telemetryPrivacyStatementLinkText": "遥测隐私声明", + "telemetry.optInErrorToastText": "尝试设置使用统计信息首选项时发生错误。", + "telemetry.optInErrorToastTitle": "错误", + "telemetry.welcomeBanner.title": "帮助我们改进 Elastic Stack!", "xpack.upgradeAssistant.appTitle": "{version} 升级助手", "xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.calloutDetail": "使用 {snapshotRestoreDocsButton} 备份您的数据。", "xpack.upgradeAssistant.checkupTab.backUpCallout.calloutBody.snapshotRestoreDocsButtonLabel": "快照和还原 API", @@ -10272,6 +12009,30 @@ "xpack.uptime.pingList.expandRow": "展开", "xpack.uptime.snapshot.pingsOverTimeTitle": "时移 Ping 数", "xpack.uptime.snapshotHistogram.yAxis.title": "Ping", + "xpack.uptime.donutChart.ariaLabel": "显示当前状态的饼图。{down} 个监测已关闭,共 {total} 个。", + "xpack.uptime.donutChart.legend.downRowLabel": "关闭", + "xpack.uptime.donutChart.legend.upRowLabel": "运行", + "xpack.uptime.durationChart.emptyPrompt.description": "在选定时间范围内此监测从未{emphasizedText}。", + "xpack.uptime.durationChart.emptyPrompt.title": "没有持续时间数据", + "xpack.uptime.emptyStateError.notAuthorized": "您无权查看 Uptime 数据,请联系系统管理员。", + "xpack.uptime.emptyStateError.notFoundPage": "未找到页面", + "xpack.uptime.filterPopout.loadingMessage": "正在加载……", + "xpack.uptime.filterPopout.searchMessage": "搜索 {title}", + "xpack.uptime.kueryBar.indexPatternMissingWarningMessage": "检索索引模式时出错。", + "xpack.uptime.kueryBar.searchPlaceholder": "搜索监测 ID、名称和协议类型......", + "xpack.uptime.monitorList.noItemForSelectedFiltersMessage": "未找到匹配选定筛选条件的监测", + "xpack.uptime.monitorList.table.description": "具有“状态”、“名称”、“URL”、“IP”、“中断历史记录”和“集成”列的“监测状态”表。该表当前显示 {length} 个项目。", + "xpack.uptime.monitorStatusBar.sslCertificateExpiry.ariaLabel": "SSL 证书过期", + "xpack.uptime.monitorStatusBar.sslCertificateExpiry.content": "SSL 证书于 {certificateValidity} 过期", + "xpack.uptime.notFountPage.homeLinkText": "返回主页", + "xpack.uptime.overviewPageLink.disabled.ariaLabel": "禁用的分页按钮表示在监测列表中无法进行进一步导航。", + "xpack.uptime.overviewPageLink.next.ariaLabel": "下页结果", + "xpack.uptime.overviewPageLink.prev.ariaLabel": "上页结果", + "xpack.uptime.overviewPageParsingErrorCallout.content": "解析筛选查询时出错。{content}", + "xpack.uptime.overviewPageParsingErrorCallout.noMessage": "没有错误消息", + "xpack.uptime.overviewPageParsingErrorCallout.title": "解析错误", + "xpack.uptime.snapshot.downCountsMessage": "{down}/{total} 个监测已关闭", + "xpack.uptime.snapshotHistogram.description": "显示从 {startTime} 到 {endTime} 的运行时间时移状态的条形图。", "xpack.watcher.constants.actionStates.acknowledgedStateText": "已确认", "xpack.watcher.constants.actionStates.configErrorStateText": "配置错误", "xpack.watcher.constants.actionStates.errorStateText": "错误", @@ -10381,6 +12142,12 @@ "xpack.watcher.thresholdWatchExpression.timeWindow.durationSizeIsRequiredValidationMessage": "“窗口持续时间大小”必填。", "xpack.watcher.watchActions.logging.logTextIsRequiredValidationMessage": "“日志文本”必填。", "xpack.watcher.watcherDescription": "通过创建、管理和监测警报来检测数据中的更改。", + "xpack.watcher.requestFlyout.closeButtonLabel": "关闭", + "xpack.watcher.requestFlyout.descriptionText": "此 Elasticsearch 请求将创建或更新此监视。", + "xpack.watcher.requestFlyout.namedTitle": "对“{id}”的请求", + "xpack.watcher.requestFlyout.unnamedTitle": "请求", + "xpack.watcher.sections.watchEdit.json.hideRequestButtonLabel": "隐藏请求", + "xpack.watcher.sections.watchEdit.json.showRequestButtonLabel": "显示请求", "esUi.cronEditor.cronDaily.fieldHour.textAtLabel": "在", "esUi.cronEditor.cronDaily.fieldTimeLabel": "时间", "esUi.cronEditor.cronHourly.fieldMinute.textAtLabel": "在", @@ -10420,6 +12187,20 @@ "esUi.cronEditor.month.october": "十月", "esUi.cronEditor.month.september": "九月", "esUi.cronEditor.textEveryLabel": "所有", + "esUi.cronEditor.cronDaily.hourSelectLabel": "小时", + "esUi.cronEditor.cronDaily.minuteSelectLabel": "分钟", + "esUi.cronEditor.cronMonthly.hourSelectLabel": "小时", + "esUi.cronEditor.cronMonthly.minuteSelectLabel": "分钟", + "esUi.cronEditor.cronWeekly.hourSelectLabel": "小时", + "esUi.cronEditor.cronWeekly.minuteSelectLabel": "分钟", + "esUi.cronEditor.cronYearly.hourSelectLabel": "小时", + "esUi.cronEditor.cronYearly.minuteSelectLabel": "分钟", + "esUi.forms.comboBoxField.placeHolderText": "键入内容然后按“ENTER”键", + "esUi.forms.fieldValidation.indexNameInvalidCharactersError": "索引名称包含无效的{characterListLength, plural, one {字符} other {字符}} { characterList }。", + "esUi.forms.fieldValidation.indexNameSpacesError": "索引名称不能包含空格。", + "esUi.forms.fieldValidation.indexNameStartsWithDotError": "索引名称不能以点 (.) 开头。", + "esUi.forms.fieldValidation.indexPatternInvalidCharactersError": "索引模式包含无效的{characterListLength, plural, one {字符} other {字符}} { characterList }。", + "esUi.forms.fieldValidation.indexPatternSpacesError": "索引模式不能包含空格。", "visTypeMarkdown.tabs.dataText": "数据", "visTypeMarkdown.tabs.optionsText": "选项", "visTypeMarkdown.params.fontSizeLabel": "基础字体大小(磅)", @@ -10431,17 +12212,19 @@ "xpack.actions.builtin.pagerduty.postingErrorMessage": "发布事件时 pagerduty 操作“{actionId}”出现错误:{errorMessage}", "xpack.actions.builtin.pagerduty.postingRetryErrorMessage": "发布事件时 pagerduty 操作“{actionId}”出现错误:状态 {status},请稍后重试", "xpack.actions.builtin.pagerduty.postingUnexpectedErrorMessage": "发布事件时 pagerduty 操作“{actionId}”出现错误:异常错误 {status}", - "xpack.actions.builtin.serverLog.errorLoggingErrorMessage": "记录消息时操作“{actionId}”出现操作:{errorMessage}", + "xpack.actions.builtin.serverLog.errorLoggingErrorMessage": "记录消息时操作“{actionId}”出现错误:{errorMessage}", "xpack.actions.builtin.slack.errorPostingErrorMessage": "发布 slack 消息时操作“{id}”出现错误:{message}", "xpack.actions.builtin.slack.errorPostingRetryDateErrorMessage": "发布 slack 消息时操作“{id}”出现错误,请在 {retryString} 重试:{message}", "xpack.actions.builtin.slack.errorPostingRetryLaterErrorMessage": "发布 slack 消息时操作“{id}”出现错误,请稍后重试", "xpack.actions.builtin.slack.unexpectedNullResponseErrorMessage": "来自 slack 的异常空响应", "xpack.actions.builtin.slack.unexpectedTextResponseErrorMessage": "来自 slack 的异常文本响应", - "xpack.actions.builtin.webhook.invalidResponseErrorMessage": "调用远程 Webhook 时操作“{id}”出现错误:{message}", - "xpack.actions.builtin.webhook.invalidResponseRetryDateErrorMessage": "调用远程 Webhook 时操作“{id}”出现错误,请在 {retryString} 重试:{message}", - "xpack.actions.builtin.webhook.invalidResponseRetryLaterErrorMessage": "调用远程 Webhook 时操作“{id}”出现错误,请稍后重试", - "xpack.actions.builtin.webhook.unreachableErrorMessage": "调用远程 Webhook 时操作“{id}”出现错误:{message}", + "xpack.actions.builtin.webhook.invalidResponseErrorMessage": "无效响应:调用远程 Webhook 时 Webhook 操作“{id}”发生错误:{message}", + "xpack.actions.builtin.webhook.invalidResponseRetryDateErrorMessage": "无效响应:调用远程 Webhook 时 Webhook 操作“{id}”发生错误,请在 {retryString} 重试:{message}", + "xpack.actions.builtin.webhook.invalidResponseRetryLaterErrorMessage": "无效响应:调用远程 Webhook 时 Webhook 操作“{id}”发生错误,请稍后重试", + "xpack.actions.builtin.webhook.unreachableErrorMessage": "Webhook无法访问:调用远程 Webhook 时 Webhook 操作“{id}”发生错误:{message}", "xpack.actions.builtin.webhook.unreachableRemoteWebhook": "远程 Webhook 无法访问,是否确定地址正确?", + "xpack.actions.builtin.webhook.webhookConfigurationError": "配置 Webhook 操作时出错:{message}", + "xpack.actions.urlWhitelistConfigurationError": "目标 {field}“{value}”不在 Kibana 白名单中", "xpack.advancedUiActions.customizePanelTimeRange.modal.addToPanelButtonTitle": "添加到面板", "xpack.advancedUiActions.customizePanelTimeRange.modal.cancelButtonTitle": "取消", "xpack.advancedUiActions.customizePanelTimeRange.modal.optionsMenuForm.panelTitleFormRowLabel": "时间范围", @@ -10450,6 +12233,678 @@ "xpack.advancedUiActions.customizeTimeRange.modal.headerTitle": "定制面板时间范围", "xpack.advancedUiActions.customizeTimeRangeMenuItem.displayName": "定制时间范围", "xpack.fileUpload.fileParser.errorReadingFile": "读取文件时出错", - "xpack.fileUpload.fileParser.noFileProvided": "错误,未提供任何文件" + "xpack.fileUpload.fileParser.noFileProvided": "错误,未提供任何文件", + "xpack.fileUpload.fileParser.noFeaturesDetected": "错误,未检测到功能", + "xpack.fileUpload.jsonIndexFilePicker.fileParseError": "检测到文件解析错误:{error}", + "xpack.fileUpload.jsonIndexFilePicker.fileProcessingError": "文件处理错误:{errorMessage}", + "xpack.fileUpload.jsonIndexFilePicker.fileSizeError": "文件大小错误:{errorMessage}", + "xpack.fileUpload.jsonUploadAndParse.indexingComplete": "索引完成!", + "xpack.fileUpload.patternReader.featuresOmitted": "不具有几何形状的一些特征已省略", + "xpack.fileUpload.jsonIndexFilePicker.acceptableFileSize": "文件大小 {fileSize} 超过最大文件大小 {maxFileSize}", + "xpack.fileUpload.jsonIndexFilePicker.parsingFile": "{featuresProcessed} 特征已解析......", + "uiActions.actionPanel.title": "选项", + "uiActions.errors.incompatibleAction": "操作不兼容", + "visTypeTimeseries.addDeleteButtons.addButtonDefaultTooltip": "添加", + "visTypeTimeseries.addDeleteButtons.cloneButtonDefaultTooltip": "克隆", + "visTypeTimeseries.addDeleteButtons.deleteButtonDefaultTooltip": "删除", + "visTypeTimeseries.addDeleteButtons.reEnableTooltip": "重新启用", + "visTypeTimeseries.addDeleteButtons.temporarilyDisableTooltip": "暂时禁用", + "visTypeTimeseries.aggLookup.addPipelineAggDescription": "{label}(使用“+”按钮添加此管道聚合)", + "visTypeTimeseries.aggLookup.averageLabel": "平均值", + "visTypeTimeseries.aggLookup.calculationLabel": "计算", + "visTypeTimeseries.aggLookup.cardinalityLabel": "基数", + "visTypeTimeseries.aggLookup.countLabel": "计数", + "visTypeTimeseries.aggLookup.cumulativeSumLabel": "累计和", + "visTypeTimeseries.aggLookup.derivativeLabel": "导数", + "visTypeTimeseries.aggLookup.deviationLabel": "标准偏差", + "visTypeTimeseries.aggLookup.filterRatioLabel": "筛选比", + "visTypeTimeseries.aggLookup.mathLabel": "数学", + "visTypeTimeseries.aggLookup.maxLabel": "最大值", + "visTypeTimeseries.aggLookup.minLabel": "最小值", + "visTypeTimeseries.aggLookup.movingAverageLabel": "移动平均值", + "visTypeTimeseries.aggLookup.overallAverageLabel": "总体平均值", + "visTypeTimeseries.aggLookup.overallMaxLabel": "总体最大值", + "visTypeTimeseries.aggLookup.overallMinLabel": "总体最大值", + "visTypeTimeseries.aggLookup.overallStdDeviationLabel": "总体标准偏差", + "visTypeTimeseries.aggLookup.overallSumLabel": "总和", + "visTypeTimeseries.aggLookup.overallSumOfSqLabel": "总平方和", + "visTypeTimeseries.aggLookup.overallVarianceLabel": "总体方差", + "visTypeTimeseries.aggLookup.percentileLabel": "百分位数", + "visTypeTimeseries.aggLookup.percentileRankLabel": "百分位数排名", + "visTypeTimeseries.aggLookup.positiveOnlyLabel": "仅正数", + "visTypeTimeseries.aggLookup.serialDifferenceLabel": "串行差分", + "visTypeTimeseries.aggLookup.seriesAggLabel": "序列聚合", + "visTypeTimeseries.aggLookup.staticValueLabel": "静态值", + "visTypeTimeseries.aggLookup.sumLabel": "和", + "visTypeTimeseries.aggLookup.sumOfSqLabel": "平方和", + "visTypeTimeseries.aggLookup.topHitLabel": "最高命中结果", + "visTypeTimeseries.aggLookup.valueCountLabel": "值计数", + "visTypeTimeseries.aggLookup.varianceLabel": "方差", + "visTypeTimeseries.aggRow.addMetricButtonTooltip": "添加指标", + "visTypeTimeseries.aggRow.deleteMetricButtonTooltip": "删除指标", + "visTypeTimeseries.aggSelect.aggGroups.metricAggLabel": "指标聚合", + "visTypeTimeseries.aggSelect.aggGroups.parentPipelineAggLabel": "父级管道聚合", + "visTypeTimeseries.aggSelect.aggGroups.siblingPipelineAggLabel": "同级管道聚合", + "visTypeTimeseries.aggSelect.aggGroups.specialAggLabel": "特殊聚合", + "visTypeTimeseries.aggSelect.metricsAggs.averageLabel": "平均值", + "visTypeTimeseries.aggSelect.metricsAggs.cardinalityLabel": "基数", + "visTypeTimeseries.aggSelect.metricsAggs.countLabel": "计数", + "visTypeTimeseries.aggSelect.metricsAggs.filterRatioLabel": "筛选比", + "visTypeTimeseries.aggSelect.metricsAggs.maxLabel": "最大值", + "visTypeTimeseries.aggSelect.metricsAggs.minLabel": "最小值", + "visTypeTimeseries.aggSelect.metricsAggs.percentileLabel": "百分位数", + "visTypeTimeseries.aggSelect.metricsAggs.percentileRankLabel": "百分位数排名", + "visTypeTimeseries.aggSelect.metricsAggs.staticValueLabel": "静态值", + "visTypeTimeseries.aggSelect.metricsAggs.stdDeviationLabel": "标准偏差", + "visTypeTimeseries.aggSelect.metricsAggs.sumLabel": "和", + "visTypeTimeseries.aggSelect.metricsAggs.sumOfSquaresLabel": "平方和", + "visTypeTimeseries.aggSelect.metricsAggs.topHitLabel": "最高命中结果", + "visTypeTimeseries.aggSelect.metricsAggs.valueCountLabel": "值计数", + "visTypeTimeseries.aggSelect.metricsAggs.varianceLabel": "方差", + "visTypeTimeseries.aggSelect.pipelineAggs.bucketScriptLabel": "存储桶脚本", + "visTypeTimeseries.aggSelect.pipelineAggs.cumulativeSumLabel": "累计和", + "visTypeTimeseries.aggSelect.pipelineAggs.derivativeLabel": "导数", + "visTypeTimeseries.aggSelect.pipelineAggs.movingAverageLabel": "移动平均值", + "visTypeTimeseries.aggSelect.pipelineAggs.positiveOnlyLabel": "仅正数", + "visTypeTimeseries.aggSelect.pipelineAggs.serialDifferenceLabel": "串行差分", + "visTypeTimeseries.aggSelect.selectAggPlaceholder": "选择聚合", + "visTypeTimeseries.aggSelect.siblingAggs.overallAverageLabel": "总体平均值", + "visTypeTimeseries.aggSelect.siblingAggs.overallMaxLabel": "总体最大值", + "visTypeTimeseries.aggSelect.siblingAggs.overallMinLabel": "总体最大值", + "visTypeTimeseries.aggSelect.siblingAggs.overallStdDeviationLabel": "总体标准偏差", + "visTypeTimeseries.aggSelect.siblingAggs.overallSumLabel": "总和", + "visTypeTimeseries.aggSelect.siblingAggs.overallSumOfSquaresLabel": "总平方和", + "visTypeTimeseries.aggSelect.siblingAggs.overallVarianceLabel": "总体方差", + "visTypeTimeseries.aggSelect.specialAggs.mathLabel": "数学", + "visTypeTimeseries.aggSelect.specialAggs.seriesAggLabel": "序列聚合", + "visTypeTimeseries.annotationsEditor.addDataSourceButtonLabel": "添加数据源", + "visTypeTimeseries.annotationsEditor.dataSourcesLabel": "数据源", + "visTypeTimeseries.annotationsEditor.fieldsLabel": "字段(必填 - 路径以逗号分隔)", + "visTypeTimeseries.annotationsEditor.howToCreateAnnotationDataSourceDescription": "单击下面的按钮以创建注释数据源。", + "visTypeTimeseries.annotationsEditor.iconLabel": "图标(必需)", + "visTypeTimeseries.annotationsEditor.ignoreGlobalFiltersLabel": "忽略全局筛选?", + "visTypeTimeseries.annotationsEditor.ignorePanelFiltersLabel": "忽略面板筛选?", + "visTypeTimeseries.annotationsEditor.indexPatternLabel": "索引模式(必需)", + "visTypeTimeseries.annotationsEditor.queryStringLabel": "查询字符串", + "visTypeTimeseries.annotationsEditor.rowTemplateHelpText": "例如,{rowTemplateExample}", + "visTypeTimeseries.annotationsEditor.rowTemplateLabel": "行模板(必需)", + "visTypeTimeseries.annotationsEditor.timeFieldLabel": "时间字段(必需)", + "visTypeTimeseries.axisLabelOptions.axisLabel": "每 {unitValue} {unitString}", + "visTypeTimeseries.calculateLabel.bucketScriptsLabel": "存储桶脚本", + "visTypeTimeseries.calculateLabel.countLabel": "计数", + "visTypeTimeseries.calculateLabel.filterRatioLabel": "筛选比", + "visTypeTimeseries.calculateLabel.lookupMetricTypeOfMetricFieldRankLabel": "{metricField} 的 {lookupMetricType}", + "visTypeTimeseries.calculateLabel.lookupMetricTypeOfTargetLabel": "{targetLabel} 的 {lookupMetricType}", + "visTypeTimeseries.calculateLabel.lookupMetricTypeOfTargetWithAdditionalLabel": "{targetLabel} ({additionalLabel}) 的 {lookupMetricType}", + "visTypeTimeseries.calculateLabel.mathLabel": "数学", + "visTypeTimeseries.calculateLabel.seriesAggLabel": "序列聚合 ({metricFunction})", + "visTypeTimeseries.calculateLabel.staticValueLabel": "{metricValue} 的静态值", + "visTypeTimeseries.calculateLabel.unknownLabel": "未知", + "visTypeTimeseries.calculation.aggregationLabel": "聚合", + "visTypeTimeseries.calculation.painlessScriptDescription": "变量是 {params} 对象上的键,即 {paramsName}。要访问桶时间间隔(以毫秒为单位),请使用 {paramsInterval}。", + "visTypeTimeseries.calculation.painlessScriptLabel": "Painless 脚本", + "visTypeTimeseries.calculation.variablesLabel": "变量", + "visTypeTimeseries.colorPicker.clearIconLabel": "清除", + "visTypeTimeseries.colorPicker.notAccessibleAriaLabel": "颜色选取器,不可访问", + "visTypeTimeseries.colorPicker.notAccessibleWithValueAriaLabel": "颜色选取器 ({value}),不可访问", + "visTypeTimeseries.colorRules.adjustChartSizeAriaLabel": "按向上/向下箭头键调整图表大小", + "visTypeTimeseries.colorRules.defaultPrimaryNameLabel": "背景", + "visTypeTimeseries.colorRules.defaultSecondaryNameLabel": "文本", + "visTypeTimeseries.colorRules.greaterThanLabel": "> 大于", + "visTypeTimeseries.colorRules.greaterThanOrEqualLabel": ">= 大于等于", + "visTypeTimeseries.colorRules.ifMetricIsLabel": "如果指标", + "visTypeTimeseries.colorRules.lessThanLabel": "< 小于", + "visTypeTimeseries.colorRules.lessThanOrEqualLabel": "<= 小于等于", + "visTypeTimeseries.colorRules.setPrimaryColorLabel": "将 {primaryName} 设置为", + "visTypeTimeseries.colorRules.setSecondaryColorLabel": "并将 {secondaryName} 设置为", + "visTypeTimeseries.colorRules.valueAriaLabel": "值", + "visTypeTimeseries.cumulativeSum.aggregationLabel": "聚合", + "visTypeTimeseries.cumulativeSum.metricLabel": "指标", + "visTypeTimeseries.dataFormatPicker.bytesLabel": "字节", + "visTypeTimeseries.dataFormatPicker.customLabel": "定制", + "visTypeTimeseries.dataFormatPicker.decimalPlacesLabel": "小数位数", + "visTypeTimeseries.dataFormatPicker.durationLabel": "持续时间", + "visTypeTimeseries.dataFormatPicker.formatStringHelpText": "请参阅{numeralJsLink}", + "visTypeTimeseries.dataFormatPicker.formatStringLabel": "格式字符串", + "visTypeTimeseries.dataFormatPicker.fromLabel": "从", + "visTypeTimeseries.dataFormatPicker.numberLabel": "数字", + "visTypeTimeseries.dataFormatPicker.percentLabel": "百分比", + "visTypeTimeseries.dataFormatPicker.toLabel": "到", + "visTypeTimeseries.defaultDataFormatterLabel": "数据格式化程序", + "visTypeTimeseries.derivative.aggregationLabel": "聚合", + "visTypeTimeseries.derivative.metricLabel": "指标", + "visTypeTimeseries.derivative.unitsLabel": "单位(1s、1m 等)", + "visTypeTimeseries.durationOptions.daysLabel": "天", + "visTypeTimeseries.durationOptions.hoursLabel": "小时", + "visTypeTimeseries.durationOptions.humanize": "可人工读取", + "visTypeTimeseries.durationOptions.microsecondsLabel": "微秒", + "visTypeTimeseries.durationOptions.millisecondsLabel": "毫秒", + "visTypeTimeseries.durationOptions.minutesLabel": "分钟", + "visTypeTimeseries.durationOptions.monthsLabel": "月", + "visTypeTimeseries.durationOptions.nanosecondsLabel": "纳秒", + "visTypeTimeseries.durationOptions.picosecondsLabel": "皮秒", + "visTypeTimeseries.durationOptions.secondsLabel": "秒", + "visTypeTimeseries.durationOptions.weeksLabel": "周", + "visTypeTimeseries.durationOptions.yearsLabel": "年", + "visTypeTimeseries.error.requestForPanelFailedErrorMessage": "对此面板的请求失败", + "visTypeTimeseries.fetchFields.loadIndexPatternFieldsErrorMessage": "无法加载 index_pattern 字段", + "visTypeTimeseries.fieldSelect.selectFieldPlaceholder": "选择字段......", + "visTypeTimeseries.filterRatio.aggregationLabel": "聚合", + "visTypeTimeseries.filterRatio.denominatorLabel": "分母", + "visTypeTimeseries.filterRatio.fieldLabel": "字段", + "visTypeTimeseries.filterRatio.metricAggregationLabel": "指标聚合", + "visTypeTimeseries.filterRatio.numeratorLabel": "分子", + "visTypeTimeseries.function.help": "TSVB 可视化", + "visTypeTimeseries.gauge.dataTab.dataButtonLabel": "数据", + "visTypeTimeseries.gauge.dataTab.metricsButtonLabel": "指标", + "visTypeTimeseries.gauge.editor.addSeriesTooltip": "添加序列", + "visTypeTimeseries.gauge.editor.cloneSeriesTooltip": "克隆序列", + "visTypeTimeseries.gauge.editor.deleteSeriesTooltip": "删除序列", + "visTypeTimeseries.gauge.editor.labelPlaceholder": "标签", + "visTypeTimeseries.gauge.editor.toggleEditorAriaLabel": "切换序列编辑器", + "visTypeTimeseries.gauge.optionsTab.backgroundColorLabel": "背景色:", + "visTypeTimeseries.gauge.optionsTab.colorRulesLabel": "颜色规则", + "visTypeTimeseries.gauge.optionsTab.dataLabel": "数据", + "visTypeTimeseries.gauge.optionsTab.gaugeLineWidthLabel": "仪表线宽", + "visTypeTimeseries.gauge.optionsTab.gaugeMaxLabel": "仪表最大值(留空表示 auto)", + "visTypeTimeseries.gauge.optionsTab.gaugeStyleLabel": "仪表样式", + "visTypeTimeseries.gauge.optionsTab.ignoreGlobalFilterLabel": "忽略全局筛选?", + "visTypeTimeseries.gauge.optionsTab.innerColorLabel": "内部颜色:", + "visTypeTimeseries.gauge.optionsTab.innerLineWidthLabel": "内部线宽", + "visTypeTimeseries.gauge.optionsTab.optionsButtonLabel": "选项", + "visTypeTimeseries.gauge.optionsTab.panelFilterLabel": "面板筛选", + "visTypeTimeseries.gauge.optionsTab.panelOptionsButtonLabel": "面板选项", + "visTypeTimeseries.gauge.optionsTab.styleLabel": "样式", + "visTypeTimeseries.gauge.styleOptions.circleLabel": "圆形", + "visTypeTimeseries.gauge.styleOptions.halfCircleLabel": "半圆", + "visTypeTimeseries.getInterval.daysLabel": "天", + "visTypeTimeseries.getInterval.hoursLabel": "小时", + "visTypeTimeseries.getInterval.minutesLabel": "分钟", + "visTypeTimeseries.getInterval.monthsLabel": "个月", + "visTypeTimeseries.getInterval.secondsLabel": "秒", + "visTypeTimeseries.getInterval.weeksLabel": "周", + "visTypeTimeseries.getInterval.yearsLabel": "年", + "visTypeTimeseries.iconSelect.asteriskLabel": "星号", + "visTypeTimeseries.iconSelect.bellLabel": "钟铃", + "visTypeTimeseries.iconSelect.boltLabel": "闪电", + "visTypeTimeseries.iconSelect.bombLabel": "炸弹", + "visTypeTimeseries.iconSelect.bugLabel": "昆虫", + "visTypeTimeseries.iconSelect.commentLabel": "注释", + "visTypeTimeseries.iconSelect.exclamationCircleLabel": "圆形嵌感叹号", + "visTypeTimeseries.iconSelect.exclamationTriangleLabel": "三角形嵌感叹号", + "visTypeTimeseries.iconSelect.fireLabel": "火苗", + "visTypeTimeseries.iconSelect.flagLabel": "旗帜", + "visTypeTimeseries.iconSelect.heartLabel": "心形", + "visTypeTimeseries.iconSelect.mapMarkerLabel": "地图标记", + "visTypeTimeseries.iconSelect.mapPinLabel": "地图图钉", + "visTypeTimeseries.iconSelect.starLabel": "五角星", + "visTypeTimeseries.iconSelect.tagLabel": "标记", + "visTypeTimeseries.indexPattern.dropLastBucketLabel": "丢弃上一存储桶?", + "visTypeTimeseries.indexPattern.intervalHelpText": "示例:auto、1m、1d、7d、1y、>=1m", + "visTypeTimeseries.indexPattern.intervalLabel": "时间间隔", + "visTypeTimeseries.indexPattern.searchByDefaultIndex": "将使用默认索引模式。要查询所有索引,请使用 *", + "visTypeTimeseries.indexPattern.timeFieldLabel": "时间字段", + "visTypeTimeseries.indexPattern.timeRange.entireTimeRange": "整个时间范围", + "visTypeTimeseries.indexPattern.timeRange.hint": "此设置控制用于匹配文档的时间跨度。\n “整个时间范围”将匹配时间选取器中选择的所有文档。\n “最后值”将仅匹配时间范围结尾的指定时间间隔的文档。", + "visTypeTimeseries.indexPattern.timeRange.label": "数据时间范围模式", + "visTypeTimeseries.indexPattern.timeRange.lastValue": "最后值", + "visTypeTimeseries.indexPattern.timeRange.selectTimeRange": "选择", + "visTypeTimeseries.indexPatternLabel": "索引模式", + "visTypeTimeseries.kbnVisTypes.metricsDescription": "使用可视化管道界面构建时间序列", + "visTypeTimeseries.kbnVisTypes.metricsTitle": "TSVB", + "visTypeTimeseries.markdown.alignOptions.bottomLabel": "下", + "visTypeTimeseries.markdown.alignOptions.middleLabel": "中", + "visTypeTimeseries.markdown.alignOptions.topLabel": "上", + "visTypeTimeseries.markdown.dataTab.dataButtonLabel": "数据", + "visTypeTimeseries.markdown.dataTab.metricsButtonLabel": "指标", + "visTypeTimeseries.markdown.editor.addSeriesTooltip": "添加序列", + "visTypeTimeseries.markdown.editor.cloneSeriesTooltip": "克隆序列", + "visTypeTimeseries.markdown.editor.deleteSeriesTooltip": "删除序列", + "visTypeTimeseries.markdown.editor.labelPlaceholder": "标签", + "visTypeTimeseries.markdown.editor.toggleEditorAriaLabel": "切换序列编辑器", + "visTypeTimeseries.markdown.editor.variableNamePlaceholder": "变量名称", + "visTypeTimeseries.markdown.optionsTab.backgroundColorLabel": "背景色:", + "visTypeTimeseries.markdown.optionsTab.customCSSLabel": "定制 CSS(支持 Less)", + "visTypeTimeseries.markdown.optionsTab.dataLabel": "数据", + "visTypeTimeseries.markdown.optionsTab.ignoreGlobalFilterLabel": "忽略全局筛选?", + "visTypeTimeseries.markdown.optionsTab.openLinksInNewTab": "在新标签页中打开链接?", + "visTypeTimeseries.markdown.optionsTab.optionsButtonLabel": "选项", + "visTypeTimeseries.markdown.optionsTab.panelFilterLabel": "面板筛选", + "visTypeTimeseries.markdown.optionsTab.panelOptionsButtonLabel": "面板选项", + "visTypeTimeseries.markdown.optionsTab.showScrollbarsLabel": "是否显示滚动条?", + "visTypeTimeseries.markdown.optionsTab.styleLabel": "样式", + "visTypeTimeseries.markdown.optionsTab.verticalAlignmentLabel": "垂直对齐:", + "visTypeTimeseries.markdownEditor.howToAccessEntireTreeDescription": "此外,还有名为 {all} 的特殊变量,可用于访问整个树。此变量可用于根据分组依据创建具有数据的列表:", + "visTypeTimeseries.markdownEditor.howToUseVariablesInMarkdownDescription": "通过 Handlebar (mustache) 语法,以下变量可用于 Markdown 中。{handlebarLink},以了解可用的表达式。", + "visTypeTimeseries.markdownEditor.howUseVariablesInMarkdownDescription.documentationLinkText": "单击此处获取文档", + "visTypeTimeseries.markdownEditor.nameLabel": "名称", + "visTypeTimeseries.markdownEditor.noVariablesAvailableDescription": "没有可用于选定数据指标的变量。", + "visTypeTimeseries.markdownEditor.valueLabel": "值", + "visTypeTimeseries.math.aggregationLabel": "聚合", + "visTypeTimeseries.math.expressionDescription": "此字段使用基本数学表达式(请参阅{link}),变量是 {params} 对象上的键,即{paramsName}要访问所有数据,对于值数组,请使用 {paramsValues},对于时间戳数组,请使用 {paramsTimestamps}。{paramsTimestamp} 可用于当前存储桶的时间戳,{paramsIndex} 可用于当前存储桶的索引,{paramsInterval} 可用于时间间隔(以毫秒为单位)。", + "visTypeTimeseries.math.expressionDescription.tinyMathLinkText": "TinyMath", + "visTypeTimeseries.math.expressionLabel": "表达式", + "visTypeTimeseries.math.variablesLabel": "变量", + "visTypeTimeseries.metric.dataTab.dataButtonLabel": "数据", + "visTypeTimeseries.metric.dataTab.metricsButtonLabel": "指标", + "visTypeTimeseries.metric.editor.addSeriesTooltip": "添加序列", + "visTypeTimeseries.metric.editor.cloneSeriesTooltip": "克隆序列", + "visTypeTimeseries.metric.editor.deleteSeriesTooltip": "删除序列", + "visTypeTimeseries.metric.editor.labelPlaceholder": "标签", + "visTypeTimeseries.metric.editor.toggleEditorAriaLabel": "切换序列编辑器", + "visTypeTimeseries.metric.optionsTab.colorRulesLabel": "颜色规则", + "visTypeTimeseries.metric.optionsTab.dataLabel": "数据", + "visTypeTimeseries.metric.optionsTab.ignoreGlobalFilterLabel": "忽略全局筛选?", + "visTypeTimeseries.metric.optionsTab.optionsButtonLabel": "选项", + "visTypeTimeseries.metric.optionsTab.panelFilterLabel": "面板筛选", + "visTypeTimeseries.metric.optionsTab.panelOptionsButtonLabel": "面板选项", + "visTypeTimeseries.metricMissingErrorMessage": "缺失 {field} 的指标", + "visTypeTimeseries.metricSelect.selectMetricPlaceholder": "选择指标......", + "visTypeTimeseries.missingPanelConfigDescription": "“{modelType}”缺失面板配置", + "visTypeTimeseries.movingAverage.aggregationLabel": "聚合", + "visTypeTimeseries.movingAverage.alpha": "Alpha 版", + "visTypeTimeseries.movingAverage.beta": "公测版", + "visTypeTimeseries.movingAverage.gamma": "Gamma 版", + "visTypeTimeseries.movingAverage.metricLabel": "指标", + "visTypeTimeseries.movingAverage.model.selectPlaceholder": "选择", + "visTypeTimeseries.movingAverage.modelLabel": "模型", + "visTypeTimeseries.movingAverage.modelOptions.exponentiallyWeightedLabel": "指数加权", + "visTypeTimeseries.movingAverage.modelOptions.holtLinearLabel": "Holt-Linear", + "visTypeTimeseries.movingAverage.modelOptions.holtWintersLabel": "Holt-Winters", + "visTypeTimeseries.movingAverage.modelOptions.linearLabel": "线性", + "visTypeTimeseries.movingAverage.modelOptions.simpleLabel": "简单", + "visTypeTimeseries.movingAverage.multiplicative": "乘法", + "visTypeTimeseries.movingAverage.multiplicative.selectPlaceholder": "选择", + "visTypeTimeseries.movingAverage.multiplicativeOptions.false": "False", + "visTypeTimeseries.movingAverage.multiplicativeOptions.true": "True", + "visTypeTimeseries.movingAverage.period": "期间", + "visTypeTimeseries.movingAverage.windowSizeHint": "窗口必须始终至少是期间大小的两倍", + "visTypeTimeseries.movingAverage.windowSizeLabel": "窗口大小", + "visTypeTimeseries.multivalueRow.valueLabel": "值:", + "visTypeTimeseries.noButtonLabel": "否", + "visTypeTimeseries.noDataDescription": "所选指标没有可显示的数据", + "visTypeTimeseries.percentile.aggregationLabel": "聚合", + "visTypeTimeseries.percentile.fieldLabel": "字段", + "visTypeTimeseries.percentile.fillToLabel": "填充到:", + "visTypeTimeseries.percentile.modeLabel": "模式:", + "visTypeTimeseries.percentile.modeOptions.bandLabel": "带", + "visTypeTimeseries.percentile.modeOptions.lineLabel": "折线图", + "visTypeTimeseries.percentile.percentileAriaLabel": "百分位数", + "visTypeTimeseries.percentile.shadeLabel": "阴影(0 到 1):", + "visTypeTimeseries.percentileRank.aggregationLabel": "聚合", + "visTypeTimeseries.percentileRank.fieldLabel": "字段", + "visTypeTimeseries.positiveOnly.aggregationLabel": "聚合", + "visTypeTimeseries.positiveOnly.metricLabel": "指标", + "visTypeTimeseries.replaceVars.errors.markdownErrorDescription": "请确认您仅在使用 Markdown、已知变量和内置 Handlebar 表达式", + "visTypeTimeseries.replaceVars.errors.markdownErrorTitle": "处理 Markdown 时出错", + "visTypeTimeseries.replaceVars.errors.unknownVarDescription": "{badVar} 为未知变量", + "visTypeTimeseries.replaceVars.errors.unknownVarTitle": "处理 Markdown 时出错", + "visTypeTimeseries.serialDiff.aggregationLabel": "聚合", + "visTypeTimeseries.serialDiff.lagLabel": "延迟", + "visTypeTimeseries.serialDiff.metricLabel": "指标", + "visTypeTimeseries.series.missingAggregationKeyErrorMessage": "响应中缺少聚合密钥,请检查您对此请求的权限。", + "visTypeTimeseries.series.shouldOneSeriesPerRequestErrorMessage": "每个请求仅应有一个序列。", + "visTypeTimeseries.seriesAgg.aggregationLabel": "聚合", + "visTypeTimeseries.seriesAgg.functionLabel": "函数", + "visTypeTimeseries.seriesAgg.functionOptions.avgLabel": "平均值", + "visTypeTimeseries.seriesAgg.functionOptions.cumulativeSumLabel": "累计和", + "visTypeTimeseries.seriesAgg.functionOptions.maxLabel": "最大值", + "visTypeTimeseries.seriesAgg.functionOptions.minLabel": "最小值", + "visTypeTimeseries.seriesAgg.functionOptions.overallAvgLabel": "总体平均值", + "visTypeTimeseries.seriesAgg.functionOptions.overallMaxLabel": "总体最大值", + "visTypeTimeseries.seriesAgg.functionOptions.overallMinLabel": "总体最大值", + "visTypeTimeseries.seriesAgg.functionOptions.overallSumLabel": "总和", + "visTypeTimeseries.seriesAgg.functionOptions.sumLabel": "和", + "visTypeTimeseries.seriesAgg.seriesAggIsNotCompatibleLabel": "序列聚合与表可视化不兼容。", + "visTypeTimeseries.seriesConfig.filterLabel": "筛选", + "visTypeTimeseries.seriesConfig.missingSeriesComponentDescription": "以下面板类型缺失序列组件:{panelType}", + "visTypeTimeseries.seriesConfig.offsetSeriesTimeLabel": "将序列时间偏移(1m、1h、1w、1d)", + "visTypeTimeseries.seriesConfig.overrideIndexPatternLabel": "覆盖索引模式?", + "visTypeTimeseries.seriesConfig.templateHelpText": "例如,{templateExample}", + "visTypeTimeseries.seriesConfig.templateLabel": "模板", + "visTypeTimeseries.sort.dragToSortAriaLabel": "拖动以排序", + "visTypeTimeseries.sort.dragToSortTooltip": "拖动以排序", + "visTypeTimeseries.splits.everything.groupByLabel": "分组依据", + "visTypeTimeseries.splits.filter.groupByLabel": "分组依据", + "visTypeTimeseries.splits.filter.queryStringLabel": "查询字符串", + "visTypeTimeseries.splits.filterItems.labelAriaLabel": "标签", + "visTypeTimeseries.splits.filterItems.labelPlaceholder": "标签", + "visTypeTimeseries.splits.filters.groupByLabel": "分组依据", + "visTypeTimeseries.splits.groupBySelect.modeOptions.everythingLabel": "所有内容", + "visTypeTimeseries.splits.groupBySelect.modeOptions.filterLabel": "筛选", + "visTypeTimeseries.splits.groupBySelect.modeOptions.filtersLabel": "筛选", + "visTypeTimeseries.splits.groupBySelect.modeOptions.termsLabel": "词", + "visTypeTimeseries.splits.terms.byLabel": "依据", + "visTypeTimeseries.splits.terms.defaultCountLabel": "文档计数(默认值)", + "visTypeTimeseries.splits.terms.directionLabel": "方向", + "visTypeTimeseries.splits.terms.dirOptions.ascendingLabel": "升序", + "visTypeTimeseries.splits.terms.dirOptions.descendingLabel": "降序", + "visTypeTimeseries.splits.terms.excludeLabel": "排除", + "visTypeTimeseries.splits.terms.groupByLabel": "分组依据", + "visTypeTimeseries.splits.terms.includeLabel": "包括", + "visTypeTimeseries.splits.terms.orderByLabel": "排序依据", + "visTypeTimeseries.splits.terms.sizePlaceholder": "大小", + "visTypeTimeseries.splits.terms.termsLabel": "词", + "visTypeTimeseries.splits.terms.topLabel": "上", + "visTypeTimeseries.static.aggregationLabel": "聚合", + "visTypeTimeseries.static.staticValuesLabel": "静态值", + "visTypeTimeseries.stdAgg.aggregationLabel": "聚合", + "visTypeTimeseries.stdAgg.fieldLabel": "字段", + "visTypeTimeseries.stdDeviation.aggregationLabel": "聚合", + "visTypeTimeseries.stdDeviation.fieldLabel": "字段", + "visTypeTimeseries.stdDeviation.modeLabel": "模式", + "visTypeTimeseries.stdDeviation.modeOptions.boundsBandLabel": "边界带", + "visTypeTimeseries.stdDeviation.modeOptions.lowerBoundLabel": "下边界", + "visTypeTimeseries.stdDeviation.modeOptions.rawLabel": "原始", + "visTypeTimeseries.stdDeviation.modeOptions.upperBoundLabel": "上边界", + "visTypeTimeseries.stdDeviation.sigmaLabel": "Sigma", + "visTypeTimeseries.stdSibling.aggregationLabel": "聚合", + "visTypeTimeseries.stdSibling.metricLabel": "指标", + "visTypeTimeseries.stdSibling.modeLabel": "模式", + "visTypeTimeseries.stdSibling.modeOptions.boundsBandLabel": "边界带", + "visTypeTimeseries.stdSibling.modeOptions.lowerBoundLabel": "下边界", + "visTypeTimeseries.stdSibling.modeOptions.rawLabel": "原始", + "visTypeTimeseries.stdSibling.modeOptions.upperBoundLabel": "上边界", + "visTypeTimeseries.stdSibling.sigmaLabel": "Sigma", + "visTypeTimeseries.table.addSeriesTooltip": "添加序列", + "visTypeTimeseries.table.aggregateFunctionLabel": "聚合函数", + "visTypeTimeseries.table.avgLabel": "平均值", + "visTypeTimeseries.table.cloneSeriesTooltip": "克隆序列", + "visTypeTimeseries.table.colorRulesLabel": "颜色规则", + "visTypeTimeseries.table.columnNotSortableTooltip": "此列不可排序", + "visTypeTimeseries.table.cumulativeSumLabel": "累计和", + "visTypeTimeseries.table.dataTab.columnLabel": "列标签", + "visTypeTimeseries.table.dataTab.columnsButtonLabel": "字段", + "visTypeTimeseries.table.dataTab.defineFieldDescription": "要实现表可视化,您需要使用字词聚合来定义要分组的字段。", + "visTypeTimeseries.table.dataTab.groupByFieldLabel": "分组依据字段", + "visTypeTimeseries.table.dataTab.rowsLabel": "行", + "visTypeTimeseries.table.deleteSeriesTooltip": "删除序列", + "visTypeTimeseries.table.fieldLabel": "字段", + "visTypeTimeseries.table.filterLabel": "筛选", + "visTypeTimeseries.table.labelAriaLabel": "标签", + "visTypeTimeseries.table.labelPlaceholder": "标签", + "visTypeTimeseries.table.maxLabel": "最大值", + "visTypeTimeseries.table.minLabel": "最小值", + "visTypeTimeseries.table.noResultsAvailableMessage": "没有可用结果。", + "visTypeTimeseries.table.noResultsAvailableWithDescriptionMessage": "没有可用结果。必须为此可视化选择分组依据字段。", + "visTypeTimeseries.table.optionsTab.dataLabel": "数据", + "visTypeTimeseries.table.optionsTab.ignoreGlobalFilterLabel": "忽略全局筛选?", + "visTypeTimeseries.table.optionsTab.itemUrlHelpText": "其支持 mustache 模板。{key} 将设为该字词。", + "visTypeTimeseries.table.optionsTab.itemUrlLabel": "项目 URL", + "visTypeTimeseries.table.optionsTab.panelFilterLabel": "面板筛选", + "visTypeTimeseries.table.optionsTab.panelOptionsButtonLabel": "面板选项", + "visTypeTimeseries.table.overallAvgLabel": "总体平均值", + "visTypeTimeseries.table.overallMaxLabel": "总体最大值", + "visTypeTimeseries.table.overallMinLabel": "总体最大值", + "visTypeTimeseries.table.overallSumLabel": "总和", + "visTypeTimeseries.table.showTrendArrowsLabel": "显示趋势箭头?", + "visTypeTimeseries.table.sumLabel": "和", + "visTypeTimeseries.table.tab.metricsLabel": "指标", + "visTypeTimeseries.table.tab.optionsLabel": "选项", + "visTypeTimeseries.table.templateHelpText": "例如,{templateExample}", + "visTypeTimeseries.table.templateLabel": "模板", + "visTypeTimeseries.table.toggleSeriesEditorAriaLabel": "切换序列编辑器", + "visTypeTimeseries.timeSeries.addSeriesTooltip": "添加序列", + "visTypeTimeseries.timeseries.annotationsTab.annotationsButtonLabel": "注释", + "visTypeTimeseries.timeSeries.axisMaxLabel": "轴最大值", + "visTypeTimeseries.timeSeries.axisMinLabel": "轴最小值", + "visTypeTimeseries.timeSeries.axisPositionLabel": "轴位置", + "visTypeTimeseries.timeSeries.barLabel": "条形图", + "visTypeTimeseries.timeSeries.chartBar.chartTypeLabel": "图表类型", + "visTypeTimeseries.timeSeries.chartBar.fillLabel": "填充(0 到 1)", + "visTypeTimeseries.timeSeries.chartBar.lineWidthLabel": "线条宽度", + "visTypeTimeseries.timeSeries.chartBar.stackedLabel": "堆叠", + "visTypeTimeseries.timeSeries.chartLine.chartTypeLabel": "图表类型", + "visTypeTimeseries.timeSeries.chartLine.fillLabel": "填充(0 到 1)", + "visTypeTimeseries.timeSeries.chartLine.lineWidthLabel": "线条宽度", + "visTypeTimeseries.timeSeries.chartLine.pointSizeLabel": "点大小", + "visTypeTimeseries.timeSeries.chartLine.stackedLabel": "堆叠", + "visTypeTimeseries.timeSeries.chartLine.stepsLabel": "步长", + "visTypeTimeseries.timeSeries.cloneSeriesTooltip": "克隆序列", + "visTypeTimeseries.timeseries.dataTab.dataButtonLabel": "数据", + "visTypeTimeseries.timeSeries.deleteSeriesTooltip": "删除序列", + "visTypeTimeseries.timeSeries.filterLabel": "筛选", + "visTypeTimeseries.timeSeries.gradientLabel": "渐变", + "visTypeTimeseries.timeSeries.hideInLegendLabel": "在图例中隐藏", + "visTypeTimeseries.timeSeries.labelPlaceholder": "标签", + "visTypeTimeseries.timeSeries.leftLabel": "左", + "visTypeTimeseries.timeseries.legendPositionOptions.bottomLabel": "下", + "visTypeTimeseries.timeseries.legendPositionOptions.leftLabel": "左", + "visTypeTimeseries.timeseries.legendPositionOptions.rightLabel": "右", + "visTypeTimeseries.timeSeries.lineLabel": "折线图", + "visTypeTimeseries.timeSeries.noneLabel": "无", + "visTypeTimeseries.timeSeries.offsetSeriesTimeLabel": "将序列时间偏移(1m、1h、1w、1d)", + "visTypeTimeseries.timeseries.optionsTab.axisMaxLabel": "轴最大值", + "visTypeTimeseries.timeseries.optionsTab.axisMinLabel": "轴最小值", + "visTypeTimeseries.timeseries.optionsTab.axisPositionLabel": "轴位置", + "visTypeTimeseries.timeseries.optionsTab.axisScaleLabel": "轴刻度", + "visTypeTimeseries.timeseries.optionsTab.backgroundColorLabel": "背景色:", + "visTypeTimeseries.timeseries.optionsTab.dataLabel": "数据", + "visTypeTimeseries.timeseries.optionsTab.displayGridLabel": "显示网格", + "visTypeTimeseries.timeseries.optionsTab.ignoreGlobalFilterLabel": "忽略全局筛选?", + "visTypeTimeseries.timeseries.optionsTab.legendPositionLabel": "图例位置", + "visTypeTimeseries.timeseries.optionsTab.panelFilterLabel": "面板筛选", + "visTypeTimeseries.timeseries.optionsTab.panelOptionsButtonLabel": "面板选项", + "visTypeTimeseries.timeseries.optionsTab.showLegendLabel": "显示图例?", + "visTypeTimeseries.timeseries.optionsTab.styleLabel": "样式", + "visTypeTimeseries.timeSeries.overrideIndexPatternLabel": "覆盖索引模式?", + "visTypeTimeseries.timeSeries.percentLabel": "百分比", + "visTypeTimeseries.timeseries.positionOptions.leftLabel": "左", + "visTypeTimeseries.timeseries.positionOptions.rightLabel": "右", + "visTypeTimeseries.timeSeries.rainbowLabel": "彩虹", + "visTypeTimeseries.timeSeries.rightLabel": "右", + "visTypeTimeseries.timeseries.scaleOptions.logLabel": "对数", + "visTypeTimeseries.timeseries.scaleOptions.normalLabel": "正常", + "visTypeTimeseries.timeSeries.separateAxisLabel": "分离轴?", + "visTypeTimeseries.timeSeries.splitColorThemeLabel": "拆分颜色主题", + "visTypeTimeseries.timeSeries.stackedLabel": "堆叠", + "visTypeTimeseries.timeSeries.stackedWithinSeriesLabel": "序列内堆叠", + "visTypeTimeseries.timeSeries.tab.metricsLabel": "指标", + "visTypeTimeseries.timeSeries.tab.optionsLabel": "选项", + "visTypeTimeseries.timeSeries.templateHelpText": "例如,{templateExample}", + "visTypeTimeseries.timeSeries.templateLabel": "模板", + "visTypeTimeseries.timeSeries.toggleSeriesEditorAriaLabel": "切换序列编辑器", + "visTypeTimeseries.topHit.aggregateWith.selectPlaceholder": "选择......", + "visTypeTimeseries.topHit.aggregateWithLabel": "聚合对象", + "visTypeTimeseries.topHit.aggregationLabel": "聚合", + "visTypeTimeseries.topHit.aggWithOptions.averageLabel": "平均值", + "visTypeTimeseries.topHit.aggWithOptions.concatenate": "连接", + "visTypeTimeseries.topHit.aggWithOptions.maxLabel": "最大值", + "visTypeTimeseries.topHit.aggWithOptions.minLabel": "最小值", + "visTypeTimeseries.topHit.aggWithOptions.sumLabel": "和", + "visTypeTimeseries.topHit.fieldLabel": "字段", + "visTypeTimeseries.topHit.order.selectPlaceholder": "选择......", + "visTypeTimeseries.topHit.orderByLabel": "排序依据", + "visTypeTimeseries.topHit.orderLabel": "顺序", + "visTypeTimeseries.topHit.orderOptions.ascLabel": "升序", + "visTypeTimeseries.topHit.orderOptions.descLabel": "降序", + "visTypeTimeseries.topHit.sizeLabel": "大小", + "visTypeTimeseries.topN.addSeriesTooltip": "添加序列", + "visTypeTimeseries.topN.cloneSeriesTooltip": "克隆序列", + "visTypeTimeseries.topN.dataTab.dataButtonLabel": "数据", + "visTypeTimeseries.topN.deleteSeriesTooltip": "删除序列", + "visTypeTimeseries.topN.labelPlaceholder": "标签", + "visTypeTimeseries.topN.optionsTab.backgroundColorLabel": "背景色:", + "visTypeTimeseries.topN.optionsTab.colorRulesLabel": "颜色规则", + "visTypeTimeseries.topN.optionsTab.dataLabel": "数据", + "visTypeTimeseries.topN.optionsTab.ignoreGlobalFilterLabel": "忽略全局筛选?", + "visTypeTimeseries.topN.optionsTab.itemUrlDescription": "其支持 mustache 模板。{key} 将设为该字词。", + "visTypeTimeseries.topN.optionsTab.itemUrlLabel": "项目 URL", + "visTypeTimeseries.topN.optionsTab.panelFilterLabel": "面板筛选", + "visTypeTimeseries.topN.optionsTab.panelOptionsButtonLabel": "面板选项", + "visTypeTimeseries.topN.optionsTab.styleLabel": "样式", + "visTypeTimeseries.topN.tab.metricsLabel": "指标", + "visTypeTimeseries.topN.tab.optionsLabel": "选项", + "visTypeTimeseries.topN.toggleSeriesEditorAriaLabel": "切换序列编辑器", + "visTypeTimeseries.unsupportedAgg.aggIsNotSupportedDescription": "不再支持 {modelType} 聚合。", + "visTypeTimeseries.unsupportedAgg.aggIsTemporaryUnsupportedDescription": "当前不支持 {modelType} 聚合。", + "visTypeTimeseries.unsupportedSplit.splitIsUnsupportedDescription": "不支持拆分依据 {modelType}。", + "visTypeTimeseries.validateInterval.notifier.maxBucketsExceededErrorMessage": "超过最大桶数:{buckets} 大于 {maxBuckets},请在面板选项中尝试较大的时间间隔。", + "visTypeTimeseries.vars.variableNameAriaLabel": "变量名称", + "visTypeTimeseries.vars.variableNamePlaceholder": "变量名称", + "visTypeTimeseries.visEditorVisualization.applyChangesLabel": "应用更改", + "visTypeTimeseries.visEditorVisualization.autoApplyLabel": "自动应用", + "visTypeTimeseries.visEditorVisualization.changesHaveNotBeenAppliedMessage": "尚未应用对此可视化的更改。", + "visTypeTimeseries.visEditorVisualization.changesSuccessfullyAppliedMessage": "已应用最新更改。", + "visTypeTimeseries.visEditorVisualization.changesWillBeAutomaticallyAppliedMessage": "将自动应用更改。", + "visTypeTimeseries.visEditorVisualization.panelInterval": "时间间隔:{panelInterval}", + "visTypeTimeseries.visPicker.gaugeLabel": "仪表盘图", + "visTypeTimeseries.visPicker.metricLabel": "指标", + "visTypeTimeseries.visPicker.tableLabel": "表", + "visTypeTimeseries.visPicker.timeSeriesLabel": "时间序列", + "visTypeTimeseries.visPicker.topNLabel": "排名前 N", + "visTypeTimeseries.yesButtonLabel": "是", + "xpack.alerting.alertsClient.validateActions.invalidGroups": "无效操作组:{groups}", + "xpack.features.advancedSettingsFeatureName": "高级设置", + "xpack.features.dashboardFeatureName": "仪表板", + "xpack.features.devToolsFeatureName": "开发工具", + "xpack.features.devToolsPrivilegesTooltip": "还应向用户授予适当的 Elasticsearch 集群和索引权限", + "xpack.features.discoverFeatureName": "Discover", + "xpack.features.indexPatternFeatureName": "索引模式管理", + "xpack.features.savedObjectsManagementFeatureName": "已保存对象管理", + "xpack.features.visualizeFeatureName": "Visualize", + "xpack.lens.app.docLoadingError": "加载已保存文档时出错", + "xpack.lens.app.docSavingError": "保存文档时出错", + "xpack.lens.app.indexPatternLoadingError": "加载索引模式时出错", + "xpack.lens.app.save": "保存", + "xpack.lens.app.saveModalType": "Lens 可视化", + "xpack.lens.app404": "404 找不到", + "xpack.lens.breadcrumbsCreate": "创建", + "xpack.lens.breadcrumbsTitle": "可视化", + "xpack.lens.chartSwitch.dataLossDescription": "切换到此图表将会丢失部分配置", + "xpack.lens.chartSwitch.dataLossLabel": "数据丢失", + "xpack.lens.configure.addConfig": "添加配置", + "xpack.lens.configure.editConfig": "编辑配置", + "xpack.lens.configure.emptyConfig": "将字段拖放到此处", + "xpack.lens.dataPanelWrapper.switchDatasource": "切换到数据源", + "xpack.lens.datatable.columns": "字段", + "xpack.lens.datatable.conjunctionSign": " & ", + "xpack.lens.datatable.expressionHelpLabel": "数据表呈现器", + "xpack.lens.datatable.label": "数据表", + "xpack.lens.datatable.suggestionLabel": "作为表", + "xpack.lens.datatable.titleLabel": "标题", + "xpack.lens.datatable.visualizationName": "数据表", + "xpack.lens.datatable.visualizationOf": "表 {operations}", + "xpack.lens.datatypes.boolean": "布尔值", + "xpack.lens.datatypes.date": "日期", + "xpack.lens.datatypes.ipAddress": "IP", + "xpack.lens.datatypes.number": "数字", + "xpack.lens.datatypes.string": "字符串", + "xpack.lens.editorFrame.emptyWorkspace": "将一些字段拖放到此处以开始", + "xpack.lens.editorFrame.emptyWorkspaceHeading": "Lens 是用于创建可视化的新工具", + "xpack.lens.editorFrame.expressionFailure": "无法成功执行表达式", + "xpack.lens.editorFrame.goToForums": "提出请求并提供反馈", + "xpack.lens.editorFrame.previewErrorLabel": "预览呈现失败", + "xpack.lens.editorFrame.suggestionPanelTitle": "建议", + "xpack.lens.editorFrame.tooltipContent": "Lens 为公测版,可能会进行更改。 设计和代码相对于正式发行版功能还不够成熟,将按原样提供,且不提供任何保证。公测版功能不受正式发行版功能支持 SLA 的约束", + "xpack.lens.embeddable.failure": "无法显示可视化", + "xpack.lens.embeddableDisplayName": "lens", + "xpack.lens.functions.mergeTables.help": "将任何数目的 kibana 表合并成单个表的助手", + "xpack.lens.functions.renameColumns.help": "用于重命名数据表列的助手", + "xpack.lens.functions.renameColumns.idMap.help": "旧列 ID 为键且相应新列 ID 为值的 JSON 编码对象。所有其他列 ID 都将保留。", + "xpack.lens.helpMenu.docLabel": "Lens 文档", + "xpack.lens.helpMenu.feedbackLinkText": "提供 Lens 应用程序的反馈", + "xpack.lens.indexPattern.avg": "平均值", + "xpack.lens.indexPattern.avgOf": "{name} 的平均值", + "xpack.lens.indexPattern.cardinality": "唯一计数", + "xpack.lens.indexPattern.cardinalityOf": "{name} 的唯一计数", + "xpack.lens.indexPattern.columnLabel": "标签", + "xpack.lens.indexPattern.count": "计数", + "xpack.lens.indexPattern.countOf": "文档计数", + "xpack.lens.indexPattern.dateHistogram": "Date histogram", + "xpack.lens.indexPattern.dateHistogram.autoInterval": "定制时间间隔", + "xpack.lens.indexPattern.dateHistogram.restrictedInterval": "由于聚合限制,时间间隔固定为 {intervalValue}。", + "xpack.lens.indexPattern.fieldDistributionLabel": "分布", + "xpack.lens.indexPattern.fieldlessOperationLabel": "要使用此函数,请选择字段。", + "xpack.lens.indexPattern.fieldPanelEmptyStringValue": "空字符串", + "xpack.lens.indexPattern.fieldPlaceholder": "字段", + "xpack.lens.indexPattern.fieldStatsCountLabel": "计数", + "xpack.lens.indexPattern.fieldStatsDisplayToggle": "切换", + "xpack.lens.indexPattern.fieldStatsNoData": "没有可显示的数据", + "xpack.lens.indexPattern.fieldTimeDistributionLabel": "时间分布", + "xpack.lens.indexPattern.fieldTopValuesLabel": "排名最前值", + "xpack.lens.indexPattern.groupByDropdown": "分组依据", + "xpack.lens.indexPattern.groupingControlLabel": "分组", + "xpack.lens.indexPattern.groupingOverallDateHistogram": "日期 - 总体", + "xpack.lens.indexPattern.groupingOverallTerms": "总体排名最前 {field}", + "xpack.lens.indexPattern.groupingSecondDateHistogram": "每个 {target} 的日期", + "xpack.lens.indexPattern.groupingSecondTerms": "每个 {target} 的排名最前值", + "xpack.lens.indexPattern.indexPatternLoadError": "加载索引模式时出错", + "xpack.lens.indexPattern.individualFieldsLabel": "各个字段", + "xpack.lens.indexPattern.invalidOperationLabel": "要使用此函数,请选择不同的字段。", + "xpack.lens.indexPattern.max": "最大值", + "xpack.lens.indexPattern.maxOf": "{name} 的最大值", + "xpack.lens.indexPattern.min": "最小值", + "xpack.lens.indexPattern.minOf": "{name} 的最小值", + "xpack.lens.indexPattern.noPatternsDescription": "请创建索引模式或切换到其他数据源", + "xpack.lens.indexPattern.noPatternsLabel": "无索引模式", + "xpack.lens.indexPattern.ofDocumentsLabel": "文档", + "xpack.lens.indexPattern.otherDocsLabel": "其他", + "xpack.lens.indexPattern.percentageOfLabel": "{percentage}% 的", + "xpack.lens.indexPattern.removeColumnLabel": "移除配置", + "xpack.lens.indexpattern.suggestions.nestingChangeLabel": "每个 {outerOperation} 的 {innerOperation}", + "xpack.lens.indexpattern.suggestions.overallLabel": "{operation} - 总体", + "xpack.lens.indexpattern.suggestions.overTimeLabel": "时移", + "xpack.lens.indexPattern.sum": "和", + "xpack.lens.indexPattern.sumOf": "“{name}”的和", + "xpack.lens.indexPattern.terms": "排名最前值", + "xpack.lens.indexPattern.terms.orderAlphabetical": "按字母顺序", + "xpack.lens.indexPattern.terms.orderAscending": "升序", + "xpack.lens.indexPattern.terms.orderBy": "排序依据", + "xpack.lens.indexPattern.terms.orderDescending": "降序", + "xpack.lens.indexPattern.terms.orderDirection": "排序方向", + "xpack.lens.indexPattern.terms.size": "值数目", + "xpack.lens.indexPattern.termsOf": "{name} 的排名最前值", + "xpack.lens.indexPattern.uniqueLabel": "{label} [{num}]", + "xpack.lens.indexPatterns.clearFiltersLabel": "清除名称和类型筛选", + "xpack.lens.indexPatterns.filterByNameAriaLabel": "搜索字段", + "xpack.lens.indexPatterns.filterByNameLabel": "搜索字段", + "xpack.lens.indexPatterns.filterByTypeLabel": "按类型筛选", + "xpack.lens.indexPatterns.toggleEmptyFieldsSwitch": "仅显示具有数据的字段", + "xpack.lens.indexPatterns.toggleFiltersPopover": "已筛选字段", + "xpack.lens.lensSavedObjectLabel": "Lens 可视化", + "xpack.lens.metric.label": "指标", + "xpack.lens.metric.valueLabel": "值", + "xpack.lens.suggestions.currentVisLabel": "当前", + "xpack.lens.visTypeAlias.description": "Lens 简化了基本可视化的创建", + "xpack.lens.visTypeAlias.promotion.buttonText": "前往 Lens", + "xpack.lens.visTypeAlias.promotion.description": "试用 Lens,以全新直观的方式创建可视化。", + "xpack.lens.visTypeAlias.title": "Lens 可视化", + "xpack.lens.visTypeAlias.type": "Lens", + "xpack.lens.xyChart.addLayerButton": "添加图层", + "xpack.lens.xyChart.chartTypeLabel": "图表类型", + "xpack.lens.xyChart.chartTypeLegend": "图表类型", + "xpack.lens.xyChart.deleteLayer": "删除图层", + "xpack.lens.xyChart.help": "X/Y 图表", + "xpack.lens.xyChart.isVisible.help": "指定图例是否可见。", + "xpack.lens.xyChart.layerSettings": "编辑图层设置", + "xpack.lens.xyChart.legend.help": "配置图表图例。", + "xpack.lens.xyChart.nestUnderRoot": "整个数据集", + "xpack.lens.xyChart.position.help": "指定图例位置。", + "xpack.lens.xyChart.renderer.help": "X/Y 图表呈现器", + "xpack.lens.xyChart.splitSeries": "拆分序列", + "xpack.lens.xyChart.title.help": "轴标题", + "xpack.lens.xyChart.xAxisLabel": "X 轴", + "xpack.lens.xyChart.yAxisLabel": "Y 轴", + "xpack.lens.xySuggestions.barChartTitle": "条形图", + "xpack.lens.xySuggestions.dateSuggestion": "{yTitle} / {xTitle}", + "xpack.lens.xySuggestions.flipTitle": "翻转", + "xpack.lens.xySuggestions.lineChartTitle": "折线图", + "xpack.lens.xySuggestions.nonDateSuggestion": "{xTitle} 的 {yTitle}", + "xpack.lens.xySuggestions.stackedChartTitle": "堆叠", + "xpack.lens.xySuggestions.unstackedChartTitle": "非堆叠", + "xpack.lens.xySuggestions.yAxixConjunctionSign": " & ", + "xpack.lens.xyVisualization.areaLabel": "面积图", + "xpack.lens.xyVisualization.barHorizontalLabel": "水平条形图", + "xpack.lens.xyVisualization.barLabel": "条形图", + "xpack.lens.xyVisualization.chartLabel": "{label} 图表", + "xpack.lens.xyVisualization.lineLabel": "折线图", + "xpack.lens.xyVisualization.mixedBarHorizontalLabel": "混合水平条形图", + "xpack.lens.xyVisualization.mixedLabel": "混合 XY", + "xpack.lens.xyVisualization.noDataLabel": "找不到结果", + "xpack.lens.xyVisualization.stackedAreaLabel": "堆叠面积图", + "xpack.lens.xyVisualization.stackedBarHorizontalLabel": "堆叠水平条形图", + "xpack.lens.xyVisualization.stackedBarLabel": "堆叠条形图", + "xpack.lens.xyVisualization.xyLabel": "XY" } } \ No newline at end of file From d8ebf7396f9829f80aadebb79fa4a7cb08307a7c Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Sun, 17 Nov 2019 14:31:58 +0000 Subject: [PATCH 07/17] [ML] Adding ML node warning to overview and analytics pages (#50766) (#50867) --- .../node_available_warning/index.ts | 0 .../node_available_warning.tsx | 4 ++-- .../components/upgrade/{index.js => index.ts} | 1 - ...upgrade_warning.js => upgrade_warning.tsx} | 22 +++++++++---------- .../hooks/use_create_analytics_form/state.ts | 2 ++ .../pages/analytics_management/page.tsx | 6 +++++ .../pages/analytics_management/route.ts | 4 ++++ .../jobs_list_view/jobs_list_view.js | 2 +- .../ml/public/overview/overview_page.tsx | 6 +++++ .../plugins/ml/public/overview/route.ts | 2 ++ 10 files changed, 33 insertions(+), 16 deletions(-) rename x-pack/legacy/plugins/ml/public/{jobs/jobs_list => }/components/node_available_warning/index.ts (100%) rename x-pack/legacy/plugins/ml/public/{jobs/jobs_list => }/components/node_available_warning/node_available_warning.tsx (95%) rename x-pack/legacy/plugins/ml/public/components/upgrade/{index.js => index.ts} (99%) rename x-pack/legacy/plugins/ml/public/components/upgrade/{upgrade_warning.js => upgrade_warning.tsx} (76%) diff --git a/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/node_available_warning/index.ts b/x-pack/legacy/plugins/ml/public/components/node_available_warning/index.ts similarity index 100% rename from x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/node_available_warning/index.ts rename to x-pack/legacy/plugins/ml/public/components/node_available_warning/index.ts diff --git a/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/node_available_warning/node_available_warning.tsx b/x-pack/legacy/plugins/ml/public/components/node_available_warning/node_available_warning.tsx similarity index 95% rename from x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/node_available_warning/node_available_warning.tsx rename to x-pack/legacy/plugins/ml/public/components/node_available_warning/node_available_warning.tsx index c4684f356fdaf..f9f9ee347fd7f 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/node_available_warning/node_available_warning.tsx +++ b/x-pack/legacy/plugins/ml/public/components/node_available_warning/node_available_warning.tsx @@ -8,8 +8,8 @@ import React, { Fragment, FC } from 'react'; import { EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { mlNodesAvailable, permissionToViewMlNodeCount } from '../../../../ml_nodes_check'; -import { getCloudDeploymentId, isCloud } from '../../../../services/ml_server_info'; +import { mlNodesAvailable, permissionToViewMlNodeCount } from '../../ml_nodes_check'; +import { getCloudDeploymentId, isCloud } from '../../services/ml_server_info'; export const NodeAvailableWarning: FC = () => { if (mlNodesAvailable() === true || permissionToViewMlNodeCount() === false) { diff --git a/x-pack/legacy/plugins/ml/public/components/upgrade/index.js b/x-pack/legacy/plugins/ml/public/components/upgrade/index.ts similarity index 99% rename from x-pack/legacy/plugins/ml/public/components/upgrade/index.js rename to x-pack/legacy/plugins/ml/public/components/upgrade/index.ts index 2b248fef39923..219d5aa76e812 100644 --- a/x-pack/legacy/plugins/ml/public/components/upgrade/index.js +++ b/x-pack/legacy/plugins/ml/public/components/upgrade/index.ts @@ -4,5 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ - export { UpgradeWarning } from './upgrade_warning'; diff --git a/x-pack/legacy/plugins/ml/public/components/upgrade/upgrade_warning.js b/x-pack/legacy/plugins/ml/public/components/upgrade/upgrade_warning.tsx similarity index 76% rename from x-pack/legacy/plugins/ml/public/components/upgrade/upgrade_warning.js rename to x-pack/legacy/plugins/ml/public/components/upgrade/upgrade_warning.tsx index 30008265bea5f..ce3b7e4413182 100644 --- a/x-pack/legacy/plugins/ml/public/components/upgrade/upgrade_warning.js +++ b/x-pack/legacy/plugins/ml/public/components/upgrade/upgrade_warning.tsx @@ -4,26 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ +import React, { FC } from 'react'; -import React from 'react'; - -import { - EuiCallOut, - EuiSpacer, -} from '@elastic/eui'; +import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import { isUpgradeInProgress } from '../../services/upgrade_service'; -export function UpgradeWarning() { +export const UpgradeWarning: FC = () => { if (isUpgradeInProgress() === true) { return ( )} + title={ + + } color="warning" iconType="alert" > @@ -45,4 +43,4 @@ export function UpgradeWarning() { } return null; -} +}; diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts index bce0a800098fd..fb97f562ea680 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/hooks/use_create_analytics_form/state.ts @@ -6,6 +6,7 @@ import { DeepPartial } from '../../../../../../common/types/common'; import { checkPermission } from '../../../../../privilege/check_privilege'; +import { mlNodesAvailable } from '../../../../../ml_nodes_check/check_ml_nodes'; import { DataFrameAnalyticsId, DataFrameAnalyticsConfig } from '../../../../common'; @@ -102,6 +103,7 @@ export const getInitialState = (): State => ({ }, jobConfig: {}, disabled: + !mlNodesAvailable() || !checkPermission('canCreateDataFrameAnalytics') || !checkPermission('canStartStopDataFrameAnalytics'), indexNames: [], diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/page.tsx b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/page.tsx index 9d5502569687c..4c8039009767f 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/page.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/page.tsx @@ -22,6 +22,8 @@ import { NavigationMenu } from '../../../components/navigation_menu'; import { DataFrameAnalyticsList } from './components/analytics_list'; import { useRefreshInterval } from './components/analytics_list/use_refresh_interval'; import { useCreateAnalyticsForm } from './hooks/use_create_analytics_form'; +import { NodeAvailableWarning } from '../../../components/node_available_warning'; +import { UpgradeWarning } from '../../../components/upgrade'; export const Page: FC = () => { const [blockRefresh, setBlockRefresh] = useState(false); @@ -62,6 +64,10 @@ export const Page: FC = () => { + + + + { const disableCreateAnomalyDetectionJob = !checkPermission('canCreateJob') || !mlNodesAvailable(); const disableCreateAnalyticsButton = + !mlNodesAvailable() || !checkPermission('canCreateDataFrameAnalytics') || !checkPermission('canStartStopDataFrameAnalytics'); return ( @@ -22,6 +25,9 @@ export const OverviewPage: FC = () => { + + + Date: Sun, 17 Nov 2019 17:43:48 +0200 Subject: [PATCH 08/17] =?UTF-8?q?ui/resize=5Fchecker=20=F0=9F=91=89=20src/?= =?UTF-8?q?plugins/kibana=5Futils=20(#44750)=20(#50875)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Moved ResizeChecker to kibana_utils. * Removed ResizeChecker from __LEGACY. --- .../legacy/console_editor/editor.test.tsx | 4 - .../editor/legacy/console_editor/editor.tsx | 2 - .../legacy/console_editor/editor_output.tsx | 7 +- .../console_history/console_history.tsx | 2 - .../legacy/console_history/history_viewer.tsx | 5 +- .../subscribe_console_resize_checker.ts | 5 +- .../application/context/app_context.tsx | 1 - .../np_ready/public/application/index.tsx | 4 +- .../console/np_ready/public/legacy.ts | 3 - .../console/np_ready/public/plugin.ts | 3 +- .../__tests__/resize_checker.js | 208 ------------------ .../components/visualization_chart.tsx | 2 +- src/plugins/kibana_utils/public/index.ts | 1 + .../public/resize_checker/index.ts | 0 .../resize_checker/resize_checker.test.ts | 199 +++++++++++++++++ .../public/resize_checker/resize_checker.ts | 15 +- test/functional/apps/console/_console.ts | 11 + .../connected_components/map/mb/view.js | 2 +- .../timeseriesexplorer/timeseriesexplorer.js | 2 +- 19 files changed, 223 insertions(+), 253 deletions(-) delete mode 100644 src/legacy/ui/public/resize_checker/__tests__/resize_checker.js rename src/{legacy/ui => plugins/kibana_utils}/public/resize_checker/index.ts (100%) create mode 100644 src/plugins/kibana_utils/public/resize_checker/resize_checker.test.ts rename src/{legacy/ui => plugins/kibana_utils}/public/resize_checker/resize_checker.ts (89%) diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.test.tsx b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.test.tsx index 87558a73087d8..03d5b3f1d8f44 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.test.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.test.tsx @@ -62,10 +62,6 @@ describe('Legacy (Ace) Console Editor Component Smoke Test', () => { updateCurrentState: () => {}, }, }, - // eslint-disable-next-line - ResizeChecker: function() { - return { on: () => {} }; - }, docLinkVersion: 'NA', }; editor = mount( diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.tsx b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.tsx index b2a38a996f6a2..10f1ef34602a6 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor.tsx @@ -63,7 +63,6 @@ const DEFAULT_INPUT_VALUE = `GET _search function _Editor({ previousStateLocation = 'stored' }: EditorProps) { const { services: { history, notifications }, - ResizeChecker, docLinkVersion, } = useAppContext(); @@ -130,7 +129,6 @@ function _Editor({ previousStateLocation = 'stored' }: EditorProps) { mappings.retrieveAutoCompleteInfo(); const unsubscribeResizer = subscribeResizeChecker( - ResizeChecker, editorRef.current!, editorInstanceRef.current ); diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor_output.tsx b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor_output.tsx index fcf9f17e3ebd7..d38e86df41464 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor_output.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_editor/editor_output.tsx @@ -31,7 +31,6 @@ function _EditorOuput() { const editorInstanceRef = useRef(null); const { services: { settings }, - ResizeChecker, } = useAppContext(); const dispatch = useEditorActionContext(); @@ -42,11 +41,7 @@ function _EditorOuput() { const editor$ = $(editorRef.current!); editorInstanceRef.current = initializeOutput(editor$, settings); editorInstanceRef.current.update(''); - const unsubscribe = subscribeResizeChecker( - ResizeChecker, - editorRef.current!, - editorInstanceRef.current - ); + const unsubscribe = subscribeResizeChecker(editorRef.current!, editorInstanceRef.current); dispatch({ type: 'setOutputEditor', value: editorInstanceRef.current }); diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_history/console_history.tsx b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_history/console_history.tsx index 881b59e6b3a1c..fdfe9ecc7b94c 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_history/console_history.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_history/console_history.tsx @@ -45,7 +45,6 @@ const CHILD_ELEMENT_PREFIX = 'historyReq'; export function ConsoleHistory({ close }: Props) { const { services: { history }, - ResizeChecker, } = useAppContext(); const dispatch = useEditorActionContext(); @@ -200,7 +199,6 @@ export function ConsoleHistory({ close }: Props) { diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_history/history_viewer.tsx b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_history/history_viewer.tsx index d531e143a79d0..c15bec0563049 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_history/history_viewer.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/console_history/history_viewer.tsx @@ -31,10 +31,9 @@ import { applyCurrentSettings } from '../console_editor/apply_editor_settings'; interface Props { settings: DevToolsSettings; req: any | null; - ResizeChecker: any; } -export function HistoryViewer({ settings, ResizeChecker, req }: Props) { +export function HistoryViewer({ settings, req }: Props) { const divRef = useRef(null); const viewerRef = useRef(null); @@ -43,7 +42,7 @@ export function HistoryViewer({ settings, ResizeChecker, req }: Props) { viewerRef.current = viewer; viewer.renderer.setShowPrintMargin(false); viewer.$blockScrolling = Infinity; - const unsubscribe = subscribeResizeChecker(ResizeChecker, divRef.current!, viewer); + const unsubscribe = subscribeResizeChecker(divRef.current!, viewer); return () => unsubscribe(); }, []); diff --git a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/subscribe_console_resize_checker.ts b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/subscribe_console_resize_checker.ts index c83c593ef404d..4ecd5d415833c 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/subscribe_console_resize_checker.ts +++ b/src/legacy/core_plugins/console/np_ready/public/application/containers/editor/legacy/subscribe_console_resize_checker.ts @@ -16,9 +16,10 @@ * specific language governing permissions and limitations * under the License. */ +import { ResizeChecker } from '../../../../../../../../../plugins/kibana_utils/public'; -export function subscribeResizeChecker(ResizeChecker: any, $el: any, ...editors: any[]) { - const checker = new ResizeChecker($el); +export function subscribeResizeChecker(el: HTMLElement, ...editors: any[]) { + const checker = new ResizeChecker(el); checker.on('resize', () => editors.forEach(e => { e.resize(); diff --git a/src/legacy/core_plugins/console/np_ready/public/application/context/app_context.tsx b/src/legacy/core_plugins/console/np_ready/public/application/context/app_context.tsx index 7bbdf731407e3..be7aa87ac2894 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/context/app_context.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/context/app_context.tsx @@ -29,7 +29,6 @@ interface ContextValue { notifications: NotificationsSetup; }; docLinkVersion: string; - ResizeChecker: any; } interface ContextProps { diff --git a/src/legacy/core_plugins/console/np_ready/public/application/index.tsx b/src/legacy/core_plugins/console/np_ready/public/application/index.tsx index d8933e60470c2..aaacfd3894d18 100644 --- a/src/legacy/core_plugins/console/np_ready/public/application/index.tsx +++ b/src/legacy/core_plugins/console/np_ready/public/application/index.tsx @@ -32,10 +32,9 @@ export function legacyBackDoorToSettings() { export function boot(deps: { docLinkVersion: string; I18nContext: any; - ResizeChecker: any; notifications: NotificationsSetup; }) { - const { I18nContext, ResizeChecker, notifications, docLinkVersion } = deps; + const { I18nContext, notifications, docLinkVersion } = deps; const storage = createStorage({ engine: window.localStorage, @@ -51,7 +50,6 @@ export function boot(deps: { value={{ docLinkVersion, services: { storage, history, settings, notifications }, - ResizeChecker, }} > diff --git a/src/legacy/core_plugins/console/np_ready/public/legacy.ts b/src/legacy/core_plugins/console/np_ready/public/legacy.ts index 8c60ff23648be..463aac74da944 100644 --- a/src/legacy/core_plugins/console/np_ready/public/legacy.ts +++ b/src/legacy/core_plugins/console/np_ready/public/legacy.ts @@ -26,7 +26,6 @@ import 'brace/mode/text'; /* eslint-disable @kbn/eslint/no-restricted-paths */ import { npSetup, npStart } from 'ui/new_platform'; import { I18nContext } from 'ui/i18n'; -import { ResizeChecker } from 'ui/resize_checker'; /* eslint-enable @kbn/eslint/no-restricted-paths */ export interface XPluginSet { @@ -34,7 +33,6 @@ export interface XPluginSet { feature_catalogue: FeatureCatalogueSetup; __LEGACY: { I18nContext: any; - ResizeChecker: any; }; } @@ -48,7 +46,6 @@ pluginInstance.setup(npSetup.core, { ...npSetup.plugins, __LEGACY: { I18nContext, - ResizeChecker, }, }); pluginInstance.start(npStart.core); diff --git a/src/legacy/core_plugins/console/np_ready/public/plugin.ts b/src/legacy/core_plugins/console/np_ready/public/plugin.ts index f02b0b5e72999..301b85b6e7395 100644 --- a/src/legacy/core_plugins/console/np_ready/public/plugin.ts +++ b/src/legacy/core_plugins/console/np_ready/public/plugin.ts @@ -30,7 +30,7 @@ export class ConsoleUIPlugin implements Plugin { async setup({ notifications }: CoreSetup, pluginSet: XPluginSet) { const { - __LEGACY: { I18nContext, ResizeChecker }, + __LEGACY: { I18nContext }, devTools, feature_catalogue, } = pluginSet; @@ -62,7 +62,6 @@ export class ConsoleUIPlugin implements Plugin { boot({ docLinkVersion: ctx.core.docLinks.DOC_LINK_VERSION, I18nContext, - ResizeChecker, notifications, }), element diff --git a/src/legacy/ui/public/resize_checker/__tests__/resize_checker.js b/src/legacy/ui/public/resize_checker/__tests__/resize_checker.js deleted file mode 100644 index 5fccf77f8f13d..0000000000000 --- a/src/legacy/ui/public/resize_checker/__tests__/resize_checker.js +++ /dev/null @@ -1,208 +0,0 @@ -/* - * 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 $ from 'jquery'; -import { delay } from 'bluebird'; -import expect from '@kbn/expect'; -import sinon from 'sinon'; - -import ngMock from 'ng_mock'; -import NoDigestPromises from 'test_utils/no_digest_promises'; - -import { ResizeChecker } from '../resize_checker'; -import EventEmitter from 'events'; - -describe('Resize Checker', () => { - NoDigestPromises.activateForSuite(); - - const teardown = []; - let setup; - - beforeEach(ngMock.module('kibana')); - beforeEach(() => { - setup = () => { - - const createEl = () => { - const el = $('
').appendTo('body').get(0); - teardown.push(() => $(el).remove()); - return el; - }; - - const createChecker = el => { - const checker = new ResizeChecker(el); - teardown.push(() => checker.destroy()); - return checker; - }; - - const createListener = () => { - let resolveFirstCallPromise; - const listener = sinon.spy(() => resolveFirstCallPromise()); - listener.firstCallPromise = new Promise(resolve => (resolveFirstCallPromise = resolve)); - return listener; - }; - - return { createEl, createChecker, createListener }; - }; - }); - - afterEach(() => { - teardown.splice(0).forEach(fn => { - fn(); - }); - }); - - describe('construction', () => { - it('accepts a jQuery wrapped element', () => { - const { createChecker } = setup(); - - createChecker($('
')); - }); - }); - - describe('events', () => { - it('is an event emitter', () => { - const { createEl, createChecker } = setup(); - - const checker = createChecker(createEl()); - expect(checker).to.be.a(EventEmitter); - }); - - it('emits a "resize" event asynchronously', async () => { - const { createEl, createChecker, createListener } = setup(); - - const el = createEl(); - const checker = createChecker(el); - const listener = createListener(); - - checker.on('resize', listener); - $(el).height(100); - sinon.assert.notCalled(listener); - await listener.firstCallPromise; - sinon.assert.calledOnce(listener); - }); - }); - - describe('enable/disabled state', () => { - it('should not trigger events while disabled', async () => { - const { createEl, createListener } = setup(); - - const el = createEl(); - const checker = new ResizeChecker(el, { disabled: true }); - const listener = createListener(); - checker.on('resize', listener); - - expect(listener.notCalled).to.be(true); - $(el).height(100); - await delay(1000); - expect(listener.notCalled).to.be(true); - }); - - it('should trigger resize events after calling enable', async () => { - const { createEl, createListener } = setup(); - - const el = createEl(); - const checker = new ResizeChecker(el, { disabled: true }); - const listener = createListener(); - checker.on('resize', listener); - - expect(listener.notCalled).to.be(true); - checker.enable(); - $(el).height(100); - await listener.firstCallPromise; - expect(listener.calledOnce).to.be(true); - }); - - it('should not trigger the first time after enable when the size does not change', async () => { - const { createEl, createListener } = setup(); - - const el = createEl(); - const checker = new ResizeChecker(el, { disabled: true }); - const listener = createListener(); - checker.on('resize', listener); - - expect(listener.notCalled).to.be(true); - $(el).height(250); - checker.enable(); - $(el).height(250); - await delay(1000); - expect(listener.notCalled).to.be(true); - }); - }); - - describe('#modifySizeWithoutTriggeringResize()', () => { - it(`does not emit "resize" events caused by the block`, async () => { - const { createChecker, createEl, createListener } = setup(); - - const el = createEl(); - const checker = createChecker(el); - const listener = createListener(); - - checker.on('resize', listener); - checker.modifySizeWithoutTriggeringResize(() => { - $(el).height(100); - }); - await delay(1000); - sinon.assert.notCalled(listener); - }); - - it('does emit "resize" when modification is made between the block and resize notification', async () => { - const { createChecker, createEl, createListener } = setup(); - - const el = createEl(); - const checker = createChecker(el); - const listener = createListener(); - - checker.on('resize', listener); - checker.modifySizeWithoutTriggeringResize(() => { - $(el).height(100); - }); - sinon.assert.notCalled(listener); - $(el).height(200); - await listener.firstCallPromise; - sinon.assert.calledOnce(listener); - }); - }); - - describe('#destroy()', () => { - it('destroys internal observer instance', () => { - const { createChecker, createEl, createListener } = setup(); - - const checker = createChecker(createEl()); - createListener(); - - checker.destroy(); - expect(!checker._observer).to.be(true); - }); - - it('does not emit future resize events', async () => { - const { createChecker, createEl, createListener } = setup(); - - const el = createEl(); - const checker = createChecker(el); - const listener = createListener(); - - checker.on('resize', listener); - checker.destroy(); - - $(el).height(100); - await delay(1000); - sinon.assert.notCalled(listener); - }); - }); -}); diff --git a/src/legacy/ui/public/visualize/components/visualization_chart.tsx b/src/legacy/ui/public/visualize/components/visualization_chart.tsx index eb7f130ec1a54..8aec7adeaec9a 100644 --- a/src/legacy/ui/public/visualize/components/visualization_chart.tsx +++ b/src/legacy/ui/public/visualize/components/visualization_chart.tsx @@ -22,7 +22,7 @@ import * as Rx from 'rxjs'; import { debounceTime, filter, share, switchMap } from 'rxjs/operators'; import { PersistedState } from '../../persisted_state'; -import { ResizeChecker } from '../../resize_checker'; +import { ResizeChecker } from '../../../../../plugins/kibana_utils/public'; import { Vis, VisualizationController } from '../../vis'; import { getUpdateStatus } from '../../vis/update_status'; diff --git a/src/plugins/kibana_utils/public/index.ts b/src/plugins/kibana_utils/public/index.ts index 3aaa6d28a9f64..04845c72cb755 100644 --- a/src/plugins/kibana_utils/public/index.ts +++ b/src/plugins/kibana_utils/public/index.ts @@ -21,6 +21,7 @@ export * from './core'; export * from './errors'; export * from './store'; export * from './parse'; +export * from './resize_checker'; export * from './render_complete'; export * from './store'; export * from './errors'; diff --git a/src/legacy/ui/public/resize_checker/index.ts b/src/plugins/kibana_utils/public/resize_checker/index.ts similarity index 100% rename from src/legacy/ui/public/resize_checker/index.ts rename to src/plugins/kibana_utils/public/resize_checker/index.ts diff --git a/src/plugins/kibana_utils/public/resize_checker/resize_checker.test.ts b/src/plugins/kibana_utils/public/resize_checker/resize_checker.test.ts new file mode 100644 index 0000000000000..d3013aaf8435d --- /dev/null +++ b/src/plugins/kibana_utils/public/resize_checker/resize_checker.test.ts @@ -0,0 +1,199 @@ +/* + * 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 { ResizeChecker } from './resize_checker'; +import { EventEmitter } from 'events'; + +// If you want to know why these mocks are created, +// please check: https://github.com/elastic/kibana/pull/44750 +jest.mock('resize-observer-polyfill'); +import ResizeObserver from 'resize-observer-polyfill'; + +class MockElement { + public clientWidth: number; + public clientHeight: number; + private onResize: any; + + constructor() { + this.clientHeight = 0; + this.clientWidth = 0; + this.onResize = null; + } + + public addEventListener(name: string, listener: any) { + this.onResize = listener; + } + + public dispatchEvent(name: string) { + if (this.onResize) { + this.onResize(); + } + } + + public removeEventListener(name: string, listener: any) { + this.onResize = null; + } +} + +(ResizeObserver as any).mockImplementation(function(this: any, callback: any) { + this.observe = function(el: MockElement) { + el.addEventListener('resize', callback); + }; + this.disconnect = function() {}; + this.unobserve = function(el: MockElement) { + el.removeEventListener('resize', callback); + }; +}); + +describe('Resize Checker', () => { + describe('events', () => { + it('is an event emitter', () => { + const el = new MockElement(); + const checker = new ResizeChecker(el as any); + + expect(checker).toBeInstanceOf(EventEmitter); + }); + + it('emits a "resize" event', done => { + const el = new MockElement(); + const checker = new ResizeChecker(el as any); + const listener = jest.fn(); + + checker.on('resize', listener); + el.clientHeight = 100; + el.dispatchEvent('resize'); + setTimeout(() => { + expect(listener.mock.calls.length).toBe(1); + done(); + }, 100); + }); + }); + + describe('enable/disabled state', () => { + it('should not trigger events while disabled', done => { + const el = new MockElement(); + const checker = new ResizeChecker(el as any, { disabled: true }); + const listener = jest.fn(); + checker.on('resize', listener); + + expect(listener).not.toBeCalled(); + el.clientHeight = 100; + el.dispatchEvent('resize'); + setTimeout(() => { + expect(listener).not.toBeCalled(); + done(); + }, 100); + }); + + it('should trigger resize events after calling enable', done => { + const el = new MockElement(); + const checker = new ResizeChecker(el as any, { disabled: true }); + const listener = jest.fn(); + checker.on('resize', listener); + + expect(listener).not.toBeCalled(); + checker.enable(); + el.clientHeight = 100; + el.dispatchEvent('resize'); + setTimeout(() => { + expect(listener).toBeCalled(); + done(); + }, 100); + }); + + it('should not trigger the first time after enable when the size does not change', done => { + const el = new MockElement(); + const checker = new ResizeChecker(el as any, { disabled: true }); + const listener = jest.fn(); + checker.on('resize', listener); + + expect(listener).not.toBeCalled(); + el.clientHeight = 100; + checker.enable(); + el.clientHeight = 100; + setTimeout(() => { + expect(listener).not.toBeCalled(); + done(); + }, 100); + }); + }); + + describe('#modifySizeWithoutTriggeringResize()', () => { + it(`does not emit "resize" events caused by the block`, done => { + const el = new MockElement(); + const checker = new ResizeChecker(el as any, { disabled: true }); + const listener = jest.fn(); + checker.on('resize', listener); + + checker.modifySizeWithoutTriggeringResize(() => { + el.clientHeight = 100; + }); + el.dispatchEvent('resize'); + setTimeout(() => { + expect(listener).not.toBeCalled(); + done(); + }, 1000); + }); + + it('does emit "resize" when modification is made between the block and resize notification', done => { + const el = new MockElement(); + const checker = new ResizeChecker(el as any, { disabled: true }); + const listener = jest.fn(); + checker.on('resize', listener); + + checker.modifySizeWithoutTriggeringResize(() => { + el.clientHeight = 100; + }); + el.dispatchEvent('resize'); + expect(listener).not.toBeCalled(); + + el.clientHeight = 200; + el.dispatchEvent('resize'); + setTimeout(() => { + expect(listener).not.toBeCalled(); + done(); + }, 100); + }); + }); + + describe('#destroy()', () => { + it('destroys internal observer instance', () => { + const el = new MockElement(); + const checker = new ResizeChecker(el as any, { disabled: true }); + + checker.destroy(); + expect(!(checker as any).observer).toBe(true); + }); + + it('does not emit future resize events', done => { + const el = new MockElement(); + const checker = new ResizeChecker(el as any, { disabled: true }); + const listener = jest.fn(); + checker.on('resize', listener); + + checker.destroy(); + + el.clientHeight = 100; + el.dispatchEvent('resize'); + setTimeout(() => { + expect(listener).not.toBeCalled(); + done(); + }, 100); + }); + }); +}); diff --git a/src/legacy/ui/public/resize_checker/resize_checker.ts b/src/plugins/kibana_utils/public/resize_checker/resize_checker.ts similarity index 89% rename from src/legacy/ui/public/resize_checker/resize_checker.ts rename to src/plugins/kibana_utils/public/resize_checker/resize_checker.ts index c00603c3b11d6..bc514474e73fb 100644 --- a/src/legacy/ui/public/resize_checker/resize_checker.ts +++ b/src/plugins/kibana_utils/public/resize_checker/resize_checker.ts @@ -18,22 +18,9 @@ */ import { EventEmitter } from 'events'; -import $ from 'jquery'; import { isEqual } from 'lodash'; import ResizeObserver from 'resize-observer-polyfill'; -function validateElArg(el: HTMLElement) { - // the ResizeChecker historically accepted jquery elements, - // so we wrap in jQuery then extract the element - const $el = $(el); - - if ($el.length !== 1) { - throw new TypeError('ResizeChecker must be constructed with a single DOM element.'); - } - - return $el.get(0); -} - function getSize(el: HTMLElement): [number, number] { return [el.clientWidth, el.clientHeight]; } @@ -50,7 +37,7 @@ export class ResizeChecker extends EventEmitter { constructor(el: HTMLElement, args: { disabled?: boolean } = {}) { super(); - this.el = validateElArg(el); + this.el = el; this.observer = new ResizeObserver(() => { if (this.expectedSize) { diff --git a/test/functional/apps/console/_console.ts b/test/functional/apps/console/_console.ts index 642314d1fb7f1..456752c0cd6eb 100644 --- a/test/functional/apps/console/_console.ts +++ b/test/functional/apps/console/_console.ts @@ -35,6 +35,8 @@ GET _search export default function({ getService, getPageObjects }: FtrProviderContext) { const retry = getService('retry'); const log = getService('log'); + const find = getService('find'); + const browser = getService('browser'); const PageObjects = getPageObjects(['common', 'console']); describe('console app', function describeIndexTests() { @@ -81,5 +83,14 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { expect(await PageObjects.console.getRequestFontSize()).to.be('24px'); }); }); + + it('should resize the editor', async () => { + const editor = await find.byCssSelector('.conApp'); + await browser.setWindowSize(1300, 1100); + const initialSize = await editor.getSize(); + await browser.setWindowSize(1000, 1100); + const afterSize = await editor.getSize(); + expect(initialSize.width).to.be.greaterThan(afterSize.width); + }); }); } diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js index 7e72fedd4b1bb..8c272cc6c4244 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/view.js @@ -6,7 +6,7 @@ import _ from 'lodash'; import React from 'react'; -import { ResizeChecker } from 'ui/resize_checker'; +import { ResizeChecker } from '../../../../../../../../src/plugins/kibana_utils/public'; import { syncLayerOrderForSingleLayer, removeOrphanedSourcesAndLayers, diff --git a/x-pack/legacy/plugins/ml/public/timeseriesexplorer/timeseriesexplorer.js b/x-pack/legacy/plugins/ml/public/timeseriesexplorer/timeseriesexplorer.js index 73d1dd2818042..857e12cbbe942 100644 --- a/x-pack/legacy/plugins/ml/public/timeseriesexplorer/timeseriesexplorer.js +++ b/x-pack/legacy/plugins/ml/public/timeseriesexplorer/timeseriesexplorer.js @@ -32,7 +32,7 @@ import { import chrome from 'ui/chrome'; import { toastNotifications } from 'ui/notify'; -import { ResizeChecker } from 'ui/resize_checker'; +import { ResizeChecker } from '../../../../../../src/plugins/kibana_utils/public'; import { ANOMALIES_TABLE_DEFAULT_QUERY_SIZE } from '../../common/constants/search'; import { parseInterval } from '../../common/util/parse_interval'; From a5b3efada7090404f48b112c4d51dd6f6264352b Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Mon, 18 Nov 2019 11:22:06 +0100 Subject: [PATCH 09/17] Add retry to find.existsByDisplayedByCssSelector (#48734) (#50883) This PR fixes timeout handling in `find.existsByDisplayedByCssSelector` for elements that are found but (not yet) displayed. --- .../apps/dashboard/dashboard_snapshots.js | 2 -- .../functional/page_objects/dashboard_page.js | 6 +++++- test/functional/services/find.ts | 20 +++++++++++++++---- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/test/functional/apps/dashboard/dashboard_snapshots.js b/test/functional/apps/dashboard/dashboard_snapshots.js index e98c4afc443f8..698f4d1de6d57 100644 --- a/test/functional/apps/dashboard/dashboard_snapshots.js +++ b/test/functional/apps/dashboard/dashboard_snapshots.js @@ -53,7 +53,6 @@ export default function ({ getService, getPageObjects, updateBaselines }) { await PageObjects.common.closeToast(); await PageObjects.dashboard.saveDashboard('tsvb'); - await PageObjects.common.closeToast(); await PageObjects.dashboard.clickFullScreenMode(); await dashboardPanelActions.openContextMenu(); await dashboardPanelActions.clickExpandPanelToggle(); @@ -74,7 +73,6 @@ export default function ({ getService, getPageObjects, updateBaselines }) { await PageObjects.common.closeToast(); await PageObjects.dashboard.saveDashboard('area'); - await PageObjects.common.closeToast(); await PageObjects.dashboard.clickFullScreenMode(); await dashboardPanelActions.openContextMenu(); await dashboardPanelActions.clickExpandPanelToggle(); diff --git a/test/functional/page_objects/dashboard_page.js b/test/functional/page_objects/dashboard_page.js index af3a15e9b3015..2460422722835 100644 --- a/test/functional/page_objects/dashboard_page.js +++ b/test/functional/page_objects/dashboard_page.js @@ -305,7 +305,8 @@ export function DashboardPageProvider({ getService, getPageObjects }) { /** * Save the current dashboard with the specified name and options and - * verify that the save was successful + * verify that the save was successful, close the toast and return the + * toast message * * @param dashName {String} * @param saveOptions {{storeTimeWithDashboard: boolean, saveAsNew: boolean, needsConfirm: false, waitDialogIsClosed: boolean }} @@ -319,8 +320,11 @@ export function DashboardPageProvider({ getService, getPageObjects }) { // Confirm that the Dashboard has actually been saved await testSubjects.existOrFail('saveDashboardSuccess'); + const message = await PageObjects.common.closeToast(); await PageObjects.header.waitUntilLoadingHasFinished(); await this.waitForSaveModalToClose(); + + return message; } async waitForSaveModalToClose() { diff --git a/test/functional/services/find.ts b/test/functional/services/find.ts index c65821ea73ace..312668b718dc0 100644 --- a/test/functional/services/find.ts +++ b/test/functional/services/find.ts @@ -303,10 +303,22 @@ export async function FindProvider({ getService }: FtrProviderContext) { timeout: number = WAIT_FOR_EXISTS_TIME ): Promise { log.debug(`Find.existsByDisplayedByCssSelector('${selector}') with timeout=${timeout}`); - return await this.exists(async drive => { - const elements = wrapAll(await drive.findElements(By.css(selector))); - return await this.filterElementIsDisplayed(elements); - }, timeout); + try { + await retry.tryForTime(timeout, async () => { + // make sure that the find timeout is not longer than the retry timeout + await this._withTimeout(Math.min(timeout, WAIT_FOR_EXISTS_TIME)); + const elements = await driver.findElements(By.css(selector)); + await this._withTimeout(defaultFindTimeout); + const displayed = await this.filterElementIsDisplayed(wrapAll(elements)); + if (displayed.length === 0) { + throw new Error(`${selector} is not displayed`); + } + }); + } catch (err) { + await this._withTimeout(defaultFindTimeout); + return false; + } + return true; } public async existsByCssSelector( From a2f71f6f5db75354648f4f4449919e7158656e44 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Mon, 18 Nov 2019 11:46:11 +0100 Subject: [PATCH 10/17] allows plugins to define validation schema for "enabled" flag (#50286) (#50885) * validation error message gives a hint about error source * allows plugins to define validation schema for "enabled" flag --- src/core/server/config/config_service.test.ts | 85 +++++++++++++++++-- src/core/server/config/config_service.ts | 26 ++++-- 2 files changed, 97 insertions(+), 14 deletions(-) diff --git a/src/core/server/config/config_service.test.ts b/src/core/server/config/config_service.test.ts index 61da9af7baa7c..131e1dd501792 100644 --- a/src/core/server/config/config_service.test.ts +++ b/src/core/server/config/config_service.test.ts @@ -55,7 +55,7 @@ test('throws if config at path does not match schema', async () => { await expect( configService.setSchema('key', schema.string()) ).rejects.toThrowErrorMatchingInlineSnapshot( - `"[key]: expected value of type [string] but got [number]"` + `"[config validation of [key]]: expected value of type [string] but got [number]"` ); }); @@ -78,11 +78,11 @@ test('re-validate config when updated', async () => { config$.next(new ObjectToConfigAdapter({ key: 123 })); await expect(valuesReceived).toMatchInlineSnapshot(` -Array [ - "value", - [Error: [key]: expected value of type [string] but got [number]], -] -`); + Array [ + "value", + [Error: [config validation of [key]]: expected value of type [string] but got [number]], + ] + `); }); test("returns undefined if fetching optional config at a path that doesn't exist", async () => { @@ -143,7 +143,7 @@ test("throws error if 'schema' is not defined for a key", async () => { const configs = configService.atPath('key'); await expect(configs.pipe(first()).toPromise()).rejects.toMatchInlineSnapshot( - `[Error: No validation schema has been defined for key]` + `[Error: No validation schema has been defined for [key]]` ); }); @@ -153,7 +153,7 @@ test("throws error if 'setSchema' called several times for the same key", async const addSchema = async () => await configService.setSchema('key', schema.string()); await addSchema(); await expect(addSchema()).rejects.toMatchInlineSnapshot( - `[Error: Validation schema for key was already registered.]` + `[Error: Validation schema for [key] was already registered.]` ); }); @@ -280,6 +280,33 @@ test('handles disabled path and marks config as used', async () => { expect(unusedPaths).toEqual([]); }); +test('does not throw if schema does not define "enabled" schema', async () => { + const initialConfig = { + pid: { + file: '/some/file.pid', + }, + }; + + const config$ = new BehaviorSubject(new ObjectToConfigAdapter(initialConfig)); + const configService = new ConfigService(config$, defaultEnv, logger); + expect( + configService.setSchema( + 'pid', + schema.object({ + file: schema.string(), + }) + ) + ).resolves.toBeUndefined(); + + const value$ = configService.atPath('pid'); + const value: any = await value$.pipe(first()).toPromise(); + expect(value.enabled).toBe(undefined); + + const valueOptional$ = configService.optionalAtPath('pid'); + const valueOptional: any = await valueOptional$.pipe(first()).toPromise(); + expect(valueOptional.enabled).toBe(undefined); +}); + test('treats config as enabled if config path is not present in config', async () => { const initialConfig = {}; @@ -292,3 +319,45 @@ test('treats config as enabled if config path is not present in config', async ( const unusedPaths = await configService.getUnusedPaths(); expect(unusedPaths).toEqual([]); }); + +test('read "enabled" even if its schema is not present', async () => { + const initialConfig = { + foo: { + enabled: true, + }, + }; + + const config$ = new BehaviorSubject(new ObjectToConfigAdapter(initialConfig)); + const configService = new ConfigService(config$, defaultEnv, logger); + + const isEnabled = await configService.isEnabledAtPath('foo'); + expect(isEnabled).toBe(true); +}); + +test('allows plugins to specify "enabled" flag via validation schema', async () => { + const initialConfig = {}; + + const config$ = new BehaviorSubject(new ObjectToConfigAdapter(initialConfig)); + const configService = new ConfigService(config$, defaultEnv, logger); + + await configService.setSchema( + 'foo', + schema.object({ enabled: schema.boolean({ defaultValue: false }) }) + ); + + expect(await configService.isEnabledAtPath('foo')).toBe(false); + + await configService.setSchema( + 'bar', + schema.object({ enabled: schema.boolean({ defaultValue: true }) }) + ); + + expect(await configService.isEnabledAtPath('bar')).toBe(true); + + await configService.setSchema( + 'baz', + schema.object({ different: schema.boolean({ defaultValue: true }) }) + ); + + expect(await configService.isEnabledAtPath('baz')).toBe(true); +}); diff --git a/src/core/server/config/config_service.ts b/src/core/server/config/config_service.ts index 8d3cc733cf250..c18a5b2000e01 100644 --- a/src/core/server/config/config_service.ts +++ b/src/core/server/config/config_service.ts @@ -54,7 +54,7 @@ export class ConfigService { public async setSchema(path: ConfigPath, schema: Type) { const namespace = pathToString(path); if (this.schemas.has(namespace)) { - throw new Error(`Validation schema for ${path} was already registered.`); + throw new Error(`Validation schema for [${path}] was already registered.`); } this.schemas.set(namespace, schema); @@ -98,14 +98,28 @@ export class ConfigService { } public async isEnabledAtPath(path: ConfigPath) { - const enabledPath = createPluginEnabledPath(path); + const namespace = pathToString(path); + + const validatedConfig = this.schemas.has(namespace) + ? await this.atPath<{ enabled?: boolean }>(path) + .pipe(first()) + .toPromise() + : undefined; + const enabledPath = createPluginEnabledPath(path); const config = await this.config$.pipe(first()).toPromise(); - if (!config.has(enabledPath)) { + + // if plugin hasn't got a config schema, we try to read "enabled" directly + const isEnabled = + validatedConfig && validatedConfig.enabled !== undefined + ? validatedConfig.enabled + : config.get(enabledPath); + + // not declared. consider that plugin is enabled by default + if (isEnabled === undefined) { return true; } - const isEnabled = config.get(enabledPath); if (isEnabled === false) { // If the plugin is _not_ enabled, we mark the entire plugin path as // handled, as it's expected that it won't be used. @@ -138,7 +152,7 @@ export class ConfigService { const namespace = pathToString(path); const schema = this.schemas.get(namespace); if (!schema) { - throw new Error(`No validation schema has been defined for ${namespace}`); + throw new Error(`No validation schema has been defined for [${namespace}]`); } return schema.validate( config, @@ -147,7 +161,7 @@ export class ConfigService { prod: this.env.mode.prod, ...this.env.packageInfo, }, - namespace + `config validation of [${namespace}]` ); } From 4cce8bb2662e8d4f61b04ad110cccc83616356ce Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 18 Nov 2019 11:47:37 +0000 Subject: [PATCH 11/17] [ML] Enabling lat_long detector function in advanced wizard (#50787) (#50890) * [ML] Enabling lat_long agg in advanced wizard * fixing tests --- .../__mocks__/results/farequote_job_caps.json | 7 ++----- .../job_service/new_job_caps/field_service.ts | 20 +++++++++++++------ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/x-pack/legacy/plugins/ml/server/models/job_service/new_job_caps/__mocks__/results/farequote_job_caps.json b/x-pack/legacy/plugins/ml/server/models/job_service/new_job_caps/__mocks__/results/farequote_job_caps.json index 99c373eddd107..c005d9a5b5aaa 100644 --- a/x-pack/legacy/plugins/ml/server/models/job_service/new_job_caps/__mocks__/results/farequote_job_caps.json +++ b/x-pack/legacy/plugins/ml/server/models/job_service/new_job_caps/__mocks__/results/farequote_job_caps.json @@ -460,9 +460,7 @@ "max": "max", "min": "min" }, - "fieldIds": [ - "responsetime" - ] + "fieldIds": [] } ], "fields": [ @@ -513,8 +511,7 @@ "low_non_null_sum", "info_content", "high_info_content", - "low_info_content", - "lat_long" + "low_info_content" ] } ] diff --git a/x-pack/legacy/plugins/ml/server/models/job_service/new_job_caps/field_service.ts b/x-pack/legacy/plugins/ml/server/models/job_service/new_job_caps/field_service.ts index 32309ad8177f4..d53d5344adb64 100644 --- a/x-pack/legacy/plugins/ml/server/models/job_service/new_job_caps/field_service.ts +++ b/x-pack/legacy/plugins/ml/server/models/job_service/new_job_caps/field_service.ts @@ -31,6 +31,8 @@ const supportedTypes: string[] = [ ES_FIELD_TYPES.SCALED_FLOAT, ES_FIELD_TYPES.SHORT, ES_FIELD_TYPES.IP, + ES_FIELD_TYPES.GEO_POINT, + ES_FIELD_TYPES.GEO_SHAPE, ]; export function fieldServiceProvider( @@ -135,6 +137,7 @@ async function combineFieldsAndAggs( const keywordFields = getKeywordFields(fields); const numericalFields = getNumericalFields(fields); const ipFields = getIpFields(fields); + const geoFields = getGeoFields(fields); const isRollup = Object.keys(rollupFields).length > 0; const mix = mixFactory(isRollup, rollupFields); @@ -142,17 +145,16 @@ async function combineFieldsAndAggs( aggs.forEach(a => { if (a.type === METRIC_AGG_TYPE && a.fields !== undefined) { switch (a.id) { + case ML_JOB_AGGREGATION.LAT_LONG: + geoFields.forEach(f => mix(f, a)); + break; case ML_JOB_AGGREGATION.DISTINCT_COUNT: case ML_JOB_AGGREGATION.HIGH_DISTINCT_COUNT: case ML_JOB_AGGREGATION.LOW_DISTINCT_COUNT: // distinct count (i.e. cardinality) takes keywords, ips // as well as numerical fields - keywordFields.forEach(f => { - mix(f, a); - }); - ipFields.forEach(f => { - mix(f, a); - }); + keywordFields.forEach(f => mix(f, a)); + ipFields.forEach(f => mix(f, a)); // note, no break to fall through to add numerical fields. default: // all other aggs take numerical fields @@ -235,3 +237,9 @@ function getNumericalFields(fields: Field[]): Field[] { f.type === ES_FIELD_TYPES.SCALED_FLOAT ); } + +function getGeoFields(fields: Field[]): Field[] { + return fields.filter( + f => f.type === ES_FIELD_TYPES.GEO_POINT || f.type === ES_FIELD_TYPES.GEO_SHAPE + ); +} From 9e841747359ad3f573eaad49e67d0213c8910df9 Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Mon, 18 Nov 2019 14:17:18 +0100 Subject: [PATCH 12/17] [ML] Add tests to create transforms (#49760) (#50898) --- .../kql_filter_bar/filter_bar/filter_bar.js | 7 +- .../kql_filter_bar/kql_filter_bar.js | 6 +- .../components/ml_in_memory_table/types.ts | 4 +- .../aggregation_dropdown/dropdown.tsx | 3 + .../agg_label_form.test.tsx.snap | 3 + .../__snapshots__/list_form.test.tsx.snap | 1 + .../aggregation_list/agg_label_form.tsx | 6 +- .../components/aggregation_list/list_form.tsx | 4 +- .../group_by_label_form.test.tsx.snap | 11 + .../__snapshots__/list_form.test.tsx.snap | 1 + .../group_by_list/group_by_label_form.tsx | 12 +- .../components/group_by_list/list_form.tsx | 4 +- .../source_index_preview.tsx | 6 +- .../step_create/step_create_form.tsx | 296 +++++---- .../components/step_define/pivot_preview.tsx | 6 +- .../step_define/step_define_form.tsx | 577 +++++++++--------- .../step_define/step_define_summary.tsx | 127 ++-- .../step_details/step_details_form.tsx | 379 ++++++------ .../step_details/step_details_summary.tsx | 6 +- .../components/wizard_nav/wizard_nav.tsx | 16 +- .../transform_list.test.tsx.snap | 1 + .../components/transform_list/columns.tsx | 16 +- .../transform_list/transform_list.tsx | 19 +- .../transform_management_section.tsx | 6 +- .../functional/apps/transform/creation.ts | 229 +++++++ .../test/functional/apps/transform/index.ts | 14 + x-pack/test/functional/config.js | 5 + x-pack/test/functional/services/index.ts | 2 + x-pack/test/functional/services/transform.ts | 34 ++ .../functional/services/transform_ui/api.ts | 43 ++ .../functional/services/transform_ui/index.ts | 12 + .../services/transform_ui/management.ts | 42 ++ .../services/transform_ui/navigation.ts | 17 + .../services/transform_ui/source_selection.ts | 30 + .../services/transform_ui/transform_table.ts | 87 +++ .../services/transform_ui/wizard.ts | 360 +++++++++++ 36 files changed, 1705 insertions(+), 687 deletions(-) create mode 100644 x-pack/test/functional/apps/transform/creation.ts create mode 100644 x-pack/test/functional/apps/transform/index.ts create mode 100644 x-pack/test/functional/services/transform.ts create mode 100644 x-pack/test/functional/services/transform_ui/api.ts create mode 100644 x-pack/test/functional/services/transform_ui/index.ts create mode 100644 x-pack/test/functional/services/transform_ui/management.ts create mode 100644 x-pack/test/functional/services/transform_ui/navigation.ts create mode 100644 x-pack/test/functional/services/transform_ui/source_selection.ts create mode 100644 x-pack/test/functional/services/transform_ui/transform_table.ts create mode 100644 x-pack/test/functional/services/transform_ui/wizard.ts diff --git a/x-pack/legacy/plugins/ml/public/components/kql_filter_bar/filter_bar/filter_bar.js b/x-pack/legacy/plugins/ml/public/components/kql_filter_bar/filter_bar/filter_bar.js index 19b170485817f..27c5e9d87b21d 100644 --- a/x-pack/legacy/plugins/ml/public/components/kql_filter_bar/filter_bar/filter_bar.js +++ b/x-pack/legacy/plugins/ml/public/components/kql_filter_bar/filter_bar/filter_bar.js @@ -178,6 +178,7 @@ export class FilterBar extends Component { onClick={this.onClickInput} autoComplete="off" spellCheck={false} + data-test-subj={this.props.testSubj} /> {this.props.isLoading && ( @@ -213,12 +214,14 @@ FilterBar.propTypes = { placeholder: PropTypes.string, onSubmit: PropTypes.func.isRequired, valueExternal: PropTypes.string, - suggestions: PropTypes.array.isRequired + suggestions: PropTypes.array.isRequired, + testSubj: PropTypes.string, }; FilterBar.defaultProps = { isLoading: false, disabled: false, placeholder: 'tag : engineering OR tag : marketing', - suggestions: [] + suggestions: [], + testSubj: undefined, }; diff --git a/x-pack/legacy/plugins/ml/public/components/kql_filter_bar/kql_filter_bar.js b/x-pack/legacy/plugins/ml/public/components/kql_filter_bar/kql_filter_bar.js index 85d301d91f8e2..ef5bacdc1777c 100644 --- a/x-pack/legacy/plugins/ml/public/components/kql_filter_bar/kql_filter_bar.js +++ b/x-pack/legacy/plugins/ml/public/components/kql_filter_bar/kql_filter_bar.js @@ -90,7 +90,7 @@ export class KqlFilterBar extends Component { render() { const { error } = this.state; - const { initialValue, placeholder, valueExternal } = this.props; + const { initialValue, placeholder, valueExternal, testSubj } = this.props; return ( @@ -103,6 +103,7 @@ export class KqlFilterBar extends Component { onSubmit={this.onSubmit} suggestions={this.state.suggestions} valueExternal={valueExternal} + testSubj={testSubj} /> { error && @@ -118,6 +119,7 @@ KqlFilterBar.propTypes = { initialValue: PropTypes.string, onSubmit: PropTypes.func.isRequired, placeholder: PropTypes.string, - valueExternal: PropTypes.string + valueExternal: PropTypes.string, + testSubj: PropTypes.string, }; diff --git a/x-pack/legacy/plugins/ml/public/components/ml_in_memory_table/types.ts b/x-pack/legacy/plugins/ml/public/components/ml_in_memory_table/types.ts index 22c35fc453b88..fac0309e0aeb6 100644 --- a/x-pack/legacy/plugins/ml/public/components/ml_in_memory_table/types.ts +++ b/x-pack/legacy/plugins/ml/public/components/ml_in_memory_table/types.ts @@ -31,6 +31,7 @@ export interface FieldDataColumnType { render?: RenderFunc; footer?: string | ReactElement | FooterFunc; textOnly?: boolean; + 'data-test-subj'?: string; } export interface ComputedColumnType { @@ -40,6 +41,7 @@ export interface ComputedColumnType { sortable?: (item: Item) => any; width?: string; truncateText?: boolean; + 'data-test-subj'?: string; } type ICON_TYPES = any; @@ -183,7 +185,7 @@ export type EuiInMemoryTableProps = CommonProps & { selection?: SelectionType; itemId?: ItemIdType; itemIdToExpandedRowMap?: Record; - rowProps?: () => void | Record; + rowProps?: (item: Item) => void | Record; cellProps?: () => void | Record; onTableChange?: (arg: OnTableChangeArg) => void; }; diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/aggregation_dropdown/dropdown.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/aggregation_dropdown/dropdown.tsx index 964909205c507..7617237e72a7d 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/aggregation_dropdown/dropdown.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/aggregation_dropdown/dropdown.tsx @@ -12,12 +12,14 @@ interface Props { options: EuiComboBoxOptionProps[]; placeholder?: string; changeHandler(d: EuiComboBoxOptionProps[]): void; + testSubj?: string; } export const DropDown: React.SFC = ({ changeHandler, options, placeholder = 'Search ...', + testSubj, }) => { return ( = ({ selectedOptions={[]} onChange={changeHandler} isClearable={false} + data-test-subj={testSubj} /> ); }; diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/aggregation_list/__snapshots__/agg_label_form.test.tsx.snap b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/aggregation_list/__snapshots__/agg_label_form.test.tsx.snap index 835ed9dc9f849..ed32fb3d6ad5f 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/aggregation_list/__snapshots__/agg_label_form.test.tsx.snap +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/aggregation_list/__snapshots__/agg_label_form.test.tsx.snap @@ -11,6 +11,7 @@ exports[`Transform: Date histogram aggregation 1`] = ` > the-group-by-agg-name @@ -24,6 +25,7 @@ exports[`Transform: Date histogram aggregation 1`] = ` button={ Date histogram aggregation 1`] = ` > Minimal initialization 1`] = ` = ({ return ( - {item.aggName} + + {item.aggName} + = ({ size="s" iconType="pencil" onClick={() => setPopoverVisibility(!isPopoverVisible)} + data-test-subj="transformAggregationEntryEditButton" /> } isOpen={isPopoverVisible} @@ -74,6 +77,7 @@ export const AggLabelForm: React.SFC = ({ size="s" iconType="cross" onClick={() => deleteHandler(item.aggName)} + data-test-subj="transformAggregationEntryDeleteButton" /> diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_form.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_form.tsx index 0d93b75bc50e5..b9437fee89efd 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_form.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/aggregation_list/list_form.tsx @@ -33,11 +33,11 @@ export const AggListForm: React.SFC = ({ const listKeys = Object.keys(list); return ( - {listKeys.map((aggName: AggName) => { + {listKeys.map((aggName: AggName, i) => { const otherAggNames = listKeys.filter(k => k !== aggName); return ( - + Date histogram aggregation 1`] = ` > the-group-by-agg-name @@ -22,6 +23,7 @@ exports[`Transform: Date histogram aggregation 1`] = ` 1m @@ -35,6 +37,7 @@ exports[`Transform: Date histogram aggregation 1`] = ` button={ Date histogram aggregation 1`] = ` > Histogram aggregation 1`] = ` > the-group-by-agg-name @@ -100,6 +105,7 @@ exports[`Transform: Histogram aggregation 1`] = ` 100 @@ -113,6 +119,7 @@ exports[`Transform: Histogram aggregation 1`] = ` button={ Histogram aggregation 1`] = ` > Terms aggregation 1`] = ` > the-group-by-agg-name @@ -180,6 +189,7 @@ exports[`Transform: Terms aggregation 1`] = ` button={ Terms aggregation 1`] = ` > Minimal initialization 1`] = ` = ({ return ( - {item.aggName} + + {item.aggName} + {interval !== undefined && ( - + {interval} @@ -77,6 +83,7 @@ export const GroupByLabelForm: React.SFC = ({ size="s" iconType="pencil" onClick={() => setPopoverVisibility(!isPopoverVisible)} + data-test-subj="transformGroupByEntryEditButton" /> } isOpen={isPopoverVisible} @@ -98,6 +105,7 @@ export const GroupByLabelForm: React.SFC = ({ size="s" iconType="cross" onClick={() => deleteHandler(item.aggName)} + data-test-subj="transformGroupByEntryDeleteButton" /> diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/group_by_list/list_form.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/group_by_list/list_form.tsx index bee708f6f25ea..484314df5067d 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/group_by_list/list_form.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/group_by_list/list_form.tsx @@ -33,11 +33,11 @@ export const GroupByListForm: React.SFC = ({ const listKeys = Object.keys(list); return ( - {listKeys.map((aggName: AggName) => { + {listKeys.map((aggName: AggName, i) => { const otherAggNames = listKeys.filter(k => k !== aggName); return ( - + = React.memo(({ cellClick, que if (status === SOURCE_INDEX_STATUS.ERROR) { return ( - + = React.memo(({ cellClick, que if (status === SOURCE_INDEX_STATUS.LOADED && tableItems.length === 0) { return ( - + = React.memo(({ cellClick, que }); return ( - + diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx index 993ba1b0bd0f5..05b68631b7856 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx @@ -260,169 +260,197 @@ export const StepCreateForm: SFC = React.memo( } return ( - - {!created && ( +
+ + {!created && ( + + + + {i18n.translate('xpack.transform.stepCreateForm.createAndStartTransformButton', { + defaultMessage: 'Create and start', + })} + + + + + {i18n.translate( + 'xpack.transform.stepCreateForm.createAndStartTransformDescription', + { + defaultMessage: + 'Creates and starts the transform. A transform will increase search and indexing load in your cluster. Please stop the transform if excessive load is experienced. After the transform is started, you will be offered options to continue exploring the transform.', + } + )} + + + + )} + {created && ( + + + + {i18n.translate('xpack.transform.stepCreateForm.startTransformButton', { + defaultMessage: 'Start', + })} + + + + + {i18n.translate('xpack.transform.stepCreateForm.startTransformDescription', { + defaultMessage: + 'Starts the transform. A transform will increase search and indexing load in your cluster. Please stop the transform if excessive load is experienced. After the transform is started, you will be offered options to continue exploring the transform.', + })} + + + + )} - - {i18n.translate('xpack.transform.stepCreateForm.createAndStartTransformButton', { - defaultMessage: 'Create and start', + + {i18n.translate('xpack.transform.stepCreateForm.createTransformButton', { + defaultMessage: 'Create', })} - {i18n.translate( - 'xpack.transform.stepCreateForm.createAndStartTransformDescription', - { - defaultMessage: - 'Creates and starts the transform. A transform will increase search and indexing load in your cluster. Please stop the transform if excessive load is experienced. After the transform is started, you will be offered options to continue exploring the transform.', - } - )} + {i18n.translate('xpack.transform.stepCreateForm.createTransformDescription', { + defaultMessage: + 'Create the transform without starting it. You will be able to start the transform later by returning to the transforms list.', + })} - )} - {created && ( - - {i18n.translate('xpack.transform.stepCreateForm.startTransformButton', { - defaultMessage: 'Start', - })} - + + {(copy: () => void) => ( + + {i18n.translate( + 'xpack.transform.stepCreateForm.copyTransformConfigToClipboardButton', + { + defaultMessage: 'Copy to clipboard', + } + )} + + )} + - {i18n.translate('xpack.transform.stepCreateForm.startTransformDescription', { - defaultMessage: - 'Starts the transform. A transform will increase search and indexing load in your cluster. Please stop the transform if excessive load is experienced. After the transform is started, you will be offered options to continue exploring the transform.', - })} + {i18n.translate( + 'xpack.transform.stepCreateForm.copyTransformConfigToClipboardDescription', + { + defaultMessage: + 'Copies to the clipboard the Kibana Dev Console command for creating the transform.', + } + )} - )} - - - - {i18n.translate('xpack.transform.stepCreateForm.createTransformButton', { - defaultMessage: 'Create', - })} - - - - - {i18n.translate('xpack.transform.stepCreateForm.createTransformDescription', { - defaultMessage: - 'Create the transform without starting it. You will be able to start the transform later by returning to the transforms list.', - })} - - - - - - - {(copy: () => void) => ( - - {i18n.translate( - 'xpack.transform.stepCreateForm.copyTransformConfigToClipboardButton', - { - defaultMessage: 'Copy to clipboard', - } - )} - - )} - - - - - {i18n.translate( - 'xpack.transform.stepCreateForm.copyTransformConfigToClipboardDescription', - { - defaultMessage: - 'Copies to the clipboard the Kibana Dev Console command for creating the transform.', - } - )} - - - - {progressPercentComplete !== undefined && isBatchTransform && ( - - - - - {i18n.translate('xpack.transform.stepCreateForm.progressTitle', { - defaultMessage: 'Progress', - })} - - - - - - - - {progressPercentComplete}% - - - - )} - {created && ( - - - - - } - title={i18n.translate('xpack.transform.stepCreateForm.transformListCardTitle', { - defaultMessage: 'Transforms', + {progressPercentComplete !== undefined && isBatchTransform && ( + + + + + {i18n.translate('xpack.transform.stepCreateForm.progressTitle', { + defaultMessage: 'Progress', })} - description={i18n.translate( - 'xpack.transform.stepCreateForm.transformListCardDescription', - { - defaultMessage: 'Return to the transform management page.', - } - )} - onClick={() => setRedirectToTransformManagement(true)} - /> - - {started === true && createIndexPattern === true && indexPatternId === undefined && ( - - - - -

- {i18n.translate( - 'xpack.transform.stepCreateForm.creatingIndexPatternMessage', - { - defaultMessage: 'Creating Kibana index pattern ...', - } - )} -

-
-
+
+ + + + - )} - {started === true && indexPatternId !== undefined && ( + + {progressPercentComplete}% + + +
+ )} + {created && ( + + + } - title={i18n.translate('xpack.transform.stepCreateForm.discoverCardTitle', { - defaultMessage: 'Discover', + icon={} + title={i18n.translate('xpack.transform.stepCreateForm.transformListCardTitle', { + defaultMessage: 'Transforms', })} description={i18n.translate( - 'xpack.transform.stepCreateForm.discoverCardDescription', + 'xpack.transform.stepCreateForm.transformListCardDescription', { - defaultMessage: 'Use Discover to explore the transform.', + defaultMessage: 'Return to the transform management page.', } )} - href={getDiscoverUrl(indexPatternId, kibanaContext.kbnBaseUrl)} + onClick={() => setRedirectToTransformManagement(true)} + data-test-subj="transformWizardCardManagement" /> - )} - - - )} -
+ {started === true && createIndexPattern === true && indexPatternId === undefined && ( + + + + +

+ {i18n.translate( + 'xpack.transform.stepCreateForm.creatingIndexPatternMessage', + { + defaultMessage: 'Creating Kibana index pattern ...', + } + )} +

+
+
+
+ )} + {started === true && indexPatternId !== undefined && ( + + } + title={i18n.translate('xpack.transform.stepCreateForm.discoverCardTitle', { + defaultMessage: 'Discover', + })} + description={i18n.translate( + 'xpack.transform.stepCreateForm.discoverCardDescription', + { + defaultMessage: 'Use Discover to explore the transform.', + } + )} + href={getDiscoverUrl(indexPatternId, kibanaContext.kbnBaseUrl)} + data-test-subj="transformWizardCardDiscover" + /> + + )} + + + )} + +
); } ); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.tsx index b53e0fca0fee1..681a0f7e6ba3f 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.tsx @@ -158,7 +158,7 @@ export const PivotPreview: SFC = React.memo(({ aggs, groupBy, if (status === PIVOT_PREVIEW_STATUS.ERROR) { return ( - + = React.memo(({ aggs, groupBy, ); } return ( - + = React.memo(({ aggs, groupBy, }; return ( - + {status === PIVOT_PREVIEW_STATUS.LOADING && } {status !== PIVOT_PREVIEW_STATUS.LOADING && ( diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx index 442ce2011f77e..8eea3b020dcfe 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx @@ -508,145 +508,318 @@ export const StepDefineForm: SFC = React.memo(({ overrides = {}, onChange return ( - - {kibanaContext.currentSavedSearch === undefined && typeof searchString === 'string' && ( - - - {indexPattern.title} - - {!disabledQuery && ( - - {!isAdvancedSourceEditorEnabled && ( - - + + {kibanaContext.currentSavedSearch === undefined && typeof searchString === 'string' && ( + + + {indexPattern.title} + + {!disabledQuery && ( + + {!isAdvancedSourceEditorEnabled && ( + + + + )} + + )} + + )} + + {isAdvancedSourceEditorEnabled && ( + + + + { + setAdvancedEditorSourceConfig(d); + + // Disable the "Apply"-Button if the config hasn't changed. + if (advancedEditorSourceConfigLastApplied === d) { + setAdvancedSourceEditorApplyButtonEnabled(false); + return; + } + + // Try to parse the string passed on from the editor. + // If parsing fails, the "Apply"-Button will be disabled + try { + JSON.parse(d); + setAdvancedSourceEditorApplyButtonEnabled(true); + } catch (e) { + setAdvancedSourceEditorApplyButtonEnabled(false); + } + }} + setOptions={{ + fontSize: '12px', + }} + theme="textmate" + aria-label={i18n.translate( + 'xpack.transform.stepDefineForm.advancedSourceEditorAriaLabel', + { + defaultMessage: 'Advanced query editor', + } + )} + /> + + + + )} + {kibanaContext.currentSavedSearch === undefined && ( + + + + { + if (isAdvancedSourceEditorEnabled && sourceConfigUpdated) { + setAdvancedSourceEditorSwitchModalVisible(true); + return; + } + + toggleAdvancedSourceEditor(); + }} + data-test-subj="transformAdvancedQueryEditorSwitch" + /> + {isAdvancedSourceEditorSwitchModalVisible && ( + setAdvancedSourceEditorSwitchModalVisible(false)} + onConfirm={() => { + setAdvancedSourceEditorSwitchModalVisible(false); + toggleAdvancedSourceEditor(true); + }} + type={'source'} /> - + )} + + {isAdvancedSourceEditorEnabled && ( + + {i18n.translate( + 'xpack.transform.stepDefineForm.advancedSourceEditorApplyButtonText', + { + defaultMessage: 'Apply changes', + } + )} + )} -
+ + + )} + {kibanaContext.currentSavedSearch !== undefined && + kibanaContext.currentSavedSearch.id !== undefined && ( + + {kibanaContext.currentSavedSearch.title} + )} -
- )} - - {isAdvancedSourceEditorEnabled && ( - - - - { - setAdvancedEditorSourceConfig(d); - - // Disable the "Apply"-Button if the config hasn't changed. - if (advancedEditorSourceConfigLastApplied === d) { - setAdvancedSourceEditorApplyButtonEnabled(false); - return; - } - // Try to parse the string passed on from the editor. - // If parsing fails, the "Apply"-Button will be disabled - try { - JSON.parse(d); - setAdvancedSourceEditorApplyButtonEnabled(true); - } catch (e) { - setAdvancedSourceEditorApplyButtonEnabled(false); - } - }} - setOptions={{ - fontSize: '12px', - }} - theme="textmate" - aria-label={i18n.translate( - 'xpack.transform.stepDefineForm.advancedSourceEditorAriaLabel', - { - defaultMessage: 'Advanced query editor', - } - )} - /> - - - - )} - {kibanaContext.currentSavedSearch === undefined && ( + {!isAdvancedPivotEditorEnabled && ( + + + + + + + + + + + + + + + + )} + + {isAdvancedPivotEditorEnabled && ( + + + + { + setAdvancedEditorConfig(d); + + // Disable the "Apply"-Button if the config hasn't changed. + if (advancedEditorConfigLastApplied === d) { + setAdvancedPivotEditorApplyButtonEnabled(false); + return; + } + + // Try to parse the string passed on from the editor. + // If parsing fails, the "Apply"-Button will be disabled + try { + JSON.parse(d); + setAdvancedPivotEditorApplyButtonEnabled(true); + } catch (e) { + setAdvancedPivotEditorApplyButtonEnabled(false); + } + }} + setOptions={{ + fontSize: '12px', + }} + theme="textmate" + aria-label={i18n.translate( + 'xpack.transform.stepDefineForm.advancedEditorAriaLabel', + { + defaultMessage: 'Advanced pivot editor', + } + )} + /> + + + + )} { - if (isAdvancedSourceEditorEnabled && sourceConfigUpdated) { - setAdvancedSourceEditorSwitchModalVisible(true); + if ( + isAdvancedPivotEditorEnabled && + (isAdvancedPivotEditorApplyButtonEnabled || + advancedEditorConfig !== advancedEditorConfigLastApplied) + ) { + setAdvancedEditorSwitchModalVisible(true); return; } - toggleAdvancedSourceEditor(); + toggleAdvancedEditor(); }} + data-test-subj="transformAdvancedPivotEditorSwitch" /> - {isAdvancedSourceEditorSwitchModalVisible && ( + {isAdvancedEditorSwitchModalVisible && ( setAdvancedSourceEditorSwitchModalVisible(false)} + onCancel={() => setAdvancedEditorSwitchModalVisible(false)} onConfirm={() => { - setAdvancedSourceEditorSwitchModalVisible(false); - toggleAdvancedSourceEditor(true); + setAdvancedEditorSwitchModalVisible(false); + toggleAdvancedEditor(); }} - type={'source'} + type={'pivot'} /> )} - {isAdvancedSourceEditorEnabled && ( + {isAdvancedPivotEditorEnabled && ( {i18n.translate( - 'xpack.transform.stepDefineForm.advancedSourceEditorApplyButtonText', + 'xpack.transform.stepDefineForm.advancedEditorApplyButtonText', { defaultMessage: 'Apply changes', } @@ -655,179 +828,19 @@ export const StepDefineForm: SFC = React.memo(({ overrides = {}, onChange )} - )} - {kibanaContext.currentSavedSearch !== undefined && - kibanaContext.currentSavedSearch.id !== undefined && ( - - {kibanaContext.currentSavedSearch.title} - - )} - - {!isAdvancedPivotEditorEnabled && ( - - - - - - - - - - - - - - - - )} - - {isAdvancedPivotEditorEnabled && ( - - - - { - setAdvancedEditorConfig(d); - - // Disable the "Apply"-Button if the config hasn't changed. - if (advancedEditorConfigLastApplied === d) { - setAdvancedPivotEditorApplyButtonEnabled(false); - return; - } - - // Try to parse the string passed on from the editor. - // If parsing fails, the "Apply"-Button will be disabled - try { - JSON.parse(d); - setAdvancedPivotEditorApplyButtonEnabled(true); - } catch (e) { - setAdvancedPivotEditorApplyButtonEnabled(false); - } - }} - setOptions={{ - fontSize: '12px', - }} - theme="textmate" - aria-label={i18n.translate( - 'xpack.transform.stepDefineForm.advancedEditorAriaLabel', - { - defaultMessage: 'Advanced pivot editor', - } - )} - /> - - - - )} - - - - { - if ( - isAdvancedPivotEditorEnabled && - (isAdvancedPivotEditorApplyButtonEnabled || - advancedEditorConfig !== advancedEditorConfigLastApplied) - ) { - setAdvancedEditorSwitchModalVisible(true); - return; - } - - toggleAdvancedEditor(); - }} - /> - {isAdvancedEditorSwitchModalVisible && ( - setAdvancedEditorSwitchModalVisible(false)} - onConfirm={() => { - setAdvancedEditorSwitchModalVisible(false); - toggleAdvancedEditor(); - }} - type={'pivot'} - /> - )} - - {isAdvancedPivotEditorEnabled && ( - - {i18n.translate('xpack.transform.stepDefineForm.advancedEditorApplyButtonText', { - defaultMessage: 'Apply changes', + {!valid && ( + + + + {i18n.translate('xpack.transform.stepDefineForm.formHelp', { + defaultMessage: + 'Transforms are scalable and automated processes for pivoting. Choose at least one group-by and aggregation to get started.', })} - - )} - - - {!valid && ( - - - - {i18n.translate('xpack.transform.stepDefineForm.formHelp', { - defaultMessage: - 'Transforms are scalable and automated processes for pivoting. Choose at least one group-by and aggregation to get started.', - })} - - - )} - + +
+ )} + +
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.tsx index 5f1f265b5f5bb..bb900766483df 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.tsx @@ -57,75 +57,80 @@ export const StepDefineSummary: FC = ({ return ( - - {kibanaContext.currentSavedSearch !== undefined && - kibanaContext.currentSavedSearch.id === undefined && - typeof searchString === 'string' && ( - - - {kibanaContext.currentIndexPattern.title} - - {useCodeBlock === false && displaySearch !== emptySearch && ( +
+ + {kibanaContext.currentSavedSearch !== undefined && + kibanaContext.currentSavedSearch.id === undefined && + typeof searchString === 'string' && ( + - {displaySearch} + {kibanaContext.currentIndexPattern.title} - )} - {useCodeBlock === true && displaySearch !== emptySearch && ( - - - {displaySearch} - - - )} - - )} + {displaySearch} + + )} + {useCodeBlock === true && displaySearch !== emptySearch && ( + + + {displaySearch} + + + )} + + )} - {kibanaContext.currentSavedSearch !== undefined && - kibanaContext.currentSavedSearch.id !== undefined && ( - - {kibanaContext.currentSavedSearch.title} - - )} + {kibanaContext.currentSavedSearch !== undefined && + kibanaContext.currentSavedSearch.id !== undefined && ( + + {kibanaContext.currentSavedSearch.title} + + )} - - - + + + - - - - + + + + +
diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx index be05ddc2838ea..aa917ca3b05ec 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx @@ -187,200 +187,217 @@ export const StepDetailsForm: SFC = React.memo(({ overrides = {}, onChang ]); return ( - - - setTransformId(e.target.value)} - aria-label={i18n.translate('xpack.transform.stepDetailsForm.transformIdInputAriaLabel', { - defaultMessage: 'Choose a unique transform ID.', +
+ + - - - setTransformDescription(e.target.value)} - aria-label={i18n.translate( - 'xpack.transform.stepDetailsForm.transformDescriptionInputAriaLabel', - { - defaultMessage: 'Choose an optional transform description.', - } - )} - /> - - - {i18n.translate('xpack.transform.stepDetailsForm.destinationIndexInvalidError', { - defaultMessage: 'Invalid destination index name.', - })} -
- - {i18n.translate( - 'xpack.transform.stepDetailsForm.destinationIndexInvalidErrorLink', - { - defaultMessage: 'Learn more about index name limitations.', - } - )} - - , - ] - } - > - setDestinationIndex(e.target.value)} - aria-label={i18n.translate( - 'xpack.transform.stepDetailsForm.destinationIndexInputAriaLabel', - { - defaultMessage: 'Choose a unique destination index name.', - } - )} - isInvalid={!indexNameEmpty && !indexNameValid} - /> -
- - + setTransformId(e.target.value)} + aria-label={i18n.translate( + 'xpack.transform.stepDetailsForm.transformIdInputAriaLabel', + { + defaultMessage: 'Choose a unique transform ID.', + } + )} + isInvalid={(!transformIdEmpty && !transformIdValid) || transformIdExists} + data-test-subj="transformIdInput" + /> + + setCreateIndexPattern(!createIndexPattern)} - /> - - - setContinuousModeEnabled(!isContinuousModeEnabled)} - disabled={isContinuousModeAvailable === false} - /> - - {isContinuousModeEnabled && ( - - + setTransformDescription(e.target.value)} + aria-label={i18n.translate( + 'xpack.transform.stepDetailsForm.transformDescriptionInputAriaLabel', { - defaultMessage: 'Select the date field that can be used to identify new documents.', + defaultMessage: 'Choose an optional transform description.', } )} - > - ({ text }))} - value={continuousModeDateField} - onChange={e => setContinuousModeDateField(e.target.value)} - /> - - + + + {i18n.translate('xpack.transform.stepDetailsForm.destinationIndexInvalidError', { + defaultMessage: 'Invalid destination index name.', + })} +
+ + {i18n.translate( + 'xpack.transform.stepDetailsForm.destinationIndexInvalidErrorLink', + { + defaultMessage: 'Learn more about index name limitations.', + } + )} + +
, + ] + } + > + setDestinationIndex(e.target.value)} + aria-label={i18n.translate( + 'xpack.transform.stepDetailsForm.destinationIndexInputAriaLabel', { - defaultMessage: 'Time delay between current time and latest input data time.', + defaultMessage: 'Choose a unique destination index name.', } )} - > - setContinuousModeDelay(e.target.value)} - aria-label={i18n.translate( - 'xpack.transform.stepDetailsForm.continuousModeAriaLabel', + isInvalid={!indexNameEmpty && !indexNameValid} + data-test-subj="transformDestinationIndexInput" + /> + + + setCreateIndexPattern(!createIndexPattern)} + data-test-subj="transformCreateIndexPatternSwitch" + /> + + + setContinuousModeEnabled(!isContinuousModeEnabled)} + disabled={isContinuousModeAvailable === false} + data-test-subj="transformContinuousModeSwitch" + /> + + {isContinuousModeEnabled && ( + + + ({ text }))} + value={continuousModeDateField} + onChange={e => setContinuousModeDateField(e.target.value)} + data-test-subj="transformContinuousDateFieldSelect" + /> + + - - - )} -
+ error={ + !isContinuousModeDelayValid && [ + i18n.translate('xpack.transform.stepDetailsForm.continuousModeDelayError', { + defaultMessage: 'Invalid delay format', + }), + ] + } + helpText={i18n.translate( + 'xpack.transform.stepDetailsForm.continuousModeDelayHelpText', + { + defaultMessage: 'Time delay between current time and latest input data time.', + } + )} + > + setContinuousModeDelay(e.target.value)} + aria-label={i18n.translate( + 'xpack.transform.stepDetailsForm.continuousModeAriaLabel', + { + defaultMessage: 'Choose a delay.', + } + )} + isInvalid={!isContinuousModeDelayValid} + data-test-subj="transformContinuousDelayInput" + /> + + + )} + +
); }); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_summary.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_summary.tsx index 1f2280fa4fb19..ddb34dfa3f87d 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_summary.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_summary.tsx @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { Fragment, SFC } from 'react'; +import React, { SFC } from 'react'; import { i18n } from '@kbn/i18n'; @@ -33,7 +33,7 @@ export const StepDetailsSummary: SFC = React.memo( : ''; return ( - +
= React.memo( )} - +
); } ); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/wizard_nav/wizard_nav.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/wizard_nav/wizard_nav.tsx index 78273d825c152..bfdfcbf696d35 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/wizard_nav/wizard_nav.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/wizard_nav/wizard_nav.tsx @@ -27,7 +27,13 @@ export const WizardNav: SFC = ({ {previous && ( - + {i18n.translate('xpack.transform.wizard.previousStepButton', { defaultMessage: 'Previous', })} @@ -36,7 +42,13 @@ export const WizardNav: SFC = ({ )} {next && ( - + {i18n.translate('xpack.transform.wizard.nextStepButton', { defaultMessage: 'Next', })} diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/__snapshots__/transform_list.test.tsx.snap b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/__snapshots__/transform_list.test.tsx.snap index 1ddbbbb2be8e0..e7a5e027e6f8d 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/__snapshots__/transform_list.test.tsx.snap +++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/__snapshots__/transform_list.test.tsx.snap @@ -9,6 +9,7 @@ exports[`Transform: Transform List Minimal initialization 1`] actions={ Array [ diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/columns.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/columns.tsx index 700b659028b7e..19ab74cc9ed85 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/columns.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/columns.tsx @@ -116,29 +116,34 @@ export const getColumns = ( }) } iconType={expandedRowItemIds.includes(item.config.id) ? 'arrowUp' : 'arrowDown'} + data-test-subj="transformListRowDetailsToggle" /> ), }, { field: TRANSFORM_LIST_COLUMN.ID, + 'data-test-subj': 'transformListColumnId', name: 'ID', sortable: true, truncateText: true, }, { field: TRANSFORM_LIST_COLUMN.DESCRIPTION, + 'data-test-subj': 'transformListColumnDescription', name: i18n.translate('xpack.transform.description', { defaultMessage: 'Description' }), sortable: true, truncateText: true, }, { field: TRANSFORM_LIST_COLUMN.CONFIG_SOURCE_INDEX, + 'data-test-subj': 'transformListColumnSourceIndex', name: i18n.translate('xpack.transform.sourceIndex', { defaultMessage: 'Source index' }), sortable: true, truncateText: true, }, { field: TRANSFORM_LIST_COLUMN.CONFIG_DEST_INDEX, + 'data-test-subj': 'transformListColumnDestinationIndex', name: i18n.translate('xpack.transform.destinationIndex', { defaultMessage: 'Destination index', }), @@ -147,6 +152,7 @@ export const getColumns = ( }, { name: i18n.translate('xpack.transform.status', { defaultMessage: 'Status' }), + 'data-test-subj': 'transformListColumnStatus', sortable: (item: TransformListRow) => item.stats.state, truncateText: true, render(item: TransformListRow) { @@ -156,6 +162,7 @@ export const getColumns = ( }, { name: i18n.translate('xpack.transform.mode', { defaultMessage: 'Mode' }), + 'data-test-subj': 'transformListColumnMode', sortable: (item: TransformListRow) => item.mode, truncateText: true, render(item: TransformListRow) { @@ -167,6 +174,7 @@ export const getColumns = ( }, { name: i18n.translate('xpack.transform.progress', { defaultMessage: 'Progress' }), + 'data-test-subj': 'transformListColumnProgress', sortable: (item: TransformListRow) => getTransformProgress(item) || 0, truncateText: true, render(item: TransformListRow) { @@ -183,7 +191,13 @@ export const getColumns = ( {isBatchTransform && ( - + {progress}% diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx index c6bae80e6de96..02abadb85fbd0 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/components/transform_list/transform_list.tsx @@ -216,7 +216,11 @@ export const TransformList: FC = ({ } actions={[ - + {i18n.translate('xpack.transform.list.emptyPromptButtonText', { defaultMessage: 'Create your first transform', })} @@ -371,7 +375,7 @@ export const TransformList: FC = ({ }; return ( - +
= ({ itemIdToExpandedRowMap={itemIdToExpandedRowMap} onTableChange={onTableChange} pagination={pagination} + rowProps={item => ({ + 'data-test-subj': `transformListRow row-${item.id}`, + })} selection={selection} sorting={sorting} search={search} - data-test-subj="transformTableTransforms" + data-test-subj={ + isLoading || transformsLoading + ? 'transformListTable loading' + : 'transformListTable loaded' + } /> - +
); }; diff --git a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx index 2e0e930990e6e..2bd03df9957d2 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/transform_management/transform_management_section.tsx @@ -138,7 +138,11 @@ export const TransformManagement: FC = () => { {isSearchSelectionVisible && ( - + diff --git a/x-pack/test/functional/apps/transform/creation.ts b/x-pack/test/functional/apps/transform/creation.ts new file mode 100644 index 0000000000000..3ab17c0d90a83 --- /dev/null +++ b/x-pack/test/functional/apps/transform/creation.ts @@ -0,0 +1,229 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +interface GroupByEntry { + identifier: string; + label: string; + intervalLabel?: string; +} + +export default function({ getService }: FtrProviderContext) { + const esArchiver = getService('esArchiver'); + const transform = getService('transform'); + + describe('creation', function() { + this.tags(['smoke']); + before(async () => { + await esArchiver.load('ml/ecommerce'); + }); + + after(async () => { + await esArchiver.unload('ml/ecommerce'); + await transform.api.cleanTransformIndices(); + }); + + const testDataList = [ + { + suiteTitle: 'batch transform with terms+date_histogram groups and avg agg', + source: 'ecommerce', + groupByEntries: [ + { + identifier: 'terms(category.keyword)', + label: 'category.keyword', + } as GroupByEntry, + { + identifier: 'date_histogram(order_date)', + label: 'order_date', + intervalLabel: '1m', + } as GroupByEntry, + ], + aggregationEntries: [ + { + identifier: 'avg(products.base_price)', + label: 'products.base_price.avg', + }, + ], + transformId: `ec_1_${Date.now()}`, + transformDescription: + 'ecommerce batch transform with groups terms(category.keyword) + date_histogram(order_date) 1m and aggregation avg(products.base_price)', + get destinationIndex(): string { + return `dest_${this.transformId}`; + }, + expected: { + row: { + status: 'stopped', + mode: 'batch', + progress: '100', + }, + }, + }, + ]; + + for (const testData of testDataList) { + describe(`${testData.suiteTitle}`, function() { + after(async () => { + await transform.api.deleteIndices(testData.destinationIndex); + }); + + it('loads the home page', async () => { + await transform.navigation.navigateTo(); + await transform.management.assertTransformListPageExists(); + }); + + it('displays the stats bar', async () => { + await transform.management.assertTransformStatsBarExists(); + }); + + it('loads the source selection modal', async () => { + await transform.management.startTransformCreation(); + }); + + it('selects the source data', async () => { + await transform.sourceSelection.selectSource(testData.source); + }); + + it('displays the define pivot step', async () => { + await transform.wizard.assertDefineStepActive(); + }); + + it('loads the source index preview', async () => { + await transform.wizard.assertSourceIndexPreviewLoaded(); + }); + + it('displays an empty pivot preview', async () => { + await transform.wizard.assertPivotPreviewEmpty(); + }); + + it('displays the query input', async () => { + await transform.wizard.assertQueryInputExists(); + await transform.wizard.assertQueryValue(''); + }); + + it('displays the advanced query editor switch', async () => { + await transform.wizard.assertAdvancedQueryEditorSwitchExists(); + await transform.wizard.assertAdvancedQueryEditorSwitchCheckState(false); + }); + + it('adds the group by entries', async () => { + for (const [index, entry] of testData.groupByEntries.entries()) { + await transform.wizard.assertGroupByInputExists(); + await transform.wizard.assertGroupByInputValue([]); + await transform.wizard.addGroupByEntry( + index, + entry.identifier, + entry.label, + entry.intervalLabel + ); + } + }); + + it('adds the aggregation entries', async () => { + for (const [index, agg] of testData.aggregationEntries.entries()) { + await transform.wizard.assertAggregationInputExists(); + await transform.wizard.assertAggregationInputValue([]); + await transform.wizard.addAggregationEntry(index, agg.identifier, agg.label); + } + }); + + it('displays the advanced pivot editor switch', async () => { + await transform.wizard.assertAdvancedPivotEditorSwitchExists(); + await transform.wizard.assertAdvancedPivotEditorSwitchCheckState(false); + }); + + it('loads the pivot preview', async () => { + await transform.wizard.assertPivotPreviewLoaded(); + }); + + it('loads the details step', async () => { + await transform.wizard.advanceToDetailsStep(); + }); + + it('inputs the transform id', async () => { + await transform.wizard.assertTransformIdInputExists(); + await transform.wizard.assertTransformIdValue(''); + await transform.wizard.setTransformId(testData.transformId); + }); + + it('inputs the transform description', async () => { + await transform.wizard.assertTransformDescriptionInputExists(); + await transform.wizard.assertTransformDescriptionValue(''); + await transform.wizard.setTransformDescription(testData.transformDescription); + }); + + it('inputs the destination index', async () => { + await transform.wizard.assertDestinationIndexInputExists(); + await transform.wizard.assertDestinationIndexValue(''); + await transform.wizard.setDestinationIndex(testData.destinationIndex); + }); + + it('displays the create index pattern switch', async () => { + await transform.wizard.assertCreateIndexPatternSwitchExists(); + await transform.wizard.assertCreateIndexPatternSwitchCheckState(true); + }); + + it('displays the continuous mode switch', async () => { + await transform.wizard.assertContinuousModeSwitchExists(); + await transform.wizard.assertContinuousModeSwitchCheckState(false); + }); + + it('loads the create step', async () => { + await transform.wizard.advanceToCreateStep(); + }); + + it('displays the create and start button', async () => { + await transform.wizard.assertCreateAndStartButtonExists(); + }); + + it('displays the create button', async () => { + await transform.wizard.assertCreateButtonExists(); + }); + + it('displays the copy to clipboard button', async () => { + await transform.wizard.assertCreateAndStartButtonExists(); + }); + + it('creates the transform', async () => { + await transform.wizard.createTransform(); + }); + + it('starts the transform and finishes processing', async () => { + await transform.wizard.startTransform(); + await transform.wizard.waitForProgressBarComplete(); + }); + + it('returns to the management page', async () => { + await transform.wizard.returnToManagement(); + }); + + it('displays the transforms table', async () => { + await transform.management.assertTransformsTableExists(); + }); + + it('displays the created transform in the transform list', async () => { + await transform.table.refreshTransformList(); + await transform.table.filterWithSearchString(testData.transformId); + const rows = await transform.table.parseTransformTable(); + expect(rows.filter(row => row.id === testData.transformId)).to.have.length(1); + }); + + it('job creation displays details for the created job in the job list', async () => { + await transform.table.assertTransformRowFields(testData.transformId, { + id: testData.transformId, + description: testData.transformDescription, + sourceIndex: testData.source, + destinationIndex: testData.destinationIndex, + status: testData.expected.row.status, + mode: testData.expected.row.mode, + progress: testData.expected.row.progress, + }); + }); + }); + } + }); +} diff --git a/x-pack/test/functional/apps/transform/index.ts b/x-pack/test/functional/apps/transform/index.ts new file mode 100644 index 0000000000000..adee997905a31 --- /dev/null +++ b/x-pack/test/functional/apps/transform/index.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function({ loadTestFile }: FtrProviderContext) { + describe('transform', function() { + this.tags(['ciGroup9', 'transform']); + + loadTestFile(require.resolve('./creation')); + }); +} diff --git a/x-pack/test/functional/config.js b/x-pack/test/functional/config.js index c5812e6ba277c..6e45c4040c7f0 100644 --- a/x-pack/test/functional/config.js +++ b/x-pack/test/functional/config.js @@ -55,6 +55,7 @@ export default async function ({ readConfigFile }) { resolve(__dirname, './apps/snapshot_restore'), resolve(__dirname, './apps/cross_cluster_replication'), resolve(__dirname, './apps/remote_clusters'), + resolve(__dirname, './apps/transform'), // This license_management file must be last because it is destructive. resolve(__dirname, './apps/license_management'), ], @@ -192,6 +193,10 @@ export default async function ({ readConfigFile }) { pathname: '/app/kibana', hash: '/management/elasticsearch/watcher/watches/', }, + transform: { + pathname: '/app/kibana/', + hash: '/management/elasticsearch/transform' + } }, // choose where esArchiver should load archives from diff --git a/x-pack/test/functional/services/index.ts b/x-pack/test/functional/services/index.ts index 9a256b3bbb141..a8e5749004afe 100644 --- a/x-pack/test/functional/services/index.ts +++ b/x-pack/test/functional/services/index.ts @@ -47,6 +47,7 @@ import { UptimeProvider } from './uptime'; import { InfraSourceConfigurationFormProvider } from './infra_source_configuration_form'; import { InfraLogStreamProvider } from './infra_log_stream'; import { MachineLearningProvider } from './ml'; +import { TransformProvider } from './transform'; import { SecurityServiceProvider, SpacesServiceProvider } from '../../common/services'; @@ -89,4 +90,5 @@ export const services = { infraSourceConfigurationForm: InfraSourceConfigurationFormProvider, infraLogStream: InfraLogStreamProvider, ml: MachineLearningProvider, + transform: TransformProvider, }; diff --git a/x-pack/test/functional/services/transform.ts b/x-pack/test/functional/services/transform.ts new file mode 100644 index 0000000000000..f495626bac9b8 --- /dev/null +++ b/x-pack/test/functional/services/transform.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../ftr_provider_context'; + +import { + TransformAPIProvider, + TransformManagementProvider, + TransformNavigationProvider, + TransformSourceSelectionProvider, + TransformTableProvider, + TransformWizardProvider, +} from './transform_ui'; + +export function TransformProvider(context: FtrProviderContext) { + const api = TransformAPIProvider(context); + const management = TransformManagementProvider(context); + const navigation = TransformNavigationProvider(context); + const sourceSelection = TransformSourceSelectionProvider(context); + const table = TransformTableProvider(context); + const wizard = TransformWizardProvider(context); + + return { + api, + management, + navigation, + sourceSelection, + table, + wizard, + }; +} diff --git a/x-pack/test/functional/services/transform_ui/api.ts b/x-pack/test/functional/services/transform_ui/api.ts new file mode 100644 index 0000000000000..9050b5944dee3 --- /dev/null +++ b/x-pack/test/functional/services/transform_ui/api.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export function TransformAPIProvider({ getService }: FtrProviderContext) { + const es = getService('es'); + const log = getService('log'); + const retry = getService('retry'); + + return { + async deleteIndices(indices: string) { + log.debug(`Deleting indices: '${indices}'...`); + if ((await es.indices.exists({ index: indices, allowNoIndices: false })) === false) { + log.debug(`Indices '${indices}' don't exist. Nothing to delete.`); + return; + } + + const deleteResponse = await es.indices.delete({ + index: indices, + }); + expect(deleteResponse) + .to.have.property('acknowledged') + .eql(true, 'Response for delete request should be acknowledged'); + + await retry.waitForWithTimeout(`'${indices}' indices to be deleted`, 30 * 1000, async () => { + if ((await es.indices.exists({ index: indices, allowNoIndices: false })) === false) { + return true; + } else { + throw new Error(`expected indices '${indices}' to be deleted`); + } + }); + }, + + async cleanTransformIndices() { + await this.deleteIndices('.transform-*'); + }, + }; +} diff --git a/x-pack/test/functional/services/transform_ui/index.ts b/x-pack/test/functional/services/transform_ui/index.ts new file mode 100644 index 0000000000000..5b5270a575028 --- /dev/null +++ b/x-pack/test/functional/services/transform_ui/index.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { TransformAPIProvider } from './api'; +export { TransformManagementProvider } from './management'; +export { TransformNavigationProvider } from './navigation'; +export { TransformSourceSelectionProvider } from './source_selection'; +export { TransformTableProvider } from './transform_table'; +export { TransformWizardProvider } from './wizard'; diff --git a/x-pack/test/functional/services/transform_ui/management.ts b/x-pack/test/functional/services/transform_ui/management.ts new file mode 100644 index 0000000000000..ffc581ad06d7b --- /dev/null +++ b/x-pack/test/functional/services/transform_ui/management.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export function TransformManagementProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + return { + async assertTransformListPageExists() { + await testSubjects.existOrFail('transformPageTransformList'); + }, + + async assertNoTransformsFoundMessageExists() { + await testSubjects.existOrFail('transformNoTransformsFound'); + }, + + async assertTransformsTableExists() { + await testSubjects.existOrFail('~transformListTable'); + }, + + async assertCreateNewTransformButtonExists() { + await testSubjects.existOrFail('transformButtonCreate'); + }, + + async assertTransformStatsBarExists() { + await testSubjects.existOrFail('transformStatsBar'); + }, + + async startTransformCreation() { + if (await testSubjects.exists('transformNoTransformsFound')) { + await testSubjects.click('transformCreateFirstButton'); + } else { + await testSubjects.click('transformButtonCreate'); + } + await testSubjects.existOrFail('transformSelectSourceModal'); + }, + }; +} diff --git a/x-pack/test/functional/services/transform_ui/navigation.ts b/x-pack/test/functional/services/transform_ui/navigation.ts new file mode 100644 index 0000000000000..a26cc90012694 --- /dev/null +++ b/x-pack/test/functional/services/transform_ui/navigation.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export function TransformNavigationProvider({ getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['common']); + + return { + async navigateTo() { + return await PageObjects.common.navigateToApp('transform'); + }, + }; +} diff --git a/x-pack/test/functional/services/transform_ui/source_selection.ts b/x-pack/test/functional/services/transform_ui/source_selection.ts new file mode 100644 index 0000000000000..d2ef2c67f0004 --- /dev/null +++ b/x-pack/test/functional/services/transform_ui/source_selection.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export function TransformSourceSelectionProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + return { + async assertSourceListContainsEntry(sourceName: string) { + await testSubjects.existOrFail(`savedObjectTitle${sourceName}`); + }, + + async filterSourceSelection(sourceName: string) { + await testSubjects.setValue('savedObjectFinderSearchInput', sourceName, { + clearWithKeyboard: true, + }); + await this.assertSourceListContainsEntry(sourceName); + }, + + async selectSource(sourceName: string) { + await this.filterSourceSelection(sourceName); + await testSubjects.clickWhenNotDisabled(`savedObjectTitle${sourceName}`); + await testSubjects.existOrFail('transformPageCreateTransform'); + }, + }; +} diff --git a/x-pack/test/functional/services/transform_ui/transform_table.ts b/x-pack/test/functional/services/transform_ui/transform_table.ts new file mode 100644 index 0000000000000..b9eff5e2b2435 --- /dev/null +++ b/x-pack/test/functional/services/transform_ui/transform_table.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export function TransformTableProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + + return new (class TransformTable { + public async parseTransformTable() { + const table = await testSubjects.find('~transformListTable'); + const $ = await table.parseDomContent(); + const rows = []; + + for (const tr of $.findTestSubjects('~transformListRow').toArray()) { + const $tr = $(tr); + + rows.push({ + id: $tr + .findTestSubject('transformListColumnId') + .find('.euiTableCellContent') + .text() + .trim(), + description: $tr + .findTestSubject('transformListColumnDescription') + .find('.euiTableCellContent') + .text() + .trim(), + sourceIndex: $tr + .findTestSubject('transformListColumnSourceIndex') + .find('.euiTableCellContent') + .text() + .trim(), + destinationIndex: $tr + .findTestSubject('transformListColumnDestinationIndex') + .find('.euiTableCellContent') + .text() + .trim(), + status: $tr + .findTestSubject('transformListColumnStatus') + .find('.euiTableCellContent') + .text() + .trim(), + mode: $tr + .findTestSubject('transformListColumnMode') + .find('.euiTableCellContent') + .text() + .trim(), + progress: $tr + .findTestSubject('transformListColumnProgress') + .findTestSubject('transformListProgress') + .attr('value'), + }); + } + + return rows; + } + + public async refreshTransformList() { + await testSubjects.click('transformRefreshTransformListButton'); + await this.waitForTransformsToLoad(); + } + + public async waitForTransformsToLoad() { + await testSubjects.existOrFail('~transformListTable', { timeout: 60 * 1000 }); + await testSubjects.existOrFail('transformListTable loaded', { timeout: 30 * 1000 }); + } + + public async filterWithSearchString(filter: string) { + await this.waitForTransformsToLoad(); + const tableListContainer = await testSubjects.find('transformListTableContainer'); + const searchBarInput = await tableListContainer.findByClassName('euiFieldSearch'); + await searchBarInput.clearValueWithKeyboard(); + await searchBarInput.type(filter); + } + + public async assertTransformRowFields(transformId: string, expectedRow: object) { + const rows = await this.parseTransformTable(); + const transformRow = rows.filter(row => row.id === transformId)[0]; + expect(transformRow).to.eql(expectedRow); + } + })(); +} diff --git a/x-pack/test/functional/services/transform_ui/wizard.ts b/x-pack/test/functional/services/transform_ui/wizard.ts new file mode 100644 index 0000000000000..c80aa62cd4912 --- /dev/null +++ b/x-pack/test/functional/services/transform_ui/wizard.ts @@ -0,0 +1,360 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ +import expect from '@kbn/expect'; + +import { FtrProviderContext } from '../../ftr_provider_context'; + +export function TransformWizardProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const comboBox = getService('comboBox'); + const retry = getService('retry'); + + return { + async clickNextButton() { + await testSubjects.existOrFail('transformWizardNavButtonNext'); + await testSubjects.clickWhenNotDisabled('transformWizardNavButtonNext'); + }, + + async assertDefineStepActive() { + await testSubjects.existOrFail('transformStepDefineForm'); + }, + + async assertDefineSummaryExists() { + await testSubjects.existOrFail('transformStepDefineSummary'); + }, + + async assertDetailsStepActive() { + await testSubjects.existOrFail('transformStepDetailsForm'); + }, + + async assertDetailsSummaryExists() { + await testSubjects.existOrFail('transformStepDetailsSummary'); + }, + + async assertCreateStepActive() { + await testSubjects.existOrFail('transformStepCreateForm'); + }, + + async advanceToDetailsStep() { + await this.clickNextButton(); + await this.assertDetailsStepActive(); + await this.assertDefineSummaryExists(); + }, + + async advanceToCreateStep() { + await this.clickNextButton(); + await this.assertCreateStepActive(); + await this.assertDefineSummaryExists(); + await this.assertDetailsSummaryExists(); + }, + + async assertSourceIndexPreviewExists(subSelector?: string) { + let selector = 'transformSourceIndexPreview'; + if (subSelector !== undefined) { + selector = `${selector} ${subSelector}`; + } else { + selector = `~${selector}`; + } + await testSubjects.existOrFail(selector); + }, + + async assertSourceIndexPreviewLoaded() { + await this.assertSourceIndexPreviewExists('loaded'); + }, + + async assertPivotPreviewExists(subSelector?: string) { + let selector = 'transformPivotPreview'; + if (subSelector !== undefined) { + selector = `${selector} ${subSelector}`; + } else { + selector = `~${selector}`; + } + await testSubjects.existOrFail(selector); + }, + + async assertPivotPreviewLoaded() { + await this.assertPivotPreviewExists('loaded'); + }, + + async assertPivotPreviewEmpty() { + await this.assertPivotPreviewExists('empty'); + }, + + async assertQueryInputExists() { + await testSubjects.existOrFail('tarnsformQueryInput'); + }, + + async assertQueryValue(expectedQuery: string) { + const actualQuery = await testSubjects.getVisibleText('tarnsformQueryInput'); + expect(actualQuery).to.eql( + expectedQuery, + `Query input text should be '${expectedQuery}' (got ${actualQuery})` + ); + }, + + async assertAdvancedQueryEditorSwitchExists() { + await testSubjects.existOrFail(`transformAdvancedQueryEditorSwitch`, { allowHidden: true }); + }, + + async assertAdvancedQueryEditorSwitchCheckState(expectedCheckState: boolean) { + const actualCheckState = + (await testSubjects.getAttribute('transformAdvancedQueryEditorSwitch', 'aria-checked')) === + 'true'; + expect(actualCheckState).to.eql( + expectedCheckState, + `Advanced query editor switch check state should be ${expectedCheckState} (got ${actualCheckState})` + ); + }, + + async assertGroupByInputExists() { + await testSubjects.existOrFail('transformGroupBySelection > comboBoxInput'); + }, + + async assertGroupByInputValue(expectedIdentifier: string[]) { + await retry.tryForTime(2000, async () => { + const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions( + 'transformGroupBySelection > comboBoxInput' + ); + expect(comboBoxSelectedOptions).to.eql(expectedIdentifier); + }); + }, + + async assertGroupByEntryExists( + index: number, + expectedLabel: string, + expectedIntervalLabel?: string + ) { + await testSubjects.existOrFail(`transformGroupByEntry ${index}`); + + const actualLabel = await testSubjects.getVisibleText( + `transformGroupByEntry ${index} > transformGroupByEntryLabel` + ); + expect(actualLabel).to.eql( + expectedLabel, + `Label for group by entry ${index} should be '${expectedLabel}' (got '${actualLabel}')` + ); + + if (expectedIntervalLabel !== undefined) { + const actualIntervalLabel = await testSubjects.getVisibleText( + `transformGroupByEntry ${index} > transformGroupByEntryIntervalLabel` + ); + expect(actualIntervalLabel).to.eql( + expectedIntervalLabel, + `Label for group by entry ${index} should be '${expectedIntervalLabel}' (got '${actualIntervalLabel}')` + ); + } + }, + + async addGroupByEntry( + index: number, + identifier: string, + expectedLabel: string, + expectedIntervalLabel?: string + ) { + await comboBox.set('transformGroupBySelection > comboBoxInput', identifier); + await this.assertGroupByInputValue([]); + await this.assertGroupByEntryExists(index, expectedLabel, expectedIntervalLabel); + }, + + async assertAggregationInputExists() { + await testSubjects.existOrFail('transformAggregationSelection > comboBoxInput'); + }, + + async assertAggregationInputValue(expectedIdentifier: string[]) { + await retry.tryForTime(2000, async () => { + const comboBoxSelectedOptions = await comboBox.getComboBoxSelectedOptions( + 'transformAggregationSelection > comboBoxInput' + ); + expect(comboBoxSelectedOptions).to.eql(expectedIdentifier); + }); + }, + + async assertAggregationEntryExists(index: number, expectedLabel: string) { + await testSubjects.existOrFail(`transformAggregationEntry ${index}`); + + const actualLabel = await testSubjects.getVisibleText( + `transformAggregationEntry ${index} > transformAggregationEntryLabel` + ); + expect(actualLabel).to.eql( + expectedLabel, + `Label for aggregation entry ${index} should be '${expectedLabel}' (got '${actualLabel}')` + ); + }, + + async addAggregationEntry(index: number, identifier: string, expectedLabel: string) { + await comboBox.set('transformAggregationSelection > comboBoxInput', identifier); + await this.assertAggregationInputValue([]); + await this.assertAggregationEntryExists(index, expectedLabel); + }, + + async assertAdvancedPivotEditorSwitchExists() { + await testSubjects.existOrFail(`transformAdvancedPivotEditorSwitch`, { allowHidden: true }); + }, + + async assertAdvancedPivotEditorSwitchCheckState(expectedCheckState: boolean) { + const actualCheckState = + (await testSubjects.getAttribute('transformAdvancedPivotEditorSwitch', 'aria-checked')) === + 'true'; + expect(actualCheckState).to.eql( + expectedCheckState, + `Advanced pivot editor switch check state should be ${expectedCheckState} (got ${actualCheckState})` + ); + }, + + async assertTransformIdInputExists() { + await testSubjects.existOrFail('transformIdInput'); + }, + + async assertTransformIdValue(expectedValue: string) { + const actualTransformId = await testSubjects.getAttribute('transformIdInput', 'value'); + expect(actualTransformId).to.eql( + expectedValue, + `Transform id input text should be ${expectedValue} (got ${actualTransformId})` + ); + }, + + async setTransformId(transformId: string) { + await testSubjects.setValue('transformIdInput', transformId, { clearWithKeyboard: true }); + await this.assertTransformIdValue(transformId); + }, + + async assertTransformDescriptionInputExists() { + await testSubjects.existOrFail('transformDescriptionInput'); + }, + + async assertTransformDescriptionValue(expectedValue: string) { + const actualTransformDescription = await testSubjects.getAttribute( + 'transformDescriptionInput', + 'value' + ); + expect(actualTransformDescription).to.eql( + expectedValue, + `Transform description input text should be ${expectedValue} (got ${actualTransformDescription})` + ); + }, + + async setTransformDescription(transformDescription: string) { + await testSubjects.setValue('transformDescriptionInput', transformDescription, { + clearWithKeyboard: true, + }); + await this.assertTransformDescriptionValue(transformDescription); + }, + + async assertDestinationIndexInputExists() { + await testSubjects.existOrFail('transformDestinationIndexInput'); + }, + + async assertDestinationIndexValue(expectedValue: string) { + const actualDestinationIndex = await testSubjects.getAttribute( + 'transformDestinationIndexInput', + 'value' + ); + expect(actualDestinationIndex).to.eql( + expectedValue, + `Destination index input text should be ${expectedValue} (got ${actualDestinationIndex})` + ); + }, + + async setDestinationIndex(destinationIndex: string) { + await testSubjects.setValue('transformDestinationIndexInput', destinationIndex, { + clearWithKeyboard: true, + }); + await this.assertDestinationIndexValue(destinationIndex); + }, + + async assertCreateIndexPatternSwitchExists() { + await testSubjects.existOrFail(`transformCreateIndexPatternSwitch`, { allowHidden: true }); + }, + + async assertCreateIndexPatternSwitchCheckState(expectedCheckState: boolean) { + const actualCheckState = + (await testSubjects.getAttribute('transformCreateIndexPatternSwitch', 'aria-checked')) === + 'true'; + expect(actualCheckState).to.eql( + expectedCheckState, + `Create index pattern switch check state should be ${expectedCheckState} (got ${actualCheckState})` + ); + }, + + async assertContinuousModeSwitchExists() { + await testSubjects.existOrFail(`transformContinuousModeSwitch`, { allowHidden: true }); + }, + + async assertContinuousModeSwitchCheckState(expectedCheckState: boolean) { + const actualCheckState = + (await testSubjects.getAttribute('transformContinuousModeSwitch', 'aria-checked')) === + 'true'; + expect(actualCheckState).to.eql( + expectedCheckState, + `Continuous mode switch check state should be ${expectedCheckState} (got ${actualCheckState})` + ); + }, + + async assertCreateAndStartButtonExists() { + await testSubjects.existOrFail(`transformWizardCreateAndStartButton`); + }, + + async assertCreateButtonExists() { + await testSubjects.existOrFail(`transformWizardCreateButton`); + }, + + async assertCopyToClipboardButtonExists() { + await testSubjects.existOrFail(`transformWizardCopyToClipboardButton`); + }, + + async assertStartButtonExists() { + await testSubjects.existOrFail(`transformWizardStartButton`); + }, + + async assertManagementCardExists() { + await testSubjects.existOrFail(`transformWizardCardManagement`); + }, + + async returnToManagement() { + await testSubjects.click('transformWizardCardManagement'); + await testSubjects.existOrFail('transformPageTransformList'); + }, + + async assertDiscoverCardExists() { + await testSubjects.existOrFail(`transformWizardCardDiscover`); + }, + + async assertProgressbarExists() { + await testSubjects.existOrFail(`transformWizardProgressBar`); + }, + + async waitForProgressBarComplete() { + await retry.tryForTime(60 * 1000, async () => { + const actualValue = await testSubjects.getAttribute('transformWizardProgressBar', 'value'); + if (actualValue === '100') { + return true; + } else { + throw new Error(`Expected progress bar value to be 100 (got ${actualValue})`); + } + }); + }, + + async createTransform() { + await testSubjects.click('transformWizardCreateButton'); + await this.assertStartButtonExists(); + await this.assertManagementCardExists(); + expect(await testSubjects.isEnabled('transformWizardCreateButton')).to.eql( + false, + 'The create button should not be enabled any more' + ); + }, + + async startTransform() { + await testSubjects.click('transformWizardStartButton'); + await this.assertDiscoverCardExists(); + expect(await testSubjects.isEnabled('transformWizardStartButton')).to.eql( + false, + 'The start button should not be enabled any more' + ); + await this.assertProgressbarExists(); + }, + }; +} From 5019991aaa206e7597f8fdf3db9949964509f586 Mon Sep 17 00:00:00 2001 From: Alexey Antonov Date: Mon, 18 Nov 2019 16:29:42 +0300 Subject: [PATCH 13/17] plugins/data - cleanup JEST: remove @kbn/expect (#50886) (#50899) --- .../public/query/timefilter/get_time.test.ts | 4 +- .../lib/diff_time_picker_vals.test.ts | 11 +- .../query/timefilter/timefilter.test.ts | 105 +++++++++--------- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/plugins/data/public/query/timefilter/get_time.test.ts b/src/plugins/data/public/query/timefilter/get_time.test.ts index 0602f61d003ec..a1eb36c2ee028 100644 --- a/src/plugins/data/public/query/timefilter/get_time.test.ts +++ b/src/plugins/data/public/query/timefilter/get_time.test.ts @@ -17,8 +17,6 @@ * under the License. */ -// @ts-ignore -import expect from '@kbn/expect'; import moment from 'moment'; import sinon from 'sinon'; import { Filter, getTime } from './get_time'; @@ -46,7 +44,7 @@ describe('get_time', () => { } as any, { from: 'now-60y', to: 'now' } ) as Filter; - expect(filter.range.date).to.eql({ + expect(filter.range.date).toEqual({ gte: '1940-02-01T00:00:00.000Z', lte: '2000-02-01T00:00:00.000Z', format: 'strict_date_optional_time', diff --git a/src/plugins/data/public/query/timefilter/lib/diff_time_picker_vals.test.ts b/src/plugins/data/public/query/timefilter/lib/diff_time_picker_vals.test.ts index 659e4d69f1ea0..f29ac74ac1e83 100644 --- a/src/plugins/data/public/query/timefilter/lib/diff_time_picker_vals.test.ts +++ b/src/plugins/data/public/query/timefilter/lib/diff_time_picker_vals.test.ts @@ -18,7 +18,6 @@ */ import moment from 'moment'; -import expect from '@kbn/expect'; import { areTimeRangesDifferent } from './diff_time_picker_vals'; describe('Diff Time Picker Values', () => { @@ -35,7 +34,7 @@ describe('Diff Time Picker Values', () => { } ); - expect(diff).to.be(false); + expect(diff).toBe(false); }); test('knows a difference', () => { const diff = areTimeRangesDifferent( @@ -49,7 +48,7 @@ describe('Diff Time Picker Values', () => { } ); - expect(diff).to.be(true); + expect(diff).toBe(true); }); }); @@ -66,7 +65,7 @@ describe('Diff Time Picker Values', () => { } ); - expect(diff).to.be(true); + expect(diff).toBe(true); }); }); @@ -86,7 +85,7 @@ describe('Diff Time Picker Values', () => { } ); - expect(diff).to.be(false); + expect(diff).toBe(false); }); test('fails if any to or from is different', () => { @@ -105,7 +104,7 @@ describe('Diff Time Picker Values', () => { } ); - expect(diff).to.be(true); + expect(diff).toBe(true); }); }); }); diff --git a/src/plugins/data/public/query/timefilter/timefilter.test.ts b/src/plugins/data/public/query/timefilter/timefilter.test.ts index cd904c76ac4d9..1280664ac8389 100644 --- a/src/plugins/data/public/query/timefilter/timefilter.test.ts +++ b/src/plugins/data/public/query/timefilter/timefilter.test.ts @@ -30,7 +30,6 @@ jest.mock('./lib/parse_querystring', () => ({ })); import sinon from 'sinon'; -import expect from '@kbn/expect'; import moment from 'moment'; import { Timefilter } from './timefilter'; import { Subscription } from 'rxjs'; @@ -79,7 +78,7 @@ describe('setTime', () => { test('should update time', () => { timefilter.setTime({ from: '5', to: '10' }); - expect(timefilter.getTime()).to.eql({ + expect(timefilter.getTime()).toEqual({ from: '5', to: '10', }); @@ -92,31 +91,31 @@ describe('setTime', () => { to: '10', [unexpectedKey]: 'I should not be added to time state', } as TimeRange); - expect(timefilter.getTime()).not.to.have.property(unexpectedKey); + expect(timefilter.getTime()).not.toHaveProperty(unexpectedKey); }); test('should allow partial updates to time', () => { timefilter.setTime({ from: '5', to: '10' }); - expect(timefilter.getTime()).to.eql({ from: '5', to: '10' }); + expect(timefilter.getTime()).toEqual({ from: '5', to: '10' }); }); test('not emit anything if the time has not changed', () => { timefilter.setTime({ from: '0', to: '1' }); - expect(update.called).to.be(false); - expect(fetch.called).to.be(false); + expect(update.called).toBe(false); + expect(fetch.called).toBe(false); }); test('emit update and fetch if the time has changed', () => { timefilter.setTime({ from: '5', to: '10' }); - expect(update.called).to.be(true); - expect(fetch.called).to.be(true); + expect(update.called).toBe(true); + expect(fetch.called).toBe(true); }); test('should return strings and not moment objects', () => { const from = moment().subtract(15, 'minutes'); const to = moment(); timefilter.setTime({ to, from }); - expect(timefilter.getTime()).to.eql({ + expect(timefilter.getTime()).toEqual({ from: from.toISOString(), to: to.toISOString(), }); @@ -152,7 +151,7 @@ describe('setRefreshInterval', () => { test('should update refresh interval', () => { timefilter.setRefreshInterval({ pause: true, value: 10 }); - expect(timefilter.getRefreshInterval()).to.eql({ pause: true, value: 10 }); + expect(timefilter.getRefreshInterval()).toEqual({ pause: true, value: 10 }); }); test('should not add unexpected object keys to refreshInterval state', () => { @@ -162,90 +161,90 @@ describe('setRefreshInterval', () => { value: 10, [unexpectedKey]: 'I should not be added to refreshInterval state', } as RefreshInterval); - expect(timefilter.getRefreshInterval()).not.to.have.property(unexpectedKey); + expect(timefilter.getRefreshInterval()).not.toHaveProperty(unexpectedKey); }); test('should allow partial updates to refresh interval', () => { timefilter.setRefreshInterval({ value: 10 }); - expect(timefilter.getRefreshInterval()).to.eql({ pause: true, value: 10 }); + expect(timefilter.getRefreshInterval()).toEqual({ pause: true, value: 10 }); }); test('should not allow negative intervals', () => { timefilter.setRefreshInterval({ value: -10 }); - expect(timefilter.getRefreshInterval()).to.eql({ pause: true, value: 0 }); + expect(timefilter.getRefreshInterval()).toEqual({ pause: true, value: 0 }); }); test('should set pause to true when interval is zero', () => { timefilter.setRefreshInterval({ value: 0, pause: false }); - expect(timefilter.getRefreshInterval()).to.eql({ pause: true, value: 0 }); + expect(timefilter.getRefreshInterval()).toEqual({ pause: true, value: 0 }); }); test('not emit anything if nothing has changed', () => { timefilter.setRefreshInterval({ pause: false, value: 0 }); - expect(update.called).to.be(false); - expect(fetch.called).to.be(false); + expect(update.called).toBe(false); + expect(fetch.called).toBe(false); }); test('emit only an update when paused', () => { timefilter.setRefreshInterval({ pause: true, value: 5000 }); - expect(update.called).to.be(true); - expect(fetch.called).to.be(false); + expect(update.called).toBe(true); + expect(fetch.called).toBe(false); }); test('emit update, not fetch, when switching to value: 0', () => { timefilter.setRefreshInterval({ pause: false, value: 5000 }); - expect(update.calledOnce).to.be(true); - expect(fetch.calledOnce).to.be(true); + expect(update.calledOnce).toBe(true); + expect(fetch.calledOnce).toBe(true); timefilter.setRefreshInterval({ pause: false, value: 0 }); - expect(update.calledTwice).to.be(true); - expect(fetch.calledTwice).to.be(false); + expect(update.calledTwice).toBe(true); + expect(fetch.calledTwice).toBe(false); }); test('should emit update, not fetch, when moving from unpaused to paused', () => { timefilter.setRefreshInterval({ pause: false, value: 5000 }); - expect(update.calledOnce).to.be(true); - expect(fetch.calledOnce).to.be(true); + expect(update.calledOnce).toBe(true); + expect(fetch.calledOnce).toBe(true); timefilter.setRefreshInterval({ pause: true, value: 5000 }); - expect(update.calledTwice).to.be(true); - expect(fetch.calledTwice).to.be(false); + expect(update.calledTwice).toBe(true); + expect(fetch.calledTwice).toBe(false); }); test('should emit update and fetch when unpaused', () => { timefilter.setRefreshInterval({ pause: true, value: 5000 }); - expect(update.calledOnce).to.be(true); - expect(fetch.calledOnce).to.be(false); + expect(update.calledOnce).toBe(true); + expect(fetch.calledOnce).toBe(false); timefilter.setRefreshInterval({ pause: false, value: 5000 }); - expect(update.calledTwice).to.be(true); - expect(fetch.calledOnce).to.be(true); + expect(update.calledTwice).toBe(true); + expect(fetch.calledOnce).toBe(true); }); test('should start auto refresh when unpaused', () => { timefilter.setRefreshInterval({ pause: false, value: 1000 }); - expect(autoRefreshFetch.callCount).to.be(0); + expect(autoRefreshFetch.callCount).toBe(0); jest.advanceTimersByTime(1000); - expect(autoRefreshFetch.callCount).to.be(1); + expect(autoRefreshFetch.callCount).toBe(1); jest.advanceTimersByTime(1000); - expect(autoRefreshFetch.callCount).to.be(2); + expect(autoRefreshFetch.callCount).toBe(2); }); test('should stop auto refresh when paused', () => { timefilter.setRefreshInterval({ pause: true, value: 1000 }); - expect(autoRefreshFetch.callCount).to.be(0); + expect(autoRefreshFetch.callCount).toBe(0); jest.advanceTimersByTime(1000); - expect(autoRefreshFetch.callCount).to.be(0); + expect(autoRefreshFetch.callCount).toBe(0); }); test('should not keep old interval when updated', () => { timefilter.setRefreshInterval({ pause: false, value: 1000 }); - expect(autoRefreshFetch.callCount).to.be(0); + expect(autoRefreshFetch.callCount).toBe(0); jest.advanceTimersByTime(1000); - expect(autoRefreshFetch.callCount).to.be(1); + expect(autoRefreshFetch.callCount).toBe(1); timefilter.setRefreshInterval({ pause: false, value: 2000 }); jest.advanceTimersByTime(2000); - expect(autoRefreshFetch.callCount).to.be(2); + expect(autoRefreshFetch.callCount).toBe(2); }); }); @@ -264,14 +263,14 @@ describe('isTimeRangeSelectorEnabled', () => { test('should emit updated when disabled', () => { timefilter.disableTimeRangeSelector(); - expect(timefilter.isTimeRangeSelectorEnabled()).to.be(false); - expect(update.called).to.be(true); + expect(timefilter.isTimeRangeSelectorEnabled()).toBe(false); + expect(update.called).toBe(true); }); test('should emit updated when enabled', () => { timefilter.enableTimeRangeSelector(); - expect(timefilter.isTimeRangeSelectorEnabled()).to.be(true); - expect(update.called).to.be(true); + expect(timefilter.isTimeRangeSelectorEnabled()).toBe(true); + expect(update.called).toBe(true); }); }); @@ -290,14 +289,14 @@ describe('isAutoRefreshSelectorEnabled', () => { test('should emit updated when disabled', () => { timefilter.disableAutoRefreshSelector(); - expect(timefilter.isAutoRefreshSelectorEnabled()).to.be(false); - expect(update.called).to.be(true); + expect(timefilter.isAutoRefreshSelectorEnabled()).toBe(false); + expect(update.called).toBe(true); }); test('should emit updated when enabled', () => { timefilter.enableAutoRefreshSelector(); - expect(timefilter.isAutoRefreshSelectorEnabled()).to.be(true); - expect(update.called).to.be(true); + expect(timefilter.isAutoRefreshSelectorEnabled()).toBe(true); + expect(update.called).toBe(true); }); }); @@ -324,8 +323,10 @@ describe('calculateBounds', () => { stubNowTime(undefined); const result = timefilter.calculateBounds(timeRange); - expect(result.min && result.min.valueOf()).to.eql(clockNowTicks - fifteenMinutesInMilliseconds); - expect(result.max && result.max.valueOf()).to.eql(clockNowTicks); + expect(result.min && result.min.valueOf()).toEqual( + clockNowTicks - fifteenMinutesInMilliseconds + ); + expect(result.max && result.max.valueOf()).toEqual(clockNowTicks); }); test('uses forceNow string', () => { @@ -339,8 +340,10 @@ describe('calculateBounds', () => { const result = timefilter.calculateBounds(timeRange); const forceNowTicks = Date.parse(forceNowString); - expect(result.min && result.min.valueOf()).to.eql(forceNowTicks - fifteenMinutesInMilliseconds); - expect(result.max && result.max.valueOf()).to.eql(forceNowTicks); + expect(result.min && result.min.valueOf()).toEqual( + forceNowTicks - fifteenMinutesInMilliseconds + ); + expect(result.max && result.max.valueOf()).toEqual(forceNowTicks); }); test(`throws Error if forceNow can't be parsed`, () => { @@ -350,6 +353,6 @@ describe('calculateBounds', () => { }; stubNowTime('not_a_parsable_date'); - expect(() => timefilter.calculateBounds(timeRange)).to.throwError(); + expect(() => timefilter.calculateBounds(timeRange)).toThrowError(); }); }); From 7927447c702517e3704bc7ea392f60d32194fc6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20C=C3=B4t=C3=A9?= Date: Mon, 18 Nov 2019 09:27:50 -0500 Subject: [PATCH 14/17] Change URLs for support menu (#50700) (#50726) --- src/core/public/chrome/constants.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/public/chrome/constants.ts b/src/core/public/chrome/constants.ts index 3411f6f629a13..c8e53b38c618e 100644 --- a/src/core/public/chrome/constants.ts +++ b/src/core/public/chrome/constants.ts @@ -18,6 +18,6 @@ */ export const ELASTIC_SUPPORT_LINK = 'https://support.elastic.co/'; -export const KIBANA_FEEDBACK_LINK = 'https://www.elastic.co/kibana/feedback'; -export const KIBANA_ASK_ELASTIC_LINK = 'https://www.elastic.co/kibana/ask-elastic'; +export const KIBANA_FEEDBACK_LINK = 'https://www.elastic.co/products/kibana/feedback'; +export const KIBANA_ASK_ELASTIC_LINK = 'https://www.elastic.co/products/kibana/ask-elastic'; export const GITHUB_CREATE_ISSUE_LINK = 'https://github.com/elastic/kibana/issues/new/choose'; From 14579af0c5ee73b2d16eeb80912ae0ba7193c761 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Mon, 18 Nov 2019 10:57:43 -0500 Subject: [PATCH 15/17] [SIEM] Detection engine placeholders (#50220) (#50909) * attempt at getting nav working * fix detection-engine href redirect issue * rough out basic page routing * kql placeholder * update page and panel headers * rough out card table poc styles * change HeaderPanel to HeaderSection * cleanup and unit test updates * rough out utilityBar poc * clean up UtilityBar naming and styles * support popovers in utility bar * refactor icon side * UtilityBar unit tests * remove page tests for now * adjust routes * add comment * cleanup chart * open/closed signals content toggle * remove EuiFilterButton icons * fix misaligned popover button * add split prop for HeaderSection * fleshing out activity monitor table * update global header to include logo * fix tests * correct table types; thanks Garrett! * LinkIcon comp poc * fix bugs, errors, tests * rm import * table cleanup * correct merge errors * switch All Rules to EuiBasicTable * expand table types and values * fleshing out all rules table * rough out rule details * move chart to separate comp * update supplement layout * example rule fail * switch to new discover-style search * add ProgressInline comp * update unit tests and snapshots * cleanup * correct merge weirdness * move text styles to all subtitle items * correct invalid nav markup; update tests; cleanup * fix console errors * add empty page * change to EuiButtonEmpty in HeaderGlobal * overflow popover * rough out edit layout * new WrapperPage comp POC * cleanup * var for timeline gutter * tests and snapshots update * fix type + review + re-arrange code * adding feature flag + fix route issue * fix type with unit test * Removing unused translation --- .../integration/lib/navigation/selectors.ts | 8 +- .../__snapshots__/utility_bar.test.tsx.snap | 36 + .../utility_bar_action.test.tsx.snap | 11 + .../utility_bar_group.test.tsx.snap | 11 + .../utility_bar_section.test.tsx.snap | 13 + .../utility_bar_text.test.tsx.snap | 9 + .../detection_engine/utility_bar/index.ts | 11 + .../detection_engine/utility_bar/styles.tsx | 118 ++ .../utility_bar/utility_bar.test.tsx | 113 ++ .../utility_bar/utility_bar.tsx | 18 + .../utility_bar/utility_bar_action.test.tsx | 45 + .../utility_bar/utility_bar_action.tsx | 72 ++ .../utility_bar/utility_bar_group.test.tsx | 29 + .../utility_bar/utility_bar_group.tsx | 18 + .../utility_bar/utility_bar_section.test.tsx | 31 + .../utility_bar/utility_bar_section.tsx | 18 + .../utility_bar/utility_bar_text.test.tsx | 27 + .../utility_bar/utility_bar_text.tsx | 18 + .../events_viewer/events_viewer.test.tsx | 2 +- .../events_viewer/events_viewer.tsx | 4 +- .../filters_global/filters_global.tsx | 21 +- .../__snapshots__/index.test.tsx.snap | 7 + .../components/header_global/index.test.tsx | 34 + .../public/components/header_global/index.tsx | 82 ++ .../components/header_global/translations.ts | 15 + .../__snapshots__/header_page.test.tsx.snap | 14 - .../__snapshots__/index.test.tsx.snap | 23 + .../header_page/header_page.test.tsx | 42 - .../components/header_page/header_page.tsx | 78 -- .../components/header_page/index.test.tsx | 228 ++++ .../public/components/header_page/index.tsx | 141 ++- .../__snapshots__/index.test.tsx.snap | 4 +- .../index.test.tsx | 88 +- .../index.tsx | 40 +- .../__snapshots__/index.test.tsx.snap | 14 + .../components/link_icon/index.test.tsx | 121 ++ .../public/components/link_icon/index.tsx | 61 + .../siem/public/components/link_to/index.ts | 4 + .../public/components/link_to/link_to.tsx | 52 +- .../link_to/redirect_to_detection_engine.tsx | 51 + .../matrix_over_time/index.test.tsx | 4 +- .../components/matrix_over_time/index.tsx | 4 +- .../ml/tables/anomalies_host_table.tsx | 4 +- .../ml/tables/anomalies_network_table.tsx | 4 +- .../components/ml_popover/ml_popover.tsx | 37 +- .../components/navigation/index.test.tsx | 33 +- .../public/components/navigation/index.tsx | 7 +- .../navigation/tab_navigation/index.test.tsx | 12 +- .../navigation/tab_navigation/index.tsx | 68 +- .../public/components/navigation/types.ts | 1 - .../components/open_timeline/index.test.tsx | 2 +- .../open_timeline/title_row/index.test.tsx | 2 +- .../open_timeline/title_row/index.tsx | 6 +- .../__snapshots__/index.test.tsx.snap | 7 + .../histogram_signals/index.test.tsx | 27 + .../histogram_signals/index.tsx | 85 ++ .../page/overview/overview_host/index.tsx | 6 +- .../page/overview/overview_network/index.tsx | 6 +- .../components/paginated_table/index.tsx | 6 +- .../__snapshots__/index.test.tsx.snap | 13 + .../components/progress_inline/index.test.tsx | 29 + .../components/progress_inline/index.tsx | 51 + .../__snapshots__/index.test.tsx.snap | 9 + .../public/components/subtitle/index.test.tsx | 77 ++ .../siem/public/components/subtitle/index.tsx | 60 + .../public/components/url_state/constants.ts | 5 +- .../public/components/url_state/helpers.ts | 16 +- .../siem/public/components/url_state/types.ts | 8 +- .../__snapshots__/index.test.tsx.snap | 47 + .../components/wrapper_page/index.test.tsx | 67 + .../public/components/wrapper_page/index.tsx | 61 + .../plugins/siem/public/lib/helpers/index.tsx | 6 + .../legacy/plugins/siem/public/pages/404.tsx | 7 +- .../detection_engine/create_rule/index.tsx | 29 + .../create_rule/translations.ts | 11 + .../detection_engine/detection_engine.tsx | 205 ++++ .../detection_engine_empty_page.tsx | 29 + .../detection_engine/edit_rule/index.tsx | 128 ++ .../edit_rule/translations.ts | 11 + .../public/pages/detection_engine/index.tsx | 45 + .../detection_engine/rule_details/index.tsx | 660 ++++++++++ .../rule_details/translations.ts | 11 + .../pages/detection_engine/rules/index.tsx | 1076 +++++++++++++++++ .../detection_engine/rules/translations.ts | 11 + .../pages/detection_engine/translations.ts | 45 + .../public/pages/home/home_navigations.tsx | 13 +- .../plugins/siem/public/pages/home/index.tsx | 169 +-- .../siem/public/pages/home/translations.ts | 4 + .../plugins/siem/public/pages/home/types.ts | 2 + .../siem/public/pages/hosts/details/index.tsx | 77 +- .../plugins/siem/public/pages/hosts/hosts.tsx | 127 +- .../pages/network/ip_details/index.test.tsx | 2 +- .../public/pages/network/ip_details/index.tsx | 327 ++--- .../siem/public/pages/network/network.tsx | 144 +-- .../siem/public/pages/overview/overview.tsx | 84 +- .../public/pages/timelines/timelines_page.tsx | 26 +- .../translations/translations/ja-JP.json | 1 - .../translations/translations/zh-CN.json | 1 - 98 files changed, 4904 insertions(+), 843 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar.test.tsx.snap create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_action.test.tsx.snap create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_group.test.tsx.snap create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_section.test.tsx.snap create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_text.test.tsx.snap create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/index.ts create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/styles.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_action.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_action.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_group.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_group.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_section.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_section.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_text.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_text.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/header_global/__snapshots__/index.test.tsx.snap create mode 100644 x-pack/legacy/plugins/siem/public/components/header_global/index.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/header_global/index.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/header_global/translations.ts delete mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/header_page.test.tsx.snap create mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/index.test.tsx.snap delete mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/header_page.test.tsx delete mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/header_page.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/header_page/index.test.tsx rename x-pack/legacy/plugins/siem/public/components/{header_panel => header_section}/__snapshots__/index.test.tsx.snap (62%) rename x-pack/legacy/plugins/siem/public/components/{header_panel => header_section}/index.test.tsx (52%) rename x-pack/legacy/plugins/siem/public/components/{header_panel => header_section}/index.tsx (64%) create mode 100644 x-pack/legacy/plugins/siem/public/components/link_icon/__snapshots__/index.test.tsx.snap create mode 100644 x-pack/legacy/plugins/siem/public/components/link_icon/index.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/link_icon/index.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_detection_engine.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/__snapshots__/index.test.tsx.snap create mode 100644 x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/progress_inline/__snapshots__/index.test.tsx.snap create mode 100644 x-pack/legacy/plugins/siem/public/components/progress_inline/index.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/progress_inline/index.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/subtitle/__snapshots__/index.test.tsx.snap create mode 100644 x-pack/legacy/plugins/siem/public/components/subtitle/index.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/subtitle/index.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/wrapper_page/__snapshots__/index.test.tsx.snap create mode 100644 x-pack/legacy/plugins/siem/public/components/wrapper_page/index.test.tsx create mode 100644 x-pack/legacy/plugins/siem/public/components/wrapper_page/index.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/create_rule/index.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/create_rule/translations.ts create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_empty_page.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/edit_rule/index.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/edit_rule/translations.ts create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/index.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rule_details/index.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rule_details/translations.ts create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/translations.ts create mode 100644 x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts diff --git a/x-pack/legacy/plugins/siem/cypress/integration/lib/navigation/selectors.ts b/x-pack/legacy/plugins/siem/cypress/integration/lib/navigation/selectors.ts index 08bf4cebedc9c..0d5f40ae53966 100644 --- a/x-pack/legacy/plugins/siem/cypress/integration/lib/navigation/selectors.ts +++ b/x-pack/legacy/plugins/siem/cypress/integration/lib/navigation/selectors.ts @@ -5,16 +5,16 @@ */ /** Top-level (global) navigation link to the `Hosts` page */ -export const NAVIGATION_HOSTS = '[data-test-subj="navigation-link-hosts"]'; +export const NAVIGATION_HOSTS = '[data-test-subj="navigation-hosts"]'; /** Top-level (global) navigation link to the `Network` page */ -export const NAVIGATION_NETWORK = '[data-test-subj="navigation-link-network"]'; +export const NAVIGATION_NETWORK = '[data-test-subj="navigation-network"]'; /** Top-level (global) navigation link to the `Overview` page */ -export const NAVIGATION_OVERVIEW = '[data-test-subj="navigation-link-overview"]'; +export const NAVIGATION_OVERVIEW = '[data-test-subj="navigation-overview"]'; /** Top-level (global) navigation link to the `Timelines` page */ -export const NAVIGATION_TIMELINES = '[data-test-subj="navigation-link-timelines"]'; +export const NAVIGATION_TIMELINES = '[data-test-subj="navigation-timelines"]'; export const HOSTS_PAGE_TABS = { allHosts: '[data-test-subj="navigation-allHosts"]', diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar.test.tsx.snap new file mode 100644 index 0000000000000..1f892acef7ef3 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar.test.tsx.snap @@ -0,0 +1,36 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UtilityBar it renders 1`] = ` + + + + + + Test text + + + + + Test popover +

+ } + > + Test action +
+
+
+ + + + Test action + + + +
+
+`; diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_action.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_action.test.tsx.snap new file mode 100644 index 0000000000000..470b40cd1d960 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_action.test.tsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UtilityBarAction it renders 1`] = ` + + + Test action + + +`; diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_group.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_group.test.tsx.snap new file mode 100644 index 0000000000000..62ff1b17dd55f --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_group.test.tsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UtilityBarGroup it renders 1`] = ` + + + + Test text + + + +`; diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_section.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_section.test.tsx.snap new file mode 100644 index 0000000000000..f81717c892755 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_section.test.tsx.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UtilityBarSection it renders 1`] = ` + + + + + Test text + + + + +`; diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_text.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_text.test.tsx.snap new file mode 100644 index 0000000000000..446b5556945d8 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/__snapshots__/utility_bar_text.test.tsx.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UtilityBarText it renders 1`] = ` + + + Test text + + +`; diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/index.ts b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/index.ts new file mode 100644 index 0000000000000..b07fe8bb847c7 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { UtilityBar } from './utility_bar'; +export { UtilityBarAction } from './utility_bar_action'; +export { UtilityBarGroup } from './utility_bar_group'; +export { UtilityBarSection } from './utility_bar_section'; +export { UtilityBarText } from './utility_bar_text'; diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/styles.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/styles.tsx new file mode 100644 index 0000000000000..9d746bf3b0515 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/styles.tsx @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import styled, { css } from 'styled-components'; + +/** + * UTILITY BAR + */ + +export interface BarProps { + border?: boolean; +} + +export const Bar = styled.aside.attrs({ + className: 'siemUtilityBar', +})` + ${({ border, theme }) => css` + ${border && + css` + border-bottom: ${theme.eui.euiBorderThin}; + padding-bottom: ${theme.eui.paddingSizes.s}; + `} + + @media only screen and (min-width: ${theme.eui.euiBreakpoints.l}) { + display: flex; + justify-content: space-between; + } + `} +`; +Bar.displayName = 'Bar'; + +export const BarSection = styled.div.attrs({ + className: 'siemUtilityBar__section', +})` + ${({ theme }) => css` + & + & { + margin-top: ${theme.eui.euiSizeS}; + } + + @media only screen and (min-width: ${theme.eui.euiBreakpoints.m}) { + display: flex; + flex-wrap: wrap; + } + + @media only screen and (min-width: ${theme.eui.euiBreakpoints.l}) { + & + & { + margin-top: 0; + margin-left: ${theme.eui.euiSize}; + } + } + `} +`; +BarSection.displayName = 'BarSection'; + +export const BarGroup = styled.div.attrs({ + className: 'siemUtilityBar__group', +})` + ${({ theme }) => css` + align-items: flex-start; + display: flex; + flex-wrap: wrap; + + & + & { + margin-top: ${theme.eui.euiSizeS}; + } + + @media only screen and (min-width: ${theme.eui.euiBreakpoints.m}) { + border-right: ${theme.eui.euiBorderThin}; + flex-wrap: nowrap; + margin-right: ${theme.eui.paddingSizes.m}; + padding-right: ${theme.eui.paddingSizes.m}; + + & + & { + margin-top: 0; + } + + &:last-child { + border-right: none; + margin-right: 0; + padding-right: 0; + } + } + + & > * { + margin-right: ${theme.eui.euiSize}; + + &:last-child { + margin-right: 0; + } + } + `} +`; +BarGroup.displayName = 'BarGroup'; + +export const BarText = styled.p.attrs({ + className: 'siemUtilityBar__text', +})` + ${({ theme }) => css` + color: ${theme.eui.textColors.subdued}; + font-size: ${theme.eui.euiFontSizeXS}; + line-height: ${theme.eui.euiLineHeight}; + white-space: nowrap; + `} +`; +BarText.displayName = 'BarText'; + +export const BarAction = styled.div.attrs({ + className: 'siemUtilityBar__action', +})` + ${({ theme }) => css` + font-size: ${theme.eui.euiFontSizeXS}; + line-height: ${theme.eui.euiLineHeight}; + `} +`; +BarAction.displayName = 'BarAction'; diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar.test.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar.test.tsx new file mode 100644 index 0000000000000..bf13a503838cf --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar.test.tsx @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json'; +import { mount, shallow } from 'enzyme'; +import toJson from 'enzyme-to-json'; +import 'jest-styled-components'; +import React from 'react'; + +import '../../../mock/ui_settings'; +import { TestProviders } from '../../../mock'; +import { + UtilityBar, + UtilityBarAction, + UtilityBarGroup, + UtilityBarSection, + UtilityBarText, +} from './index'; + +jest.mock('../../../lib/settings/use_kibana_ui_setting'); + +describe('UtilityBar', () => { + test('it renders', () => { + const wrapper = shallow( + + + + + {'Test text'} + + + + {'Test popover'}

}> + {'Test action'} +
+
+
+ + + + {'Test action'} + + +
+
+ ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); + + test('it applies border styles when border is true', () => { + const wrapper = mount( + + + + + {'Test text'} + + + + {'Test popover'}

}> + {'Test action'} +
+
+
+ + + + {'Test action'} + + +
+
+ ); + const siemUtilityBar = wrapper.find('.siemUtilityBar').first(); + + expect(siemUtilityBar).toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin); + expect(siemUtilityBar).toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.s); + }); + + test('it DOES NOT apply border styles when border is false', () => { + const wrapper = mount( + + + + + {'Test text'} + + + + {'Test popover'}

}> + {'Test action'} +
+
+
+ + + + {'Test action'} + + +
+
+ ); + const siemUtilityBar = wrapper.find('.siemUtilityBar').first(); + + expect(siemUtilityBar).not.toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin); + expect(siemUtilityBar).not.toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.s); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar.tsx new file mode 100644 index 0000000000000..f226e0e055391 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar.tsx @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { Bar, BarProps } from './styles'; + +export interface UtilityBarProps extends BarProps { + children: React.ReactNode; +} + +export const UtilityBar = React.memo(({ border, children }) => ( + {children} +)); +UtilityBar.displayName = 'UtilityBar'; diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_action.test.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_action.test.tsx new file mode 100644 index 0000000000000..7a1c35183e503 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_action.test.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { mount, shallow } from 'enzyme'; +import toJson from 'enzyme-to-json'; +import 'jest-styled-components'; +import React from 'react'; + +import '../../../mock/ui_settings'; +import { TestProviders } from '../../../mock'; +import { UtilityBarAction } from './index'; + +jest.mock('../../../lib/settings/use_kibana_ui_setting'); + +describe('UtilityBarAction', () => { + test('it renders', () => { + const wrapper = shallow( + + {'Test action'} + + ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); + + test('it renders a popover', () => { + const wrapper = mount( + + {'Test popover'}

}> + {'Test action'} +
+
+ ); + + expect( + wrapper + .find('.euiPopover') + .first() + .exists() + ).toBe(true); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_action.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_action.tsx new file mode 100644 index 0000000000000..ae4362bdbcd7b --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_action.tsx @@ -0,0 +1,72 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiPopover } from '@elastic/eui'; +import React, { useState } from 'react'; + +import { LinkIcon, LinkIconProps } from '../../link_icon'; +import { BarAction } from './styles'; + +const Popover = React.memo( + ({ children, color, iconSide, iconSize, iconType, popoverContent }) => { + const [popoverState, setPopoverState] = useState(false); + + return ( + setPopoverState(!popoverState)} + > + {children} + + } + closePopover={() => setPopoverState(false)} + isOpen={popoverState} + > + {popoverContent} + + ); + } +); +Popover.displayName = 'Popover'; + +export interface UtilityBarActionProps extends LinkIconProps { + popoverContent?: React.ReactNode; +} + +export const UtilityBarAction = React.memo( + ({ children, color, href, iconSide, iconSize, iconType, onClick, popoverContent }) => ( + + {popoverContent ? ( + + {children} + + ) : ( + + {children} + + )} + + ) +); +UtilityBarAction.displayName = 'UtilityBarAction'; diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_group.test.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_group.test.tsx new file mode 100644 index 0000000000000..84ad96c5a1e5e --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_group.test.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shallow } from 'enzyme'; +import toJson from 'enzyme-to-json'; +import React from 'react'; + +import '../../../mock/ui_settings'; +import { TestProviders } from '../../../mock'; +import { UtilityBarGroup, UtilityBarText } from './index'; + +jest.mock('../../../lib/settings/use_kibana_ui_setting'); + +describe('UtilityBarGroup', () => { + test('it renders', () => { + const wrapper = shallow( + + + {'Test text'} + + + ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_group.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_group.tsx new file mode 100644 index 0000000000000..1e23fd3498199 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_group.tsx @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { BarGroup } from './styles'; + +export interface UtilityBarGroupProps { + children: React.ReactNode; +} + +export const UtilityBarGroup = React.memo(({ children }) => ( + {children} +)); +UtilityBarGroup.displayName = 'UtilityBarGroup'; diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_section.test.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_section.test.tsx new file mode 100644 index 0000000000000..2dfc1d3b8d193 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_section.test.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shallow } from 'enzyme'; +import toJson from 'enzyme-to-json'; +import React from 'react'; + +import '../../../mock/ui_settings'; +import { TestProviders } from '../../../mock'; +import { UtilityBarGroup, UtilityBarSection, UtilityBarText } from './index'; + +jest.mock('../../../lib/settings/use_kibana_ui_setting'); + +describe('UtilityBarSection', () => { + test('it renders', () => { + const wrapper = shallow( + + + + {'Test text'} + + + + ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_section.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_section.tsx new file mode 100644 index 0000000000000..c457e6bc3dee0 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_section.tsx @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { BarSection } from './styles'; + +export interface UtilityBarSectionProps { + children: React.ReactNode; +} + +export const UtilityBarSection = React.memo(({ children }) => ( + {children} +)); +UtilityBarSection.displayName = 'UtilityBarSection'; diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_text.test.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_text.test.tsx new file mode 100644 index 0000000000000..0743e5cab02b4 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_text.test.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shallow } from 'enzyme'; +import toJson from 'enzyme-to-json'; +import React from 'react'; + +import '../../../mock/ui_settings'; +import { TestProviders } from '../../../mock'; +import { UtilityBarText } from './index'; + +jest.mock('../../../lib/settings/use_kibana_ui_setting'); + +describe('UtilityBarText', () => { + test('it renders', () => { + const wrapper = shallow( + + {'Test text'} + + ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_text.tsx b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_text.tsx new file mode 100644 index 0000000000000..f8eb25f03d4ad --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/detection_engine/utility_bar/utility_bar_text.tsx @@ -0,0 +1,18 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { BarText } from './styles'; + +export interface UtilityBarTextProps { + children: string; +} + +export const UtilityBarText = React.memo(({ children }) => ( + {children} +)); +UtilityBarText.displayName = 'UtilityBarText'; diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx index a97ef2cf5ca0c..4e59acc4f6713 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.test.tsx @@ -52,7 +52,7 @@ describe('EventsViewer', () => { expect( wrapper - .find(`[data-test-subj="header-panel-subtitle"]`) + .find(`[data-test-subj="header-section-subtitle"]`) .first() .text() ).toEqual('Showing: 12 events'); diff --git a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx index 2b588283bd198..c5c0fe3503561 100644 --- a/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx +++ b/x-pack/legacy/plugins/siem/public/components/events_viewer/events_viewer.tsx @@ -16,7 +16,7 @@ import { Direction } from '../../graphql/types'; import { useKibanaCore } from '../../lib/compose/kibana_core'; import { KqlMode } from '../../store/timeline/model'; import { AutoSizer } from '../auto_sizer'; -import { HeaderPanel } from '../header_panel'; +import { HeaderSection } from '../header_section'; import { ColumnHeader } from '../timeline/body/column_headers/column_header'; import { defaultHeaders } from '../timeline/body/column_headers/default_headers'; import { Sort } from '../timeline/body/sort'; @@ -130,7 +130,7 @@ export const EventsViewer = React.memo( totalCount = 0, }) => ( <> - ` +const Wrapper = styled.aside<{ isSticky?: boolean }>` ${props => css` position: relative; z-index: ${props.theme.eui.euiZNavigation}; background: ${props.theme.eui.euiColorEmptyShade}; border-bottom: ${props.theme.eui.euiBorderThin}; - box-sizing: content-box; - margin: 0 -${gutterTimeline} 0 -${props.theme.eui.euiSizeL}; - padding: ${props.theme.eui.euiSize} ${gutterTimeline} ${props.theme.eui.euiSize} ${ - props.theme.eui.euiSizeL - }; + padding: ${props.theme.eui.paddingSizes.m} ${gutterTimeline} ${ + props.theme.eui.paddingSizes.m + } ${props.theme.eui.paddingSizes.l}; ${props.isSticky && ` @@ -39,8 +38,7 @@ const Aside = styled.aside<{ isSticky?: boolean }>` } `} `; - -Aside.displayName = 'Aside'; +Wrapper.displayName = 'Wrapper'; export interface FiltersGlobalProps { children: React.ReactNode; @@ -49,11 +47,10 @@ export interface FiltersGlobalProps { export const FiltersGlobal = pure(({ children }) => ( {({ style, isSticky }) => ( - + )} )); - FiltersGlobal.displayName = 'FiltersGlobal'; diff --git a/x-pack/legacy/plugins/siem/public/components/header_global/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/header_global/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000000..665a5c75f3684 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/header_global/__snapshots__/index.test.tsx.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`HeaderGlobal it renders 1`] = ` + + + +`; diff --git a/x-pack/legacy/plugins/siem/public/components/header_global/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/header_global/index.test.tsx new file mode 100644 index 0000000000000..ebd1da634ed1a --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/header_global/index.test.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shallow } from 'enzyme'; +import toJson from 'enzyme-to-json'; +import React from 'react'; + +import { TestProviders } from '../../mock'; +import '../../mock/match_media'; +import '../../mock/ui_settings'; +import { HeaderGlobal } from './index'; + +jest.mock('../../lib/settings/use_kibana_ui_setting'); + +// Test will fail because we will to need to mock some core services to make the test work +// For now let's forget about SiemSearchBar +jest.mock('../search_bar', () => ({ + SiemSearchBar: () => null, +})); + +describe('HeaderGlobal', () => { + test('it renders', () => { + const wrapper = shallow( + + + + ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/header_global/index.tsx b/x-pack/legacy/plugins/siem/public/components/header_global/index.tsx new file mode 100644 index 0000000000000..168cacf3e97e1 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/header_global/index.tsx @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink } from '@elastic/eui'; +import { pickBy } from 'lodash/fp'; +import React from 'react'; +import styled, { css } from 'styled-components'; + +import { gutterTimeline } from '../../lib/helpers'; +import { navTabs } from '../../pages/home/home_navigations'; +import { SiemPageName } from '../../pages/home/types'; +import { getOverviewUrl } from '../link_to'; +import { MlPopover } from '../ml_popover/ml_popover'; +import { SiemNavigation } from '../navigation'; +import * as i18n from './translations'; + +const Wrapper = styled.header` + ${({ theme }) => css` + background: ${theme.eui.euiColorEmptyShade}; + border-bottom: ${theme.eui.euiBorderThin}; + padding: ${theme.eui.paddingSizes.m} ${gutterTimeline} ${theme.eui.paddingSizes.m} + ${theme.eui.paddingSizes.l}; + `} +`; +Wrapper.displayName = 'Wrapper'; + +const FlexItem = styled(EuiFlexItem)` + min-width: 0; +`; +FlexItem.displayName = 'FlexItem'; + +interface HeaderGlobalProps { + hideDetectionEngine?: boolean; +} +export const HeaderGlobal = React.memo(({ hideDetectionEngine = true }) => ( + + + + + + + + + + + + key !== SiemPageName.detectionEngine, navTabs) + : navTabs + } + /> + + + + + + + + + + + + + {i18n.BUTTON_ADD_DATA} + + + + + + +)); +HeaderGlobal.displayName = 'HeaderGlobal'; diff --git a/x-pack/legacy/plugins/siem/public/components/header_global/translations.ts b/x-pack/legacy/plugins/siem/public/components/header_global/translations.ts new file mode 100644 index 0000000000000..c713f63016594 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/header_global/translations.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const SIEM = i18n.translate('xpack.siem.headerGlobal.siem', { + defaultMessage: 'SIEM', +}); + +export const BUTTON_ADD_DATA = i18n.translate('xpack.siem.headerGlobal.buttonAddData', { + defaultMessage: 'Add data', +}); diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/header_page.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/header_page.test.tsx.snap deleted file mode 100644 index 280acc0c63334..0000000000000 --- a/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/header_page.test.tsx.snap +++ /dev/null @@ -1,14 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`rendering renders correctly 1`] = ` - -

- My test supplement. -

-
-`; diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000000..0fe2890dc9f24 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/header_page/__snapshots__/index.test.tsx.snap @@ -0,0 +1,23 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`HeaderPage it renders 1`] = ` + + +

+ Test supplement +

+
+
+`; diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/header_page.test.tsx b/x-pack/legacy/plugins/siem/public/components/header_page/header_page.test.tsx deleted file mode 100644 index 16f2156e568e5..0000000000000 --- a/x-pack/legacy/plugins/siem/public/components/header_page/header_page.test.tsx +++ /dev/null @@ -1,42 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { shallow } from 'enzyme'; -import toJson from 'enzyme-to-json'; -import React from 'react'; - -import { HeaderPage } from './index'; - -describe('rendering', () => { - test('renders correctly', () => { - const wrapper = shallow( - -

{'My test supplement.'}

-
- ); - expect(toJson(wrapper)).toMatchSnapshot(); - }); - test('renders as a draggable when provided arguments', () => { - const wrapper = shallow( - -

{'My test supplement.'}

-
- ); - const draggableHeader = wrapper.dive().find('[data-test-subj="page_headline_draggable"]'); - expect(draggableHeader.exists()).toBeTruthy(); - }); -}); diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/header_page.tsx b/x-pack/legacy/plugins/siem/public/components/header_page/header_page.tsx deleted file mode 100644 index 2ba543b34307a..0000000000000 --- a/x-pack/legacy/plugins/siem/public/components/header_page/header_page.tsx +++ /dev/null @@ -1,78 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiText, EuiTitle } from '@elastic/eui'; -import React from 'react'; -import { pure } from 'recompose'; -import styled from 'styled-components'; -import { DefaultDraggable } from '../draggables'; - -const Header = styled.header` - ${({ theme }) => ` - border-bottom: ${theme.eui.euiBorderThin}; - padding-bottom: ${theme.eui.euiSizeL}; - margin: ${theme.eui.euiSizeL} 0; - `} -`; - -Header.displayName = 'Header'; - -interface DraggableArguments { - field: string; - value: string; -} - -export interface HeaderPageProps { - badgeLabel?: string; - badgeTooltip?: string; - children?: React.ReactNode; - draggableArguments?: DraggableArguments; - subtitle?: string | React.ReactNode; - title: string | React.ReactNode; -} - -export const HeaderPage = pure( - ({ badgeLabel, badgeTooltip, children, draggableArguments, subtitle, title, ...rest }) => ( -
- - - -

- {!draggableArguments ? ( - title - ) : ( - - )} - {badgeLabel && ( - <> - {' '} - - - )} -

-
- - - {subtitle} - -
- - {children && {children}} -
-
- ) -); - -HeaderPage.displayName = 'HeaderPage'; diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/header_page/index.test.tsx new file mode 100644 index 0000000000000..ae33b63e93d39 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/header_page/index.test.tsx @@ -0,0 +1,228 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json'; +import { mount, shallow } from 'enzyme'; +import toJson from 'enzyme-to-json'; +import 'jest-styled-components'; +import React from 'react'; + +import { TestProviders } from '../../mock'; +import '../../mock/ui_settings'; +import { HeaderPage } from './index'; + +jest.mock('../../lib/settings/use_kibana_ui_setting'); + +describe('HeaderPage', () => { + test('it renders', () => { + const wrapper = shallow( + + +

{'Test supplement'}

+
+
+ ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); + + test('it renders the title', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper + .find('[data-test-subj="header-page-title"]') + .first() + .exists() + ).toBe(true); + }); + + test('it renders the back link when provided', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper + .find('.siemHeaderPage__linkBack') + .first() + .exists() + ).toBe(true); + }); + + test('it DOES NOT render the back link when not provided', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper + .find('.siemHeaderPage__linkBack') + .first() + .exists() + ).toBe(false); + }); + + test('it renders the first subtitle when provided', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper + .find('[data-test-subj="header-page-subtitle"]') + .first() + .exists() + ).toBe(true); + }); + + test('it DOES NOT render the first subtitle when not provided', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper + .find('[data-test-subj="header-section-subtitle"]') + .first() + .exists() + ).toBe(false); + }); + + test('it renders the second subtitle when provided', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper + .find('[data-test-subj="header-page-subtitle-2"]') + .first() + .exists() + ).toBe(true); + }); + + test('it DOES NOT render the second subtitle when not provided', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper + .find('[data-test-subj="header-section-subtitle-2"]') + .first() + .exists() + ).toBe(false); + }); + + test('it renders supplements when children provided', () => { + const wrapper = mount( + + +

{'Test supplement'}

+
+
+ ); + + expect( + wrapper + .find('[data-test-subj="header-page-supplements"]') + .first() + .exists() + ).toBe(true); + }); + + test('it DOES NOT render supplements when children not provided', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper + .find('[data-test-subj="header-page-supplements"]') + .first() + .exists() + ).toBe(false); + }); + + test('it applies border styles when border is true', () => { + const wrapper = mount( + + + + ); + const siemHeaderPage = wrapper.find('.siemHeaderPage').first(); + + expect(siemHeaderPage).toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin); + expect(siemHeaderPage).toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.l); + }); + + test('it DOES NOT apply border styles when border is false', () => { + const wrapper = mount( + + + + ); + const siemHeaderPage = wrapper.find('.siemHeaderPage').first(); + + expect(siemHeaderPage).not.toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin); + expect(siemHeaderPage).not.toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.l); + }); + + test('it renders as a draggable when arguments provided', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper + .find('[data-test-subj="header-page-draggable"]') + .first() + .exists() + ).toBe(true); + }); + + test('it DOES NOT render as a draggable when arguments not provided', () => { + const wrapper = mount( + + + + ); + + expect( + wrapper + .find('[data-test-subj="header-page-draggable"]') + .first() + .exists() + ).toBe(false); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/header_page/index.tsx b/x-pack/legacy/plugins/siem/public/components/header_page/index.tsx index 9d89cdfc32893..4db2a35c600e9 100644 --- a/x-pack/legacy/plugins/siem/public/components/header_page/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/header_page/index.tsx @@ -4,4 +4,143 @@ * you may not use this file except in compliance with the Elastic License. */ -export { HeaderPage } from './header_page'; +import { EuiBetaBadge, EuiBadge, EuiFlexGroup, EuiFlexItem, EuiTitle } from '@elastic/eui'; +import React from 'react'; +import styled, { css } from 'styled-components'; + +import { DefaultDraggable } from '../draggables'; +import { LinkIcon, LinkIconProps } from '../link_icon'; +import { Subtitle, SubtitleProps } from '../subtitle'; + +interface HeaderProps { + border?: boolean; +} + +const Header = styled.header.attrs({ + className: 'siemHeaderPage', +})` + ${({ border, theme }) => css` + margin-bottom: ${theme.eui.euiSizeL}; + + ${border && + css` + border-bottom: ${theme.eui.euiBorderThin}; + padding-bottom: ${theme.eui.paddingSizes.l}; + `} + `} +`; +Header.displayName = 'Header'; + +const FlexItem = styled(EuiFlexItem)` + display: block; +`; +FlexItem.displayName = 'FlexItem'; + +const LinkBack = styled.div.attrs({ + className: 'siemHeaderPage__linkBack', +})` + ${({ theme }) => css` + font-size: ${theme.eui.euiFontSizeXS}; + line-height: ${theme.eui.euiLineHeight}; + margin-bottom: ${theme.eui.euiSizeS}; + `} +`; +LinkBack.displayName = 'LinkBack'; + +const Badge = styled(EuiBadge)` + letter-spacing: 0; +`; +Badge.displayName = 'Badge'; + +interface BackOptions { + href: LinkIconProps['href']; + text: LinkIconProps['children']; +} + +interface BadgeOptions { + beta?: boolean; + text: string; + tooltip?: string; +} + +interface DraggableArguments { + field: string; + value: string; +} + +export interface HeaderPageProps extends HeaderProps { + backOptions?: BackOptions; + badgeOptions?: BadgeOptions; + children?: React.ReactNode; + draggableArguments?: DraggableArguments; + subtitle?: SubtitleProps['items']; + subtitle2?: SubtitleProps['items']; + title: string | React.ReactNode; +} + +export const HeaderPage = React.memo( + ({ + backOptions, + badgeOptions, + border, + children, + draggableArguments, + subtitle, + subtitle2, + title, + ...rest + }) => ( +
+ + + {backOptions && ( + + + {backOptions.text} + + + )} + + +

+ {!draggableArguments ? ( + title + ) : ( + + )} + {badgeOptions && ( + <> + {' '} + {badgeOptions.beta ? ( + + ) : ( + {badgeOptions.text} + )} + + )} +

+
+ + {subtitle && } + {subtitle2 && } +
+ + {children && ( + + {children} + + )} +
+
+ ) +); +HeaderPage.displayName = 'HeaderPage'; diff --git a/x-pack/legacy/plugins/siem/public/components/header_panel/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/header_section/__snapshots__/index.test.tsx.snap similarity index 62% rename from x-pack/legacy/plugins/siem/public/components/header_panel/__snapshots__/index.test.tsx.snap rename to x-pack/legacy/plugins/siem/public/components/header_section/__snapshots__/index.test.tsx.snap index 39250c38ef8fc..ecd2b15a841f6 100644 --- a/x-pack/legacy/plugins/siem/public/components/header_panel/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/siem/public/components/header_section/__snapshots__/index.test.tsx.snap @@ -1,8 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`HeaderPanel it renders 1`] = ` +exports[`HeaderSection it renders 1`] = ` - diff --git a/x-pack/legacy/plugins/siem/public/components/header_panel/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/header_section/index.test.tsx similarity index 52% rename from x-pack/legacy/plugins/siem/public/components/header_panel/index.test.tsx rename to x-pack/legacy/plugins/siem/public/components/header_section/index.test.tsx index 9cdb85bcb3d76..fffeece818d13 100644 --- a/x-pack/legacy/plugins/siem/public/components/header_panel/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/header_section/index.test.tsx @@ -10,17 +10,17 @@ import toJson from 'enzyme-to-json'; import 'jest-styled-components'; import React from 'react'; -import '../../mock/ui_settings'; import { TestProviders } from '../../mock'; -import { HeaderPanel } from './index'; +import '../../mock/ui_settings'; +import { HeaderSection } from './index'; jest.mock('../../lib/settings/use_kibana_ui_setting'); -describe('HeaderPanel', () => { +describe('HeaderSection', () => { test('it renders', () => { const wrapper = shallow( - + ); @@ -30,13 +30,13 @@ describe('HeaderPanel', () => { test('it renders the title', () => { const wrapper = mount( - + ); expect( wrapper - .find('[data-test-subj="header-panel-title"]') + .find('[data-test-subj="header-section-title"]') .first() .exists() ).toBe(true); @@ -45,13 +45,13 @@ describe('HeaderPanel', () => { test('it renders the subtitle when provided', () => { const wrapper = mount( - + ); expect( wrapper - .find(`[data-test-subj="header-panel-subtitle"]`) + .find('[data-test-subj="header-section-subtitle"]') .first() .exists() ).toBe(true); @@ -60,13 +60,13 @@ describe('HeaderPanel', () => { test('it DOES NOT render the subtitle when not provided', () => { const wrapper = mount( - + ); expect( wrapper - .find(`[data-test-subj="header-panel-subtitle"]`) + .find('[data-test-subj="header-section-subtitle"]') .first() .exists() ).toBe(false); @@ -75,13 +75,13 @@ describe('HeaderPanel', () => { test('it renders a transparent inspect button when showInspect is false', () => { const wrapper = mount( - + ); expect( wrapper - .find(`[data-test-subj="transparent-inspect-container"]`) + .find('[data-test-subj="transparent-inspect-container"]') .first() .exists() ).toBe(true); @@ -90,13 +90,13 @@ describe('HeaderPanel', () => { test('it renders an opaque inspect button when showInspect is true', () => { const wrapper = mount( - + ); expect( wrapper - .find(`[data-test-subj="opaque-inspect-container"]`) + .find('[data-test-subj="opaque-inspect-container"]') .first() .exists() ).toBe(true); @@ -105,15 +105,15 @@ describe('HeaderPanel', () => { test('it renders supplements when children provided', () => { const wrapper = mount( - +

{'Test children'}

-
+
); expect( wrapper - .find('[data-test-subj="header-panel-supplements"]') + .find('[data-test-subj="header-section-supplements"]') .first() .exists() ).toBe(true); @@ -122,13 +122,13 @@ describe('HeaderPanel', () => { test('it DOES NOT render supplements when children not provided', () => { const wrapper = mount( - + ); expect( wrapper - .find('[data-test-subj="header-panel-supplements"]') + .find('[data-test-subj="header-section-supplements"]') .first() .exists() ).toBe(false); @@ -137,24 +137,58 @@ describe('HeaderPanel', () => { test('it applies border styles when border is true', () => { const wrapper = mount( - + ); - const siemHeaderPanel = wrapper.find('.siemHeaderPanel').first(); + const siemHeaderSection = wrapper.find('.siemHeaderSection').first(); - expect(siemHeaderPanel).toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin); - expect(siemHeaderPanel).toHaveStyleRule('padding-bottom', euiDarkVars.euiSizeL); + expect(siemHeaderSection).toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin); + expect(siemHeaderSection).toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.l); }); test('it DOES NOT apply border styles when border is false', () => { const wrapper = mount( - + + + ); + const siemHeaderSection = wrapper.find('.siemHeaderSection').first(); + + expect(siemHeaderSection).not.toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin); + expect(siemHeaderSection).not.toHaveStyleRule('padding-bottom', euiDarkVars.paddingSizes.l); + }); + + test('it splits the title and supplement areas evenly when split is true', () => { + const wrapper = mount( + + +

{'Test children'}

+
+
+ ); + + expect( + wrapper + .find('.euiFlexItem--flexGrowZero[data-test-subj="header-section-supplements"]') + .first() + .exists() + ).toBe(false); + }); + + test('it DOES NOT split the title and supplement areas evenly when split is false', () => { + const wrapper = mount( + + +

{'Test children'}

+
); - const siemHeaderPanel = wrapper.find('.siemHeaderPanel').first(); - expect(siemHeaderPanel).not.toHaveStyleRule('border-bottom', euiDarkVars.euiBorderThin); - expect(siemHeaderPanel).not.toHaveStyleRule('padding-bottom', euiDarkVars.euiSizeL); + expect( + wrapper + .find('.euiFlexItem--flexGrowZero[data-test-subj="header-section-supplements"]') + .first() + .exists() + ).toBe(true); }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/header_panel/index.tsx b/x-pack/legacy/plugins/siem/public/components/header_section/index.tsx similarity index 64% rename from x-pack/legacy/plugins/siem/public/components/header_panel/index.tsx rename to x-pack/legacy/plugins/siem/public/components/header_section/index.tsx index e7b3fb9f2f400..e46ae55a57a45 100644 --- a/x-pack/legacy/plugins/siem/public/components/header_panel/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/header_section/index.tsx @@ -4,51 +4,52 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiFlexGroup, EuiFlexItem, EuiIconTip, EuiText, EuiTitle } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiIconTip, EuiTitle } from '@elastic/eui'; import React from 'react'; import styled, { css } from 'styled-components'; import { InspectButton } from '../inspect'; +import { Subtitle } from '../subtitle'; interface HeaderProps { border?: boolean; } const Header = styled.header.attrs({ - className: 'siemHeaderPanel', + className: 'siemHeaderSection', })` - ${props => css` - margin-bottom: ${props.theme.eui.euiSizeL}; + ${({ border, theme }) => css` + margin-bottom: ${theme.eui.euiSizeL}; user-select: text; - ${props.border && - ` - border-bottom: ${props.theme.eui.euiBorderThin}; - padding-bottom: ${props.theme.eui.euiSizeL}; - `} + ${border && + css` + border-bottom: ${theme.eui.euiBorderThin}; + padding-bottom: ${theme.eui.paddingSizes.l}; + `} `} `; - Header.displayName = 'Header'; -export interface HeaderPanelProps extends HeaderProps { +export interface HeaderSectionProps extends HeaderProps { children?: React.ReactNode; id?: string; + split?: boolean; subtitle?: string | React.ReactNode; showInspect?: boolean; title: string | React.ReactNode; tooltip?: string; } -export const HeaderPanel = React.memo( - ({ border, children, id, showInspect = false, subtitle, title, tooltip }) => ( +export const HeaderSection = React.memo( + ({ border, children, id, showInspect = false, split, subtitle, title, tooltip }) => (
-

+

{title} {tooltip && ( <> @@ -59,11 +60,7 @@ export const HeaderPanel = React.memo(

- {subtitle && ( - -

{subtitle}

-
- )} + {subtitle && }
{id && ( @@ -75,7 +72,7 @@ export const HeaderPanel = React.memo(
{children && ( - + {children} )} @@ -83,5 +80,4 @@ export const HeaderPanel = React.memo(
) ); - -HeaderPanel.displayName = 'HeaderPanel'; +HeaderSection.displayName = 'HeaderSection'; diff --git a/x-pack/legacy/plugins/siem/public/components/link_icon/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/link_icon/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000000..5902768383cb0 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/link_icon/__snapshots__/index.test.tsx.snap @@ -0,0 +1,14 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LinkIcon it renders 1`] = ` + + + Test link + + +`; diff --git a/x-pack/legacy/plugins/siem/public/components/link_icon/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/link_icon/index.test.tsx new file mode 100644 index 0000000000000..8e4387f35056e --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/link_icon/index.test.tsx @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { mount, shallow } from 'enzyme'; +import toJson from 'enzyme-to-json'; +import 'jest-styled-components'; +import React from 'react'; + +import { TestProviders } from '../../mock'; +import '../../mock/ui_settings'; +import { LinkIcon } from './index'; + +jest.mock('../../lib/settings/use_kibana_ui_setting'); + +describe('LinkIcon', () => { + test('it renders', () => { + const wrapper = shallow( + + + {'Test link'} + + + ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); + + test('it renders an action button when onClick is provided', () => { + const wrapper = mount( + + alert('Test alert')}> + {'Test link'} + + + ); + + expect( + wrapper + .find('button') + .first() + .exists() + ).toBe(true); + }); + + test('it renders an action link when href is provided', () => { + const wrapper = mount( + + + {'Test link'} + + + ); + + expect( + wrapper + .find('a') + .first() + .exists() + ).toBe(true); + }); + + test('it renders an icon', () => { + const wrapper = mount( + + {'Test link'} + + ); + + expect( + wrapper + .find('.euiIcon') + .first() + .exists() + ).toBe(true); + }); + + test('it positions the icon to the right when iconSide is right', () => { + const wrapper = mount( + + + {'Test link'} + + + ); + + expect(wrapper.find('.siemLinkIcon').first()).toHaveStyleRule('flex-direction', 'row-reverse'); + }); + + test('it positions the icon to the left when iconSide is left (or not provided)', () => { + const wrapper = mount( + + + {'Test link'} + + + ); + + expect(wrapper.find('.siemLinkIcon').first()).not.toHaveStyleRule( + 'flex-direction', + 'row-reverse' + ); + }); + + test('it renders a label', () => { + const wrapper = mount( + + {'Test link'} + + ); + + expect( + wrapper + .find('.siemLinkIcon__label') + .first() + .exists() + ).toBe(true); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/link_icon/index.tsx b/x-pack/legacy/plugins/siem/public/components/link_icon/index.tsx new file mode 100644 index 0000000000000..d83183adcf5e5 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/link_icon/index.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiIcon, EuiLink, IconSize, IconType } from '@elastic/eui'; +import { LinkAnchorProps } from '@elastic/eui/src/components/link/link'; +import React from 'react'; +import styled, { css } from 'styled-components'; + +interface LinkProps { + color?: LinkAnchorProps['color']; + href?: string; + iconSide?: 'left' | 'right'; + onClick?: Function; +} + +const Link = styled(({ iconSide, children, ...rest }) => {children})< + LinkProps +>` + ${({ iconSide, theme }) => css` + align-items: center; + display: inline-flex; + vertical-align: top; + white-space: nowrap; + + ${iconSide === 'left' && + css` + .euiIcon { + margin-right: ${theme.eui.euiSizeXS}; + } + `} + + ${iconSide === 'right' && + css` + flex-direction: row-reverse; + + .euiIcon { + margin-left: ${theme.eui.euiSizeXS}; + } + `} + `} +`; +Link.displayName = 'Link'; + +export interface LinkIconProps extends LinkProps { + children: string; + iconSize?: IconSize; + iconType: IconType; +} + +export const LinkIcon = React.memo( + ({ children, color, href, iconSide = 'left', iconSize = 's', iconType, onClick }) => ( + + + {children} + + ) +); +LinkIcon.displayName = 'LinkIcon'; diff --git a/x-pack/legacy/plugins/siem/public/components/link_to/index.ts b/x-pack/legacy/plugins/siem/public/components/link_to/index.ts index 7eb39de3d96b4..10198345755c3 100644 --- a/x-pack/legacy/plugins/siem/public/components/link_to/index.ts +++ b/x-pack/legacy/plugins/siem/public/components/link_to/index.ts @@ -5,6 +5,10 @@ */ export { LinkToPage } from './link_to'; +export { + getDetectionEngineUrl, + RedirectToDetectionEnginePage, +} from './redirect_to_detection_engine'; export { getOverviewUrl, RedirectToOverviewPage } from './redirect_to_overview'; export { getHostsUrl, getHostDetailsUrl } from './redirect_to_hosts'; export { getNetworkUrl, getIPDetailsUrl, RedirectToNetworkPage } from './redirect_to_network'; diff --git a/x-pack/legacy/plugins/siem/public/components/link_to/link_to.tsx b/x-pack/legacy/plugins/siem/public/components/link_to/link_to.tsx index 0360c1004f151..0125b52e3ad33 100644 --- a/x-pack/legacy/plugins/siem/public/components/link_to/link_to.tsx +++ b/x-pack/legacy/plugins/siem/public/components/link_to/link_to.tsx @@ -8,12 +8,19 @@ import React from 'react'; import { match as RouteMatch, Redirect, Route, Switch } from 'react-router-dom'; import { pure } from 'recompose'; +import { SiemPageName } from '../../pages/home/types'; +import { HostsTableType } from '../../store/hosts/model'; +import { + RedirectToCreateRulePage, + RedirectToDetectionEnginePage, + RedirectToEditRulePage, + RedirectToRuleDetailsPage, + RedirectToRulesPage, +} from './redirect_to_detection_engine'; import { RedirectToHostsPage, RedirectToHostDetailsPage } from './redirect_to_hosts'; import { RedirectToNetworkPage } from './redirect_to_network'; import { RedirectToOverviewPage } from './redirect_to_overview'; import { RedirectToTimelinesPage } from './redirect_to_timelines'; -import { HostsTableType } from '../../store/hosts/model'; -import { SiemPageName } from '../../pages/home/types'; interface LinkToPageProps { match: RouteMatch<{}>; @@ -22,39 +29,62 @@ interface LinkToPageProps { export const LinkToPage = pure(({ match }) => ( - + + + + + diff --git a/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_detection_engine.tsx b/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_detection_engine.tsx new file mode 100644 index 0000000000000..74aec076ec4d5 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/link_to/redirect_to_detection_engine.tsx @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { RouteComponentProps } from 'react-router-dom'; + +import { RedirectWrapper } from './redirect_wrapper'; + +export type DetectionEngineComponentProps = RouteComponentProps<{ + search: string; +}>; + +export const DETECTION_ENGINE_PAGE_NAME = 'detection-engine'; + +export const RedirectToDetectionEnginePage = ({ + location: { search }, +}: DetectionEngineComponentProps) => ( + +); + +export const RedirectToRulesPage = ({ location: { search } }: DetectionEngineComponentProps) => { + return ; +}; + +export const RedirectToCreateRulePage = ({ + location: { search }, +}: DetectionEngineComponentProps) => { + return ; +}; + +export const RedirectToRuleDetailsPage = ({ + location: { search }, +}: DetectionEngineComponentProps) => { + return ; +}; + +export const RedirectToEditRulePage = ({ location: { search } }: DetectionEngineComponentProps) => { + return ( + + ); +}; + +export const getDetectionEngineUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}`; +export const getRulesUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules`; +export const getCreateRuleUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules/create-rule`; +export const getRuleDetailsUrl = () => `#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules/rule-details`; +export const getEditRuleUrl = () => + `#/link-to/${DETECTION_ENGINE_PAGE_NAME}/rules/rule-details/edit-rule`; diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.test.tsx index 3334447739fc5..9d2ef203361bf 100644 --- a/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.test.tsx @@ -26,9 +26,9 @@ jest.mock('../../lib/settings/use_kibana_ui_setting', () => { return { useKibanaUiSetting: () => [false] }; }); -jest.mock('../header_panel', () => { +jest.mock('../header_section', () => { return { - HeaderPanel: () =>
, + HeaderSection: () =>
, }; }); diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.tsx b/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.tsx index 3523723574be6..75e1531ea2b5b 100644 --- a/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/matrix_over_time/index.tsx @@ -12,7 +12,7 @@ import darkTheme from '@elastic/eui/dist/eui_theme_dark.json'; import lightTheme from '@elastic/eui/dist/eui_theme_light.json'; import { EuiLoadingContent } from '@elastic/eui'; import { BarChart } from '../charts/barchart'; -import { HeaderPanel } from '../header_panel'; +import { HeaderSection } from '../header_section'; import { ChartSeriesData, UpdateDateRange } from '../charts/common'; import { MatrixOverTimeHistogramData } from '../../graphql/types'; import { DEFAULT_DARK_MODE } from '../../../common/constants'; @@ -113,7 +113,7 @@ export const MatrixOverTimeHistogram = ({ onMouseEnter={() => setShowInspect(true)} onMouseLeave={() => setShowInspect(false)} > - ( } else { return ( - ( } else { return ( - { anchorPosition="downRight" id="integrations-popover" button={ - setIsPopoverOpen(!isPopoverOpen)} > {i18n.ANOMALY_DETECTION} - + } isOpen={isPopoverOpen} closePopover={() => setIsPopoverOpen(!isPopoverOpen)} @@ -183,7 +184,7 @@ export const MlPopover = React.memo(() => { anchorPosition="downRight" id="integrations-popover" button={ - { }} > {i18n.ANOMALY_DETECTION} - + } isOpen={isPopoverOpen} closePopover={() => setIsPopoverOpen(!isPopoverOpen)} diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx index cf519da617183..97cf9522f488f 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/index.test.tsx @@ -61,6 +61,13 @@ describe('SIEM Navigation', () => { expect(setBreadcrumbs).toHaveBeenNthCalledWith(1, { detailName: undefined, navTabs: { + 'detection-engine': { + disabled: false, + href: '#/link-to/detection-engine', + id: 'detection-engine', + name: 'Detection engine', + urlKey: 'detection-engine', + }, hosts: { disabled: false, href: '#/link-to/hosts', @@ -132,9 +139,17 @@ describe('SIEM Navigation', () => { tabName: undefined, }); wrapper.update(); - expect(setBreadcrumbs).toHaveBeenNthCalledWith(2, { + expect(setBreadcrumbs).toHaveBeenNthCalledWith(1, { detailName: undefined, + filters: [], navTabs: { + 'detection-engine': { + disabled: false, + href: '#/link-to/detection-engine', + id: 'detection-engine', + name: 'Detection engine', + urlKey: 'detection-engine', + }, hosts: { disabled: false, href: '#/link-to/hosts', @@ -164,17 +179,13 @@ describe('SIEM Navigation', () => { urlKey: 'timeline', }, }, - pageName: 'network', - pathName: '/network', - search: '', - tabName: undefined, - query: { query: '', language: 'kuery' }, - filters: [], + pageName: 'hosts', + pathName: '/hosts', + query: { language: 'kuery', query: '' }, savedQuery: undefined, - timeline: { - id: '', - isOpen: false, - }, + search: '', + tabName: 'authentications', + timeline: { id: '', isOpen: false }, timerange: { global: { linkTo: ['timeline'], diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx index ae8d09eeff112..7209be4d715f3 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/index.tsx @@ -6,17 +6,16 @@ import { isEqual } from 'lodash/fp'; import React, { useEffect } from 'react'; -import { compose } from 'redux'; import { connect } from 'react-redux'; +import { compose } from 'redux'; import { RouteSpyState } from '../../utils/route/types'; import { useRouteSpy } from '../../utils/route/use_route_spy'; - +import { makeMapStateToProps } from '../url_state/helpers'; import { setBreadcrumbs } from './breadcrumbs'; import { TabNavigation } from './tab_navigation'; import { TabNavigationProps } from './tab_navigation/types'; import { SiemNavigationComponentProps } from './types'; -import { makeMapStateToProps } from '../url_state/helpers'; export const SiemNavigationComponent = React.memo( ({ @@ -29,7 +28,6 @@ export const SiemNavigationComponent = React.memo { const pageName = SiemPageName.hosts; @@ -78,7 +78,7 @@ describe('Tab Navigation', () => { }); test('it carries the url state in the link', () => { const wrapper = shallow(); - const firstTab = wrapper.find('[data-test-subj="navigation-link-network"]'); + const firstTab = wrapper.find('[data-test-subj="navigation-network"]'); expect(firstTab.props().href).toBe( "#/link-to/network?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))" ); @@ -147,7 +147,7 @@ describe('Tab Navigation', () => { test('it carries the url state in the link', () => { const wrapper = shallow(); const firstTab = wrapper.find( - `[data-test-subj="navigation-link-${HostsTableType.authentications}"]` + `[data-test-subj="navigation-${HostsTableType.authentications}"]` ); expect(firstTab.props().href).toBe( `#/${pageName}/${hostName}/${HostsTableType.authentications}?query=(language:kuery,query:'host.name:%22siem-es%22')&timerange=(global:(linkTo:!(timeline),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)),timeline:(linkTo:!(global),timerange:(from:1558048243696,fromStr:now-24h,kind:relative,to:1558134643697,toStr:now)))` diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx index 3e3c02a1abfa4..27d10cb02a856 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/navigation/tab_navigation/index.tsx @@ -3,40 +3,17 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { EuiTab, EuiTabs, EuiLink } from '@elastic/eui'; +import { EuiTab, EuiTabs } from '@elastic/eui'; import { getOr } from 'lodash/fp'; - import React, { useEffect, useState } from 'react'; -import styled from 'styled-components'; -import classnames from 'classnames'; import { trackUiAction as track, METRIC_TYPE, TELEMETRY_EVENT } from '../../../lib/track_usage'; import { getSearch } from '../helpers'; import { TabNavigationProps } from './types'; -const TabContainer = styled.div` - .euiLink { - color: inherit !important; - - &:focus { - outline: 0; - background: none; - } - - .euiTab.euiTab-isSelected { - cursor: pointer; - } - } - - &.showBorder { - padding: 8px 8px 0; - } -`; - -TabContainer.displayName = 'TabContainer'; - export const TabNavigation = React.memo(props => { - const { display = 'condensed', navTabs, pageName, showBorder, tabName } = props; + const { display, navTabs, pageName, tabName } = props; + const mapLocationToTab = (): string => { return getOr( '', @@ -44,6 +21,7 @@ export const TabNavigation = React.memo(props => { Object.values(navTabs).find(item => tabName === item.id || pageName === item.id) ); }; + const [selectedTabId, setSelectedTabId] = useState(mapLocationToTab()); useEffect(() => { const currentTabSelected = mapLocationToTab(); @@ -57,31 +35,21 @@ export const TabNavigation = React.memo(props => { const renderTabs = (): JSX.Element[] => Object.values(navTabs).map(tab => ( - { + track(METRIC_TYPE.CLICK, `${TELEMETRY_EVENT.TAB_CLICKED}${tab.id}`); + }} > - - { - track(METRIC_TYPE.CLICK, `${TELEMETRY_EVENT.TAB_CLICKED}${tab.id}`); - }} - > - {tab.name} - - - + {tab.name} + )); - return ( - - {renderTabs()} - - ); + + return {renderTabs()}; }); +TabNavigation.displayName = 'TabNavigation'; diff --git a/x-pack/legacy/plugins/siem/public/components/navigation/types.ts b/x-pack/legacy/plugins/siem/public/components/navigation/types.ts index 2918a19df52fd..a8e16c82fbf80 100644 --- a/x-pack/legacy/plugins/siem/public/components/navigation/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/navigation/types.ts @@ -9,7 +9,6 @@ import { UrlStateType } from '../url_state/constants'; export interface SiemNavigationComponentProps { display?: 'default' | 'condensed'; navTabs: Record; - showBorder?: boolean; } export type SearchNavTab = NavTab | { urlKey: UrlStateType; isDetailPage: boolean }; diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/open_timeline/index.test.tsx index 7a0caf14af302..f5207fc6a35fd 100644 --- a/x-pack/legacy/plugins/siem/public/components/open_timeline/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/index.test.tsx @@ -492,7 +492,7 @@ describe('StatefulOpenTimeline', () => { expect( wrapper - .find('[data-test-subj="header-panel-title"]') + .find('[data-test-subj="header-section-title"]') .first() .text() ).toEqual(title); diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/title_row/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/open_timeline/title_row/index.test.tsx index db3d192f06ba1..9303c09c994aa 100644 --- a/x-pack/legacy/plugins/siem/public/components/open_timeline/title_row/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/title_row/index.test.tsx @@ -30,7 +30,7 @@ describe('TitleRow', () => { expect( wrapper - .find('[data-test-subj="header-panel-title"]') + .find('[data-test-subj="header-section-title"]') .first() .text() ).toEqual(title); diff --git a/x-pack/legacy/plugins/siem/public/components/open_timeline/title_row/index.tsx b/x-pack/legacy/plugins/siem/public/components/open_timeline/title_row/index.tsx index f9b107e08afa2..78281a27bb360 100644 --- a/x-pack/legacy/plugins/siem/public/components/open_timeline/title_row/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/open_timeline/title_row/index.tsx @@ -10,7 +10,7 @@ import { pure } from 'recompose'; import * as i18n from '../translations'; import { OpenTimelineProps } from '../types'; -import { HeaderPanel } from '../../header_panel'; +import { HeaderSection } from '../../header_section'; type Props = Pick & { /** The number of timelines currently selected */ @@ -23,7 +23,7 @@ type Props = Pick( ({ onAddTimelinesToFavorites, onDeleteSelected, selectedTimelinesCount, title }) => ( - + {(onAddTimelinesToFavorites || onDeleteSelected) && ( {onAddTimelinesToFavorites && ( @@ -55,7 +55,7 @@ export const TitleRow = pure( )} )} - + ) ); diff --git a/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000000..caf4334cacf57 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/__snapshots__/index.test.tsx.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`HistogramSignals it renders 1`] = ` + + + +`; diff --git a/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.test.tsx new file mode 100644 index 0000000000000..2412d05f3f47d --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.test.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shallow } from 'enzyme'; +import toJson from 'enzyme-to-json'; +import React from 'react'; + +import '../../../../mock/ui_settings'; +import { TestProviders } from '../../../../mock'; +import { HistogramSignals } from './index'; + +jest.mock('../../../../lib/settings/use_kibana_ui_setting'); + +describe('HistogramSignals', () => { + test('it renders', () => { + const wrapper = shallow( + + + + ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.tsx new file mode 100644 index 0000000000000..fa26664930fe5 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/page/detection_engine/histogram_signals/index.tsx @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + Axis, + Chart, + HistogramBarSeries, + Settings, + getAxisId, + getSpecId, + niceTimeFormatByDay, + timeFormatter, +} from '@elastic/charts'; +import React from 'react'; +import { npStart } from 'ui/new_platform'; + +export const HistogramSignals = React.memo(() => { + const sampleChartData = [ + { x: 1571090784000, y: 2, a: 'a' }, + { x: 1571090784000, y: 2, b: 'b' }, + { x: 1571093484000, y: 7, a: 'a' }, + { x: 1571096184000, y: 3, a: 'a' }, + { x: 1571098884000, y: 2, a: 'a' }, + { x: 1571101584000, y: 7, a: 'a' }, + { x: 1571104284000, y: 3, a: 'a' }, + { x: 1571106984000, y: 2, a: 'a' }, + { x: 1571109684000, y: 7, a: 'a' }, + { x: 1571112384000, y: 3, a: 'a' }, + { x: 1571115084000, y: 2, a: 'a' }, + { x: 1571117784000, y: 7, a: 'a' }, + { x: 1571120484000, y: 3, a: 'a' }, + { x: 1571123184000, y: 2, a: 'a' }, + { x: 1571125884000, y: 7, a: 'a' }, + { x: 1571128584000, y: 3, a: 'a' }, + { x: 1571131284000, y: 2, a: 'a' }, + { x: 1571133984000, y: 7, a: 'a' }, + { x: 1571136684000, y: 3, a: 'a' }, + { x: 1571139384000, y: 2, a: 'a' }, + { x: 1571142084000, y: 7, a: 'a' }, + { x: 1571144784000, y: 3, a: 'a' }, + { x: 1571147484000, y: 2, a: 'a' }, + { x: 1571150184000, y: 7, a: 'a' }, + { x: 1571152884000, y: 3, a: 'a' }, + { x: 1571155584000, y: 2, a: 'a' }, + { x: 1571158284000, y: 7, a: 'a' }, + { x: 1571160984000, y: 3, a: 'a' }, + { x: 1571163684000, y: 2, a: 'a' }, + { x: 1571166384000, y: 7, a: 'a' }, + { x: 1571169084000, y: 3, a: 'a' }, + { x: 1571171784000, y: 2, a: 'a' }, + { x: 1571174484000, y: 7, a: 'a' }, + ]; + + return ( + + + + + + + + + + ); +}); +HistogramSignals.displayName = 'HistogramSignals'; diff --git a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx index 3d4a2bc31f2fc..ebabde44c61e9 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_host/index.tsx @@ -9,7 +9,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React, { useState } from 'react'; import { pure } from 'recompose'; -import { HeaderPanel } from '../../../header_panel'; +import { HeaderSection } from '../../../header_section'; import { manageQuery } from '../../../page/manage_query'; import { ID as OverviewHostQueryId, @@ -42,7 +42,7 @@ export const OverviewHost = pure(({ endDate, startDate, setQu return ( setIsHover(true)} onMouseLeave={() => setIsHover(false)}> - (({ endDate, startDate, setQu - + {({ overviewHost, loading, id, inspect, refetch }) => ( diff --git a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_network/index.tsx b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_network/index.tsx index c1629a50341db..b6f1a9cdf26e4 100644 --- a/x-pack/legacy/plugins/siem/public/components/page/overview/overview_network/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/page/overview/overview_network/index.tsx @@ -9,7 +9,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React, { useState } from 'react'; import { pure } from 'recompose'; -import { HeaderPanel } from '../../../header_panel'; +import { HeaderSection } from '../../../header_section'; import { manageQuery } from '../../../page/manage_query'; import { ID as OverviewNetworkQueryId, @@ -42,7 +42,7 @@ export const OverviewNetwork = pure(({ endDate, startDate, setQuery }) return ( setIsHover(true)} onMouseLeave={() => setIsHover(false)}> - (({ endDate, startDate, setQuery }) defaultMessage="View network" /> - + {({ overviewNetwork, loading, id, inspect, refetch }) => ( diff --git a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx index 646d003051e83..7be0c1885811b 100644 --- a/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/paginated_table/index.tsx @@ -35,7 +35,7 @@ import { import { TlsColumns } from '../page/network/tls_table/columns'; import { UncommonProcessTableColumns } from '../page/hosts/uncommon_process_table'; import { UsersColumns } from '../page/network/users_table/columns'; -import { HeaderPanel } from '../header_panel'; +import { HeaderSection } from '../header_section'; import { Loader } from '../loader'; import { useStateToaster } from '../toasters'; import { DEFAULT_MAX_TABLE_QUERY_SIZE } from '../../../common/constants'; @@ -234,7 +234,7 @@ export const PaginatedTable = memo( onMouseEnter={() => setShowInspect(true)} onMouseLeave={() => setShowInspect(false)} > - ( tooltip={headerTooltip} > {!loadingInitial && headerSupplement} - + {loadingInitial ? ( diff --git a/x-pack/legacy/plugins/siem/public/components/progress_inline/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/progress_inline/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000000..c62712e6cfe59 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/progress_inline/__snapshots__/index.test.tsx.snap @@ -0,0 +1,13 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ProgressInline it renders 1`] = ` + + + Test progress + + +`; diff --git a/x-pack/legacy/plugins/siem/public/components/progress_inline/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/progress_inline/index.test.tsx new file mode 100644 index 0000000000000..269bcebdae01a --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/progress_inline/index.test.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shallow } from 'enzyme'; +import toJson from 'enzyme-to-json'; +import React from 'react'; + +import { TestProviders } from '../../mock'; +import '../../mock/ui_settings'; +import { ProgressInline } from './index'; + +jest.mock('../../lib/settings/use_kibana_ui_setting'); + +describe('ProgressInline', () => { + test('it renders', () => { + const wrapper = shallow( + + + {'Test progress'} + + + ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/progress_inline/index.tsx b/x-pack/legacy/plugins/siem/public/components/progress_inline/index.tsx new file mode 100644 index 0000000000000..90eca051e3d11 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/progress_inline/index.tsx @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { EuiProgress } from '@elastic/eui'; +import React from 'react'; +import styled, { css } from 'styled-components'; + +const Wrapper = styled.dl` + ${({ theme }) => css` + align-items: center; + display: inline-flex; + + & > * + * { + margin-left: ${theme.eui.euiSizeS}; + } + + .siemProgressInline__bar { + width: 100px; + } + `} +`; +Wrapper.displayName = 'Wrapper'; + +export interface ProgressInlineProps { + children: string; + current: number; + max: number; + unit: string; +} + +export const ProgressInline = React.memo( + ({ children, current, max, unit }) => ( + +
{children}
+ +
+ +
+ +
+ {current.toLocaleString()} + {'/'} + {max.toLocaleString()} {unit} +
+
+ ) +); +ProgressInline.displayName = 'ProgressInline'; diff --git a/x-pack/legacy/plugins/siem/public/components/subtitle/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/subtitle/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000000..2522d4d1de084 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/subtitle/__snapshots__/index.test.tsx.snap @@ -0,0 +1,9 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Subtitle it renders 1`] = ` + + + +`; diff --git a/x-pack/legacy/plugins/siem/public/components/subtitle/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/subtitle/index.test.tsx new file mode 100644 index 0000000000000..77506f8a466a5 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/subtitle/index.test.tsx @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { mount, shallow } from 'enzyme'; +import toJson from 'enzyme-to-json'; +import React from 'react'; + +import '../../mock/ui_settings'; +import { TestProviders } from '../../mock'; +import { Subtitle } from './index'; + +jest.mock('../../lib/settings/use_kibana_ui_setting'); + +describe('Subtitle', () => { + test('it renders', () => { + const wrapper = shallow( + + + + ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); + + test('it renders one subtitle string item', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('.siemSubtitle__item--text').length).toEqual(1); + }); + + test('it renders multiple subtitle string items', () => { + const wrapper = mount( + + + + ); + + expect(wrapper.find('.siemSubtitle__item--text').length).toEqual(2); + }); + + test('it renders one subtitle React.ReactNode item', () => { + const wrapper = mount( + + {'Test subtitle'}} /> + + ); + + expect(wrapper.find('.siemSubtitle__item--node').length).toEqual(1); + }); + + test('it renders multiple subtitle React.ReactNode items', () => { + const wrapper = mount( + + {'Test subtitle 1'}, {'Test subtitle 2'}]} /> + + ); + + expect(wrapper.find('.siemSubtitle__item--node').length).toEqual(2); + }); + + test('it renders multiple subtitle items of mixed type', () => { + const wrapper = mount( + + {'Test subtitle 2'}]} /> + + ); + + expect(wrapper.find('.siemSubtitle__item').length).toEqual(2); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/subtitle/index.tsx b/x-pack/legacy/plugins/siem/public/components/subtitle/index.tsx new file mode 100644 index 0000000000000..123e14d239182 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/subtitle/index.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import styled, { css } from 'styled-components'; + +const Wrapper = styled.div` + ${({ theme }) => css` + margin-top: ${theme.eui.euiSizeS}; + + .siemSubtitle__item { + color: ${theme.eui.textColors.subdued}; + font-size: ${theme.eui.euiFontSizeXS}; + line-height: ${theme.eui.euiLineHeight}; + + @media only screen and (min-width: ${theme.eui.euiBreakpoints.s}) { + display: inline-block; + margin-right: ${theme.eui.euiSize}; + + &:last-child { + margin-right: 0; + } + } + } + `} +`; +Wrapper.displayName = 'Wrapper'; + +interface SubtitleItemProps { + children: string | React.ReactNode; +} + +const SubtitleItem = React.memo(({ children }) => { + if (typeof children === 'string') { + return

{children}

; + } else { + return
{children}
; + } +}); +SubtitleItem.displayName = 'SubtitleItem'; + +export interface SubtitleProps { + items: string | React.ReactNode | Array; +} + +export const Subtitle = React.memo(({ items }) => { + return ( + + {Array.isArray(items) ? ( + items.map((item, i) => {item}) + ) : ( + {items} + )} + + ); +}); +Subtitle.displayName = 'Subtitle'; diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/constants.ts b/x-pack/legacy/plugins/siem/public/components/url_state/constants.ts index c709a9370ec61..2e700e3e23b64 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/constants.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/constants.ts @@ -6,17 +6,18 @@ export enum CONSTANTS { appQuery = 'query', + detectionEnginePage = 'detectionEngine.page', filters = 'filters', - savedQuery = 'savedQuery', hostsDetails = 'hosts.details', hostsPage = 'hosts.page', networkDetails = 'network.details', networkPage = 'network.page', overviewPage = 'overview.page', + savedQuery = 'savedQuery', timelinePage = 'timeline.page', timerange = 'timerange', timeline = 'timeline', unknown = 'unknown', } -export type UrlStateType = 'host' | 'network' | 'overview' | 'timeline'; +export type UrlStateType = 'detection-engine' | 'host' | 'network' | 'overview' | 'timeline'; diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts index f7487d7a81a7a..aa340b54c1699 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/helpers.ts @@ -72,12 +72,14 @@ export const replaceQueryStringInLocation = (location: Location, queryString: st }; export const getUrlType = (pageName: string): UrlStateType => { - if (pageName === SiemPageName.hosts) { + if (pageName === SiemPageName.overview) { + return 'overview'; + } else if (pageName === SiemPageName.hosts) { return 'host'; } else if (pageName === SiemPageName.network) { return 'network'; - } else if (pageName === SiemPageName.overview) { - return 'overview'; + } else if (pageName === SiemPageName.detectionEngine) { + return 'detection-engine'; } else if (pageName === SiemPageName.timelines) { return 'timeline'; } @@ -97,7 +99,9 @@ export const getCurrentLocation = ( pageName: string, detailName: string | undefined ): LocationTypes => { - if (pageName === SiemPageName.hosts) { + if (pageName === SiemPageName.overview) { + return CONSTANTS.overviewPage; + } else if (pageName === SiemPageName.hosts) { if (detailName != null) { return CONSTANTS.hostsDetails; } @@ -107,8 +111,8 @@ export const getCurrentLocation = ( return CONSTANTS.networkDetails; } return CONSTANTS.networkPage; - } else if (pageName === SiemPageName.overview) { - return CONSTANTS.overviewPage; + } else if (pageName === SiemPageName.detectionEngine) { + return CONSTANTS.detectionEnginePage; } else if (pageName === SiemPageName.timelines) { return CONSTANTS.timelinePage; } diff --git a/x-pack/legacy/plugins/siem/public/components/url_state/types.ts b/x-pack/legacy/plugins/siem/public/components/url_state/types.ts index 44c050a1990ce..13618125325e1 100644 --- a/x-pack/legacy/plugins/siem/public/components/url_state/types.ts +++ b/x-pack/legacy/plugins/siem/public/components/url_state/types.ts @@ -25,6 +25,7 @@ export const ALL_URL_STATE_KEYS: KeyUrlState[] = [ ]; export const URL_STATE_KEYS: Record = { + 'detection-engine': [], host: [ CONSTANTS.appQuery, CONSTANTS.filters, @@ -39,15 +40,16 @@ export const URL_STATE_KEYS: Record = { CONSTANTS.timerange, CONSTANTS.timeline, ], - timeline: [CONSTANTS.timeline, CONSTANTS.timerange], overview: [CONSTANTS.timeline, CONSTANTS.timerange], + timeline: [CONSTANTS.timeline, CONSTANTS.timerange], }; export type LocationTypes = - | CONSTANTS.networkDetails - | CONSTANTS.networkPage + | CONSTANTS.detectionEnginePage | CONSTANTS.hostsDetails | CONSTANTS.hostsPage + | CONSTANTS.networkDetails + | CONSTANTS.networkPage | CONSTANTS.overviewPage | CONSTANTS.timelinePage | CONSTANTS.unknown; diff --git a/x-pack/legacy/plugins/siem/public/components/wrapper_page/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/wrapper_page/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000000..e5311bfb050a3 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/wrapper_page/__snapshots__/index.test.tsx.snap @@ -0,0 +1,47 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`WrapperPage it renders 1`] = ` + + +

+ Test page +

+
+
+`; + +exports[`WrapperPage restrict width custom max width when restrictWidth is number 1`] = ` + + +

+ Test page +

+
+
+`; + +exports[`WrapperPage restrict width custom max width when restrictWidth is string 1`] = ` + + +

+ Test page +

+
+
+`; + +exports[`WrapperPage restrict width default max width when restrictWidth is true 1`] = ` + + +

+ Test page +

+
+
+`; diff --git a/x-pack/legacy/plugins/siem/public/components/wrapper_page/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/wrapper_page/index.test.tsx new file mode 100644 index 0000000000000..95e80e8b9e5de --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/wrapper_page/index.test.tsx @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { shallow } from 'enzyme'; +import toJson from 'enzyme-to-json'; +import React from 'react'; + +import { TestProviders } from '../../mock'; +import '../../mock/ui_settings'; +import { WrapperPage } from './index'; + +jest.mock('../../lib/settings/use_kibana_ui_setting'); + +describe('WrapperPage', () => { + test('it renders', () => { + const wrapper = shallow( + + +

{'Test page'}

+
+
+ ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); + + describe('restrict width', () => { + test('default max width when restrictWidth is true', () => { + const wrapper = shallow( + + +

{'Test page'}

+
+
+ ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); + + test('custom max width when restrictWidth is number', () => { + const wrapper = shallow( + + +

{'Test page'}

+
+
+ ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); + + test('custom max width when restrictWidth is string', () => { + const wrapper = shallow( + + +

{'Test page'}

+
+
+ ); + + expect(toJson(wrapper)).toMatchSnapshot(); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/components/wrapper_page/index.tsx b/x-pack/legacy/plugins/siem/public/components/wrapper_page/index.tsx new file mode 100644 index 0000000000000..5998aa527206e --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/components/wrapper_page/index.tsx @@ -0,0 +1,61 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import classNames from 'classnames'; +import React from 'react'; +import styled, { css } from 'styled-components'; + +import { gutterTimeline } from '../../lib/helpers'; + +const Wrapper = styled.div` + ${({ theme }) => css` + padding: ${theme.eui.paddingSizes.l} ${gutterTimeline} ${theme.eui.paddingSizes.l} + ${theme.eui.paddingSizes.l}; + + &.siemWrapperPage--restrictWidthDefault, + &.siemWrapperPage--restrictWidthCustom { + box-sizing: content-box; + margin: 0 auto; + } + + &.siemWrapperPage--restrictWidthDefault { + max-width: 1000px; + } + `} +`; +Wrapper.displayName = 'Wrapper'; + +export interface WrapperPageProps { + children: React.ReactNode; + className?: string; + restrictWidth?: boolean | number | string; + style?: Record; +} + +export const WrapperPage = React.memo( + ({ children, className, restrictWidth, style }) => { + const classes = classNames(className, { + siemWrapperPage: true, + 'siemWrapperPage--restrictWidthDefault': + restrictWidth && typeof restrictWidth === 'boolean' && restrictWidth === true, + 'siemWrapperPage--restrictWidthCustom': restrictWidth && typeof restrictWidth !== 'boolean', + }); + + let customStyle: WrapperPageProps['style']; + + if (restrictWidth && typeof restrictWidth !== 'boolean') { + const value = typeof restrictWidth === 'number' ? `${restrictWidth}px` : restrictWidth; + customStyle = { ...style, maxWidth: value }; + } + + return ( + + {children} + + ); + } +); +WrapperPage.displayName = 'WrapperPage'; diff --git a/x-pack/legacy/plugins/siem/public/lib/helpers/index.tsx b/x-pack/legacy/plugins/siem/public/lib/helpers/index.tsx index 659ecbadc34d2..5706dcc50ed25 100644 --- a/x-pack/legacy/plugins/siem/public/lib/helpers/index.tsx +++ b/x-pack/legacy/plugins/siem/public/lib/helpers/index.tsx @@ -42,3 +42,9 @@ export const assertUnreachable = ( ): never => { throw new Error(`${message}: ${x}`); }; + +/** + * Global variables + */ + +export const gutterTimeline = '70px'; // Michael: Temporary until timeline is moved. diff --git a/x-pack/legacy/plugins/siem/public/pages/404.tsx b/x-pack/legacy/plugins/siem/public/pages/404.tsx index 58a3c904b89a0..f806a5a7fcdd3 100644 --- a/x-pack/legacy/plugins/siem/public/pages/404.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/404.tsx @@ -8,13 +8,14 @@ import React from 'react'; import { pure } from 'recompose'; import { FormattedMessage } from '@kbn/i18n/react'; +import { WrapperPage } from '../components/wrapper_page'; + export const NotFoundPage = pure(() => ( -
+ -
+ )); - NotFoundPage.displayName = 'NotFoundPage'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/create_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/create_rule/index.tsx new file mode 100644 index 0000000000000..47a3527aff99c --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/create_rule/index.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; + +import { HeaderPage } from '../../../components/header_page'; +import { WrapperPage } from '../../../components/wrapper_page'; +import { SpyRoute } from '../../../utils/route/spy_routes'; +import * as i18n from './translations'; + +export const CreateRuleComponent = React.memo(() => { + return ( + <> + + + + + + + ); +}); +CreateRuleComponent.displayName = 'CreateRuleComponent'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/create_rule/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/create_rule/translations.ts new file mode 100644 index 0000000000000..884f3f3741228 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/create_rule/translations.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const PAGE_TITLE = i18n.translate('xpack.siem.detectionEngine.createRule.pageTitle', { + defaultMessage: 'Create new rule', +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx new file mode 100644 index 0000000000000..9b63a6e160e42 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine.tsx @@ -0,0 +1,205 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + EuiButton, + EuiFilterButton, + EuiFilterGroup, + EuiPanel, + EuiSelect, + EuiSpacer, +} from '@elastic/eui'; +import React, { useState } from 'react'; +import { StickyContainer } from 'react-sticky'; + +import { FiltersGlobal } from '../../components/filters_global'; +import { HeaderPage } from '../../components/header_page'; +import { HeaderSection } from '../../components/header_section'; +import { HistogramSignals } from '../../components/page/detection_engine/histogram_signals'; +import { SiemSearchBar } from '../../components/search_bar'; +import { + UtilityBar, + UtilityBarAction, + UtilityBarGroup, + UtilityBarSection, + UtilityBarText, +} from '../../components/detection_engine/utility_bar'; +import { WrapperPage } from '../../components/wrapper_page'; +import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source'; +import { SpyRoute } from '../../utils/route/spy_routes'; +import { DetectionEngineEmptyPage } from './detection_engine_empty_page'; +import * as i18n from './translations'; + +const OpenSignals = React.memo(() => { + return ( + <> + + + + {`${i18n.PANEL_SUBTITLE_SHOWING}: 7,712 signals`} + + + + {'Selected: 20 signals'} + + {'Batch actions context menu here.'}

} + > + {'Batch actions'} +
+ + + {'Select all signals on all pages'} + +
+ + + {'Clear 7 filters'} + + {'Clear aggregation'} + +
+ + + + {'Customize columns context menu here.'}

} + > + {'Customize columns'} +
+ + {'Aggregate data'} +
+
+
+ + {/* Michael: Open signals datagrid here. Talk to Chandler Prall about possibility of early access. If not possible, use basic table. */} + + ); +}); + +const ClosedSignals = React.memo(() => { + return ( + <> + + + + {`${i18n.PANEL_SUBTITLE_SHOWING}: 7,712 signals`} + + + + + + {'Customize columns context menu here.'}

} + > + {'Customize columns'} +
+ + {'Aggregate data'} +
+
+
+ + {/* Michael: Closed signals datagrid here. Talk to Chandler Prall about possibility of early access. If not possible, use basic table. */} + + ); +}); + +export const DetectionEngineComponent = React.memo(() => { + const sampleChartOptions = [ + { text: 'Risk scores', value: 'risk_scores' }, + { text: 'Severities', value: 'severities' }, + { text: 'Top destination IPs', value: 'destination_ips' }, + { text: 'Top event actions', value: 'event_actions' }, + { text: 'Top event categories', value: 'event_categories' }, + { text: 'Top host names', value: 'host_names' }, + { text: 'Top rule types', value: 'rule_types' }, + { text: 'Top rules', value: 'rules' }, + { text: 'Top source IPs', value: 'source_ips' }, + { text: 'Top users', value: 'users' }, + ]; + + const filterGroupOptions = ['open', 'closed']; + const [filterGroupState, setFilterGroupState] = useState(filterGroupOptions[0]); + + return ( + <> + + {({ indicesExist, indexPattern }) => { + return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( + + + + + + + + + {i18n.BUTTON_MANAGE_RULES} + + + + + + {}} + prepend="Stack by" + value={sampleChartOptions[0].value} + /> + + + + + + + + + + + setFilterGroupState(filterGroupOptions[0])} + withNext + > + {'Open signals'} + + + setFilterGroupState(filterGroupOptions[1])} + > + {'Closed signals'} + + + + + {filterGroupState === filterGroupOptions[0] ? : } + + + + ) : ( + + + + + + ); + }} + + + + + ); +}); +DetectionEngineComponent.displayName = 'DetectionEngineComponent'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_empty_page.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_empty_page.tsx new file mode 100644 index 0000000000000..cb3e690615395 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/detection_engine_empty_page.tsx @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import chrome from 'ui/chrome'; +import { documentationLinks } from 'ui/documentation_links'; + +import { EmptyPage } from '../../components/empty_page'; +import * as i18n from './translations'; + +const basePath = chrome.getBasePath(); + +export const DetectionEngineEmptyPage = React.memo(() => ( + +)); +DetectionEngineEmptyPage.displayName = 'DetectionEngineEmptyPage'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/edit_rule/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/edit_rule/index.tsx new file mode 100644 index 0000000000000..9b8607fdc7685 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/edit_rule/index.tsx @@ -0,0 +1,128 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + EuiButton, + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiSpacer, + EuiTabbedContent, +} from '@elastic/eui'; +import React from 'react'; + +import { HeaderPage } from '../../../components/header_page'; +import { HeaderSection } from '../../../components/header_section'; +import { WrapperPage } from '../../../components/wrapper_page'; +import { SpyRoute } from '../../../utils/route/spy_routes'; +import * as i18n from './translations'; + +const Define = React.memo(() => ( + <> + + + + + + +)); +Define.displayName = 'Define'; + +const About = React.memo(() => ( + <> + + + + + + +)); +About.displayName = 'About'; + +const Schedule = React.memo(() => ( + <> + + + + + + +)); +Schedule.displayName = 'Schedule'; + +export const EditRuleComponent = React.memo(() => { + return ( + <> + + + + + + {'Cancel'} + + + + + + {'Save changes'} + + + + + + , + }, + { + id: 'tabAbout', + name: 'About', + content: , + }, + { + id: 'tabSchedule', + name: 'Schedule', + content: , + }, + ]} + /> + + + + + + + {'Cancel'} + + + + + + {'Save changes'} + + + + + + + + ); +}); +EditRuleComponent.displayName = 'EditRuleComponent'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/edit_rule/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/edit_rule/translations.ts new file mode 100644 index 0000000000000..cc2e2565eb8d0 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/edit_rule/translations.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const PAGE_TITLE = i18n.translate('xpack.siem.detectionEngine.editRule.pageTitle', { + defaultMessage: 'Edit rule settings', +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/index.tsx new file mode 100644 index 0000000000000..90524b4da0af4 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/index.tsx @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { Redirect, Route, Switch, RouteComponentProps } from 'react-router-dom'; + +import { CreateRuleComponent } from './create_rule'; +import { DetectionEngineComponent } from './detection_engine'; +import { EditRuleComponent } from './edit_rule'; +import { RuleDetailsComponent } from './rule_details'; +import { RulesComponent } from './rules'; + +const detectionEnginePath = `/:pageName(detection-engine)`; + +type Props = Partial> & { url: string }; + +export const DetectionEngineContainer = React.memo(() => ( + + } strict /> + } /> + } + /> + } + /> + } + /> + ( + + )} + /> + +)); +DetectionEngineContainer.displayName = 'DetectionEngineContainer'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rule_details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rule_details/index.tsx new file mode 100644 index 0000000000000..da3e5fb2083dd --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rule_details/index.tsx @@ -0,0 +1,660 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { + EuiBasicTable, + EuiButton, + EuiButtonIcon, + EuiCallOut, + EuiFilterButton, + EuiFilterGroup, + EuiFlexGroup, + EuiFlexItem, + EuiIconTip, + EuiPanel, + EuiPopover, + EuiSelect, + EuiSpacer, + EuiSwitch, + EuiTabbedContent, + EuiTextColor, +} from '@elastic/eui'; +import moment from 'moment'; +import React, { useState } from 'react'; +import { StickyContainer } from 'react-sticky'; + +import { getEmptyTagValue } from '../../../components/empty_value'; +import { FiltersGlobal } from '../../../components/filters_global'; +import { HeaderPage } from '../../../components/header_page'; +import { HeaderSection } from '../../../components/header_section'; +import { HistogramSignals } from '../../../components/page/detection_engine/histogram_signals'; +import { ProgressInline } from '../../../components/progress_inline'; +import { SiemSearchBar } from '../../../components/search_bar'; +import { + UtilityBar, + UtilityBarAction, + UtilityBarGroup, + UtilityBarSection, + UtilityBarText, +} from '../../../components/detection_engine/utility_bar'; +import { WrapperPage } from '../../../components/wrapper_page'; +import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../../containers/source'; +import { SpyRoute } from '../../../utils/route/spy_routes'; +import { DetectionEngineEmptyPage } from '../detection_engine_empty_page'; +import * as i18n from './translations'; + +// Michael: Will need to change this to get the current datetime format from Kibana settings. +const dateTimeFormat = (value: string) => { + return moment(value).format('M/D/YYYY, h:mm A'); +}; + +const OpenSignals = React.memo(() => { + return ( + <> + + + + {'Showing: 439 signals'} + + + + {'Selected: 20 signals'} + + {'Batch actions context menu here.'}

} + > + {'Batch actions'} +
+ + + {'Select all signals on all pages'} + +
+ + + {'Clear 7 filters'} + + {'Clear aggregation'} + +
+ + + + {'Customize columns context menu here.'}

} + > + {'Customize columns'} +
+ + {'Aggregate data'} +
+
+
+ + {/* Michael: Open signals datagrid here. Talk to Chandler Prall about possibility of early access. If not possible, use basic table. */} + + ); +}); + +const ClosedSignals = React.memo(() => { + return ( + <> + + + + {'Showing: 439 signals'} + + + + + + {'Customize columns context menu here.'}

} + > + {'Customize columns'} +
+ + {'Aggregate data'} +
+
+
+ + {/* Michael: Closed signals datagrid here. Talk to Chandler Prall about possibility of early access. If not possible, use basic table. */} + + ); +}); + +const Signals = React.memo(() => { + const sampleChartOptions = [ + { text: 'Risk scores', value: 'risk_scores' }, + { text: 'Severities', value: 'severities' }, + { text: 'Top destination IPs', value: 'destination_ips' }, + { text: 'Top event actions', value: 'event_actions' }, + { text: 'Top event categories', value: 'event_categories' }, + { text: 'Top host names', value: 'host_names' }, + { text: 'Top source IPs', value: 'source_ips' }, + { text: 'Top users', value: 'users' }, + ]; + + const filterGroupOptions = ['open', 'closed']; + const [filterGroupState, setFilterGroupState] = useState(filterGroupOptions[0]); + + return ( + <> + + + + + {}} + prepend="Stack by" + value={sampleChartOptions[0].value} + /> + + + + + + + + + + + setFilterGroupState(filterGroupOptions[0])} + withNext + > + {'Open signals'} + + + setFilterGroupState(filterGroupOptions[1])} + > + {'Closed signals'} + + + + + {filterGroupState === filterGroupOptions[0] ? : } + + + ); +}); +Signals.displayName = 'Signals'; + +const ActivityMonitor = React.memo(() => { + interface ColumnTypes { + id: number; + ran: string; + lookedBackTo: string; + status: string; + response: string | undefined; + } + + interface PageTypes { + index: number; + size: number; + } + + interface SortTypes { + field: string; + direction: string; + } + + const actions = [ + { + available: (item: ColumnTypes) => item.status === 'Running', + description: 'Stop', + icon: 'stop', + isPrimary: true, + name: 'Stop', + onClick: () => {}, + type: 'icon', + }, + { + available: (item: ColumnTypes) => item.status === 'Stopped', + description: 'Resume', + icon: 'play', + isPrimary: true, + name: 'Resume', + onClick: () => {}, + type: 'icon', + }, + ]; + + // Michael: Are we able to do custom, in-table-header filters, as shown in my wireframes? + const columns = [ + { + field: 'ran', + name: 'Ran', + render: (value: ColumnTypes['ran']) => , + sortable: true, + truncateText: true, + }, + { + field: 'lookedBackTo', + name: 'Looked back to', + render: (value: ColumnTypes['lookedBackTo']) => ( + + ), + sortable: true, + truncateText: true, + }, + { + field: 'status', + name: 'Status', + sortable: true, + truncateText: true, + }, + { + field: 'response', + name: 'Response', + render: (value: ColumnTypes['response']) => { + return value === undefined ? ( + getEmptyTagValue() + ) : ( + <> + {value === 'Fail' ? ( + + {value} + + ) : ( + {value} + )} + + ); + }, + sortable: true, + truncateText: true, + }, + { + actions, + width: '40px', + }, + ]; + + const sampleTableData = [ + { + id: 1, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Running', + }, + { + id: 2, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Stopped', + }, + { + id: 3, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Fail', + }, + { + id: 4, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 5, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 6, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 7, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 8, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 9, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 10, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 11, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 12, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 13, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 14, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 15, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 16, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 17, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 18, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 19, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 20, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 21, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + ]; + + const [itemsTotalState] = useState(sampleTableData.length); + const [pageState, setPageState] = useState({ index: 0, size: 20 }); + // const [selectedState, setSelectedState] = useState([]); + const [sortState, setSortState] = useState({ field: 'ran', direction: 'desc' }); + + return ( + <> + + + + + + + + + {'Showing: 39 activites'} + + + + {'Selected: 2 activities'} + + {'Stop selected'} + + + + {'Clear 7 filters'} + + + + + { + setPageState(page); + setSortState(sort); + }} + pagination={{ + pageIndex: pageState.index, + pageSize: pageState.size, + totalItemCount: itemsTotalState, + pageSizeOptions: [5, 10, 20], + }} + selection={{ + selectable: (item: ColumnTypes) => item.status !== 'Completed', + selectableMessage: (selectable: boolean) => + selectable ? undefined : 'Completed runs cannot be acted upon', + onSelectionChange: (selectedItems: ColumnTypes[]) => { + // setSelectedState(selectedItems); + }, + }} + sorting={{ + sort: sortState, + }} + /> + + + ); +}); +ActivityMonitor.displayName = 'ActivityMonitor'; + +export const RuleDetailsComponent = React.memo(() => { + const [popoverState, setPopoverState] = useState(false); + + return ( + <> + + {({ indicesExist, indexPattern }) => { + return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( + + + + + + + + {'Status: Running'} + , + ]} + title="Automated exfiltration" + > + + + {}} /> + + + + + + + {'Edit rule settings'} + + + + + setPopoverState(!popoverState)} + /> + } + closePopover={() => setPopoverState(false)} + isOpen={popoverState} + > +

{'Overflow context menu here.'}

+
+
+
+
+
+
+ + +

{'Full fail message here.'}

+
+ + + + + + + + + + + + + + + {/*

{'Description'}

*/} + + {/* + +

{'Description'}

+
+ + +

{'Severity'}

+
+ + +

{'Risk score boost'}

+
+ + +

{'References'}

+
+ + +

{'False positives'}

+
+ + +

{'Mitre ATT&CK types'}

+
+ + +

{'Tags'}

+
+
*/} +
+
+ + + + + + +
+ + + + , + }, + { + id: 'tabActivityMonitor', + name: 'Activity monitor', + content: , + }, + ]} + /> +
+
+ ) : ( + + + + + + ); + }} +
+ + + + ); +}); +RuleDetailsComponent.displayName = 'RuleDetailsComponent'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rule_details/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rule_details/translations.ts new file mode 100644 index 0000000000000..3dd5945ff597c --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rule_details/translations.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const PAGE_TITLE = i18n.translate('xpack.siem.detectionEngine.ruleDetails.pageTitle', { + defaultMessage: 'Rule details', +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx new file mode 100644 index 0000000000000..41a6cf54ff5ab --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/index.tsx @@ -0,0 +1,1076 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; +import { + EuiBadge, + EuiBasicTable, + EuiButton, + EuiFieldSearch, + EuiFlexGroup, + EuiFlexItem, + EuiHealth, + EuiIconTip, + EuiLink, + EuiPanel, + EuiSpacer, + EuiSwitch, + EuiTabbedContent, + EuiTextColor, +} from '@elastic/eui'; +import moment from 'moment'; +import React, { useState } from 'react'; + +import { getEmptyTagValue } from '../../../components/empty_value'; +import { HeaderPage } from '../../../components/header_page'; +import { HeaderSection } from '../../../components/header_section'; +import { + UtilityBar, + UtilityBarAction, + UtilityBarGroup, + UtilityBarSection, + UtilityBarText, +} from '../../../components/detection_engine/utility_bar'; +import { WrapperPage } from '../../../components/wrapper_page'; +import { SpyRoute } from '../../../utils/route/spy_routes'; +import * as i18n from './translations'; + +// Michael: Will need to change this to get the current datetime format from Kibana settings. +const dateTimeFormat = (value: string) => { + return moment(value).format('M/D/YYYY, h:mm A'); +}; + +const AllRules = React.memo(() => { + interface RuleTypes { + href: string; + name: string; + status: string; + } + + interface LastResponseTypes { + type: string; + message?: string; + } + + interface ColumnTypes { + id: number; + rule: RuleTypes; + method: string; + severity: string; + lastCompletedRun: string; + lastResponse: LastResponseTypes; + tags: string | string[]; + activate: boolean; + } + + interface PageTypes { + index: number; + size: number; + } + + interface SortTypes { + field: string; + direction: string; + } + + const actions = [ + { + description: 'Edit rule settings', + icon: 'visControls', + name: 'Edit rule settings', + onClick: () => {}, + }, + { + description: 'Run rule manually…', + icon: 'play', + name: 'Run rule manually…', + onClick: () => {}, + }, + { + description: 'Duplicate rule…', + icon: 'copy', + name: 'Duplicate rule…', + onClick: () => {}, + }, + { + description: 'Export rule', + icon: 'exportAction', + name: 'Export rule', + onClick: () => {}, + }, + { + description: 'Delete rule…', + icon: 'trash', + name: 'Delete rule…', + onClick: () => {}, + }, + ]; + + // Michael: Are we able to do custom, in-table-header filters, as shown in my wireframes? + const columns = [ + { + field: 'rule', + name: 'Rule', + render: (value: ColumnTypes['rule']) => ( +
+ {value.name}{' '} + {value.status} +
+ ), + sortable: true, + truncateText: true, + width: '24%', + }, + { + field: 'method', + name: 'Method', + sortable: true, + truncateText: true, + }, + { + field: 'severity', + name: 'Severity', + render: (value: ColumnTypes['severity']) => ( + + {value} + + ), + sortable: true, + truncateText: true, + }, + { + field: 'lastCompletedRun', + name: 'Last completed run', + render: (value: ColumnTypes['lastCompletedRun']) => { + return value === undefined ? ( + getEmptyTagValue() + ) : ( + + ); + }, + sortable: true, + truncateText: true, + width: '16%', + }, + { + field: 'lastResponse', + name: 'Last response', + render: (value: ColumnTypes['lastResponse']) => { + return value === undefined ? ( + getEmptyTagValue() + ) : ( + <> + {value.type === 'Fail' ? ( + + {value.type} + + ) : ( + {value.type} + )} + + ); + }, + sortable: true, + truncateText: true, + }, + { + field: 'tags', + name: 'Tags', + render: (value: ColumnTypes['tags']) => ( +
+ {typeof value !== 'string' ? ( + <> + {value.map((tag, i) => ( + + {tag} + + ))} + + ) : ( + {value} + )} +
+ ), + sortable: true, + truncateText: true, + width: '20%', + }, + { + align: 'center', + field: 'activate', + name: 'Activate', + render: (value: ColumnTypes['activate']) => ( + // Michael: Uncomment props below when EUI 14.9.0 is added to Kibana. + {}} showLabel={false} /> + ), + sortable: true, + width: '65px', + }, + { + actions, + width: '40px', + }, + ]; + + const sampleTableData = [ + { + id: 1, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Low', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: ['attack.t1234', 'attack.t4321'], + activate: true, + }, + { + id: 2, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Medium', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Fail', + message: 'Full fail message here.', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 3, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'High', + tags: 'attack.t1234', + activate: false, + }, + { + id: 4, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 5, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 6, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 7, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 8, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 9, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 10, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 11, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 12, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 13, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 14, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 15, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 16, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 17, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 18, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 19, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 20, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + { + id: 21, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + status: 'Experimental', + }, + method: 'Custom query', + severity: 'Critical', + lastCompletedRun: '2019-12-28 00:00:00.000-05:00', + lastResponse: { + type: 'Success', + }, + tags: 'attack.t1234', + activate: true, + }, + ]; + + const [itemsTotalState] = useState(sampleTableData.length); + const [pageState, setPageState] = useState({ index: 0, size: 20 }); + // const [selectedState, setSelectedState] = useState([]); + const [sortState, setSortState] = useState({ field: 'rule', direction: 'asc' }); + + return ( + <> + + + + + + + + + + + {'Showing: 39 rules'} + + + + {'Selected: 2 rules'} + + {'Batch actions context menu here.'}

} + > + {'Batch actions'} +
+
+ + + {'Clear 7 filters'} + +
+
+ + { + setPageState(page); + setSortState(sort); + }} + pagination={{ + pageIndex: pageState.index, + pageSize: pageState.size, + totalItemCount: itemsTotalState, + pageSizeOptions: [5, 10, 20], + }} + selection={{ + selectable: () => true, + onSelectionChange: (selectedItems: ColumnTypes[]) => { + // setSelectedState(selectedItems); + }, + }} + sorting={{ + sort: sortState, + }} + /> +
+ + ); +}); +AllRules.displayName = 'AllRules'; + +const ActivityMonitor = React.memo(() => { + interface RuleTypes { + href: string; + name: string; + } + + interface ColumnTypes { + id: number; + rule: RuleTypes; + ran: string; + lookedBackTo: string; + status: string; + response: string | undefined; + } + + interface PageTypes { + index: number; + size: number; + } + + interface SortTypes { + field: string; + direction: string; + } + + const actions = [ + { + available: (item: ColumnTypes) => item.status === 'Running', + description: 'Stop', + icon: 'stop', + isPrimary: true, + name: 'Stop', + onClick: () => {}, + type: 'icon', + }, + { + available: (item: ColumnTypes) => item.status === 'Stopped', + description: 'Resume', + icon: 'play', + isPrimary: true, + name: 'Resume', + onClick: () => {}, + type: 'icon', + }, + ]; + + // Michael: Are we able to do custom, in-table-header filters, as shown in my wireframes? + const columns = [ + { + field: 'rule', + name: 'Rule', + render: (value: ColumnTypes['rule']) => {value.name}, + sortable: true, + truncateText: true, + }, + { + field: 'ran', + name: 'Ran', + render: (value: ColumnTypes['ran']) => , + sortable: true, + truncateText: true, + }, + { + field: 'lookedBackTo', + name: 'Looked back to', + render: (value: ColumnTypes['lookedBackTo']) => ( + + ), + sortable: true, + truncateText: true, + }, + { + field: 'status', + name: 'Status', + sortable: true, + truncateText: true, + }, + { + field: 'response', + name: 'Response', + render: (value: ColumnTypes['response']) => { + return value === undefined ? ( + getEmptyTagValue() + ) : ( + <> + {value === 'Fail' ? ( + + {value} + + ) : ( + {value} + )} + + ); + }, + sortable: true, + truncateText: true, + }, + { + actions, + width: '40px', + }, + ]; + + const sampleTableData = [ + { + id: 1, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Running', + }, + { + id: 2, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Stopped', + }, + { + id: 3, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Fail', + }, + { + id: 4, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 5, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 6, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 7, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 8, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 9, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 10, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 11, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 12, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 13, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 14, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 15, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 16, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 17, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 18, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 19, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 20, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + { + id: 21, + rule: { + href: '#/detection-engine/rules/rule-details', + name: 'Automated exfiltration', + }, + ran: '2019-12-28 00:00:00.000-05:00', + lookedBackTo: '2019-12-28 00:00:00.000-05:00', + status: 'Completed', + response: 'Success', + }, + ]; + + const [itemsTotalState] = useState(sampleTableData.length); + const [pageState, setPageState] = useState({ index: 0, size: 20 }); + // const [selectedState, setSelectedState] = useState([]); + const [sortState, setSortState] = useState({ field: 'ran', direction: 'desc' }); + + return ( + <> + + + + + + + + + {'Showing: 39 activites'} + + + + {'Selected: 2 activities'} + + {'Stop selected'} + + + + {'Clear 7 filters'} + + + + + { + setPageState(page); + setSortState(sort); + }} + pagination={{ + pageIndex: pageState.index, + pageSize: pageState.size, + totalItemCount: itemsTotalState, + pageSizeOptions: [5, 10, 20], + }} + selection={{ + selectable: (item: ColumnTypes) => item.status !== 'Completed', + selectableMessage: (selectable: boolean) => + selectable ? undefined : 'Completed runs cannot be acted upon', + onSelectionChange: (selectedItems: ColumnTypes[]) => { + // setSelectedState(selectedItems); + }, + }} + sorting={{ + sort: sortState, + }} + /> + + + ); +}); +ActivityMonitor.displayName = 'ActivityMonitor'; + +export const RulesComponent = React.memo(() => { + return ( + <> + + + + + + {'Import rule…'} + + + + + + {'Add new rule'} + + + + + + , + }, + { + id: 'tabActivityMonitor', + name: 'Activity monitor', + content: , + }, + ]} + /> + + + + + ); +}); +RulesComponent.displayName = 'RulesComponent'; diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/translations.ts new file mode 100644 index 0000000000000..2b20c726d4b3f --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/translations.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const PAGE_TITLE = i18n.translate('xpack.siem.detectionEngine.rules.pageTitle', { + defaultMessage: 'Rules', +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts new file mode 100644 index 0000000000000..a7e7fa5133a64 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/translations.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { i18n } from '@kbn/i18n'; + +export const PAGE_TITLE = i18n.translate('xpack.siem.detectionEngine.pageTitle', { + defaultMessage: 'Detection engine', +}); + +export const PAGE_SUBTITLE = i18n.translate('xpack.siem.detectionEngine.pageSubtitle', { + defaultMessage: 'Last signal: X minutes ago', +}); + +export const BUTTON_MANAGE_RULES = i18n.translate('xpack.siem.detectionEngine.buttonManageRules', { + defaultMessage: 'Manage rules', +}); + +export const PANEL_SUBTITLE_SHOWING = i18n.translate( + 'xpack.siem.detectionEngine.panelSubtitleShowing', + { + defaultMessage: 'Showing', + } +); + +export const EMPTY_TITLE = i18n.translate('xpack.siem.detectionEngine.emptyTitle', { + defaultMessage: + 'It looks like you don’t have any indices relevant to the detction engine in the SIEM application', +}); + +export const EMPTY_ACTION_PRIMARY = i18n.translate( + 'xpack.siem.detectionEngine.emptyActionPrimary', + { + defaultMessage: 'View setup instructions', + } +); + +export const EMPTY_ACTION_SECONDARY = i18n.translate( + 'xpack.siem.detectionEngine.emptyActionSecondary', + { + defaultMessage: 'Go to documentation', + } +); diff --git a/x-pack/legacy/plugins/siem/public/pages/home/home_navigations.tsx b/x-pack/legacy/plugins/siem/public/pages/home/home_navigations.tsx index 53bcac028b877..220f8a958aa43 100644 --- a/x-pack/legacy/plugins/siem/public/pages/home/home_navigations.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/home/home_navigations.tsx @@ -3,14 +3,16 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import * as i18n from './translations'; -import { SiemPageName, SiemNavTab } from './types'; + import { + getDetectionEngineUrl, getOverviewUrl, getNetworkUrl, getTimelinesUrl, getHostsUrl, } from '../../components/link_to'; +import * as i18n from './translations'; +import { SiemPageName, SiemNavTab } from './types'; export const navTabs: SiemNavTab = { [SiemPageName.overview]: { @@ -34,6 +36,13 @@ export const navTabs: SiemNavTab = { disabled: false, urlKey: 'network', }, + [SiemPageName.detectionEngine]: { + id: SiemPageName.detectionEngine, + name: i18n.DETECTION_ENGINE, + href: getDetectionEngineUrl(), + disabled: false, + urlKey: 'detection-engine', + }, [SiemPageName.timelines]: { id: SiemPageName.timelines, name: i18n.TIMELINES, diff --git a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx index da53ac8fceac4..eb816876bdba8 100644 --- a/x-pack/legacy/plugins/siem/public/pages/home/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/home/index.tsx @@ -4,35 +4,32 @@ * you may not use this file except in compliance with the Elastic License. */ -import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiPage, EuiPageBody } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n/react'; import * as React from 'react'; import { Redirect, Route, Switch } from 'react-router-dom'; import { pure } from 'recompose'; import styled from 'styled-components'; -import { i18n } from '@kbn/i18n'; import { AutoSizer } from '../../components/auto_sizer'; import { DragDropContextWrapper } from '../../components/drag_and_drop/drag_drop_context_wrapper'; import { Flyout, flyoutHeaderHeight } from '../../components/flyout'; +import { HeaderGlobal } from '../../components/header_global'; import { HelpMenu } from '../../components/help_menu'; import { LinkToPage } from '../../components/link_to'; -import { SiemNavigation } from '../../components/navigation'; +import { MlHostConditionalContainer } from '../../components/ml/conditional_links/ml_host_conditional_container'; +import { MlNetworkConditionalContainer } from '../../components/ml/conditional_links/ml_network_conditional_container'; import { StatefulTimeline } from '../../components/timeline'; import { AutoSaveWarningMsg } from '../../components/timeline/auto_save_warning'; +import { UseUrlState } from '../../components/url_state'; +import { WithSource } from '../../containers/source'; +import { SpyRoute } from '../../utils/route/spy_routes'; import { NotFoundPage } from '../404'; +import { DetectionEngineContainer } from '../detection_engine'; import { HostsContainer } from '../hosts'; import { NetworkContainer } from '../network'; import { Overview } from '../overview'; import { Timelines } from '../timelines'; -import { WithSource } from '../../containers/source'; -import { MlPopover } from '../../components/ml_popover/ml_popover'; -import { MlHostConditionalContainer } from '../../components/ml/conditional_links/ml_host_conditional_container'; -import { MlNetworkConditionalContainer } from '../../components/ml/conditional_links/ml_network_conditional_container'; import { navTabs } from './home_navigations'; import { SiemPageName } from './types'; -import { UseUrlState } from '../../components/url_state'; -import { SpyRoute } from '../../utils/route/spy_routes'; /* * This is import is important to keep because if we do not have it @@ -44,30 +41,8 @@ import 'uiExports/embeddableFactories'; const WrappedByAutoSizer = styled.div` height: 100%; `; - WrappedByAutoSizer.displayName = 'WrappedByAutoSizer'; -const gutterTimeline = '70px'; // Temporary until timeline is moved - MichaelMarcialis - -const Page = styled(EuiPage)` - ${({ theme }) => ` - padding: 0 ${gutterTimeline} ${theme.eui.euiSizeL} ${theme.eui.euiSizeL}; - `} -`; - -Page.displayName = 'Page'; - -const NavGlobal = styled.nav` - ${({ theme }) => ` - background: ${theme.eui.euiColorEmptyShade}; - border-bottom: ${theme.eui.euiBorderThin}; - margin: 0 -${gutterTimeline} 0 -${theme.eui.euiSizeL}; - padding: ${theme.eui.euiSize} ${gutterTimeline} ${theme.eui.euiSize} ${theme.eui.euiSizeL}; - `} -`; - -NavGlobal.displayName = 'NavGlobal'; - const usersViewing = ['elastic']; // TODO: get the users viewing this timeline from Elasticsearch (persistance) /** the global Kibana navigation at the top of every page */ @@ -85,8 +60,9 @@ export const HomePage = pure(() => ( {({ measureRef, windowMeasurement: { height: windowHeight = 0 } }) => ( - - + + +
{({ browserFields, indexPattern }) => ( @@ -111,90 +87,59 @@ export const HomePage = pure(() => ( /> - - - - - - - - - - - - - - - - - - - - - - - - - } - /> - ( - - )} - /> - ( - - )} - /> - } - /> - - ( - - )} - /> - ( - - )} - /> - - - + + + } + /> + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + } + /> + + ( + + )} + /> + ( + + )} + /> + + )} - +
+ + +
)}
)); - HomePage.displayName = 'HomePage'; diff --git a/x-pack/legacy/plugins/siem/public/pages/home/translations.ts b/x-pack/legacy/plugins/siem/public/pages/home/translations.ts index 30725828ac5eb..b87ea1c17a117 100644 --- a/x-pack/legacy/plugins/siem/public/pages/home/translations.ts +++ b/x-pack/legacy/plugins/siem/public/pages/home/translations.ts @@ -18,6 +18,10 @@ export const NETWORK = i18n.translate('xpack.siem.navigation.network', { defaultMessage: 'Network', }); +export const DETECTION_ENGINE = i18n.translate('xpack.siem.navigation.detectionEngine', { + defaultMessage: 'Detection engine', +}); + export const TIMELINES = i18n.translate('xpack.siem.navigation.timelines', { defaultMessage: 'Timelines', }); diff --git a/x-pack/legacy/plugins/siem/public/pages/home/types.ts b/x-pack/legacy/plugins/siem/public/pages/home/types.ts index 4adf4485d8e29..101c6a69b08d1 100644 --- a/x-pack/legacy/plugins/siem/public/pages/home/types.ts +++ b/x-pack/legacy/plugins/siem/public/pages/home/types.ts @@ -10,6 +10,7 @@ export enum SiemPageName { overview = 'overview', hosts = 'hosts', network = 'network', + detectionEngine = 'detection-engine', timelines = 'timelines', } @@ -17,6 +18,7 @@ export type SiemNavTabKey = | SiemPageName.overview | SiemPageName.hosts | SiemPageName.network + | SiemPageName.detectionEngine | SiemPageName.timelines; export type SiemNavTab = Record; diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx index 738c0771ffc32..d30665c5a2142 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/details/index.tsx @@ -6,39 +6,40 @@ import { EuiHorizontalRule, EuiSpacer } from '@elastic/eui'; import React, { useContext, useEffect } from 'react'; -import { compose } from 'redux'; import { connect } from 'react-redux'; import { StickyContainer } from 'react-sticky'; +import { compose } from 'redux'; -import { inputsSelectors, State } from '../../../store'; import { FiltersGlobal } from '../../../components/filters_global'; import { HeaderPage } from '../../../components/header_page'; -import { KpiHostDetailsQuery } from '../../../containers/kpi_host_details'; import { LastEventTime } from '../../../components/last_event_time'; +import { AnomalyTableProvider } from '../../../components/ml/anomaly/anomaly_table_provider'; import { hostToCriteria } from '../../../components/ml/criteria/host_to_criteria'; -import { MlCapabilitiesContext } from '../../../components/ml/permissions/ml_capabilities_provider'; import { hasMlUserPermissions } from '../../../components/ml/permissions/has_ml_user_permissions'; -import { AnomalyTableProvider } from '../../../components/ml/anomaly/anomaly_table_provider'; +import { MlCapabilitiesContext } from '../../../components/ml/permissions/ml_capabilities_provider'; import { scoreIntervalToDateTime } from '../../../components/ml/score/score_interval_to_datetime'; -import { setHostDetailsTablesActivePageToZero as dispatchHostDetailsTablesActivePageToZero } from '../../../store/hosts/actions'; import { SiemNavigation } from '../../../components/navigation'; -import { manageQuery } from '../../../components/page/manage_query'; -import { HostOverview } from '../../../components/page/hosts/host_overview'; import { KpiHostsComponent } from '../../../components/page/hosts'; +import { HostOverview } from '../../../components/page/hosts/host_overview'; +import { manageQuery } from '../../../components/page/manage_query'; import { SiemSearchBar } from '../../../components/search_bar'; +import { WrapperPage } from '../../../components/wrapper_page'; import { HostOverviewByNameQuery } from '../../../containers/hosts/overview'; +import { KpiHostDetailsQuery } from '../../../containers/kpi_host_details'; import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../../containers/source'; import { LastEventIndexKey } from '../../../graphql/types'; +import { useKibanaCore } from '../../../lib/compose/kibana_core'; import { convertToBuildEsQuery } from '../../../lib/keury'; +import { inputsSelectors, State } from '../../../store'; +import { setHostDetailsTablesActivePageToZero as dispatchHostDetailsTablesActivePageToZero } from '../../../store/hosts/actions'; import { setAbsoluteRangeDatePicker as dispatchAbsoluteRangeDatePicker } from '../../../store/inputs/actions'; import { SpyRoute } from '../../../utils/route/spy_routes'; -import { useKibanaCore } from '../../../lib/compose/kibana_core'; import { esQuery } from '../../../../../../../../src/plugins/data/public'; import { HostsEmptyPage } from '../hosts_empty_page'; +import { HostDetailsTabs } from './details_tabs'; import { navTabsHostDetails } from './nav_tabs'; import { HostDetailsComponentProps, HostDetailsProps } from './types'; -import { HostDetailsTabs } from './details_tabs'; import { type } from './utils'; const HostOverviewManage = manageQuery(HostOverview); @@ -63,6 +64,7 @@ const HostDetailsComponent = React.memo( }, [detailName]); const capabilities = useContext(MlCapabilitiesContext); const core = useKibanaCore(); + return ( <> @@ -96,14 +98,16 @@ const HostDetailsComponent = React.memo( ...filters, ], }); + return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( - <> - - - - + + + + + ( } title={detailName} /> + ( - - - + + + + ) : ( - <> - + + + - + ); }} + ); } ); - HostDetailsComponent.displayName = 'HostDetailsComponent'; export const makeMapStateToProps = () => { diff --git a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx index a149b5224bd67..4ff666464404e 100644 --- a/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/hosts/hosts.tsx @@ -6,35 +6,35 @@ import { EuiSpacer } from '@elastic/eui'; import * as React from 'react'; -import { compose } from 'redux'; import { connect } from 'react-redux'; import { StickyContainer } from 'react-sticky'; +import { compose } from 'redux'; import { FiltersGlobal } from '../../components/filters_global'; -import { GlobalTimeArgs } from '../../containers/global_time'; import { HeaderPage } from '../../components/header_page'; -import { KpiHostsQuery } from '../../containers/kpi_hosts'; import { LastEventTime } from '../../components/last_event_time'; +import { hasMlUserPermissions } from '../../components/ml/permissions/has_ml_user_permissions'; +import { MlCapabilitiesContext } from '../../components/ml/permissions/ml_capabilities_provider'; import { SiemNavigation } from '../../components/navigation'; import { KpiHostsComponent } from '../../components/page/hosts'; import { manageQuery } from '../../components/page/manage_query'; -import { hasMlUserPermissions } from '../../components/ml/permissions/has_ml_user_permissions'; -import { MlCapabilitiesContext } from '../../components/ml/permissions/ml_capabilities_provider'; import { SiemSearchBar } from '../../components/search_bar'; +import { WrapperPage } from '../../components/wrapper_page'; +import { GlobalTimeArgs } from '../../containers/global_time'; +import { KpiHostsQuery } from '../../containers/kpi_hosts'; import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source'; import { LastEventIndexKey } from '../../graphql/types'; +import { useKibanaCore } from '../../lib/compose/kibana_core'; import { convertToBuildEsQuery } from '../../lib/keury'; import { inputsSelectors, State, hostsModel } from '../../store'; import { setAbsoluteRangeDatePicker as dispatchSetAbsoluteRangeDatePicker } from '../../store/inputs/actions'; import { SpyRoute } from '../../utils/route/spy_routes'; -import { useKibanaCore } from '../../lib/compose/kibana_core'; - import { esQuery } from '../../../../../../../src/plugins/data/public'; import { HostsEmptyPage } from './hosts_empty_page'; +import { HostsTabs } from './hosts_tabs'; import { navTabsHosts } from './nav_tabs'; import * as i18n from './translations'; import { HostsComponentProps, HostsComponentReduxProps } from './types'; -import { HostsTabs } from './hosts_tabs'; const KpiHostsComponentManage = manageQuery(KpiHostsComponent); @@ -64,76 +64,77 @@ const HostsComponent = React.memo( filters, }); return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( - <> - - - - + + + + + } title={i18n.PAGE_TITLE} /> - <> - - {({ kpiHosts, loading, id, inspect, refetch }) => ( - { - setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); - }} - /> - )} - - - - - - - - + + + {({ kpiHosts, loading, id, inspect, refetch }) => ( + { + setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); + }} + /> + )} + + + + + + + + + + + ) : ( - <> - + + + - + ); }} + ); } ); - HostsComponent.displayName = 'HostsComponent'; const makeMapStateToProps = () => { diff --git a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.test.tsx index 174b6fc5ad5d1..a059c4beeaa28 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.test.tsx @@ -163,7 +163,7 @@ describe('Ip Details', () => { wrapper.update(); expect( wrapper - .find('[data-test-subj="ip-details-headline"] [data-test-subj="page_headline_title"]') + .find('[data-test-subj="ip-details-headline"] [data-test-subj="header-page-title"]') .text() ).toEqual('fe80::24ce:f7ff:fede:a571'); }); diff --git a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx index 66a96e71808ef..96111f0479938 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/ip_details/index.tsx @@ -20,9 +20,11 @@ import { manageQuery } from '../../../components/page/manage_query'; import { FlowTargetSelectConnected } from '../../../components/page/network/flow_target_select_connected'; import { IpOverview } from '../../../components/page/network/ip_overview'; import { SiemSearchBar } from '../../../components/search_bar'; +import { WrapperPage } from '../../../components/wrapper_page'; import { IpOverviewQuery } from '../../../containers/ip_overview'; import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../../containers/source'; import { FlowTargetSourceDest, LastEventIndexKey } from '../../../graphql/types'; +import { useKibanaCore } from '../../../lib/compose/kibana_core'; import { decodeIpv6 } from '../../../lib/helpers'; import { convertToBuildEsQuery } from '../../../lib/keury'; import { ConditionalFlexGroup } from '../../../pages/network/navigation/conditional_flex_group'; @@ -31,16 +33,15 @@ import { setAbsoluteRangeDatePicker as dispatchAbsoluteRangeDatePicker } from '. import { setIpDetailsTablesActivePageToZero as dispatchIpDetailsTablesActivePageToZero } from '../../../store/network/actions'; import { SpyRoute } from '../../../utils/route/spy_routes'; import { NetworkEmptyPage } from '../network_empty_page'; - -import { IPDetailsComponentProps } from './types'; -export { getBreadcrumbs } from './utils'; -import { TlsQueryTable } from './tls_query_table'; -import { UsersQueryTable } from './users_query_table'; -import { NetworkTopNFlowQueryTable } from './network_top_n_flow_query_table'; import { NetworkHttpQueryTable } from './network_http_query_table'; import { NetworkTopCountriesQueryTable } from './network_top_countries_query_table'; +import { NetworkTopNFlowQueryTable } from './network_top_n_flow_query_table'; +import { TlsQueryTable } from './tls_query_table'; +import { IPDetailsComponentProps } from './types'; +import { UsersQueryTable } from './users_query_table'; import { esQuery } from '../../../../../../../../src/plugins/data/public'; -import { useKibanaCore } from '../../../lib/compose/kibana_core'; + +export { getBreadcrumbs } from './utils'; const IpOverviewManage = manageQuery(IpOverview); @@ -85,193 +86,197 @@ export const IPDetailsComponent = React.memo( queries: [query], filters, }); + return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( - } - title={ip} - draggableArguments={{ field: `${flowTarget}.ip`, value: ip }} - > - - + + } + title={ip} + > + + - - {({ id, inspect, ipOverviewData, loading, refetch }) => ( - - {({ isLoadingAnomaliesData, anomaliesData }) => ( - { - const fromTo = scoreIntervalToDateTime(score, interval); - setAbsoluteRangeDatePicker({ - id: 'global', - from: fromTo.from, - to: fromTo.to, - }); - }} - /> - )} - - )} - + + {({ id, inspect, ipOverviewData, loading, refetch }) => ( + + {({ isLoadingAnomaliesData, anomaliesData }) => ( + { + const fromTo = scoreIntervalToDateTime(score, interval); + setAbsoluteRangeDatePicker({ + id: 'global', + from: fromTo.from, + to: fromTo.to, + }); + }} + /> + )} + + )} + - + - - - - + + + + - - - - + + + + - + - - - - + + + + - - - - + + + + - + - + - + - + - + - + - + - + + ) : ( - <> - + + - + ); }} + ); } ); - IPDetailsComponent.displayName = 'IPDetailsComponent'; const makeMapStateToProps = () => { diff --git a/x-pack/legacy/plugins/siem/public/pages/network/network.tsx b/x-pack/legacy/plugins/siem/public/pages/network/network.tsx index 33e901d5dbac8..aa7572e5741bd 100644 --- a/x-pack/legacy/plugins/siem/public/pages/network/network.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/network/network.tsx @@ -17,6 +17,7 @@ import { SiemNavigation } from '../../components/navigation'; import { manageQuery } from '../../components/page/manage_query'; import { KpiNetworkComponent } from '../../components/page/network'; import { SiemSearchBar } from '../../components/search_bar'; +import { WrapperPage } from '../../components/wrapper_page'; import { KpiNetworkQuery } from '../../containers/kpi_network'; import { indicesExistOrDataTemporarilyUnavailable, WithSource } from '../../containers/source'; import { LastEventIndexKey } from '../../graphql/types'; @@ -48,6 +49,7 @@ const NetworkComponent = React.memo( capabilitiesFetched, }) => { const core = useKibanaCore(); + return ( <> @@ -58,95 +60,95 @@ const NetworkComponent = React.memo( queries: [query], filters, }); + return indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( - } - title={i18n.PAGE_TITLE} - /> - - - - - - - {({ kpiNetwork, loading, id, inspect, refetch }) => ( - { - setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); - }} - /> + + } + title={i18n.PAGE_TITLE} + /> + + + + + + + {({ kpiNetwork, loading, id, inspect, refetch }) => ( + { + setAbsoluteRangeDatePicker({ id: 'global', from: min, to: max }); + }} + /> + )} + + + {capabilitiesFetched && !isInitializing ? ( + <> + + + + + + + + + ) : ( + )} - - - {capabilitiesFetched && !isInitializing ? ( - <> - - - - - - - - - ) : ( - - )} - - + + + ) : ( - <> - + + - + ); }} + ); } ); - NetworkComponent.displayName = 'NetworkComponent'; const makeMapStateToProps = () => { diff --git a/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx b/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx index d8965f4d49491..de976b1a5c5a3 100644 --- a/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/overview/overview.tsx @@ -7,68 +7,72 @@ import { EuiFlexGroup } from '@elastic/eui'; import moment from 'moment'; import React from 'react'; -import { pure } from 'recompose'; import chrome from 'ui/chrome'; import { documentationLinks } from 'ui/documentation_links'; +import { EmptyPage } from '../../components/empty_page'; import { HeaderPage } from '../../components/header_page'; import { OverviewHost } from '../../components/page/overview/overview_host'; import { OverviewNetwork } from '../../components/page/overview/overview_network'; +import { WrapperPage } from '../../components/wrapper_page'; import { GlobalTime } from '../../containers/global_time'; - -import { Summary } from './summary'; -import { EmptyPage } from '../../components/empty_page'; import { WithSource, indicesExistOrDataTemporarilyUnavailable } from '../../containers/source'; import { SpyRoute } from '../../utils/route/spy_routes'; - +import { Summary } from './summary'; import * as i18n from './translations'; const basePath = chrome.getBasePath(); -export const OverviewComponent = pure(() => { +export const OverviewComponent = React.memo(() => { const dateEnd = Date.now(); const dateRange = moment.duration(24, 'hours').asMilliseconds(); const dateStart = dateEnd - dateRange; return ( <> - + + + + + {({ indicesExist }) => + indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( + + {({ setQuery }) => ( + + + + + + )} + + ) : ( + + ) + } + + - - {({ indicesExist }) => - indicesExistOrDataTemporarilyUnavailable(indicesExist) ? ( - - {({ setQuery }) => ( - - - - - - )} - - ) : ( - - ) - } - ); }); - OverviewComponent.displayName = 'OverviewComponent'; diff --git a/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.tsx b/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.tsx index 90eae605de4b7..93c8397e21431 100644 --- a/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/timelines/timelines_page.tsx @@ -10,14 +10,13 @@ import styled from 'styled-components'; import { HeaderPage } from '../../components/header_page'; import { StatefulOpenTimeline } from '../../components/open_timeline'; +import { WrapperPage } from '../../components/wrapper_page'; import { SpyRoute } from '../../utils/route/spy_routes'; - import * as i18n from './translations'; const TimelinesContainer = styled.div` width: 100%: `; - TimelinesContainer.displayName = 'TimelinesContainer'; interface TimelinesProps { @@ -30,16 +29,19 @@ export const DEFAULT_SEARCH_RESULTS_PER_PAGE = 10; export const TimelinesPage = React.memo(({ apolloClient }) => ( <> - - - - - + + + + + + + + )); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 645b8c427dc15..a5c950a3f2be0 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -10511,7 +10511,6 @@ "xpack.siem.formatted.duration.noDurationTooltip": "期間がありません", "xpack.siem.formatted.duration.zeroNanosecondsTooltip": "0ナノ秒", "xpack.siem.formattedDuration.tooltipLabel": "生", - "xpack.siem.global.addData": "データの投入", "xpack.siem.headerPage.pageSubtitle": "前回のイベント: {beat}", "xpack.siem.host.details.architectureLabel": "アーキテクチャー", "xpack.siem.host.details.firstSeenTitle": "初回の認識", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index d542e66b247ec..bb876ca1eb8b9 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -10601,7 +10601,6 @@ "xpack.siem.formatted.duration.noDurationTooltip": "无持续时间", "xpack.siem.formatted.duration.zeroNanosecondsTooltip": "零纳秒", "xpack.siem.formattedDuration.tooltipLabel": "原始", - "xpack.siem.global.addData": "添加数据", "xpack.siem.headerPage.pageSubtitle": "最后事件:{beat}", "xpack.siem.host.details.architectureLabel": "架构", "xpack.siem.host.details.firstSeenTitle": "首次看到时间", From dd5f7db7f4179c40cac618100d23f5a8b5bc50e7 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Mon, 18 Nov 2019 16:00:23 +0000 Subject: [PATCH 16/17] [ML] Injectables refactor (#50512) (#50910) * [ML] Injectables refactor * removing unrelated files * additional typescript conversion * more typescript conversion * adding some return types * fixing eui errors * typescripting license checks * updated based on review * fixing merge conflict error * converting tests to jest * fixing types --- .../common/util/__tests__/parse_interval.js | 70 --------------- .../ml/common/util/group_color_utils.d.ts | 6 -- ...up_color_utils.js => group_color_utils.ts} | 26 ++++-- .../plugins/ml/common/util/job_utils.d.ts | 2 + .../ml/common/util/parse_interval.test.ts | 34 +++++++ .../{parse_interval.js => parse_interval.ts} | 27 +++--- .../plugins/ml/common/util/string_utils.d.ts | 8 -- .../string_utils.js => string_utils.test.ts} | 19 ++-- .../util/{string_utils.js => string_utils.ts} | 21 +---- .../ml/common/util/validation_utils.d.ts | 7 -- .../ml/common/util/validation_utils.js | 54 ----------- .../ml/common/util/validation_utils.ts | 33 +++++++ x-pack/legacy/plugins/ml/index.ts | 0 .../components/anomalies_table/links_menu.js | 11 +-- .../kibana/__mocks__/kibana_context_value.ts | 1 - .../public/contexts/kibana/kibana_context.ts | 1 - .../contexts/kibana/use_kibana_context.ts | 1 - .../pages/analytics_exploration/directive.tsx | 14 +-- .../pages/analytics_management/directive.tsx | 15 ++-- .../{directive.js => directive.tsx} | 50 +++++++---- .../file_datavisualizer_view.js | 2 +- .../components/import_view/import_view.js | 4 +- .../file_datavisualizer_directive.js | 49 ---------- .../file_datavisualizer_directive.tsx | 75 ++++++++++++++++ .../file_based/{index.js => index.ts} | 1 - .../datavisualizer/index_based/directive.tsx | 14 +-- .../ml/public/explorer/explorer_controller.js | 4 +- .../dedicated_index_switch.tsx | 1 - .../model_plot/model_plot_switch.tsx | 1 - .../sparse_data/sparse_data_switch.tsx | 1 - .../new_job_new/pages/job_type/directive.tsx | 15 ++-- .../new_job_new/pages/new_job/directive.tsx | 14 ++- .../components/job_settings_form.tsx | 1 - .../jobs/new_job_new/recognize/directive.tsx | 13 ++- .../jobs/new_job_new/recognize/resolvers.ts | 10 +-- .../jobs/new_job_new/utils/new_job_utils.ts | 79 ++++++++-------- .../{check_license.js => check_license.tsx} | 73 ++++++--------- .../ml/public/privilege/check_privilege.ts | 18 ++-- ...mat_service.js => field_format_service.ts} | 89 +++++++++---------- .../ml/public/services/job_service.d.ts | 1 + .../{breadcrumbs.js => breadcrumbs.ts} | 46 +++++----- .../edit/{directive.js => directive.tsx} | 21 +++-- .../edit/index.js => calendars/edit/index.ts} | 1 - .../settings/calendars/edit/new_calendar.d.ts | 13 +++ .../calendars/list/calendars_list.d.ts | 12 +++ .../list/{directive.js => directive.tsx} | 12 +-- .../{edit/index.js => list/index.ts} | 2 - .../edit/{directive.js => directive.tsx} | 24 ++--- .../filter_lists/edit/edit_filter_list.d.ts | 13 +++ .../{list/index.js => edit/index.ts} | 2 - .../filter_lists/{index.js => index.ts} | 1 - .../list/{directive.js => directive.tsx} | 41 +++++---- .../filter_lists/list/filter_lists.d.ts} | 7 +- .../index.js => filter_lists/list/index.ts} | 2 - .../ml/public/settings/{index.js => index.ts} | 2 - ...gs_directive.js => settings_directive.tsx} | 37 ++++---- .../timeseriesexplorer/timeseriesexplorer.js | 3 +- .../plugins/ml/public/util/index_utils.ts | 18 +--- .../{check_license.js => check_license.ts} | 44 +++++---- .../lib/check_license/check_license.d.ts | 17 ---- .../{check_license.js => check_license.ts} | 40 +++++---- .../job_validation/validate_bucket_span.js | 2 +- .../job_validation/validate_time_range.js | 2 +- 63 files changed, 571 insertions(+), 656 deletions(-) delete mode 100644 x-pack/legacy/plugins/ml/common/util/__tests__/parse_interval.js delete mode 100644 x-pack/legacy/plugins/ml/common/util/group_color_utils.d.ts rename x-pack/legacy/plugins/ml/common/util/{group_color_utils.js => group_color_utils.ts} (56%) create mode 100644 x-pack/legacy/plugins/ml/common/util/parse_interval.test.ts rename x-pack/legacy/plugins/ml/common/util/{parse_interval.js => parse_interval.ts} (73%) delete mode 100644 x-pack/legacy/plugins/ml/common/util/string_utils.d.ts rename x-pack/legacy/plugins/ml/common/util/{__tests__/string_utils.js => string_utils.test.ts} (63%) rename x-pack/legacy/plugins/ml/common/util/{string_utils.js => string_utils.ts} (59%) delete mode 100644 x-pack/legacy/plugins/ml/common/util/validation_utils.d.ts delete mode 100644 x-pack/legacy/plugins/ml/common/util/validation_utils.js create mode 100644 x-pack/legacy/plugins/ml/common/util/validation_utils.ts mode change 100644 => 100755 x-pack/legacy/plugins/ml/index.ts rename x-pack/legacy/plugins/ml/public/datavisualizer/{directive.js => directive.tsx} (50%) delete mode 100644 x-pack/legacy/plugins/ml/public/datavisualizer/file_based/file_datavisualizer_directive.js create mode 100644 x-pack/legacy/plugins/ml/public/datavisualizer/file_based/file_datavisualizer_directive.tsx rename x-pack/legacy/plugins/ml/public/datavisualizer/file_based/{index.js => index.ts} (99%) rename x-pack/legacy/plugins/ml/public/license/{check_license.js => check_license.tsx} (67%) rename x-pack/legacy/plugins/ml/public/services/{field_format_service.js => field_format_service.ts} (65%) rename x-pack/legacy/plugins/ml/public/settings/{breadcrumbs.js => breadcrumbs.ts} (74%) rename x-pack/legacy/plugins/ml/public/settings/calendars/edit/{directive.js => directive.tsx} (87%) rename x-pack/legacy/plugins/ml/public/settings/{filter_lists/edit/index.js => calendars/edit/index.ts} (99%) create mode 100644 x-pack/legacy/plugins/ml/public/settings/calendars/edit/new_calendar.d.ts create mode 100644 x-pack/legacy/plugins/ml/public/settings/calendars/list/calendars_list.d.ts rename x-pack/legacy/plugins/ml/public/settings/calendars/list/{directive.js => directive.tsx} (92%) rename x-pack/legacy/plugins/ml/public/settings/calendars/{edit/index.js => list/index.ts} (99%) rename x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/{directive.js => directive.tsx} (81%) create mode 100644 x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/edit_filter_list.d.ts rename x-pack/legacy/plugins/ml/public/settings/filter_lists/{list/index.js => edit/index.ts} (99%) rename x-pack/legacy/plugins/ml/public/settings/filter_lists/{index.js => index.ts} (99%) rename x-pack/legacy/plugins/ml/public/settings/filter_lists/list/{directive.js => directive.tsx} (64%) rename x-pack/legacy/plugins/ml/{common/util/parse_interval.d.ts => public/settings/filter_lists/list/filter_lists.d.ts} (67%) rename x-pack/legacy/plugins/ml/public/settings/{calendars/list/index.js => filter_lists/list/index.ts} (99%) rename x-pack/legacy/plugins/ml/public/settings/{index.js => index.ts} (99%) rename x-pack/legacy/plugins/ml/public/settings/{settings_directive.js => settings_directive.tsx} (80%) rename x-pack/legacy/plugins/ml/server/lib/check_license/__tests__/{check_license.js => check_license.ts} (80%) delete mode 100644 x-pack/legacy/plugins/ml/server/lib/check_license/check_license.d.ts rename x-pack/legacy/plugins/ml/server/lib/check_license/{check_license.js => check_license.ts} (66%) diff --git a/x-pack/legacy/plugins/ml/common/util/__tests__/parse_interval.js b/x-pack/legacy/plugins/ml/common/util/__tests__/parse_interval.js deleted file mode 100644 index d06e1412b5090..0000000000000 --- a/x-pack/legacy/plugins/ml/common/util/__tests__/parse_interval.js +++ /dev/null @@ -1,70 +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; - * you may not use this file except in compliance with the Elastic License. - */ - - - -import { parseInterval } from '../parse_interval'; -import expect from '@kbn/expect'; - -describe('ML parse interval util', function () { - it('correctly parses an interval containing unit and value', function () { - let duration = parseInterval('1d'); - expect(duration.as('d')).to.be(1); - - duration = parseInterval('2y'); - expect(duration.as('y')).to.be(2); - - duration = parseInterval('5M'); - expect(duration.as('M')).to.be(5); - - duration = parseInterval('5m'); - expect(duration.as('m')).to.be(5); - - duration = parseInterval('250ms'); - expect(duration.as('ms')).to.be(250); - - duration = parseInterval('100s'); - expect(duration.as('s')).to.be(100); - - duration = parseInterval('23d'); - expect(duration.as('d')).to.be(23); - - duration = parseInterval('52w'); - expect(duration.as('w')).to.be(52); - - duration = parseInterval('0s'); - expect(duration.as('s')).to.be(0); - - duration = parseInterval('0h'); - expect(duration.as('h')).to.be(0); - - }); - - it('correctly handles zero value intervals', function () { - let duration = parseInterval('0h'); - expect(duration.as('h')).to.be(0); - - duration = parseInterval('0d'); - expect(duration).to.not.be.ok(); - }); - - it('returns null for an invalid interval', function () { - let duration = parseInterval(''); - expect(duration).to.not.be.ok(); - - duration = parseInterval(null); - expect(duration).to.not.be.ok(); - - duration = parseInterval('234asdf'); - expect(duration).to.not.be.ok(); - - duration = parseInterval('m'); - expect(duration).to.not.be.ok(); - - duration = parseInterval('1.5h'); - expect(duration).to.not.be.ok(); - }); -}); diff --git a/x-pack/legacy/plugins/ml/common/util/group_color_utils.d.ts b/x-pack/legacy/plugins/ml/common/util/group_color_utils.d.ts deleted file mode 100644 index 4a1a6ebb8fdf3..0000000000000 --- a/x-pack/legacy/plugins/ml/common/util/group_color_utils.d.ts +++ /dev/null @@ -1,6 +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; - * you may not use this file except in compliance with the Elastic License. - */ -export function tabColor(name: string): string; diff --git a/x-pack/legacy/plugins/ml/common/util/group_color_utils.js b/x-pack/legacy/plugins/ml/common/util/group_color_utils.ts similarity index 56% rename from x-pack/legacy/plugins/ml/common/util/group_color_utils.js rename to x-pack/legacy/plugins/ml/common/util/group_color_utils.ts index 51afa2aff4d95..92f5c6b2c1347 100644 --- a/x-pack/legacy/plugins/ml/common/util/group_color_utils.js +++ b/x-pack/legacy/plugins/ml/common/util/group_color_utils.ts @@ -4,9 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import * as euiVars from '@elastic/eui/dist/eui_theme_dark.json'; -import { stringHash } from './string_utils'; - +import euiVars from '@elastic/eui/dist/eui_theme_dark.json'; const COLORS = [ euiVars.euiColorVis0, @@ -20,18 +18,32 @@ const COLORS = [ euiVars.euiColorVis8, euiVars.euiColorVis9, euiVars.euiColorDarkShade, - euiVars.euiColorPrimary + euiVars.euiColorPrimary, ]; -const colorMap = {}; +const colorMap: Record = {}; -export function tabColor(name) { +export function tabColor(name: string): string { if (colorMap[name] === undefined) { const n = stringHash(name); - const color = COLORS[(n % COLORS.length)]; + const color = COLORS[n % COLORS.length]; colorMap[name] = color; return color; } else { return colorMap[name]; } } + +function stringHash(str: string): number { + let hash = 0; + let chr = 0; + if (str.length === 0) { + return hash; + } + for (let i = 0; i < str.length; i++) { + chr = str.charCodeAt(i); + hash = (hash << 5) - hash + chr; // eslint-disable-line no-bitwise + hash |= 0; // eslint-disable-line no-bitwise + } + return hash < 0 ? hash * -2 : hash; +} diff --git a/x-pack/legacy/plugins/ml/common/util/job_utils.d.ts b/x-pack/legacy/plugins/ml/common/util/job_utils.d.ts index 3e4db7f34bde2..e2ea6c163b6ce 100644 --- a/x-pack/legacy/plugins/ml/common/util/job_utils.d.ts +++ b/x-pack/legacy/plugins/ml/common/util/job_utils.d.ts @@ -33,3 +33,5 @@ export const ML_DATA_PREVIEW_COUNT: number; export function isJobIdValid(jobId: string): boolean; export function processCreatedBy(customSettings: { created_by?: string }): void; + +export function mlFunctionToESAggregation(functionName: string): string | null; diff --git a/x-pack/legacy/plugins/ml/common/util/parse_interval.test.ts b/x-pack/legacy/plugins/ml/common/util/parse_interval.test.ts new file mode 100644 index 0000000000000..1717b2f0dd80b --- /dev/null +++ b/x-pack/legacy/plugins/ml/common/util/parse_interval.test.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { parseInterval } from './parse_interval'; + +describe('ML parse interval util', () => { + test('correctly parses an interval containing unit and value', () => { + expect(parseInterval('1d')!.as('d')).toBe(1); + expect(parseInterval('2y')!.as('y')).toBe(2); + expect(parseInterval('5M')!.as('M')).toBe(5); + expect(parseInterval('5m')!.as('m')).toBe(5); + expect(parseInterval('250ms')!.as('ms')).toBe(250); + expect(parseInterval('100s')!.as('s')).toBe(100); + expect(parseInterval('23d')!.as('d')).toBe(23); + expect(parseInterval('52w')!.as('w')).toBe(52); + expect(parseInterval('0s')!.as('s')).toBe(0); + expect(parseInterval('0s')!.as('h')).toBe(0); + }); + + test('correctly handles zero value intervals', () => { + expect(parseInterval('0h')!.as('h')).toBe(0); + expect(parseInterval('0d')).toBe(null); + }); + + test('returns null for an invalid interval', () => { + expect(parseInterval('')).toBe(null); + expect(parseInterval('234asdf')).toBe(null); + expect(parseInterval('m')).toBe(null); + expect(parseInterval('1.5h')).toBe(null); + }); +}); diff --git a/x-pack/legacy/plugins/ml/common/util/parse_interval.js b/x-pack/legacy/plugins/ml/common/util/parse_interval.ts similarity index 73% rename from x-pack/legacy/plugins/ml/common/util/parse_interval.js rename to x-pack/legacy/plugins/ml/common/util/parse_interval.ts index bff1d3ca05e70..98a41be96941b 100644 --- a/x-pack/legacy/plugins/ml/common/util/parse_interval.js +++ b/x-pack/legacy/plugins/ml/common/util/parse_interval.ts @@ -4,17 +4,17 @@ * you may not use this file except in compliance with the Elastic License. */ - - -import moment from 'moment'; +import { duration, Duration, unitOfTime } from 'moment'; import dateMath from '@elastic/datemath'; +type SupportedUnits = unitOfTime.Base; + // Assume interval is in the form (value)(unit), such as "1h" const INTERVAL_STRING_RE = new RegExp('^([0-9]*)\\s*(' + dateMath.units.join('|') + ')$'); // moment.js is only designed to allow fractional values between 0 and 1 // for units of hour or less. -const SUPPORT_ZERO_DURATION_UNITS = ['ms', 's', 'm', 'h']; +const SUPPORT_ZERO_DURATION_UNITS: SupportedUnits[] = ['ms', 's', 'm', 'h']; // Parses an interval String, such as 7d, 1h or 30m to a moment duration. // Differs from the Kibana ui/utils/parse_interval in the following ways: @@ -25,22 +25,25 @@ const SUPPORT_ZERO_DURATION_UNITS = ['ms', 's', 'm', 'h']; // to work with units less than 'day'. // 3. Fractional intervals e.g. 1.5h or 4.5d are not allowed, in line with the behaviour // of the Elasticsearch date histogram aggregation. -export function parseInterval(interval) { - const matches = String(interval).trim().match(INTERVAL_STRING_RE); - if (!Array.isArray(matches)) return null; - if (matches.length < 3) return null; +export function parseInterval(interval: string): Duration | null { + const matches = String(interval) + .trim() + .match(INTERVAL_STRING_RE); + if (!Array.isArray(matches) || matches.length < 3) { + return null; + } try { - const value = parseInt(matches[1]); - const unit = matches[2]; + const value = parseInt(matches[1], 10); + const unit = matches[2] as SupportedUnits; // In line with moment.js, only allow zero value intervals when the unit is less than 'day'. // And check for isNaN as e.g. valueless 'm' will pass the regex test. - if (isNaN(value) || (value < 1 && SUPPORT_ZERO_DURATION_UNITS.indexOf(unit) === -1)) { + if (isNaN(value) || (value < 1 && SUPPORT_ZERO_DURATION_UNITS.indexOf(unit) === -1)) { return null; } - return moment.duration(value, unit); + return duration(value, unit); } catch (e) { return null; } diff --git a/x-pack/legacy/plugins/ml/common/util/string_utils.d.ts b/x-pack/legacy/plugins/ml/common/util/string_utils.d.ts deleted file mode 100644 index f8dbc00643d07..0000000000000 --- a/x-pack/legacy/plugins/ml/common/util/string_utils.d.ts +++ /dev/null @@ -1,8 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -export function renderTemplate(str: string, data: string): string; -export function stringHash(str: string): string; diff --git a/x-pack/legacy/plugins/ml/common/util/__tests__/string_utils.js b/x-pack/legacy/plugins/ml/common/util/string_utils.test.ts similarity index 63% rename from x-pack/legacy/plugins/ml/common/util/__tests__/string_utils.js rename to x-pack/legacy/plugins/ml/common/util/string_utils.test.ts index e2a791c74bd7b..aba2dbd230ada 100644 --- a/x-pack/legacy/plugins/ml/common/util/__tests__/string_utils.js +++ b/x-pack/legacy/plugins/ml/common/util/string_utils.test.ts @@ -4,29 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ - - -import expect from '@kbn/expect'; -import { renderTemplate } from '../string_utils'; +import { renderTemplate } from './string_utils'; describe('ML - string utils', () => { describe('renderTemplate', () => { - - it('returns plain string', () => { + test('returns plain string', () => { const templateString = 'plain string'; const result = renderTemplate(templateString); - expect(result).to.be(result); + expect(result).toBe(result); }); - it('returns rendered template with one replacement', () => { + test('returns rendered template with one replacement', () => { const templateString = 'string with {{one}} replacement'; const result = renderTemplate(templateString, { one: '1' }); - expect(result).to.be('string with 1 replacement'); + expect(result).toBe('string with 1 replacement'); }); - it('returns rendered template with two replacements', () => { + test('returns rendered template with two replacements', () => { const templateString = 'string with {{one}} replacement, and a {{two}} one.'; const result = renderTemplate(templateString, { one: '1', two: '2nd' }); - expect(result).to.be('string with 1 replacement, and a 2nd one.'); + expect(result).toBe('string with 1 replacement, and a 2nd one.'); }); - }); }); diff --git a/x-pack/legacy/plugins/ml/common/util/string_utils.js b/x-pack/legacy/plugins/ml/common/util/string_utils.ts similarity index 59% rename from x-pack/legacy/plugins/ml/common/util/string_utils.js rename to x-pack/legacy/plugins/ml/common/util/string_utils.ts index 383c77f151b72..432baabe773cc 100644 --- a/x-pack/legacy/plugins/ml/common/util/string_utils.js +++ b/x-pack/legacy/plugins/ml/common/util/string_utils.ts @@ -4,15 +4,12 @@ * you may not use this file except in compliance with the Elastic License. */ - - - // A simple template renderer, it replaces mustache/angular style {{...}} tags with // the values provided via the data object -export function renderTemplate(str, data) { +export function renderTemplate(str: string, data?: Record): string { const matches = str.match(/{{(.*?)}}/g); - if (Array.isArray(matches)) { + if (Array.isArray(matches) && data !== undefined) { matches.forEach(v => { str = str.replace(v, data[v.replace(/{{|}}/g, '')]); }); @@ -20,17 +17,3 @@ export function renderTemplate(str, data) { return str; } - -export function stringHash(str) { - let hash = 0; - let chr = ''; - if (str.length === 0) { - return hash; - } - for (let i = 0; i < str.length; i++) { - chr = str.charCodeAt(i); - hash = ((hash << 5) - hash) + chr; - hash |= 0; - } - return hash < 0 ? hash * -2 : hash; -} diff --git a/x-pack/legacy/plugins/ml/common/util/validation_utils.d.ts b/x-pack/legacy/plugins/ml/common/util/validation_utils.d.ts deleted file mode 100644 index 073b111dbb498..0000000000000 --- a/x-pack/legacy/plugins/ml/common/util/validation_utils.d.ts +++ /dev/null @@ -1,7 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -export function isValidJson(json: string): boolean; diff --git a/x-pack/legacy/plugins/ml/common/util/validation_utils.js b/x-pack/legacy/plugins/ml/common/util/validation_utils.js deleted file mode 100644 index 140a8bca45ca2..0000000000000 --- a/x-pack/legacy/plugins/ml/common/util/validation_utils.js +++ /dev/null @@ -1,54 +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; - * you may not use this file except in compliance with the Elastic License. - */ - - - -import { VALIDATION_STATUS } from '../constants/validation'; - -// get the most severe status level from a list of messages -const contains = (arr, str) => arr.findIndex(v => v === str) >= 0; -export function getMostSevereMessageStatus(messages) { - const statuses = messages.map(m => m.status); - return [ - VALIDATION_STATUS.INFO, - VALIDATION_STATUS.WARNING, - VALIDATION_STATUS.ERROR - ].reduce((previous, current) => { - return contains(statuses, current) ? current : previous; - }, VALIDATION_STATUS.SUCCESS); -} - -// extends an angular directive's scope with the necessary methods -// needed to embed the job validation button -export function addJobValidationMethods($scope, service) { - $scope.getDuration = () => ({ - start: $scope.formConfig.start, - end: $scope.formConfig.end - }); - - // isCurrentJobConfig is used to track if the form configuration - // changed since the last job validation was done - $scope.isCurrentJobConfig = false; - // need to pass true as third argument here to track granular changes - $scope.$watch('formConfig', () => { $scope.isCurrentJobConfig = false; }, true); - $scope.getJobConfig = () => { - $scope.isCurrentJobConfig = true; - return service.getJobFromConfig($scope.formConfig); - }; -} - -export function isValidJson(json) { - if(json === null) { - return false; - } - - try { - JSON.parse(json); - return true; - } catch (error) { - return false; - } -} diff --git a/x-pack/legacy/plugins/ml/common/util/validation_utils.ts b/x-pack/legacy/plugins/ml/common/util/validation_utils.ts new file mode 100644 index 0000000000000..1ae5a899a4b4d --- /dev/null +++ b/x-pack/legacy/plugins/ml/common/util/validation_utils.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { VALIDATION_STATUS } from '../constants/validation'; + +// get the most severe status level from a list of messages +const contains = (arr: string[], str: string) => arr.indexOf(str) >= 0; + +export function getMostSevereMessageStatus(messages: Array<{ status: string }>): VALIDATION_STATUS { + const statuses = messages.map(m => m.status); + return [VALIDATION_STATUS.INFO, VALIDATION_STATUS.WARNING, VALIDATION_STATUS.ERROR].reduce( + (previous, current) => { + return contains(statuses, current) ? current : previous; + }, + VALIDATION_STATUS.SUCCESS + ); +} + +export function isValidJson(json: string) { + if (json === null) { + return false; + } + + try { + JSON.parse(json); + return true; + } catch (error) { + return false; + } +} diff --git a/x-pack/legacy/plugins/ml/index.ts b/x-pack/legacy/plugins/ml/index.ts old mode 100644 new mode 100755 diff --git a/x-pack/legacy/plugins/ml/public/components/anomalies_table/links_menu.js b/x-pack/legacy/plugins/ml/public/components/anomalies_table/links_menu.js index d0522e61f3fd8..dfa12e34928d2 100644 --- a/x-pack/legacy/plugins/ml/public/components/anomalies_table/links_menu.js +++ b/x-pack/legacy/plugins/ml/public/components/anomalies_table/links_menu.js @@ -33,7 +33,7 @@ import { ml } from '../../services/ml_api_service'; import { mlJobService } from '../../services/job_service'; import { getUrlForRecord, openCustomUrlWindow } from '../../util/custom_url_utils'; import { formatHumanReadableDateTimeSeconds } from '../../util/date_utils'; -import { getIndexPatterns } from '../../util/index_utils'; +import { getIndexPatternIdFromName } from '../../util/index_utils'; import { replaceStringTokens } from '../../util/string_utils'; @@ -214,7 +214,6 @@ export const LinksMenu = injectI18n(class LinksMenu extends Component { const { intl } = this.props; const categoryId = this.props.anomaly.entityValue; const record = this.props.anomaly.source; - const indexPatterns = getIndexPatterns(); const job = mlJobService.getJob(this.props.anomaly.jobId); if (job === undefined) { @@ -260,13 +259,7 @@ export const LinksMenu = injectI18n(class LinksMenu extends Component { // index configured in the datafeed. If a Kibana index pattern has not been created // for this index, then the user will see a warning message on the Discover tab advising // them that no matching index pattern has been configured. - let indexPatternId = index; - for (let j = 0; j < indexPatterns.length; j++) { - if (indexPatterns[j].get('title') === index) { - indexPatternId = indexPatterns[j].id; - break; - } - } + const indexPatternId = getIndexPatternIdFromName(index) || index; // Get the definition of the category and use the terms or regex to view the // matching events in the Kibana Discover tab depending on whether the diff --git a/x-pack/legacy/plugins/ml/public/contexts/kibana/__mocks__/kibana_context_value.ts b/x-pack/legacy/plugins/ml/public/contexts/kibana/__mocks__/kibana_context_value.ts index 8f54c7f1ad656..0b85769b423f9 100644 --- a/x-pack/legacy/plugins/ml/public/contexts/kibana/__mocks__/kibana_context_value.ts +++ b/x-pack/legacy/plugins/ml/public/contexts/kibana/__mocks__/kibana_context_value.ts @@ -17,6 +17,5 @@ export const kibanaContextValueMock = { currentIndexPattern: indexPatternMock, currentSavedSearch: savedSearchMock, indexPatterns: indexPatternsMock, - kbnBaseUrl: 'url', kibanaConfig: kibanaConfigMock, }; diff --git a/x-pack/legacy/plugins/ml/public/contexts/kibana/kibana_context.ts b/x-pack/legacy/plugins/ml/public/contexts/kibana/kibana_context.ts index afa2a53ef5dcf..aed230a53f62a 100644 --- a/x-pack/legacy/plugins/ml/public/contexts/kibana/kibana_context.ts +++ b/x-pack/legacy/plugins/ml/public/contexts/kibana/kibana_context.ts @@ -21,7 +21,6 @@ export interface KibanaContextValue { currentIndexPattern: IndexPattern; currentSavedSearch: SavedSearch; indexPatterns: IndexPatterns; - kbnBaseUrl: string; kibanaConfig: KibanaConfigTypeFix; } diff --git a/x-pack/legacy/plugins/ml/public/contexts/kibana/use_kibana_context.ts b/x-pack/legacy/plugins/ml/public/contexts/kibana/use_kibana_context.ts index 1cc670875dff3..658a6980aa1ae 100644 --- a/x-pack/legacy/plugins/ml/public/contexts/kibana/use_kibana_context.ts +++ b/x-pack/legacy/plugins/ml/public/contexts/kibana/use_kibana_context.ts @@ -16,7 +16,6 @@ export const useKibanaContext = () => { context.currentIndexPattern === undefined || context.currentSavedSearch === undefined || context.indexPatterns === undefined || - context.kbnBaseUrl === undefined || context.kibanaConfig === undefined ) { throw new Error('required attribute is undefined'); diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/directive.tsx b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/directive.tsx index 4a28e0b0b881e..0a44a7a442980 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/directive.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_exploration/directive.tsx @@ -13,10 +13,9 @@ const module = uiModules.get('apps/ml', ['react']); import { IndexPatterns } from 'ui/index_patterns'; import { I18nContext } from 'ui/i18n'; -import { IPrivate } from 'ui/private'; import { InjectorService } from '../../../../common/types/angular'; -import { SearchItemsProvider } from '../../../jobs/new_job_new/utils/new_job_utils'; +import { createSearchItems } from '../../../jobs/new_job_new/utils/new_job_utils'; import { KibanaConfigTypeFix, KibanaContext } from '../../../contexts/kibana'; @@ -31,19 +30,20 @@ module.directive('mlDataFrameAnalyticsExploration', ($injector: InjectorService) globalState.fetch(); const indexPatterns = $injector.get('indexPatterns'); - const kbnBaseUrl = $injector.get('kbnBaseUrl'); const kibanaConfig = $injector.get('config'); - const Private = $injector.get('Private'); + const $route = $injector.get('$route'); - const createSearchItems = Private(SearchItemsProvider); - const { indexPattern, savedSearch, combinedQuery } = createSearchItems(); + const { indexPattern, savedSearch, combinedQuery } = createSearchItems( + kibanaConfig, + $route.current.locals.indexPattern, + $route.current.locals.savedSearch + ); const kibanaContext = { combinedQuery, currentIndexPattern: indexPattern, currentSavedSearch: savedSearch, indexPatterns, - kbnBaseUrl, kibanaConfig, }; diff --git a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/directive.tsx b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/directive.tsx index 5eb1981e9a5aa..a60afbbcfae48 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/directive.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame_analytics/pages/analytics_management/directive.tsx @@ -13,10 +13,8 @@ const module = uiModules.get('apps/ml', ['react']); import { IndexPatterns } from 'ui/index_patterns'; import { I18nContext } from 'ui/i18n'; -import { IPrivate } from 'ui/private'; - import { InjectorService } from '../../../../common/types/angular'; -import { SearchItemsProvider } from '../../../jobs/new_job_new/utils/new_job_utils'; +import { createSearchItems } from '../../../jobs/new_job_new/utils/new_job_utils'; import { KibanaConfigTypeFix, KibanaContext } from '../../../contexts/kibana'; @@ -28,19 +26,20 @@ module.directive('mlDataFrameAnalyticsManagement', ($injector: InjectorService) restrict: 'E', link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => { const indexPatterns = $injector.get('indexPatterns'); - const kbnBaseUrl = $injector.get('kbnBaseUrl'); const kibanaConfig = $injector.get('config'); - const Private = $injector.get('Private'); + const $route = $injector.get('$route'); - const createSearchItems = Private(SearchItemsProvider); - const { indexPattern, savedSearch, combinedQuery } = createSearchItems(); + const { indexPattern, savedSearch, combinedQuery } = createSearchItems( + kibanaConfig, + $route.current.locals.indexPattern, + $route.current.locals.savedSearch + ); const kibanaContext = { combinedQuery, currentIndexPattern: indexPattern, currentSavedSearch: savedSearch, indexPatterns, - kbnBaseUrl, kibanaConfig, }; diff --git a/x-pack/legacy/plugins/ml/public/datavisualizer/directive.js b/x-pack/legacy/plugins/ml/public/datavisualizer/directive.tsx similarity index 50% rename from x-pack/legacy/plugins/ml/public/datavisualizer/directive.js rename to x-pack/legacy/plugins/ml/public/datavisualizer/directive.tsx index 022f4ea536b44..130042b6dd730 100644 --- a/x-pack/legacy/plugins/ml/public/datavisualizer/directive.js +++ b/x-pack/legacy/plugins/ml/public/datavisualizer/directive.tsx @@ -4,38 +4,52 @@ * you may not use this file except in compliance with the Elastic License. */ -import 'ngreact'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { I18nContext } from 'ui/i18n'; -import { wrapInI18nContext } from 'ui/i18n'; +// @ts-ignore import { uiModules } from 'ui/modules'; const module = uiModules.get('apps/ml', ['react']); +import uiRoutes from 'ui/routes'; import { getDataVisualizerBreadcrumbs } from './breadcrumbs'; import { checkBasicLicense } from '../license/check_license'; import { checkFindFileStructurePrivilege } from '../privilege/check_privilege'; -import uiRoutes from 'ui/routes'; - const template = `
`; -uiRoutes - .when('/datavisualizer', { - template, - k7Breadcrumbs: getDataVisualizerBreadcrumbs, - resolve: { - CheckLicense: checkBasicLicense, - privileges: checkFindFileStructurePrivilege, - } - }); - +uiRoutes.when('/datavisualizer', { + template, + k7Breadcrumbs: getDataVisualizerBreadcrumbs, + resolve: { + CheckLicense: checkBasicLicense, + privileges: checkFindFileStructurePrivilege, + }, +}); +// @ts-ignore import { DatavisualizerSelector } from './datavisualizer_selector'; -module.directive('datavisualizerSelector', function ($injector) { - const reactDirective = $injector.get('reactDirective'); - - return reactDirective(wrapInI18nContext(DatavisualizerSelector), undefined, { restrict: 'E' }, { }); +module.directive('datavisualizerSelector', function() { + return { + scope: {}, + restrict: 'E', + link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => { + ReactDOM.render( + + + , + element[0] + ); + + element.on('$destroy', () => { + ReactDOM.unmountComponentAtNode(element[0]); + scope.$destroy(); + }); + }, + }; }); diff --git a/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js b/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js index 94e9c2c2ac584..852f068ef419f 100644 --- a/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js +++ b/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/components/file_datavisualizer_view/file_datavisualizer_view.js @@ -7,7 +7,7 @@ import { FormattedMessage } from '@kbn/i18n/react'; import React, { - Component, + Component } from 'react'; import { diff --git a/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/components/import_view/import_view.js b/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/components/import_view/import_view.js index c1ffcad948adb..c89f618aa835b 100644 --- a/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/components/import_view/import_view.js +++ b/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/components/import_view/import_view.js @@ -25,7 +25,7 @@ import { ImportErrors } from '../import_errors'; import { ImportSummary } from '../import_summary'; import { ImportSettings } from '../import_settings'; import { ExperimentalBadge } from '../experimental_badge'; -import { getIndexPatternNames, refreshIndexPatterns } from '../../../../util/index_utils'; +import { getIndexPatternNames, loadIndexPatterns } from '../../../../util/index_utils'; import { ml } from '../../../../services/ml_api_service'; import { hasImportPermission } from '../utils'; @@ -359,7 +359,7 @@ export class ImportView extends Component { } async loadIndexPatternNames() { - await refreshIndexPatterns(); + await loadIndexPatterns(); const indexPatternNames = getIndexPatternNames(); this.setState({ indexPatternNames }); } diff --git a/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/file_datavisualizer_directive.js b/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/file_datavisualizer_directive.js deleted file mode 100644 index 8b76e57029bd1..0000000000000 --- a/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/file_datavisualizer_directive.js +++ /dev/null @@ -1,49 +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; - * you may not use this file except in compliance with the Elastic License. - */ - - -import 'ngreact'; - -import { wrapInI18nContext } from 'ui/i18n'; -import { uiModules } from 'ui/modules'; - -const module = uiModules.get('apps/ml', ['react']); - -import { getFileDataVisualizerBreadcrumbs } from './breadcrumbs'; -import { checkBasicLicense } from '../../license/check_license'; -import { checkFindFileStructurePrivilege } from '../../privilege/check_privilege'; -import { getMlNodeCount } from '../../ml_nodes_check/check_ml_nodes'; -import { loadMlServerInfo } from '../../services/ml_server_info'; -import { loadIndexPatterns } from '../../util/index_utils'; -import { FileDataVisualizerPage } from './file_datavisualizer'; - -import uiRoutes from 'ui/routes'; - -const template = ` -
- -`; - -uiRoutes - .when('/filedatavisualizer/?', { - template, - k7Breadcrumbs: getFileDataVisualizerBreadcrumbs, - resolve: { - CheckLicense: checkBasicLicense, - privileges: checkFindFileStructurePrivilege, - indexPatterns: loadIndexPatterns, - mlNodeCount: getMlNodeCount, - loadMlServerInfo, - } - }); - -module.directive('fileDatavisualizerPage', function ($injector) { - const reactDirective = $injector.get('reactDirective'); - const indexPatterns = $injector.get('indexPatterns'); - const kibanaConfig = $injector.get('config'); - - return reactDirective(wrapInI18nContext(FileDataVisualizerPage), undefined, { restrict: 'E' }, { indexPatterns, kibanaConfig }); -}); diff --git a/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/file_datavisualizer_directive.tsx b/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/file_datavisualizer_directive.tsx new file mode 100644 index 0000000000000..6b9c053b88d34 --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/file_datavisualizer_directive.tsx @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; +import { I18nContext } from 'ui/i18n'; + +// @ts-ignore +import { uiModules } from 'ui/modules'; + +const module = uiModules.get('apps/ml', ['react']); + +import uiRoutes from 'ui/routes'; +import { IndexPatterns } from 'ui/index_patterns'; +import { KibanaConfigTypeFix } from '../../contexts/kibana'; +// @ts-ignore +import { getFileDataVisualizerBreadcrumbs } from './breadcrumbs'; +import { InjectorService } from '../../../common/types/angular'; +import { checkBasicLicense } from '../../license/check_license'; +import { checkFindFileStructurePrivilege } from '../../privilege/check_privilege'; +import { getMlNodeCount } from '../../ml_nodes_check/check_ml_nodes'; +import { loadMlServerInfo } from '../../services/ml_server_info'; +import { loadIndexPatterns } from '../../util/index_utils'; +// @ts-ignore +import { FileDataVisualizerPage } from './file_datavisualizer'; + +const template = ` +
+ +`; + +uiRoutes.when('/filedatavisualizer/?', { + template, + k7Breadcrumbs: getFileDataVisualizerBreadcrumbs, + resolve: { + CheckLicense: checkBasicLicense, + privileges: checkFindFileStructurePrivilege, + indexPatterns: loadIndexPatterns, + mlNodeCount: getMlNodeCount, + loadMlServerInfo, + }, +}); + +interface Props { + indexPatterns: IndexPatterns; + kibanaConfig: KibanaConfigTypeFix; +} + +module.directive('fileDatavisualizerPage', function($injector: InjectorService) { + return { + scope: {}, + restrict: 'E', + link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => { + const indexPatterns = $injector.get('indexPatterns'); + const kibanaConfig = $injector.get('config'); + + const props: Props = { + indexPatterns, + kibanaConfig, + }; + ReactDOM.render( + {React.createElement(FileDataVisualizerPage, props)}, + element[0] + ); + + element.on('$destroy', () => { + ReactDOM.unmountComponentAtNode(element[0]); + scope.$destroy(); + }); + }, + }; +}); diff --git a/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/index.js b/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/index.ts similarity index 99% rename from x-pack/legacy/plugins/ml/public/datavisualizer/file_based/index.js rename to x-pack/legacy/plugins/ml/public/datavisualizer/file_based/index.ts index cd7a23430202a..15796ea9ff0bd 100644 --- a/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/index.js +++ b/x-pack/legacy/plugins/ml/public/datavisualizer/file_based/index.ts @@ -4,5 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ - import './file_datavisualizer_directive'; diff --git a/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/directive.tsx b/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/directive.tsx index e621bdd1d8b92..9336986fa1556 100644 --- a/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/directive.tsx +++ b/x-pack/legacy/plugins/ml/public/datavisualizer/index_based/directive.tsx @@ -13,11 +13,10 @@ const module = uiModules.get('apps/ml', ['react']); import { I18nContext } from 'ui/i18n'; import { IndexPatterns } from 'ui/index_patterns'; -import { IPrivate } from 'ui/private'; import { InjectorService } from '../../../common/types/angular'; import { KibanaConfigTypeFix, KibanaContext } from '../../contexts/kibana/kibana_context'; -import { SearchItemsProvider } from '../../jobs/new_job_new/utils/new_job_utils'; +import { createSearchItems } from '../../jobs/new_job_new/utils/new_job_utils'; import { Page } from './page'; @@ -27,19 +26,20 @@ module.directive('mlDataVisualizer', ($injector: InjectorService) => { restrict: 'E', link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => { const indexPatterns = $injector.get('indexPatterns'); - const kbnBaseUrl = $injector.get('kbnBaseUrl'); const kibanaConfig = $injector.get('config'); - const Private = $injector.get('Private'); + const $route = $injector.get('$route'); - const createSearchItems = Private(SearchItemsProvider); - const { indexPattern, savedSearch, combinedQuery } = createSearchItems(); + const { indexPattern, savedSearch, combinedQuery } = createSearchItems( + kibanaConfig, + $route.current.locals.indexPattern, + $route.current.locals.savedSearch + ); const kibanaContext = { combinedQuery, currentIndexPattern: indexPattern, currentSavedSearch: savedSearch, indexPatterns, - kbnBaseUrl, kibanaConfig, }; diff --git a/x-pack/legacy/plugins/ml/public/explorer/explorer_controller.js b/x-pack/legacy/plugins/ml/public/explorer/explorer_controller.js index 6315dd95ea8e4..3bedd90f05c37 100644 --- a/x-pack/legacy/plugins/ml/public/explorer/explorer_controller.js +++ b/x-pack/legacy/plugins/ml/public/explorer/explorer_controller.js @@ -23,7 +23,7 @@ import { import { getAnomalyExplorerBreadcrumbs } from './breadcrumbs'; import { checkFullLicense } from '../license/check_license'; import { checkGetJobsPrivilege } from '../privilege/check_privilege'; -import { getIndexPatterns, loadIndexPatterns } from '../util/index_utils'; +import { loadIndexPatterns } from '../util/index_utils'; import { TimeBuckets } from 'plugins/ml/util/time_buckets'; import { explorer$ } from './explorer_dashboard_service'; import { mlTimefilterRefresh$ } from '../services/timefilter_refresh_service'; @@ -114,7 +114,7 @@ module.controller('MlExplorerController', function ( } // Populate the map of jobs / detectors / field formatters for the selected IDs. - mlFieldFormatService.populateFormats(selectedJobIds, getIndexPatterns()) + mlFieldFormatService.populateFormats(selectedJobIds) .catch((err) => { console.log('Error populating field formats:', err); }) diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/dedicated_index/dedicated_index_switch.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/dedicated_index/dedicated_index_switch.tsx index 5c8f346d71baf..b50a8e0affa2d 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/dedicated_index/dedicated_index_switch.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/dedicated_index/dedicated_index_switch.tsx @@ -30,7 +30,6 @@ export const DedicatedIndexSwitch: FC = () => { checked={useDedicatedIndex} onChange={toggleModelPlot} data-test-subj="mlJobWizardSwitchUseDedicatedIndex" - showLabel={false} label={i18n.translate( 'xpack.ml.newJob.wizard.jobDetailsStep.advancedSection.useDedicatedIndex.title', { diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/model_plot/model_plot_switch.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/model_plot/model_plot_switch.tsx index 41ac4311b71ce..226742bf3e97a 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/model_plot/model_plot_switch.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/job_details_step/components/advanced_section/components/model_plot/model_plot_switch.tsx @@ -30,7 +30,6 @@ export const ModelPlotSwitch: FC = () => { checked={modelPlotEnabled} onChange={toggleModelPlot} data-test-subj="mlJobWizardSwitchModelPlot" - showLabel={false} label={i18n.translate( 'xpack.ml.newJob.wizard.jobDetailsStep.advancedSection.enableModelPlot.title', { diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/sparse_data/sparse_data_switch.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/sparse_data/sparse_data_switch.tsx index 3f2c45f0c62bd..76461e1306333 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/sparse_data/sparse_data_switch.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/components/pick_fields_step/components/sparse_data/sparse_data_switch.tsx @@ -44,7 +44,6 @@ export const SparseDataSwitch: FC = () => { checked={sparseData} onChange={toggleSparseData} data-test-subj="mlJobWizardSwitchSparseData" - showLabel={false} label={i18n.translate('xpack.ml.newJob.wizard.pickFieldsStep.sparseData.title', { defaultMessage: 'Sparse data', })} diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/job_type/directive.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/job_type/directive.tsx index 592dce3ce5b17..0b83c0eced240 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/job_type/directive.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/job_type/directive.tsx @@ -14,10 +14,8 @@ import { timefilter } from 'ui/timefilter'; import { IndexPatterns } from 'ui/index_patterns'; import { I18nContext } from 'ui/i18n'; -import { IPrivate } from 'ui/private'; import { InjectorService } from '../../../../../common/types/angular'; - -import { SearchItemsProvider } from '../../../new_job_new/utils/new_job_utils'; +import { createSearchItems } from '../../../new_job_new/utils/new_job_utils'; import { Page } from './page'; import { KibanaContext, KibanaConfigTypeFix } from '../../../../contexts/kibana'; @@ -32,18 +30,19 @@ module.directive('mlJobTypePage', ($injector: InjectorService) => { timefilter.disableAutoRefreshSelector(); const indexPatterns = $injector.get('indexPatterns'); - const kbnBaseUrl = $injector.get('kbnBaseUrl'); const kibanaConfig = $injector.get('config'); - const Private = $injector.get('Private'); + const $route = $injector.get('$route'); - const createSearchItems = Private(SearchItemsProvider); - const { indexPattern, savedSearch, combinedQuery } = createSearchItems(); + const { indexPattern, savedSearch, combinedQuery } = createSearchItems( + kibanaConfig, + $route.current.locals.indexPattern, + $route.current.locals.savedSearch + ); const kibanaContext = { combinedQuery, currentIndexPattern: indexPattern, currentSavedSearch: savedSearch, indexPatterns, - kbnBaseUrl, kibanaConfig, }; diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/new_job/directive.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/new_job/directive.tsx index 815e0228882ce..1725211861c0c 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/new_job/directive.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/pages/new_job/directive.tsx @@ -14,10 +14,8 @@ import { timefilter } from 'ui/timefilter'; import { IndexPatterns } from 'ui/index_patterns'; import { I18nContext } from 'ui/i18n'; -import { IPrivate } from 'ui/private'; import { InjectorService } from '../../../../../common/types/angular'; - -import { SearchItemsProvider } from '../../utils/new_job_utils'; +import { createSearchItems } from '../../utils/new_job_utils'; import { Page, PageProps } from './page'; import { JOB_TYPE } from '../../common/job_creator/util/constants'; @@ -32,9 +30,7 @@ module.directive('mlNewJobPage', ($injector: InjectorService) => { timefilter.disableAutoRefreshSelector(); const indexPatterns = $injector.get('indexPatterns'); - const kbnBaseUrl = $injector.get('kbnBaseUrl'); const kibanaConfig = $injector.get('config'); - const Private = $injector.get('Private'); const $route = $injector.get('$route'); const existingJobsAndGroups = $route.current.locals.existingJobsAndGroups; @@ -43,15 +39,17 @@ module.directive('mlNewJobPage', ($injector: InjectorService) => { } const jobType: JOB_TYPE = $route.current.locals.jobType; - const createSearchItems = Private(SearchItemsProvider); - const { indexPattern, savedSearch, combinedQuery } = createSearchItems(); + const { indexPattern, savedSearch, combinedQuery } = createSearchItems( + kibanaConfig, + $route.current.locals.indexPattern, + $route.current.locals.savedSearch + ); const kibanaContext = { combinedQuery, currentIndexPattern: indexPattern, currentSavedSearch: savedSearch, indexPatterns, - kbnBaseUrl, kibanaConfig, }; diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/components/job_settings_form.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/components/job_settings_form.tsx index 8dda1415a0ab8..bae16d620af5b 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/components/job_settings_form.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/components/job_settings_form.tsx @@ -247,7 +247,6 @@ export const JobSettingsForm: FC = ({ useDedicatedIndex: checked, }); }} - showLabel={false} label={i18n.translate('xpack.ml.newJob.recognize.useDedicatedIndexLabel', { defaultMessage: 'Use dedicated index', })} diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/directive.tsx b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/directive.tsx index cf80cad30b7ca..5629fd63ce7ba 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/directive.tsx +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/directive.tsx @@ -14,10 +14,9 @@ import { timefilter } from 'ui/timefilter'; import { IndexPatterns } from 'ui/index_patterns'; import { I18nContext } from 'ui/i18n'; -import { IPrivate } from 'ui/private'; import { InjectorService } from '../../../../common/types/angular'; -import { SearchItemsProvider } from '../../new_job_new/utils/new_job_utils'; +import { createSearchItems } from '../../new_job_new/utils/new_job_utils'; import { Page } from './page'; import { KibanaContext, KibanaConfigTypeFix } from '../../../contexts/kibana'; @@ -32,22 +31,22 @@ module.directive('mlRecognizePage', ($injector: InjectorService) => { timefilter.disableAutoRefreshSelector(); const indexPatterns = $injector.get('indexPatterns'); - const kbnBaseUrl = $injector.get('kbnBaseUrl'); const kibanaConfig = $injector.get('config'); - const Private = $injector.get('Private'); const $route = $injector.get('$route'); const moduleId = $route.current.params.id; const existingGroupIds: string[] = $route.current.locals.existingJobsAndGroups.groupIds; - const createSearchItems = Private(SearchItemsProvider); - const { indexPattern, savedSearch, combinedQuery } = createSearchItems(); + const { indexPattern, savedSearch, combinedQuery } = createSearchItems( + kibanaConfig, + $route.current.locals.indexPattern, + $route.current.locals.savedSearch + ); const kibanaContext = { combinedQuery, currentIndexPattern: indexPattern, currentSavedSearch: savedSearch, indexPatterns, - kbnBaseUrl, kibanaConfig, }; diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/resolvers.ts b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/resolvers.ts index d92ec7152adf8..d2ca22972c201 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/resolvers.ts +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/recognize/resolvers.ts @@ -7,7 +7,6 @@ import chrome from 'ui/chrome'; import { i18n } from '@kbn/i18n'; import { toastNotifications } from 'ui/notify'; -import { IPrivate } from 'ui/private'; import { mlJobService } from '../../../services/job_service'; import { ml } from '../../../services/ml_api_service'; import { KibanaObjects } from './page'; @@ -17,12 +16,7 @@ import { KibanaObjects } from './page'; * Redirects to the Anomaly Explorer to view the jobs if they have been created, * or the recognizer job wizard for the module if not. */ -export function checkViewOrCreateJobs( - Private: IPrivate, - $route: any, - kbnBaseUrl: string, - kbnUrl: any -) { +export function checkViewOrCreateJobs($route: any) { return new Promise((resolve, reject) => { const moduleId = $route.current.params.id; const indexPatternId = $route.current.params.index; @@ -58,7 +52,7 @@ export function checkViewOrCreateJobs( }), }); - kbnUrl.redirect(`/jobs`); + window.location.href = '#/jobs'; reject(); }); }); diff --git a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/utils/new_job_utils.ts b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/utils/new_job_utils.ts index 85beb32fffa3c..cb4e7a21997e6 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/new_job_new/utils/new_job_utils.ts +++ b/x-pack/legacy/plugins/ml/public/jobs/new_job_new/utils/new_job_utils.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import { IndexPattern } from 'ui/index_patterns'; import { SavedSearch } from 'src/legacy/core_plugins/kibana/public/discover/types'; import { KibanaConfigTypeFix } from '../../../contexts/kibana'; -import { InjectorService } from '../../../../common/types/angular'; import { esQuery, IIndexPattern } from '../../../../../../../../src/plugins/data/public'; export interface SearchItems { @@ -18,55 +18,50 @@ export interface SearchItems { // Provider for creating the items used for searching and job creation. // Uses the $route object to retrieve the indexPattern and savedSearch from the url -export function SearchItemsProvider($injector: InjectorService) { - const kibanaConfig = $injector.get('config'); - const $route = $injector.get('$route'); - function createSearchItems() { - let indexPattern = $route.current.locals.indexPattern; - - // query is only used by the data visualizer as it needs - // a lucene query_string. - // Using a blank query will cause match_all:{} to be used - // when passed through luceneStringToDsl - let query = { - query: '', - language: 'lucene', - }; - - let combinedQuery: any = { - bool: { - must: [ - { - match_all: {}, - }, - ], - }, - }; +export function createSearchItems( + kibanaConfig: KibanaConfigTypeFix, + indexPattern: IndexPattern, + savedSearch: SavedSearch +) { + // query is only used by the data visualizer as it needs + // a lucene query_string. + // Using a blank query will cause match_all:{} to be used + // when passed through luceneStringToDsl + let query = { + query: '', + language: 'lucene', + }; - const savedSearch = $route.current.locals.savedSearch; - if (indexPattern.id === undefined && savedSearch.id !== undefined) { - const searchSource = savedSearch.searchSource; - indexPattern = searchSource.getField('index'); + let combinedQuery: any = { + bool: { + must: [ + { + match_all: {}, + }, + ], + }, + }; - query = searchSource.getField('query'); - const fs = searchSource.getField('filter'); + if (indexPattern.id === undefined && savedSearch.id !== undefined) { + const searchSource = savedSearch.searchSource; + indexPattern = searchSource.getField('index'); - const filters = fs.length ? fs : []; + query = searchSource.getField('query'); + const fs = searchSource.getField('filter'); - const esQueryConfigs = esQuery.getEsQueryConfig(kibanaConfig); - combinedQuery = esQuery.buildEsQuery(indexPattern, [query], filters, esQueryConfigs); - } + const filters = fs.length ? fs : []; - return { - indexPattern, - savedSearch, - query, - combinedQuery, - }; + const esQueryConfigs = esQuery.getEsQueryConfig(kibanaConfig); + combinedQuery = esQuery.buildEsQuery(indexPattern, [query], filters, esQueryConfigs); } - return createSearchItems; + return { + indexPattern, + savedSearch, + query, + combinedQuery, + }; } // Only model plot cardinality relevant diff --git a/x-pack/legacy/plugins/ml/public/license/check_license.js b/x-pack/legacy/plugins/ml/public/license/check_license.tsx similarity index 67% rename from x-pack/legacy/plugins/ml/public/license/check_license.js rename to x-pack/legacy/plugins/ml/public/license/check_license.tsx index 9e86fa429b0da..8457e462567cc 100644 --- a/x-pack/legacy/plugins/ml/public/license/check_license.js +++ b/x-pack/legacy/plugins/ml/public/license/check_license.tsx @@ -4,51 +4,44 @@ * you may not use this file except in compliance with the Elastic License. */ - import React from 'react'; +// @ts-ignore No declaration file for module +import { banners } from 'ui/notify'; +import { EuiCallOut } from '@elastic/eui'; +// @ts-ignore No declaration file for module import { xpackInfo } from '../../../xpack_main/public/services/xpack_info'; -import { banners, addAppRedirectMessageToUrl } from 'ui/notify'; import { LICENSE_TYPE } from '../../common/constants/license'; import { LICENSE_STATUS_VALID } from '../../../../common/constants/license_status'; -import chrome from 'ui/chrome'; -import { EuiCallOut } from '@elastic/eui'; - let licenseHasExpired = true; -let licenseType = null; -let expiredLicenseBannerId; +let licenseType: LICENSE_TYPE | null = null; +let expiredLicenseBannerId: string; -export function checkFullLicense(kbnBaseUrl, kbnUrl) { +export function checkFullLicense() { const features = getFeatures(); licenseType = features.licenseType; if (features.isAvailable === false) { // ML is not enabled - return redirectToKibana(features, kbnBaseUrl); - + return redirectToKibana(); } else if (features.licenseType === LICENSE_TYPE.BASIC) { - // ML is enabled, but only with a basic or gold license - return redirectToBasic(kbnUrl); - + return redirectToBasic(); } else { - // ML is enabled setLicenseExpired(features); return Promise.resolve(features); } } -export function checkBasicLicense(kbnBaseUrl) { +export function checkBasicLicense() { const features = getFeatures(); licenseType = features.licenseType; if (features.isAvailable === false) { // ML is not enabled - return redirectToKibana(features, kbnBaseUrl); - + return redirectToKibana(); } else { - // ML is enabled setLicenseExpired(features); return Promise.resolve(features); @@ -58,38 +51,32 @@ export function checkBasicLicense(kbnBaseUrl) { // a wrapper for checkFullLicense which doesn't resolve if the license has expired. // this is used by all create jobs pages to redirect back to the jobs list // if the user's license has expired. -export function checkLicenseExpired(kbnBaseUrl, kbnUrl) { - return checkFullLicense(kbnBaseUrl, kbnUrl) - .then((features) => { +export function checkLicenseExpired() { + return checkFullLicense() + .then((features: any) => { if (features.hasExpired) { - kbnUrl.redirect('/jobs'); - return Promise.halt(); + window.location.href = '#/jobs'; + return Promise.reject(); } else { return Promise.resolve(features); } }) .catch(() => { - return Promise.halt(); + return Promise.reject(); }); } -function setLicenseExpired(features) { - licenseHasExpired = (features.hasExpired || false); +function setLicenseExpired(features: any) { + licenseHasExpired = features.hasExpired || false; // If the license has expired ML app will still work for 7 days and then // the job management endpoints (e.g. create job, start datafeed) will be restricted. // Therefore we need to keep the app enabled but show an info banner to the user. - if(licenseHasExpired) { + if (licenseHasExpired) { const message = features.message; if (expiredLicenseBannerId === undefined) { // Only show the banner once with no way to dismiss it expiredLicenseBannerId = banners.add({ - component: ( - - ), + component: , }); } } @@ -99,16 +86,14 @@ function getFeatures() { return xpackInfo.get('features.ml'); } -function redirectToKibana(features, kbnBaseUrl) { - const { message } = features; - const newUrl = addAppRedirectMessageToUrl(chrome.addBasePath(kbnBaseUrl), (message || '')); - window.location.href = newUrl; - return Promise.halt(); +function redirectToKibana() { + window.location.href = '/'; + return Promise.reject(); } -function redirectToBasic(kbnUrl) { - kbnUrl.redirect('/datavisualizer'); - return Promise.halt(); +function redirectToBasic() { + window.location.href = '#/datavisualizer'; + return Promise.reject(); } export function hasLicenseExpired() { @@ -116,10 +101,10 @@ export function hasLicenseExpired() { } export function isFullLicense() { - return (licenseType === LICENSE_TYPE.FULL); + return licenseType === LICENSE_TYPE.FULL; } -export function xpackFeatureAvailable(feature) { +export function xpackFeatureAvailable(feature: string) { // each plugin can register their own set of features. // so we need specific checks for each one. // this list can grow if we need to check other plugin's features. diff --git a/x-pack/legacy/plugins/ml/public/privilege/check_privilege.ts b/x-pack/legacy/plugins/ml/public/privilege/check_privilege.ts index d930763c3cac5..10842a2d15453 100644 --- a/x-pack/legacy/plugins/ml/public/privilege/check_privilege.ts +++ b/x-pack/legacy/plugins/ml/public/privilege/check_privilege.ts @@ -15,18 +15,18 @@ import { ACCESS_DENIED_PATH } from '../management/management_urls'; let privileges: Privileges = getDefaultPrivileges(); // manage_ml requires all monitor and admin cluster privileges: https://github.com/elastic/elasticsearch/blob/664a29c8905d8ce9ba8c18aa1ed5c5de93a0eabc/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilege.java#L53 -export function canGetManagementMlJobs(kbnUrl: any) { +export function canGetManagementMlJobs() { return new Promise((resolve, reject) => { getManageMlPrivileges().then( ({ capabilities, isPlatinumOrTrialLicense, mlFeatureEnabledInSpace }) => { privileges = capabilities; - // Loop through all privilages to ensure they are all set to true. + // Loop through all privileges to ensure they are all set to true. const isManageML = Object.values(privileges).every(p => p === true); if (isManageML === true && isPlatinumOrTrialLicense === true) { return resolve({ mlFeatureEnabledInSpace }); } else { - kbnUrl.redirect(ACCESS_DENIED_PATH); + window.location.href = ACCESS_DENIED_PATH; return reject(); } } @@ -34,7 +34,7 @@ export function canGetManagementMlJobs(kbnUrl: any) { }); } -export function checkGetJobsPrivilege(kbnUrl: any): Promise { +export function checkGetJobsPrivilege(): Promise { return new Promise((resolve, reject) => { getPrivileges().then(({ capabilities, isPlatinumOrTrialLicense }) => { privileges = capabilities; @@ -46,14 +46,14 @@ export function checkGetJobsPrivilege(kbnUrl: any): Promise { if (privileges.canGetJobs || isPlatinumOrTrialLicense === false) { return resolve(privileges); } else { - kbnUrl.redirect('/access-denied'); + window.location.href = '#/access-denied'; return reject(); } }); }); } -export function checkCreateJobsPrivilege(kbnUrl: any): Promise { +export function checkCreateJobsPrivilege(): Promise { return new Promise((resolve, reject) => { getPrivileges().then(({ capabilities, isPlatinumOrTrialLicense }) => { privileges = capabilities; @@ -65,14 +65,14 @@ export function checkCreateJobsPrivilege(kbnUrl: any): Promise { } else { // if the user has no permission to create a job, // redirect them back to the Transforms Management page - kbnUrl.redirect('/jobs'); + window.location.href = '#/jobs'; return reject(); } }); }); } -export function checkFindFileStructurePrivilege(kbnUrl: any): Promise { +export function checkFindFileStructurePrivilege(): Promise { return new Promise((resolve, reject) => { getPrivileges().then(({ capabilities }) => { privileges = capabilities; @@ -81,7 +81,7 @@ export function checkFindFileStructurePrivilege(kbnUrl: any): Promise; +type IndexPatternIdsByJob = Record; + // Service for accessing FieldFormat objects configured for a Kibana index pattern // for use in formatting the actual and typical values from anomalies. class FieldFormatService { - constructor() { - this.indexPatternIdsByJob = {}; - this.formatsByJob = {}; - } + indexPatternIdsByJob: IndexPatternIdsByJob = {}; + formatsByJob: FormatsByJobId = {}; // Populate the service with the FieldFormats for the list of jobs with the // specified IDs. List of Kibana index patterns is passed, with a title @@ -26,60 +24,57 @@ class FieldFormatService { // configured in the datafeed of each job. // Builds a map of Kibana FieldFormats (plugins/data/common/field_formats) // against detector index by job ID. - populateFormats(jobIds, indexPatterns) { + populateFormats(jobIds: string[]) { return new Promise((resolve, reject) => { // Populate a map of index pattern IDs against job ID, by finding the ID of the index // pattern with a title attribute which matches the index configured in the datafeed. // If a Kibana index pattern has not been created // for this index, then no custom field formatting will occur. - _.each(jobIds, (jobId) => { + jobIds.forEach(jobId => { const jobObj = mlJobService.getJob(jobId); const datafeedIndices = jobObj.datafeed_config.indices; - const indexPattern = _.find(indexPatterns, (index) => { - return _.find(datafeedIndices, (datafeedIndex) => { - return index.get('title') === datafeedIndex; - }); - }); - - // Check if index pattern has been configured to match the index in datafeed. - if (indexPattern !== undefined) { - this.indexPatternIdsByJob[jobId] = indexPattern.id; + const id = getIndexPatternIdFromName(datafeedIndices.length ? datafeedIndices[0] : ''); + if (id !== null) { + this.indexPatternIdsByJob[jobId] = id; } }); - const promises = jobIds.map(jobId => Promise.all([ - this.getFormatsForJob(jobId) - ])); + const promises = jobIds.map(jobId => Promise.all([this.getFormatsForJob(jobId)])); - Promise.all(promises).then((fmtsByJobByDetector) => { - _.each(fmtsByJobByDetector, (formatsByDetector, index) => { - this.formatsByJob[jobIds[index]] = formatsByDetector[0]; - }); - - resolve(this.formatsByJob); - }).catch(err => { - console.log('fieldFormatService error populating formats:', err); - reject({ formats: {}, err }); - }); + Promise.all(promises) + .then(fmtsByJobByDetector => { + fmtsByJobByDetector.forEach((formatsByDetector, i) => { + this.formatsByJob[jobIds[i]] = formatsByDetector[0]; + }); + resolve(this.formatsByJob); + }) + .catch(err => { + reject({ formats: {}, err }); + }); }); } // Return the FieldFormat to use for formatting values from // the detector from the job with the specified ID. - getFieldFormat(jobId, detectorIndex) { - return _.get(this.formatsByJob, [jobId, detectorIndex]); + getFieldFormat(jobId: string, detectorIndex: number) { + if (this.formatsByJob.hasOwnProperty(jobId)) { + return this.formatsByJob[jobId][detectorIndex]; + } } - // Utility for returning the FieldFormat from a full populated Kibana index pattern object // containing the list of fields by name with their formats. - getFieldFormatFromIndexPattern(fullIndexPattern, fieldName, esAggName) { + getFieldFormatFromIndexPattern( + fullIndexPattern: IndexPattern, + fieldName: string, + esAggName: string + ) { // Don't use the field formatter for distinct count detectors as // e.g. distinct_count(clientip) should be formatted as a count, not as an IP address. - let fieldFormat = undefined; + let fieldFormat; if (esAggName !== 'cardinality') { - const fieldList = _.get(fullIndexPattern, 'fields', []); + const fieldList = fullIndexPattern.fields; const field = fieldList.getByName(fieldName); if (field !== undefined) { fieldFormat = field.format; @@ -89,34 +84,34 @@ class FieldFormatService { return fieldFormat; } - getFormatsForJob(jobId) { + getFormatsForJob(jobId: string): Promise { return new Promise((resolve, reject) => { - const jobObj = mlJobService.getJob(jobId); const detectors = jobObj.analysis_config.detectors || []; - const formatsByDetector = {}; + const formatsByDetector: any[] = []; const indexPatternId = this.indexPatternIdsByJob[jobId]; if (indexPatternId !== undefined) { // Load the full index pattern configuration to obtain the formats of each field. getIndexPatternById(indexPatternId) - .then((indexPatternData) => { + .then(indexPatternData => { // Store the FieldFormat for each job by detector_index. - const fieldList = _.get(indexPatternData, 'fields', []); - _.each(detectors, (dtr) => { + const fieldList = indexPatternData.fields; + detectors.forEach(dtr => { const esAgg = mlFunctionToESAggregation(dtr.function); // distinct_count detectors should fall back to the default // formatter as the values are just counts. if (dtr.field_name !== undefined && esAgg !== 'cardinality') { const field = fieldList.getByName(dtr.field_name); if (field !== undefined) { - formatsByDetector[dtr.detector_index] = field.format; + formatsByDetector[dtr.detector_index!] = field.format; } } }); resolve(formatsByDetector); - }).catch(err => { + }) + .catch(err => { reject(err); }); } else { diff --git a/x-pack/legacy/plugins/ml/public/services/job_service.d.ts b/x-pack/legacy/plugins/ml/public/services/job_service.d.ts index 383932c812216..bbfe0061d13f9 100644 --- a/x-pack/legacy/plugins/ml/public/services/job_service.d.ts +++ b/x-pack/legacy/plugins/ml/public/services/job_service.d.ts @@ -34,6 +34,7 @@ declare interface JobService { createResultsUrl(jobId: string[], start: number, end: number, location: string): string; getJobAndGroupIds(): ExistingJobsAndGroups; searchPreview(job: CombinedJob): Promise>; + getJob(jobId: string): CombinedJob; } export const mlJobService: JobService; diff --git a/x-pack/legacy/plugins/ml/public/settings/breadcrumbs.js b/x-pack/legacy/plugins/ml/public/settings/breadcrumbs.ts similarity index 74% rename from x-pack/legacy/plugins/ml/public/settings/breadcrumbs.js rename to x-pack/legacy/plugins/ml/public/settings/breadcrumbs.ts index e4e37b67c1872..2cdfa5bfcf4d0 100644 --- a/x-pack/legacy/plugins/ml/public/settings/breadcrumbs.js +++ b/x-pack/legacy/plugins/ml/public/settings/breadcrumbs.ts @@ -4,19 +4,13 @@ * you may not use this file except in compliance with the Elastic License. */ - -import { ML_BREADCRUMB, ANOMALY_DETECTION_BREADCRUMB, SETTINGS } from '../breadcrumbs'; import { i18n } from '@kbn/i18n'; - +import { ML_BREADCRUMB, ANOMALY_DETECTION_BREADCRUMB, SETTINGS } from '../breadcrumbs'; export function getSettingsBreadcrumbs() { // Whilst top level nav menu with tabs remains, // use root ML breadcrumb. - return [ - ML_BREADCRUMB, - ANOMALY_DETECTION_BREADCRUMB, - SETTINGS - ]; + return [ML_BREADCRUMB, ANOMALY_DETECTION_BREADCRUMB, SETTINGS]; } export function getCalendarManagementBreadcrumbs() { @@ -24,10 +18,10 @@ export function getCalendarManagementBreadcrumbs() { ...getSettingsBreadcrumbs(), { text: i18n.translate('xpack.ml.settings.breadcrumbs.calendarManagementLabel', { - defaultMessage: 'Calendar management' + defaultMessage: 'Calendar management', }), - href: '#/settings/calendars_list' - } + href: '#/settings/calendars_list', + }, ]; } @@ -36,10 +30,10 @@ export function getCreateCalendarBreadcrumbs() { ...getCalendarManagementBreadcrumbs(), { text: i18n.translate('xpack.ml.settings.breadcrumbs.calendarManagement.createLabel', { - defaultMessage: 'Create' + defaultMessage: 'Create', }), - href: '#/settings/calendars_list/new_calendar' - } + href: '#/settings/calendars_list/new_calendar', + }, ]; } @@ -48,10 +42,10 @@ export function getEditCalendarBreadcrumbs() { ...getCalendarManagementBreadcrumbs(), { text: i18n.translate('xpack.ml.settings.breadcrumbs.calendarManagement.editLabel', { - defaultMessage: 'Edit' + defaultMessage: 'Edit', }), - href: '#/settings/calendars_list/edit_calendar' - } + href: '#/settings/calendars_list/edit_calendar', + }, ]; } @@ -60,10 +54,10 @@ export function getFilterListsBreadcrumbs() { ...getSettingsBreadcrumbs(), { text: i18n.translate('xpack.ml.settings.breadcrumbs.filterListsLabel', { - defaultMessage: 'Filter lists' + defaultMessage: 'Filter lists', }), - href: '#/settings/filter_lists' - } + href: '#/settings/filter_lists', + }, ]; } @@ -72,10 +66,10 @@ export function getCreateFilterListBreadcrumbs() { ...getFilterListsBreadcrumbs(), { text: i18n.translate('xpack.ml.settings.breadcrumbs.filterLists.createLabel', { - defaultMessage: 'Create' + defaultMessage: 'Create', }), - href: '#/settings/filter_lists/new' - } + href: '#/settings/filter_lists/new', + }, ]; } @@ -84,9 +78,9 @@ export function getEditFilterListBreadcrumbs() { ...getFilterListsBreadcrumbs(), { text: i18n.translate('xpack.ml.settings.breadcrumbs.filterLists.editLabel', { - defaultMessage: 'Edit' + defaultMessage: 'Edit', }), - href: '#/settings/filter_lists/edit' - } + href: '#/settings/filter_lists/edit', + }, ]; } diff --git a/x-pack/legacy/plugins/ml/public/settings/calendars/edit/directive.js b/x-pack/legacy/plugins/ml/public/settings/calendars/edit/directive.tsx similarity index 87% rename from x-pack/legacy/plugins/ml/public/settings/calendars/edit/directive.js rename to x-pack/legacy/plugins/ml/public/settings/calendars/edit/directive.tsx index 34b647ef370d5..cc1674848d374 100644 --- a/x-pack/legacy/plugins/ml/public/settings/calendars/edit/directive.js +++ b/x-pack/legacy/plugins/ml/public/settings/calendars/edit/directive.tsx @@ -4,21 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ - import 'ngreact'; import React from 'react'; import ReactDOM from 'react-dom'; +// @ts-ignore import { uiModules } from 'ui/modules'; const module = uiModules.get('apps/ml', ['react']); +import uiRoutes from 'ui/routes'; +import { I18nContext } from 'ui/i18n'; import { checkFullLicense } from '../../../license/check_license'; import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege'; import { checkMlNodesAvailable } from '../../../ml_nodes_check'; import { getCreateCalendarBreadcrumbs, getEditCalendarBreadcrumbs } from '../../breadcrumbs'; -import uiRoutes from 'ui/routes'; -import { I18nContext } from 'ui/i18n'; +import { NewCalendar } from './new_calendar'; const template = `
@@ -33,7 +34,7 @@ uiRoutes CheckLicense: checkFullLicense, privileges: checkGetJobsPrivilege, checkMlNodesAvailable, - } + }, }) .when('/settings/calendars_list/edit_calendar/:calendarId', { template, @@ -42,21 +43,19 @@ uiRoutes CheckLicense: checkFullLicense, privileges: checkGetJobsPrivilege, checkMlNodesAvailable, - } + }, }); -import { NewCalendar } from './new_calendar.js'; - -module.directive('mlNewCalendar', function ($route) { +module.directive('mlNewCalendar', function($route: any) { return { restrict: 'E', replace: false, scope: {}, - link: function (scope, element) { + link(scope: ng.IScope, element: ng.IAugmentedJQuery) { const props = { calendarId: $route.current.params.calendarId, canCreateCalendar: checkPermission('canCreateCalendar'), - canDeleteCalendar: checkPermission('canDeleteCalendar') + canDeleteCalendar: checkPermission('canDeleteCalendar'), }; ReactDOM.render( @@ -65,6 +64,6 @@ module.directive('mlNewCalendar', function ($route) { , element[0] ); - } + }, }; }); diff --git a/x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/index.js b/x-pack/legacy/plugins/ml/public/settings/calendars/edit/index.ts similarity index 99% rename from x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/index.js rename to x-pack/legacy/plugins/ml/public/settings/calendars/edit/index.ts index 3839017291326..aa8b2ec2c29c9 100644 --- a/x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/index.js +++ b/x-pack/legacy/plugins/ml/public/settings/calendars/edit/index.ts @@ -4,5 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ - import './directive'; diff --git a/x-pack/legacy/plugins/ml/public/settings/calendars/edit/new_calendar.d.ts b/x-pack/legacy/plugins/ml/public/settings/calendars/edit/new_calendar.d.ts new file mode 100644 index 0000000000000..d6de538d6388a --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/settings/calendars/edit/new_calendar.d.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FC } from 'react'; + +declare const NewCalendar: FC<{ + calendarId: string; + canCreateCalendar: boolean; + canDeleteCalendar: boolean; +}>; diff --git a/x-pack/legacy/plugins/ml/public/settings/calendars/list/calendars_list.d.ts b/x-pack/legacy/plugins/ml/public/settings/calendars/list/calendars_list.d.ts new file mode 100644 index 0000000000000..05debad543075 --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/settings/calendars/list/calendars_list.d.ts @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FC } from 'react'; + +declare const CalendarsList: FC<{ + canCreateCalendar: boolean; + canDeleteCalendar: boolean; +}>; diff --git a/x-pack/legacy/plugins/ml/public/settings/calendars/list/directive.js b/x-pack/legacy/plugins/ml/public/settings/calendars/list/directive.tsx similarity index 92% rename from x-pack/legacy/plugins/ml/public/settings/calendars/list/directive.js rename to x-pack/legacy/plugins/ml/public/settings/calendars/list/directive.tsx index 32085fa0e9939..1b90a27c07ada 100644 --- a/x-pack/legacy/plugins/ml/public/settings/calendars/list/directive.js +++ b/x-pack/legacy/plugins/ml/public/settings/calendars/list/directive.tsx @@ -8,16 +8,18 @@ import 'ngreact'; import React from 'react'; import ReactDOM from 'react-dom'; +// @ts-ignore import { uiModules } from 'ui/modules'; const module = uiModules.get('apps/ml', ['react']); +import uiRoutes from 'ui/routes'; +import { I18nContext } from 'ui/i18n'; import { checkFullLicense } from '../../../license/check_license'; import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege'; import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes'; import { getCalendarManagementBreadcrumbs } from '../../breadcrumbs'; -import uiRoutes from 'ui/routes'; -import { I18nContext } from 'ui/i18n'; +import { CalendarsList } from './calendars_list'; const template = `
@@ -34,14 +36,12 @@ uiRoutes.when('/settings/calendars_list', { }, }); -import { CalendarsList } from './calendars_list'; - -module.directive('mlCalendarsList', function () { +module.directive('mlCalendarsList', function() { return { restrict: 'E', replace: false, scope: {}, - link: function (scope, element) { + link(scope: ng.IScope, element: ng.IAugmentedJQuery) { const props = { canCreateCalendar: checkPermission('canCreateCalendar'), canDeleteCalendar: checkPermission('canDeleteCalendar'), diff --git a/x-pack/legacy/plugins/ml/public/settings/calendars/edit/index.js b/x-pack/legacy/plugins/ml/public/settings/calendars/list/index.ts similarity index 99% rename from x-pack/legacy/plugins/ml/public/settings/calendars/edit/index.js rename to x-pack/legacy/plugins/ml/public/settings/calendars/list/index.ts index fd75a9ceb9b49..aa8b2ec2c29c9 100644 --- a/x-pack/legacy/plugins/ml/public/settings/calendars/edit/index.js +++ b/x-pack/legacy/plugins/ml/public/settings/calendars/list/index.ts @@ -4,6 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ - - import './directive'; diff --git a/x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/directive.js b/x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/directive.tsx similarity index 81% rename from x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/directive.js rename to x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/directive.tsx index 1d6c72c13f9f2..b70b7eebfccc9 100644 --- a/x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/directive.js +++ b/x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/directive.tsx @@ -4,22 +4,22 @@ * you may not use this file except in compliance with the Elastic License. */ - import 'ngreact'; import React from 'react'; import ReactDOM from 'react-dom'; +// @ts-ignore import { uiModules } from 'ui/modules'; const module = uiModules.get('apps/ml', ['react']); -import { getCreateFilterListBreadcrumbs, getEditFilterListBreadcrumbs } from '../../breadcrumbs'; -import { checkFullLicense } from 'plugins/ml/license/check_license'; -import { checkGetJobsPrivilege, checkPermission } from 'plugins/ml/privilege/check_privilege'; -import { getMlNodeCount } from 'plugins/ml/ml_nodes_check/check_ml_nodes'; -import { EditFilterList } from './edit_filter_list'; - import uiRoutes from 'ui/routes'; import { I18nContext } from 'ui/i18n'; +import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes'; +import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege'; +import { checkFullLicense } from '../../../license/check_license'; +import { getCreateFilterListBreadcrumbs, getEditFilterListBreadcrumbs } from '../../breadcrumbs'; + +import { EditFilterList } from './edit_filter_list'; const template = `
@@ -34,7 +34,7 @@ uiRoutes CheckLicense: checkFullLicense, privileges: checkGetJobsPrivilege, mlNodeCount: getMlNodeCount, - } + }, }) .when('/settings/filter_lists/edit_filter_list/:filterId', { template, @@ -43,15 +43,15 @@ uiRoutes CheckLicense: checkFullLicense, privileges: checkGetJobsPrivilege, mlNodeCount: getMlNodeCount, - } + }, }); -module.directive('mlEditFilterList', function ($route) { +module.directive('mlEditFilterList', function($route: any) { return { restrict: 'E', replace: false, scope: {}, - link: function (scope, element) { + link(scope: ng.IScope, element: ng.IAugmentedJQuery) { const props = { filterId: $route.current.params.filterId, canCreateFilter: checkPermission('canCreateFilter'), @@ -64,6 +64,6 @@ module.directive('mlEditFilterList', function ($route) { , element[0] ); - } + }, }; }); diff --git a/x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/edit_filter_list.d.ts b/x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/edit_filter_list.d.ts new file mode 100644 index 0000000000000..71d82d7694cf0 --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/edit_filter_list.d.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { FC } from 'react'; + +declare const EditFilterList: FC<{ + filterId: string; + canCreateFilter: boolean; + canDeleteFilter: boolean; +}>; diff --git a/x-pack/legacy/plugins/ml/public/settings/filter_lists/list/index.js b/x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/index.ts similarity index 99% rename from x-pack/legacy/plugins/ml/public/settings/filter_lists/list/index.js rename to x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/index.ts index fd75a9ceb9b49..aa8b2ec2c29c9 100644 --- a/x-pack/legacy/plugins/ml/public/settings/filter_lists/list/index.js +++ b/x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/index.ts @@ -4,6 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ - - import './directive'; diff --git a/x-pack/legacy/plugins/ml/public/settings/filter_lists/index.js b/x-pack/legacy/plugins/ml/public/settings/filter_lists/index.ts similarity index 99% rename from x-pack/legacy/plugins/ml/public/settings/filter_lists/index.js rename to x-pack/legacy/plugins/ml/public/settings/filter_lists/index.ts index 463b602c20c9a..6a942d5c251df 100644 --- a/x-pack/legacy/plugins/ml/public/settings/filter_lists/index.js +++ b/x-pack/legacy/plugins/ml/public/settings/filter_lists/index.ts @@ -4,6 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ - import './edit'; import './list'; diff --git a/x-pack/legacy/plugins/ml/public/settings/filter_lists/list/directive.js b/x-pack/legacy/plugins/ml/public/settings/filter_lists/list/directive.tsx similarity index 64% rename from x-pack/legacy/plugins/ml/public/settings/filter_lists/list/directive.js rename to x-pack/legacy/plugins/ml/public/settings/filter_lists/list/directive.tsx index 311bf1bcb358a..7b572344c603b 100644 --- a/x-pack/legacy/plugins/ml/public/settings/filter_lists/list/directive.js +++ b/x-pack/legacy/plugins/ml/public/settings/filter_lists/list/directive.tsx @@ -4,45 +4,44 @@ * you may not use this file except in compliance with the Elastic License. */ - import 'ngreact'; import React from 'react'; import ReactDOM from 'react-dom'; +// @ts-ignore import { uiModules } from 'ui/modules'; const module = uiModules.get('apps/ml', ['react']); -import { getFilterListsBreadcrumbs } from '../../breadcrumbs'; -import { checkFullLicense } from 'plugins/ml/license/check_license'; -import { checkGetJobsPrivilege, checkPermission } from 'plugins/ml/privilege/check_privilege'; -import { getMlNodeCount } from 'plugins/ml/ml_nodes_check/check_ml_nodes'; -import { FilterLists } from './filter_lists'; - import uiRoutes from 'ui/routes'; import { I18nContext } from 'ui/i18n'; +import { checkFullLicense } from '../../../license/check_license'; +import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege'; +import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes'; +import { getFilterListsBreadcrumbs } from '../../breadcrumbs'; + +import { FilterLists } from './filter_lists'; const template = `
`; -uiRoutes - .when('/settings/filter_lists', { - template, - k7Breadcrumbs: getFilterListsBreadcrumbs, - resolve: { - CheckLicense: checkFullLicense, - privileges: checkGetJobsPrivilege, - mlNodeCount: getMlNodeCount, - } - }); - -module.directive('mlFilterLists', function () { +uiRoutes.when('/settings/filter_lists', { + template, + k7Breadcrumbs: getFilterListsBreadcrumbs, + resolve: { + CheckLicense: checkFullLicense, + privileges: checkGetJobsPrivilege, + mlNodeCount: getMlNodeCount, + }, +}); + +module.directive('mlFilterLists', function() { return { restrict: 'E', replace: false, scope: {}, - link: function (scope, element) { + link(scope: ng.IScope, element: ng.IAugmentedJQuery) { const props = { canCreateFilter: checkPermission('canCreateFilter'), canDeleteFilter: checkPermission('canDeleteFilter'), @@ -54,6 +53,6 @@ module.directive('mlFilterLists', function () { , element[0] ); - } + }, }; }); diff --git a/x-pack/legacy/plugins/ml/common/util/parse_interval.d.ts b/x-pack/legacy/plugins/ml/public/settings/filter_lists/list/filter_lists.d.ts similarity index 67% rename from x-pack/legacy/plugins/ml/common/util/parse_interval.d.ts rename to x-pack/legacy/plugins/ml/public/settings/filter_lists/list/filter_lists.d.ts index 7c4f40cc6c9d7..b14b19594e0b5 100644 --- a/x-pack/legacy/plugins/ml/common/util/parse_interval.d.ts +++ b/x-pack/legacy/plugins/ml/public/settings/filter_lists/list/filter_lists.d.ts @@ -4,6 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Duration } from 'moment'; +import { FC } from 'react'; -export function parseInterval(interval: string): Duration; +declare const FilterLists: FC<{ + canCreateFilter: boolean; + canDeleteFilter: boolean; +}>; diff --git a/x-pack/legacy/plugins/ml/public/settings/calendars/list/index.js b/x-pack/legacy/plugins/ml/public/settings/filter_lists/list/index.ts similarity index 99% rename from x-pack/legacy/plugins/ml/public/settings/calendars/list/index.js rename to x-pack/legacy/plugins/ml/public/settings/filter_lists/list/index.ts index fd75a9ceb9b49..aa8b2ec2c29c9 100644 --- a/x-pack/legacy/plugins/ml/public/settings/calendars/list/index.js +++ b/x-pack/legacy/plugins/ml/public/settings/filter_lists/list/index.ts @@ -4,6 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ - - import './directive'; diff --git a/x-pack/legacy/plugins/ml/public/settings/index.js b/x-pack/legacy/plugins/ml/public/settings/index.ts similarity index 99% rename from x-pack/legacy/plugins/ml/public/settings/index.js rename to x-pack/legacy/plugins/ml/public/settings/index.ts index 659f135d98c50..d9fc996ae4a30 100644 --- a/x-pack/legacy/plugins/ml/public/settings/index.js +++ b/x-pack/legacy/plugins/ml/public/settings/index.ts @@ -4,8 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ - - import './settings_directive'; import './calendars'; import './filter_lists'; diff --git a/x-pack/legacy/plugins/ml/public/settings/settings_directive.js b/x-pack/legacy/plugins/ml/public/settings/settings_directive.tsx similarity index 80% rename from x-pack/legacy/plugins/ml/public/settings/settings_directive.js rename to x-pack/legacy/plugins/ml/public/settings/settings_directive.tsx index 8692d903b1bd0..5102c7650a7b1 100644 --- a/x-pack/legacy/plugins/ml/public/settings/settings_directive.js +++ b/x-pack/legacy/plugins/ml/public/settings/settings_directive.tsx @@ -4,44 +4,41 @@ * you may not use this file except in compliance with the Elastic License. */ - import 'ngreact'; import React from 'react'; import ReactDOM from 'react-dom'; +// @ts-ignore import { uiModules } from 'ui/modules'; const module = uiModules.get('apps/ml', ['react']); +import { I18nContext } from 'ui/i18n'; +import uiRoutes from 'ui/routes'; +import { timefilter } from 'ui/timefilter'; import { checkFullLicense } from '../license/check_license'; import { checkGetJobsPrivilege, checkPermission } from '../privilege/check_privilege'; import { getMlNodeCount } from '../ml_nodes_check/check_ml_nodes'; import { getSettingsBreadcrumbs } from './breadcrumbs'; -import { I18nContext } from 'ui/i18n'; -import uiRoutes from 'ui/routes'; -import { timefilter } from 'ui/timefilter'; - const template = `
`; -uiRoutes - .when('/settings', { - template, - k7Breadcrumbs: getSettingsBreadcrumbs, - resolve: { - CheckLicense: checkFullLicense, - privileges: checkGetJobsPrivilege, - mlNodeCount: getMlNodeCount, - } - }); - +uiRoutes.when('/settings', { + template, + k7Breadcrumbs: getSettingsBreadcrumbs, + resolve: { + CheckLicense: checkFullLicense, + privileges: checkGetJobsPrivilege, + mlNodeCount: getMlNodeCount, + }, +}); +// @ts-ignore import { Settings } from './settings.js'; -module.directive('mlSettings', function () { - +module.directive('mlSettings', function() { const canGetFilters = checkPermission('canGetFilters'); const canGetCalendars = checkPermission('canGetCalendars'); @@ -49,7 +46,7 @@ module.directive('mlSettings', function () { restrict: 'E', replace: false, scope: {}, - link: function (scope, element) { + link(scope: ng.IScope, element: ng.IAugmentedJQuery) { timefilter.disableTimeRangeSelector(); timefilter.disableAutoRefreshSelector(); @@ -59,6 +56,6 @@ module.directive('mlSettings', function () { , element[0] ); - } + }, }; }); diff --git a/x-pack/legacy/plugins/ml/public/timeseriesexplorer/timeseriesexplorer.js b/x-pack/legacy/plugins/ml/public/timeseriesexplorer/timeseriesexplorer.js index 857e12cbbe942..1dec12a396578 100644 --- a/x-pack/legacy/plugins/ml/public/timeseriesexplorer/timeseriesexplorer.js +++ b/x-pack/legacy/plugins/ml/public/timeseriesexplorer/timeseriesexplorer.js @@ -68,7 +68,6 @@ import { mlJobService } from '../services/job_service'; import { mlResultsService } from '../services/results_service'; import { mlTimefilterRefresh$ } from '../services/timefilter_refresh_service'; -import { getIndexPatterns } from '../util/index_utils'; import { getBoundsRoundedToInterval } from '../util/time_buckets'; import { APP_STATE_ACTION, CHARTS_POINT_TARGET, TIME_FIELD_NAME } from './timeseriesexplorer_constants'; @@ -827,7 +826,7 @@ export class TimeSeriesExplorer extends React.Component { () => { this.updateControlsForDetector(() => { // Populate the map of jobs / detectors / field formatters for the selected IDs and refresh. - mlFieldFormatService.populateFormats([jobId], getIndexPatterns()) + mlFieldFormatService.populateFormats([jobId]) .catch((err) => { console.log('Error populating field formats:', err); }) // Load the data - if the FieldFormats failed to populate // the default formatting will be used for metric values. diff --git a/x-pack/legacy/plugins/ml/public/util/index_utils.ts b/x-pack/legacy/plugins/ml/public/util/index_utils.ts index 8a1cfda54a5f4..5c15cdd6b8df0 100644 --- a/x-pack/legacy/plugins/ml/public/util/index_utils.ts +++ b/x-pack/legacy/plugins/ml/public/util/index_utils.ts @@ -17,8 +17,6 @@ type IndexPatternSavedObject = SimpleSavedObject; let indexPatternCache: IndexPatternSavedObject[] = []; let fullIndexPatterns: IndexPatterns | null = null; -export let refreshIndexPatterns: (() => Promise) | null = null; - export function loadIndexPatterns() { fullIndexPatterns = data.indexPatterns.indexPatterns; const savedObjectsClient = chrome.getSavedObjectsClient(); @@ -30,20 +28,6 @@ export function loadIndexPatterns() { }) .then(response => { indexPatternCache = response.savedObjects; - if (refreshIndexPatterns === null) { - refreshIndexPatterns = () => { - return new Promise((resolve, reject) => { - loadIndexPatterns() - .then(resp => { - resolve(resp); - }) - .catch(error => { - reject(error); - }); - }); - }; - } - return indexPatternCache; }); } @@ -62,7 +46,7 @@ export function getIndexPatternIdFromName(name: string) { return indexPatternCache[j].id; } } - return name; + return null; } export function loadCurrentIndexPattern(indexPatterns: IndexPatterns, $route: Record) { diff --git a/x-pack/legacy/plugins/ml/server/lib/check_license/__tests__/check_license.js b/x-pack/legacy/plugins/ml/server/lib/check_license/__tests__/check_license.ts similarity index 80% rename from x-pack/legacy/plugins/ml/server/lib/check_license/__tests__/check_license.js rename to x-pack/legacy/plugins/ml/server/lib/check_license/__tests__/check_license.ts index 25411bb2775d5..5a8b4fdcc76db 100644 --- a/x-pack/legacy/plugins/ml/server/lib/check_license/__tests__/check_license.js +++ b/x-pack/legacy/plugins/ml/server/lib/check_license/__tests__/check_license.ts @@ -4,20 +4,18 @@ * you may not use this file except in compliance with the Elastic License. */ - - import expect from '@kbn/expect'; import sinon from 'sinon'; import { set } from 'lodash'; +import { XPackInfo } from '../../../../../../../legacy/plugins/xpack_main/server/lib/xpack_info'; import { checkLicense } from '../check_license'; describe('check_license', () => { - - let mockLicenseInfo; - beforeEach(() => mockLicenseInfo = {}); + let mockLicenseInfo: XPackInfo; + beforeEach(() => (mockLicenseInfo = {} as XPackInfo)); describe('license information is undefined', () => { - beforeEach(() => mockLicenseInfo = undefined); + beforeEach(() => (mockLicenseInfo = {} as XPackInfo)); it('should set isAvailable to false', () => { expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); @@ -37,7 +35,7 @@ describe('check_license', () => { }); describe('license information is not available', () => { - beforeEach(() => mockLicenseInfo.isAvailable = () => false); + beforeEach(() => (mockLicenseInfo.isAvailable = () => false)); it('should set isAvailable to false', () => { expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); @@ -64,14 +62,21 @@ describe('check_license', () => { describe('& ML is disabled in Elasticsearch', () => { beforeEach(() => { - set(mockLicenseInfo, 'feature', sinon.stub().withArgs('ml').returns({ isEnabled: () => false })); + set( + mockLicenseInfo, + 'feature', + sinon + .stub() + .withArgs('ml') + .returns({ isEnabled: () => false }) + ); }); - it ('should set showLinks to false', () => { + it('should set showLinks to false', () => { expect(checkLicense(mockLicenseInfo).showLinks).to.be(false); }); - it ('should set isAvailable to false', () => { + it('should set isAvailable to false', () => { expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false); }); @@ -86,7 +91,14 @@ describe('check_license', () => { describe('& ML is enabled in Elasticsearch', () => { beforeEach(() => { - set(mockLicenseInfo, 'feature', sinon.stub().withArgs('ml').returns({ isEnabled: () => true })); + set( + mockLicenseInfo, + 'feature', + sinon + .stub() + .withArgs('ml') + .returns({ isEnabled: () => true }) + ); }); describe('& license is trial or platinum', () => { @@ -99,11 +111,11 @@ describe('check_license', () => { expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true); }); - it ('should set showLinks to true', () => { + it('should set showLinks to true', () => { expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); }); - it ('should set enableLinks to true', () => { + it('should set enableLinks to true', () => { expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true); }); @@ -119,11 +131,11 @@ describe('check_license', () => { expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true); }); - it ('should set showLinks to true', () => { + it('should set showLinks to true', () => { expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); }); - it ('should set enableLinks to true', () => { + it('should set enableLinks to true', () => { expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true); }); @@ -143,7 +155,7 @@ describe('check_license', () => { expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true); }); - it ('should set showLinks to true', () => { + it('should set showLinks to true', () => { expect(checkLicense(mockLicenseInfo).showLinks).to.be(true); }); }); diff --git a/x-pack/legacy/plugins/ml/server/lib/check_license/check_license.d.ts b/x-pack/legacy/plugins/ml/server/lib/check_license/check_license.d.ts deleted file mode 100644 index 6987df3c3c0ba..0000000000000 --- a/x-pack/legacy/plugins/ml/server/lib/check_license/check_license.d.ts +++ /dev/null @@ -1,17 +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; - * you may not use this file except in compliance with the Elastic License. - */ - -import { LICENSE_TYPE } from '../../../common/constants/license'; -interface Response { - isAvailable: boolean; - showLinks: boolean; - enableLinks: boolean; - licenseType: LICENSE_TYPE; - hasExpired: boolean; - message: string; -} - -export function checkLicense(xpackLicenseInfo: any): Response; diff --git a/x-pack/legacy/plugins/ml/server/lib/check_license/check_license.js b/x-pack/legacy/plugins/ml/server/lib/check_license/check_license.ts similarity index 66% rename from x-pack/legacy/plugins/ml/server/lib/check_license/check_license.js rename to x-pack/legacy/plugins/ml/server/lib/check_license/check_license.ts index b64bc98539ffd..bb92705880dde 100644 --- a/x-pack/legacy/plugins/ml/server/lib/check_license/check_license.js +++ b/x-pack/legacy/plugins/ml/server/lib/check_license/check_license.ts @@ -4,11 +4,20 @@ * you may not use this file except in compliance with the Elastic License. */ - -import { LICENSE_TYPE } from '../../../common/constants/license'; import { i18n } from '@kbn/i18n'; +import { LICENSE_TYPE } from '../../../common/constants/license'; +import { XPackInfo } from '../../../../../../legacy/plugins/xpack_main/server/lib/xpack_info'; + +interface Response { + isAvailable: boolean; + showLinks: boolean; + enableLinks: boolean; + licenseType?: LICENSE_TYPE; + hasExpired?: boolean; + message?: string; +} -export function checkLicense(xpackLicenseInfo) { +export function checkLicense(xpackLicenseInfo: XPackInfo): Response { // If, for some reason, we cannot get the license information // from Elasticsearch, assume worst case and disable the Machine Learning UI if (!xpackLicenseInfo || !xpackLicenseInfo.isAvailable()) { @@ -16,9 +25,13 @@ export function checkLicense(xpackLicenseInfo) { isAvailable: false, showLinks: true, enableLinks: false, - message: i18n.translate('xpack.ml.checkLicense.licenseInformationNotAvailableThisTimeMessage', { - defaultMessage: 'You cannot use Machine Learning because license information is not available at this time.' - }) + message: i18n.translate( + 'xpack.ml.checkLicense.licenseInformationNotAvailableThisTimeMessage', + { + defaultMessage: + 'You cannot use Machine Learning because license information is not available at this time.', + } + ), }; } @@ -29,18 +42,15 @@ export function checkLicense(xpackLicenseInfo) { showLinks: false, enableLinks: false, message: i18n.translate('xpack.ml.checkLicense.mlIsUnavailableMessage', { - defaultMessage: 'Machine Learning is unavailable' - }) + defaultMessage: 'Machine Learning is unavailable', + }), }; } - const VALID_FULL_LICENSE_MODES = [ - 'trial', - 'platinum' - ]; + const VALID_FULL_LICENSE_MODES = ['trial', 'platinum']; const isLicenseModeValid = xpackLicenseInfo.license.isOneOf(VALID_FULL_LICENSE_MODES); - const licenseType = (isLicenseModeValid === true) ? LICENSE_TYPE.FULL : LICENSE_TYPE.BASIC; + const licenseType = isLicenseModeValid === true ? LICENSE_TYPE.FULL : LICENSE_TYPE.BASIC; const isLicenseActive = xpackLicenseInfo.license.isActive(); const licenseTypeName = xpackLicenseInfo.license.getType(); @@ -54,8 +64,8 @@ export function checkLicense(xpackLicenseInfo) { licenseType, message: i18n.translate('xpack.ml.checkLicense.licenseHasExpiredMessage', { defaultMessage: 'Your {licenseTypeName} Machine Learning license has expired.', - values: { licenseTypeName } - }) + values: { licenseTypeName }, + }), }; } diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/validate_bucket_span.js b/x-pack/legacy/plugins/ml/server/models/job_validation/validate_bucket_span.js index 2a9d3064ee825..c80c4851bde59 100644 --- a/x-pack/legacy/plugins/ml/server/models/job_validation/validate_bucket_span.js +++ b/x-pack/legacy/plugins/ml/server/models/job_validation/validate_bucket_span.js @@ -9,7 +9,7 @@ import { estimateBucketSpanFactory } from '../../models/bucket_span_estimator'; import { mlFunctionToESAggregation } from '../../../common/util/job_utils'; import { SKIP_BUCKET_SPAN_ESTIMATION } from '../../../common/constants/validation'; -import { parseInterval } from '../../../common/util/parse_interval.js'; +import { parseInterval } from '../../../common/util/parse_interval'; import { validateJobObject } from './validate_job_object'; diff --git a/x-pack/legacy/plugins/ml/server/models/job_validation/validate_time_range.js b/x-pack/legacy/plugins/ml/server/models/job_validation/validate_time_range.js index 9c48fae1739b9..bffad443b4c14 100644 --- a/x-pack/legacy/plugins/ml/server/models/job_validation/validate_time_range.js +++ b/x-pack/legacy/plugins/ml/server/models/job_validation/validate_time_range.js @@ -9,7 +9,7 @@ import _ from 'lodash'; import { ES_FIELD_TYPES } from '../../../../../../../src/plugins/data/server'; -import { parseInterval } from '../../../common/util/parse_interval.js'; +import { parseInterval } from '../../../common/util/parse_interval'; import { validateJobObject } from './validate_job_object'; const BUCKET_SPAN_COMPARE_FACTOR = 25; From bf75969ab01b89f9f17145c09137a5408f986323 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Mon, 18 Nov 2019 17:17:48 +0100 Subject: [PATCH 17/17] remove outdated comment (#50895) (#50913) --- .../kibana-plugin-server.httpservicesetup.createrouter.md | 2 +- .../core/server/kibana-plugin-server.httpservicesetup.md | 4 ++-- src/core/server/http/types.ts | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/development/core/server/kibana-plugin-server.httpservicesetup.createrouter.md b/docs/development/core/server/kibana-plugin-server.httpservicesetup.createrouter.md index 6431589c55bd1..71a7fd8fb6a22 100644 --- a/docs/development/core/server/kibana-plugin-server.httpservicesetup.createrouter.md +++ b/docs/development/core/server/kibana-plugin-server.httpservicesetup.createrouter.md @@ -21,7 +21,7 @@ Each route can have only one handler function, which is executed when the route ```ts const router = createRouter(); -// handler is called when '${my-plugin-id}/path' resource is requested with `GET` method +// handler is called when '/path' resource is requested with `GET` method router.get({ path: '/path', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); ``` diff --git a/docs/development/core/server/kibana-plugin-server.httpservicesetup.md b/docs/development/core/server/kibana-plugin-server.httpservicesetup.md index d943ad53af843..dba0ad8c8560c 100644 --- a/docs/development/core/server/kibana-plugin-server.httpservicesetup.md +++ b/docs/development/core/server/kibana-plugin-server.httpservicesetup.md @@ -27,7 +27,7 @@ export interface HttpServiceSetup ## Example -To handle an incoming request in your plugin you should: - Create a `Router` instance. Router is already configured to use `plugin-id` to prefix path segment for your routes. +To handle an incoming request in your plugin you should: - Create a `Router` instance. ```ts const router = httpSetup.createRouter(); @@ -61,7 +61,7 @@ const handler = async (context: RequestHandlerContext, request: KibanaRequest, r } ``` -- Register route handler for GET request to 'my-app/path/{id}' path +- Register route handler for GET request to 'path/{id}' path ```ts import { schema, TypeOf } from '@kbn/config-schema'; diff --git a/src/core/server/http/types.ts b/src/core/server/http/types.ts index 6f5cb02fd8cba..2c3dfedd1d181 100644 --- a/src/core/server/http/types.ts +++ b/src/core/server/http/types.ts @@ -52,7 +52,7 @@ export type RequestHandlerContextProvider< * * @example * To handle an incoming request in your plugin you should: - * - Create a `Router` instance. Router is already configured to use `plugin-id` to prefix path segment for your routes. + * - Create a `Router` instance. * ```ts * const router = httpSetup.createRouter(); * ``` @@ -87,7 +87,7 @@ export type RequestHandlerContextProvider< * } * ``` * - * - Register route handler for GET request to 'my-app/path/{id}' path + * - Register route handler for GET request to 'path/{id}' path * ```ts * import { schema, TypeOf } from '@kbn/config-schema'; * const router = httpSetup.createRouter(); @@ -184,7 +184,7 @@ export interface HttpServiceSetup { * @example * ```ts * const router = createRouter(); - * // handler is called when '${my-plugin-id}/path' resource is requested with `GET` method + * // handler is called when '/path' resource is requested with `GET` method * router.get({ path: '/path', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); * ``` * @public