From 5499bd168dfed9889a9db32d37e1ef74580d82b9 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Thu, 21 Oct 2021 10:28:51 +0200 Subject: [PATCH 01/18] cleanup unncessary type (#115830) --- .../migrations/core/call_cluster.ts | 154 +----------------- .../migrations/core/elastic_index.ts | 2 +- .../saved_objects/migrations/core/index.ts | 1 - .../spaces_usage_collector.ts | 2 +- 4 files changed, 5 insertions(+), 154 deletions(-) diff --git a/src/core/server/saved_objects/migrations/core/call_cluster.ts b/src/core/server/saved_objects/migrations/core/call_cluster.ts index f37bbdd14a899..d158bf7d131f5 100644 --- a/src/core/server/saved_objects/migrations/core/call_cluster.ts +++ b/src/core/server/saved_objects/migrations/core/call_cluster.ts @@ -13,164 +13,16 @@ */ import type { estypes } from '@elastic/elasticsearch'; -import { IndexMapping } from '../../mappings'; - -export interface CallCluster { - (path: 'bulk', opts: { body: object[] }): Promise; - (path: 'count', opts: CountOpts): Promise<{ count: number; _shards: estypes.ShardStatistics }>; - (path: 'clearScroll', opts: { scrollId: string }): Promise; - (path: 'indices.create', opts: IndexCreationOpts): Promise; - (path: 'indices.exists', opts: IndexOpts): Promise; - (path: 'indices.existsAlias', opts: { name: string }): Promise; - (path: 'indices.get', opts: IndexOpts & Ignorable): Promise; - (path: 'indices.getAlias', opts: { name: string } & Ignorable): Promise; - (path: 'indices.getMapping', opts: IndexOpts): Promise; - (path: 'indices.getSettings', opts: IndexOpts): Promise; - (path: 'indices.refresh', opts: IndexOpts): Promise; - (path: 'indices.updateAliases', opts: UpdateAliasesOpts): Promise; - (path: 'indices.deleteTemplate', opts: { name: string }): Promise; - (path: 'cat.templates', opts: { format: 'json'; name: string }): Promise>; - (path: 'reindex', opts: ReindexOpts): Promise; - (path: 'scroll', opts: ScrollOpts): Promise; - (path: 'search', opts: SearchOpts): Promise; - (path: 'tasks.get', opts: { taskId: string }): Promise<{ - completed: boolean; - error?: ErrorResponse; - }>; -} - -/////////////////////////////////////////////////////////////////// -// callCluster argument type definitions -/////////////////////////////////////////////////////////////////// - -export interface Ignorable { - ignore: number[]; -} - -export interface CountOpts { - body: { - query: object; - }; - index: string; -} - -export interface PutMappingOpts { - body: IndexMapping; - index: string; -} - -export interface IndexOpts { - index: string; -} - -export interface IndexCreationOpts { - index: string; - body?: { - mappings?: IndexMapping; - settings?: { - number_of_shards: number; - auto_expand_replicas: string; - }; - }; -} - -export interface ReindexOpts { - body: { - dest: IndexOpts; - source: IndexOpts & { size: number }; - script?: { - source: string; - lang: 'painless'; - }; - }; - refresh: boolean; - waitForCompletion: boolean; -} export type AliasAction = - | { remove_index: IndexOpts } + | { + remove_index: { index: string }; + } | { remove: { index: string; alias: string } } | { add: { index: string; alias: string } }; -export interface UpdateAliasesOpts { - body: { - actions: AliasAction[]; - }; -} - -export interface SearchOpts { - body: object; - index: string; - scroll?: string; -} - -export interface ScrollOpts { - scroll: string; - scrollId: string; -} - -/////////////////////////////////////////////////////////////////// -// callCluster result type definitions -/////////////////////////////////////////////////////////////////// - -export interface NotFound { - status: 404; -} - -export interface MappingResult { - [index: string]: { - mappings: IndexMapping; - }; -} - -export interface AliasResult { - [alias: string]: object; -} - -export interface IndexSettingsResult { - [indexName: string]: { - settings: { - index: { - number_of_shards: string; - auto_expand_replicas: string; - provided_name: string; - creation_date: string; - number_of_replicas: string; - uuid: string; - version: { created: '7000001' }; - }; - }; - }; -} - export interface RawDoc { _id: estypes.Id; _source: any; _type?: string; } - -export interface SearchResults { - hits: { - hits: RawDoc[]; - }; - _scroll_id?: string; - _shards: estypes.ShardStatistics; -} - -export interface ErrorResponse { - type: string; - reason: string; -} - -export interface BulkResult { - items: Array<{ index: { error?: ErrorResponse } }>; -} - -export interface IndexInfo { - aliases: AliasResult; - mappings: IndexMapping; -} - -export interface IndicesInfo { - [index: string]: IndexInfo; -} diff --git a/src/core/server/saved_objects/migrations/core/elastic_index.ts b/src/core/server/saved_objects/migrations/core/elastic_index.ts index 5068c24df3414..dc98139ad513e 100644 --- a/src/core/server/saved_objects/migrations/core/elastic_index.ts +++ b/src/core/server/saved_objects/migrations/core/elastic_index.ts @@ -306,7 +306,7 @@ export async function convertToAlias( * alias, meaning that it will only point to one index at a time, so we * remove any other indices from the alias. * - * @param {CallCluster} client + * @param {MigrationEsClient} client * @param {string} index * @param {string} alias * @param {AliasAction[]} aliasActions - Optional actions to be added to the updateAliases call diff --git a/src/core/server/saved_objects/migrations/core/index.ts b/src/core/server/saved_objects/migrations/core/index.ts index 49c7124b9629b..84733f1bca061 100644 --- a/src/core/server/saved_objects/migrations/core/index.ts +++ b/src/core/server/saved_objects/migrations/core/index.ts @@ -9,7 +9,6 @@ export { DocumentMigrator } from './document_migrator'; export { IndexMigrator } from './index_migrator'; export { buildActiveMappings } from './build_active_mappings'; -export type { CallCluster } from './call_cluster'; export type { LogFn, SavedObjectsMigrationLogger } from './migration_logger'; export type { MigrationResult, MigrationStatus } from './migration_coordinator'; export { createMigrationEsClient } from './migration_es_client'; diff --git a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts index 06be065c28a71..27bc935b0ee3f 100644 --- a/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts +++ b/x-pack/plugins/spaces/server/usage_collection/spaces_usage_collector.ts @@ -30,7 +30,7 @@ interface SpacesAggregationResponse { /** * - * @param {CallCluster} callCluster + * @param {ElasticsearchClient} esClient * @param {string} kibanaIndex * @param {PluginsSetup['features']} features * @param {boolean} spacesAvailable From 1eb0f4aecd78e6b0829274734583c4d5214ee30a Mon Sep 17 00:00:00 2001 From: Stratoula Kalafateli Date: Thu, 21 Oct 2021 11:31:07 +0300 Subject: [PATCH 02/18] [XY] Fixes the scale type on a terms aggregation on a number field (#115729) --- .../xy/public/config/get_axis.test.ts | 78 +++++++++++++++++++ .../vis_types/xy/public/config/get_axis.ts | 7 +- 2 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/plugins/vis_types/xy/public/config/get_axis.test.ts diff --git a/src/plugins/vis_types/xy/public/config/get_axis.test.ts b/src/plugins/vis_types/xy/public/config/get_axis.test.ts new file mode 100644 index 0000000000000..7dddae5702b2e --- /dev/null +++ b/src/plugins/vis_types/xy/public/config/get_axis.test.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getScale } from './get_axis'; +import type { Scale } from '../types'; + +describe('getScale', () => { + const axisScale = { + type: 'linear', + mode: 'normal', + scaleType: 'linear', + } as Scale; + + it('returns linear type for a number', () => { + const format = { id: 'number' }; + const scale = getScale(axisScale, {}, format, true); + expect(scale.type).toBe('linear'); + }); + + it('returns ordinal type for a terms aggregation on a number field', () => { + const format = { + id: 'terms', + params: { + id: 'number', + otherBucketLabel: 'Other', + missingBucketLabel: 'Missing', + }, + }; + const scale = getScale(axisScale, {}, format, true); + expect(scale.type).toBe('ordinal'); + }); + + it('returns ordinal type for a terms aggregation on a string field', () => { + const format = { + id: 'terms', + params: { + id: 'string', + otherBucketLabel: 'Other', + missingBucketLabel: 'Missing', + }, + }; + const scale = getScale(axisScale, {}, format, true); + expect(scale.type).toBe('ordinal'); + }); + + it('returns ordinal type for a range aggregation on a number field', () => { + const format = { + id: 'range', + params: { + id: 'number', + }, + }; + const scale = getScale(axisScale, {}, format, true); + expect(scale.type).toBe('ordinal'); + }); + + it('returns time type for a date histogram aggregation', () => { + const format = { + id: 'date', + params: { + pattern: 'HH:mm', + }, + }; + const scale = getScale(axisScale, { date: true }, format, true); + expect(scale.type).toBe('time'); + }); + + it('returns linear type for an histogram aggregation', () => { + const format = { id: 'number' }; + const scale = getScale(axisScale, { interval: 1 }, format, true); + expect(scale.type).toBe('linear'); + }); +}); diff --git a/src/plugins/vis_types/xy/public/config/get_axis.ts b/src/plugins/vis_types/xy/public/config/get_axis.ts index 09495725296cd..0d6c67d064cf8 100644 --- a/src/plugins/vis_types/xy/public/config/get_axis.ts +++ b/src/plugins/vis_types/xy/public/config/get_axis.ts @@ -120,7 +120,7 @@ function getScaleType( return type; } -function getScale( +export function getScale( scale: Scale, params: Aspect['params'], format: Aspect['format'], @@ -130,7 +130,10 @@ function getScale( isCategoryAxis ? getScaleType( scale, - format?.id === 'number' || (format?.params?.id === 'number' && format?.id !== 'range'), + format?.id === 'number' || + (format?.params?.id === 'number' && + format?.id !== BUCKET_TYPES.RANGE && + format?.id !== BUCKET_TYPES.TERMS), 'date' in params, 'interval' in params ) From 647ca9a82b4ccf929e690be9ef2f86e00119d4cb Mon Sep 17 00:00:00 2001 From: Kevin Lacabane Date: Thu, 21 Oct 2021 10:46:20 +0200 Subject: [PATCH 03/18] Fix nodedetail flaky test (#115612) * add test-subj to page loading component * wait for page to load before navigating to node * enable node_detail functional tests * a different approach * bump timeouts * Revert "add test-subj to page loading component" This reverts commit 1840cdf7b8a75e6c4d4e7c840a263c9e7b5e8fb4. * remove unused method Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../apps/monitoring/elasticsearch/node_detail.js | 3 +-- .../apps/monitoring/elasticsearch/node_detail_mb.js | 3 +-- .../functional/services/monitoring/elasticsearch_nodes.js | 7 +++++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/x-pack/test/functional/apps/monitoring/elasticsearch/node_detail.js b/x-pack/test/functional/apps/monitoring/elasticsearch/node_detail.js index 07fda7a143a99..6b1658dd9ed0e 100644 --- a/x-pack/test/functional/apps/monitoring/elasticsearch/node_detail.js +++ b/x-pack/test/functional/apps/monitoring/elasticsearch/node_detail.js @@ -14,8 +14,7 @@ export default function ({ getService, getPageObjects }) { const nodesList = getService('monitoringElasticsearchNodes'); const nodeDetail = getService('monitoringElasticsearchNodeDetail'); - // FLAKY https://github.com/elastic/kibana/issues/115130 - describe.skip('Elasticsearch node detail', () => { + describe('Elasticsearch node detail', () => { describe('Active Nodes', () => { const { setup, tearDown } = getLifecycleMethods(getService, getPageObjects); diff --git a/x-pack/test/functional/apps/monitoring/elasticsearch/node_detail_mb.js b/x-pack/test/functional/apps/monitoring/elasticsearch/node_detail_mb.js index 70c9b42b37f42..9130ce91e7b4d 100644 --- a/x-pack/test/functional/apps/monitoring/elasticsearch/node_detail_mb.js +++ b/x-pack/test/functional/apps/monitoring/elasticsearch/node_detail_mb.js @@ -14,8 +14,7 @@ export default function ({ getService, getPageObjects }) { const nodesList = getService('monitoringElasticsearchNodes'); const nodeDetail = getService('monitoringElasticsearchNodeDetail'); - // Failing: See https://github.com/elastic/kibana/issues/115255 - describe.skip('Elasticsearch node detail mb', () => { + describe('Elasticsearch node detail mb', () => { describe('Active Nodes', () => { const { setup, tearDown } = getLifecycleMethods(getService, getPageObjects); diff --git a/x-pack/test/functional/services/monitoring/elasticsearch_nodes.js b/x-pack/test/functional/services/monitoring/elasticsearch_nodes.js index 25bfd82f01da7..e94e85fc96486 100644 --- a/x-pack/test/functional/services/monitoring/elasticsearch_nodes.js +++ b/x-pack/test/functional/services/monitoring/elasticsearch_nodes.js @@ -51,8 +51,11 @@ export function MonitoringElasticsearchNodesProvider({ getService, getPageObject return pageId !== null; } - clickRowByResolver(nodeResolver) { - return testSubjects.click(SUBJ_NODE_LINK_PREFIX + nodeResolver); + async clickRowByResolver(nodeResolver) { + await retry.waitForWithTimeout('redirection to node detail', 30000, async () => { + await testSubjects.click(SUBJ_NODE_LINK_PREFIX + nodeResolver, 5000); + return testSubjects.exists('elasticsearchNodeDetailStatus', { timeout: 5000 }); + }); } async waitForTableToFinishLoading() { From 933ece47ad9f83781d1781b6aaf2853f8ce66b48 Mon Sep 17 00:00:00 2001 From: Shivindera Singh Date: Thu, 21 Oct 2021 10:50:22 +0200 Subject: [PATCH 04/18] [AppServices] Space privilege "Index pattern management" read still shows delete button (#53682) (#115390) * [AppServices] Space privilege Index pattern management read still shows delete button (#53682) --- .../edit_index_pattern/edit_index_pattern.tsx | 6 +- .../indexed_fields_table.test.tsx.snap | 10 +++ .../table/__snapshots__/table.test.tsx.snap | 16 ++++ .../components/table/table.test.tsx | 24 ++++++ .../components/table/table.tsx | 3 +- .../indexed_fields_table.test.tsx | 30 ++++--- .../indexed_fields_table.tsx | 22 +++-- .../indexed_fields_table/types.ts | 1 + .../scripted_field_table.test.tsx.snap | 7 ++ .../header/__snapshots__/header.test.tsx.snap | 81 +------------------ .../components/header/header.tsx | 30 ++++--- .../table/__snapshots__/table.test.tsx.snap | 11 ++- .../components/table/table.test.tsx | 21 ++++- .../components/table/table.tsx | 2 + .../scripted_field_table.test.tsx | 52 +++++++----- .../scripted_fields_table.tsx | 23 ++++-- .../scripted_fields_table/types.ts | 1 + .../edit_index_pattern/tabs/tabs.tsx | 16 ++-- 18 files changed, 210 insertions(+), 146 deletions(-) diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/edit_index_pattern.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/edit_index_pattern.tsx index 214da048bab94..e6c9b7bc5a061 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/edit_index_pattern.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/edit_index_pattern.tsx @@ -56,7 +56,7 @@ const confirmModalOptionsDelete = { export const EditIndexPattern = withRouter( ({ indexPattern, history, location }: EditIndexPatternProps) => { - const { uiSettings, overlays, chrome, data } = + const { application, uiSettings, overlays, chrome, data } = useKibana().services; const [fields, setFields] = useState(indexPattern.getNonScriptedFields()); const [conflictedFields, setConflictedFields] = useState( @@ -134,12 +134,14 @@ export const EditIndexPattern = withRouter( const showTagsSection = Boolean(indexPattern.timeFieldName || (tags && tags.length > 0)); const kibana = useKibana(); const docsUrl = kibana.services.docLinks!.links.elasticsearch.mapping; + const userEditPermission = !!application?.capabilities?.indexPatterns?.save; + return (
{showTagsSection && ( diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap index c03899554ffff..8b6e0a1682750 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap @@ -78,6 +78,7 @@ exports[`IndexedFieldsTable IndexedFieldsTable with rollup index pattern should "terms", ], "isMapped": false, + "isUserEditable": false, "kbnType": undefined, "name": "Elastic", "searchable": true, @@ -96,6 +97,7 @@ exports[`IndexedFieldsTable IndexedFieldsTable with rollup index pattern should "date_histogram (interval: 30s, delay: 30s, UTC)", ], "isMapped": false, + "isUserEditable": false, "kbnType": undefined, "name": "timestamp", "type": "date", @@ -111,6 +113,7 @@ exports[`IndexedFieldsTable IndexedFieldsTable with rollup index pattern should "hasRuntime": false, "info": Array [], "isMapped": false, + "isUserEditable": false, "kbnType": undefined, "name": "conflictingField", "type": "keyword, long", @@ -133,6 +136,7 @@ exports[`IndexedFieldsTable IndexedFieldsTable with rollup index pattern should "value_count", ], "isMapped": false, + "isUserEditable": false, "kbnType": undefined, "name": "amount", "type": "long", @@ -166,6 +170,7 @@ exports[`IndexedFieldsTable should filter based on the query bar 1`] = ` "hasRuntime": false, "info": Array [], "isMapped": false, + "isUserEditable": false, "kbnType": undefined, "name": "Elastic", "searchable": true, @@ -200,6 +205,7 @@ exports[`IndexedFieldsTable should filter based on the type filter 1`] = ` "hasRuntime": false, "info": Array [], "isMapped": false, + "isUserEditable": false, "kbnType": undefined, "name": "timestamp", "type": "date", @@ -233,6 +239,7 @@ exports[`IndexedFieldsTable should render normally 1`] = ` "hasRuntime": false, "info": Array [], "isMapped": false, + "isUserEditable": false, "kbnType": undefined, "name": "Elastic", "searchable": true, @@ -248,6 +255,7 @@ exports[`IndexedFieldsTable should render normally 1`] = ` "hasRuntime": false, "info": Array [], "isMapped": false, + "isUserEditable": false, "kbnType": undefined, "name": "timestamp", "type": "date", @@ -263,6 +271,7 @@ exports[`IndexedFieldsTable should render normally 1`] = ` "hasRuntime": false, "info": Array [], "isMapped": false, + "isUserEditable": false, "kbnType": undefined, "name": "conflictingField", "type": "keyword, long", @@ -277,6 +286,7 @@ exports[`IndexedFieldsTable should render normally 1`] = ` "hasRuntime": false, "info": Array [], "isMapped": false, + "isUserEditable": false, "kbnType": undefined, "name": "amount", "type": "long", diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap index 60138dd5fd8a6..881de58b8a86b 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/__snapshots__/table.test.tsx.snap @@ -105,6 +105,7 @@ exports[`Table should render normally 1`] = ` Object { "actions": Array [ Object { + "available": [Function], "data-test-subj": "editFieldFormat", "description": "Edit", "icon": "pencil", @@ -141,6 +142,7 @@ exports[`Table should render normally 1`] = ` "hasRuntime": false, "info": Array [], "isMapped": true, + "isUserEditable": true, "kbnType": "string", "name": "Elastic", "searchable": true, @@ -152,6 +154,7 @@ exports[`Table should render normally 1`] = ` "hasRuntime": false, "info": Array [], "isMapped": true, + "isUserEditable": true, "kbnType": "date", "name": "timestamp", "type": "date", @@ -162,6 +165,7 @@ exports[`Table should render normally 1`] = ` "hasRuntime": false, "info": Array [], "isMapped": true, + "isUserEditable": true, "kbnType": "conflict", "name": "conflictingField", "type": "text, long", @@ -172,10 +176,22 @@ exports[`Table should render normally 1`] = ` "hasRuntime": true, "info": Array [], "isMapped": false, + "isUserEditable": true, "kbnType": "text", "name": "customer", "type": "keyword", }, + Object { + "displayName": "noedit", + "excluded": false, + "hasRuntime": true, + "info": Array [], + "isMapped": false, + "isUserEditable": false, + "kbnType": "text", + "name": "noedit", + "type": "keyword", + }, ] } pagination={ diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx index 5fb2ba46882cc..71b2e59aefc81 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx @@ -26,6 +26,7 @@ const items: IndexedFieldItem[] = [ kbnType: 'string', excluded: false, isMapped: true, + isUserEditable: true, hasRuntime: false, }, { @@ -36,6 +37,7 @@ const items: IndexedFieldItem[] = [ info: [], excluded: false, isMapped: true, + isUserEditable: true, hasRuntime: false, }, { @@ -46,6 +48,7 @@ const items: IndexedFieldItem[] = [ info: [], excluded: false, isMapped: true, + isUserEditable: true, hasRuntime: false, }, { @@ -56,6 +59,18 @@ const items: IndexedFieldItem[] = [ info: [], excluded: false, isMapped: false, + isUserEditable: true, + hasRuntime: true, + }, + { + name: 'noedit', + displayName: 'noedit', + type: 'keyword', + kbnType: 'text', + info: [], + excluded: false, + isMapped: false, + isUserEditable: false, hasRuntime: true, }, ]; @@ -114,6 +129,13 @@ describe('Table', () => { expect(editField).toBeCalled(); }); + test('should not allow edit or deletion for user with only read access', () => { + const editAvailable = renderTable().prop('columns')[6].actions[0].available(items[4]); + const deleteAvailable = renderTable().prop('columns')[7].actions[0].available(items[4]); + expect(editAvailable).toBeFalsy(); + expect(deleteAvailable).toBeFalsy(); + }); + test('render name', () => { const mappedField = { name: 'customer', @@ -122,6 +144,7 @@ describe('Table', () => { kbnType: 'string', type: 'keyword', isMapped: true, + isUserEditable: true, hasRuntime: false, }; @@ -134,6 +157,7 @@ describe('Table', () => { kbnType: 'string', type: 'keyword', isMapped: false, + isUserEditable: true, hasRuntime: true, }; diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.tsx index 4e672b615aeac..e3e59a4093334 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.tsx @@ -319,6 +319,7 @@ export class Table extends PureComponent { onClick: editField, type: 'icon', 'data-test-subj': 'editFieldFormat', + available: (field) => field.isUserEditable, }, ], width: '40px', @@ -333,7 +334,7 @@ export class Table extends PureComponent { onClick: (field) => deleteField(field.name), type: 'icon', 'data-test-subj': 'deleteField', - available: (field) => !field.isMapped, + available: (field) => !field.isMapped && field.isUserEditable, }, ], width: '40px', diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx index 4928e3026d151..0427bfba7105d 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { shallow, ShallowWrapper } from 'enzyme'; import { IndexPatternField, IndexPattern, IndexPatternType } from 'src/plugins/data/public'; import { IndexedFieldsTable } from './indexed_fields_table'; import { getFieldInfo } from '../../utils'; @@ -78,15 +78,21 @@ const fields = [ displayName: 'Elastic', searchable: true, esTypes: ['keyword'], + isUserEditable: true, }, - { name: 'timestamp', displayName: 'timestamp', esTypes: ['date'] }, - { name: 'conflictingField', displayName: 'conflictingField', esTypes: ['keyword', 'long'] }, - { name: 'amount', displayName: 'amount', esTypes: ['long'] }, + { name: 'timestamp', displayName: 'timestamp', esTypes: ['date'], isUserEditable: true }, + { + name: 'conflictingField', + displayName: 'conflictingField', + esTypes: ['keyword', 'long'], + isUserEditable: true, + }, + { name: 'amount', displayName: 'amount', esTypes: ['long'], isUserEditable: true }, ].map(mockFieldToIndexPatternField); describe('IndexedFieldsTable', () => { test('should render normally', async () => { - const component = shallow( + const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow( { indexedFieldTypeFilter="" fieldFilter="" /> - ); + ).dive(); await new Promise((resolve) => process.nextTick(resolve)); component.update(); @@ -106,7 +112,7 @@ describe('IndexedFieldsTable', () => { }); test('should filter based on the query bar', async () => { - const component = shallow( + const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow( { indexedFieldTypeFilter="" fieldFilter="" /> - ); + ).dive(); await new Promise((resolve) => process.nextTick(resolve)); component.setProps({ fieldFilter: 'Elast' }); @@ -127,7 +133,7 @@ describe('IndexedFieldsTable', () => { }); test('should filter based on the type filter', async () => { - const component = shallow( + const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow( { indexedFieldTypeFilter="" fieldFilter="" /> - ); + ).dive(); await new Promise((resolve) => process.nextTick(resolve)); component.setProps({ indexedFieldTypeFilter: 'date' }); @@ -149,7 +155,7 @@ describe('IndexedFieldsTable', () => { describe('IndexedFieldsTable with rollup index pattern', () => { test('should render normally', async () => { - const component = shallow( + const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow( { indexedFieldTypeFilter="" fieldFilter="" /> - ); + ).dive(); await new Promise((resolve) => process.nextTick(resolve)); component.update(); diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx index 4e9aeb1874c89..b058ad4a7672c 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx @@ -9,8 +9,10 @@ import React, { Component } from 'react'; import { createSelector } from 'reselect'; import { IndexPatternField, IndexPattern } from '../../../../../../plugins/data/public'; +import { useKibana } from '../../../../../../plugins/kibana_react/public'; import { Table } from './components/table'; import { IndexedFieldItem } from './types'; +import { IndexPatternManagmentContext } from '../../../types'; interface IndexedFieldsTableProps { fields: IndexPatternField[]; @@ -23,16 +25,23 @@ interface IndexedFieldsTableProps { getFieldInfo: (indexPattern: IndexPattern, field: IndexPatternField) => string[]; }; fieldWildcardMatcher: (filters: any[]) => (val: any) => boolean; + userEditPermission: boolean; } interface IndexedFieldsTableState { fields: IndexedFieldItem[]; } -export class IndexedFieldsTable extends Component< - IndexedFieldsTableProps, - IndexedFieldsTableState -> { +const withHooks = (Comp: typeof Component) => { + return (props: any) => { + const { application } = useKibana().services; + const userEditPermission = !!application?.capabilities?.indexPatterns?.save; + + return ; + }; +}; + +class IndexedFields extends Component { constructor(props: IndexedFieldsTableProps) { super(props); @@ -50,7 +59,7 @@ export class IndexedFieldsTable extends Component< } mapFields(fields: IndexPatternField[]): IndexedFieldItem[] { - const { indexPattern, fieldWildcardMatcher, helpers } = this.props; + const { indexPattern, fieldWildcardMatcher, helpers, userEditPermission } = this.props; const sourceFilters = indexPattern.sourceFilters && indexPattern.sourceFilters.map((f: Record) => f.value); @@ -68,6 +77,7 @@ export class IndexedFieldsTable extends Component< excluded: fieldWildcardMatch ? fieldWildcardMatch(field.name) : false, info: helpers.getFieldInfo && helpers.getFieldInfo(indexPattern, field), isMapped: !!field.isMapped, + isUserEditable: userEditPermission, hasRuntime: !!field.runtimeField, }; })) || @@ -114,3 +124,5 @@ export class IndexedFieldsTable extends Component< ); } } + +export const IndexedFieldsTable = withHooks(IndexedFields); diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/types.ts b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/types.ts index 662134a003eb6..c5ac96ad149cc 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/types.ts +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/types.ts @@ -16,5 +16,6 @@ export interface IndexedFieldItem extends IndexedFieldItemBase { excluded: boolean; kbnType: string; isMapped: boolean; + isUserEditable: boolean; hasRuntime: boolean; } diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/__snapshots__/scripted_field_table.test.tsx.snap b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/__snapshots__/scripted_field_table.test.tsx.snap index c0ecc441e9018..95be4d4a7d632 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/__snapshots__/scripted_field_table.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/__snapshots__/scripted_field_table.test.tsx.snap @@ -27,11 +27,13 @@ exports[`ScriptedFieldsTable should filter based on the lang filter 1`] = ` items={ Array [ Object { + "isUserEditable": false, "lang": "painless", "name": "ScriptedField", "script": "x++", }, Object { + "isUserEditable": false, "lang": "painless", "name": "JustATest", "script": "z++", @@ -65,6 +67,7 @@ exports[`ScriptedFieldsTable should filter based on the query bar 1`] = ` items={ Array [ Object { + "isUserEditable": false, "lang": "painless", "name": "JustATest", "script": "z++", @@ -123,11 +126,13 @@ exports[`ScriptedFieldsTable should render normally 1`] = ` items={ Array [ Object { + "isUserEditable": false, "lang": "painless", "name": "ScriptedField", "script": "x++", }, Object { + "isUserEditable": false, "lang": "painless", "name": "JustATest", "script": "z++", @@ -161,11 +166,13 @@ exports[`ScriptedFieldsTable should show a delete modal 1`] = ` items={ Array [ Object { + "isUserEditable": false, "lang": "painless", "name": "ScriptedField", "script": "x++", }, Object { + "isUserEditable": false, "lang": "painless", "name": "JustATest", "script": "z++", diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/header/__snapshots__/header.test.tsx.snap b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/header/__snapshots__/header.test.tsx.snap index f4eb2a0e74089..e9916bc1bdeec 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/header/__snapshots__/header.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/header/__snapshots__/header.test.tsx.snap @@ -6,23 +6,7 @@ exports[`Header should render normally 1`] = ` Object { "action": "PUSH", "block": [MockFunction], - "createHref": [MockFunction] { - "calls": Array [ - Array [ - Object { - "hash": "", - "pathname": "patterns/test/create-field/", - "search": "", - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, + "createHref": [MockFunction], "createSubHistory": [MockFunction], "go": [MockFunction], "goBack": [MockFunction], @@ -136,69 +120,6 @@ exports[`Header should render normally 1`] = `
- -
- - - - - -
-
diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/header/header.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/header/header.tsx index 22da83b179652..a5930e14f0a0d 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/header/header.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/header/header.tsx @@ -22,7 +22,9 @@ interface HeaderProps extends RouteComponentProps { } export const Header = withRouter(({ indexPatternId, history }: HeaderProps) => { - const docLinks = useKibana().services.docLinks?.links; + const { application, docLinks } = useKibana().services; + const links = docLinks?.links; + const userEditPermission = !!application?.capabilities?.indexPatterns?.save; return ( @@ -39,7 +41,7 @@ export const Header = withRouter(({ indexPatternId, history }: HeaderProps) => { defaultMessage="Scripted fields are deprecated. Use {runtimeDocs} instead." values={{ runtimeDocs: ( - + { - - - - - + {userEditPermission && ( + + + + + + )} ); }); diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/table/__snapshots__/table.test.tsx.snap b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/table/__snapshots__/table.test.tsx.snap index 8439887dd468a..c312ae5ecbbe6 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/table/__snapshots__/table.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/table/__snapshots__/table.test.tsx.snap @@ -37,6 +37,7 @@ exports[`Table should render normally 1`] = ` Object { "actions": Array [ Object { + "available": [Function], "description": "Edit this field", "icon": "pencil", "name": "Edit", @@ -44,6 +45,7 @@ exports[`Table should render normally 1`] = ` "type": "icon", }, Object { + "available": [Function], "color": "danger", "description": "Delete this field", "icon": "trash", @@ -60,10 +62,17 @@ exports[`Table should render normally 1`] = ` items={ Array [ Object { - "lang": "Elastic", + "isUserEditable": true, + "lang": "painless", "name": "1", "script": "", }, + Object { + "isUserEditable": false, + "lang": "painless", + "name": "2", + "script": "", + }, ] } pagination={ diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/table/table.test.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/table/table.test.tsx index 5a9f4eb871181..7b53b9a6c20c5 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/table/table.test.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/table/table.test.tsx @@ -15,8 +15,10 @@ import { IIndexPattern } from 'src/plugins/data/public'; const getIndexPatternMock = (mockedFields: any = {}) => ({ ...mockedFields } as IIndexPattern); -// @ts-expect-error invalid lang type -const items: ScriptedFieldItem[] = [{ name: '1', lang: 'Elastic', script: '' }]; +const items: ScriptedFieldItem[] = [ + { name: '1', lang: 'painless', script: '', isUserEditable: true }, + { name: '2', lang: 'painless', script: '', isUserEditable: false }, +]; describe('Table', () => { let indexPattern: IIndexPattern; @@ -93,4 +95,19 @@ describe('Table', () => { component.prop('columns')[4].actions[1].onClick(); expect(deleteField).toBeCalled(); }); + + test('should not allow edit or deletion for user with only read access', () => { + const component = shallow( + {}} + deleteField={() => {}} + /> + ); + const editAvailable = component.prop('columns')[4].actions[0].available(items[1]); + const deleteAvailable = component.prop('columns')[4].actions[1].available(items[1]); + expect(editAvailable).toBeFalsy(); + expect(deleteAvailable).toBeFalsy(); + }); }); diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/table/table.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/table/table.tsx index 3fcc2ebb0b93e..5064f40f43297 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/table/table.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/components/table/table.tsx @@ -106,6 +106,7 @@ export class Table extends PureComponent { ), icon: 'pencil', onClick: editField, + available: (field) => !!field.isUserEditable, }, { type: 'icon', @@ -122,6 +123,7 @@ export class Table extends PureComponent { icon: 'trash', color: 'danger', onClick: deleteField, + available: (field) => !!field.isUserEditable, }, ], width: '40px', diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/scripted_field_table.test.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/scripted_field_table.test.tsx index 1f5220aa01c44..d797da7221575 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/scripted_field_table.test.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/scripted_field_table.test.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { shallow } from 'enzyme'; +import { shallow, ShallowWrapper } from 'enzyme'; import { ScriptedFieldsTable } from '../scripted_fields_table'; import { IIndexPattern, IndexPattern } from '../../../../../../plugins/data/common'; @@ -48,21 +48,28 @@ describe('ScriptedFieldsTable', () => { beforeEach(() => { indexPattern = getIndexPatternMock({ getScriptedFields: () => [ - { name: 'ScriptedField', lang: 'painless', script: 'x++' }, - { name: 'JustATest', lang: 'painless', script: 'z++' }, + { isUserEditable: true, name: 'ScriptedField', lang: 'painless', script: 'x++' }, + { + isUserEditable: false, + name: 'JustATest', + lang: 'painless', + script: 'z++', + }, ], }) as IndexPattern; }); test('should render normally', async () => { - const component = shallow( + const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow< + typeof ScriptedFieldsTable + >( {}} /> - ); + ).dive(); // Allow the componentWillMount code to execute // https://github.com/airbnb/enzyme/issues/450 @@ -73,14 +80,14 @@ describe('ScriptedFieldsTable', () => { }); test('should filter based on the query bar', async () => { - const component = shallow( + const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow( {}} /> - ); + ).dive(); // Allow the componentWillMount code to execute // https://github.com/airbnb/enzyme/issues/450 @@ -94,14 +101,16 @@ describe('ScriptedFieldsTable', () => { }); test('should filter based on the lang filter', async () => { - const component = shallow( + const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow< + typeof ScriptedFieldsTable + >( [ - { name: 'ScriptedField', lang: 'painless', script: 'x++' }, - { name: 'JustATest', lang: 'painless', script: 'z++' }, - { name: 'Bad', lang: 'somethingElse', script: 'z++' }, + { isUserEditable: true, name: 'ScriptedField', lang: 'painless', script: 'x++' }, + { isUserEditable: true, name: 'JustATest', lang: 'painless', script: 'z++' }, + { isUserEditable: true, name: 'Bad', lang: 'somethingElse', script: 'z++' }, ], }) as IndexPattern } @@ -109,7 +118,7 @@ describe('ScriptedFieldsTable', () => { helpers={helpers} saveIndexPattern={async () => {}} /> - ); + ).dive(); // Allow the componentWillMount code to execute // https://github.com/airbnb/enzyme/issues/450 @@ -123,7 +132,7 @@ describe('ScriptedFieldsTable', () => { }); test('should hide the table if there are no scripted fields', async () => { - const component = shallow( + const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow( { helpers={helpers} saveIndexPattern={async () => {}} /> - ); + ).dive(); // Allow the componentWillMount code to execute // https://github.com/airbnb/enzyme/issues/450 @@ -145,14 +154,16 @@ describe('ScriptedFieldsTable', () => { }); test('should show a delete modal', async () => { - const component = shallow( + const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow< + typeof ScriptedFieldsTable + >( {}} /> - ); + ).dive(); await component.update(); // Fire `componentWillMount()` // @ts-expect-error lang is not valid @@ -165,7 +176,9 @@ describe('ScriptedFieldsTable', () => { test('should delete a field', async () => { const removeScriptedField = jest.fn(); - const component = shallow( + const component: ShallowWrapper, React.Component<{}, {}, any>> = shallow< + typeof ScriptedFieldsTable + >( { painlessDocLink={'painlessDoc'} saveIndexPattern={async () => {}} /> - ); + ).dive(); await component.update(); // Fire `componentWillMount()` - // @ts-expect-error lang is not valid + // @ts-expect-error component.instance().startDeleteField({ name: 'ScriptedField', lang: '', script: '' }); await component.update(); + // @ts-expect-error await component.instance().deleteField(); await component.update(); diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/scripted_fields_table.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/scripted_fields_table.tsx index 7fd5170b669f7..76433a7b49e27 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/scripted_fields_table.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/scripted_fields_table.tsx @@ -15,8 +15,10 @@ import { import { Table, Header, CallOuts, DeleteScritpedFieldConfirmationModal } from './components'; import { ScriptedFieldItem } from './types'; +import { IndexPatternManagmentContext } from '../../../types'; import { IndexPattern, DataPublicPluginStart } from '../../../../../../plugins/data/public'; +import { useKibana } from '../../../../../../plugins/kibana_react/public'; interface ScriptedFieldsTableProps { indexPattern: IndexPattern; @@ -29,6 +31,7 @@ interface ScriptedFieldsTableProps { onRemoveField?: () => void; painlessDocLink: string; saveIndexPattern: DataPublicPluginStart['indexPatterns']['updateSavedObject']; + userEditPermission: boolean; } interface ScriptedFieldsTableState { @@ -38,10 +41,16 @@ interface ScriptedFieldsTableState { fields: ScriptedFieldItem[]; } -export class ScriptedFieldsTable extends Component< - ScriptedFieldsTableProps, - ScriptedFieldsTableState -> { +const withHooks = (Comp: typeof Component) => { + return (props: any) => { + const { application } = useKibana().services; + const userEditPermission = !!application?.capabilities?.indexPatterns?.save; + + return ; + }; +}; + +class ScriptedFields extends Component { constructor(props: ScriptedFieldsTableProps) { super(props); @@ -79,7 +88,7 @@ export class ScriptedFieldsTable extends Component< getFilteredItems = () => { const { fields } = this.state; - const { fieldFilter, scriptedFieldLanguageFilter } = this.props; + const { fieldFilter, scriptedFieldLanguageFilter, userEditPermission } = this.props; let languageFilteredFields = fields; @@ -99,6 +108,8 @@ export class ScriptedFieldsTable extends Component< ); } + filteredFields.forEach((field) => (field.isUserEditable = userEditPermission)); + return filteredFields; }; @@ -157,3 +168,5 @@ export class ScriptedFieldsTable extends Component< ); } } + +export const ScriptedFieldsTable = withHooks(ScriptedFields); diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/types.ts b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/types.ts index 5c6adc824d225..45a59b97b9490 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/types.ts +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/scripted_fields_table/types.ts @@ -11,4 +11,5 @@ export interface ScriptedFieldItem { name: string; lang: estypes.ScriptLanguage; script: string; + isUserEditable?: boolean; } diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx index e6c4edfc13e9c..821f1d533e201 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx @@ -80,7 +80,7 @@ export function Tabs({ location, refreshFields, }: TabsProps) { - const { uiSettings, docLinks, indexPatternFieldEditor } = + const { application, uiSettings, docLinks, indexPatternFieldEditor } = useKibana().services; const [fieldFilter, setFieldFilter] = useState(''); const [indexedFieldTypeFilter, setIndexedFieldTypeFilter] = useState(''); @@ -149,6 +149,7 @@ export function Tabs({ [uiSettings] ); + const userEditPermission = !!application?.capabilities?.indexPatterns?.save; const getFilterSection = useCallback( (type: string) => { return ( @@ -174,11 +175,13 @@ export function Tabs({ aria-label={filterAriaLabel} /> - - openFieldEditor()} data-test-subj="addField"> - {addFieldButtonLabel} - - + {userEditPermission && ( + + openFieldEditor()} data-test-subj="addField"> + {addFieldButtonLabel} + + + )} )} {type === TAB_SCRIPTED_FIELDS && scriptedFieldLanguages.length > 0 && ( @@ -201,6 +204,7 @@ export function Tabs({ scriptedFieldLanguageFilter, scriptedFieldLanguages, openFieldEditor, + userEditPermission, ] ); From 800cfb504c842f13a6b3ad76a78f2dfad982b5ec Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Thu, 21 Oct 2021 11:03:53 +0200 Subject: [PATCH 05/18] [expressions] select_filter and remove_filter functions (#113533) --- .../src/filters/build_filters/types.ts | 2 + .../data/common/search/expressions/index.ts | 2 + .../search/expressions/remove_filter.test.ts | 206 ++++++++++++++++ .../search/expressions/remove_filter.ts | 69 ++++++ .../search/expressions/select_filter.test.ts | 223 ++++++++++++++++++ .../search/expressions/select_filter.ts | 69 ++++++ .../data/public/search/search_service.ts | 4 + .../data/server/search/search_service.ts | 4 + 8 files changed, 579 insertions(+) create mode 100644 src/plugins/data/common/search/expressions/remove_filter.test.ts create mode 100644 src/plugins/data/common/search/expressions/remove_filter.ts create mode 100644 src/plugins/data/common/search/expressions/select_filter.test.ts create mode 100644 src/plugins/data/common/search/expressions/select_filter.ts diff --git a/packages/kbn-es-query/src/filters/build_filters/types.ts b/packages/kbn-es-query/src/filters/build_filters/types.ts index 12830e4b6eeac..3f6d586feed98 100644 --- a/packages/kbn-es-query/src/filters/build_filters/types.ts +++ b/packages/kbn-es-query/src/filters/build_filters/types.ts @@ -56,6 +56,8 @@ export type FilterMeta = { negate?: boolean; // controlledBy is there to identify who owns the filter controlledBy?: string; + // allows grouping of filters + group?: string; // index and type are optional only because when you create a new filter, there are no defaults index?: string; isMultiIndex?: boolean; diff --git a/src/plugins/data/common/search/expressions/index.ts b/src/plugins/data/common/search/expressions/index.ts index fbf3ba7f32c5c..05496b4d8c885 100644 --- a/src/plugins/data/common/search/expressions/index.ts +++ b/src/plugins/data/common/search/expressions/index.ts @@ -36,6 +36,8 @@ export * from './field'; export * from './phrase_filter'; export * from './exists_filter'; export * from './range_filter'; +export * from './remove_filter'; +export * from './select_filter'; export * from './kibana_filter'; export * from './filters_to_ast'; export * from './timerange'; diff --git a/src/plugins/data/common/search/expressions/remove_filter.test.ts b/src/plugins/data/common/search/expressions/remove_filter.test.ts new file mode 100644 index 0000000000000..f5a924769df70 --- /dev/null +++ b/src/plugins/data/common/search/expressions/remove_filter.test.ts @@ -0,0 +1,206 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { createMockContext } from '../../../../expressions/common'; +import { functionWrapper } from './utils'; +import { removeFilterFunction } from './remove_filter'; +import { KibanaContext } from './kibana_context_type'; + +describe('interpreter/functions#removeFilter', () => { + const fn = functionWrapper(removeFilterFunction); + const kibanaContext: KibanaContext = { + type: 'kibana_context', + filters: [ + { + meta: { + group: 'g1', + }, + query: {}, + }, + { + meta: { + group: 'g2', + }, + query: {}, + }, + { + meta: { + group: 'g1', + controlledBy: 'i1', + }, + query: {}, + }, + { + meta: { + group: 'g1', + controlledBy: 'i2', + }, + query: {}, + }, + { + meta: { + controlledBy: 'i1', + }, + query: {}, + }, + ], + }; + + it('removes all filters when called without arguments', () => { + const actual = fn(kibanaContext, {}, createMockContext()); + expect(actual).toMatchInlineSnapshot(` + Object { + "filters": Array [], + "type": "kibana_context", + } + `); + }); + + it('removes filters belonging to certain group', () => { + const actual = fn(kibanaContext, { group: 'g1' }, createMockContext()); + expect(actual).toMatchInlineSnapshot(` + Object { + "filters": Array [ + Object { + "meta": Object { + "group": "g2", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "controlledBy": "i1", + }, + "query": Object {}, + }, + ], + "type": "kibana_context", + } + `); + }); + + it('removes ungrouped filters', () => { + const actual = fn(kibanaContext, { ungrouped: true }, createMockContext()); + expect(actual).toMatchInlineSnapshot(` + Object { + "filters": Array [ + Object { + "meta": Object { + "group": "g1", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "group": "g2", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "controlledBy": "i1", + "group": "g1", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "controlledBy": "i2", + "group": "g1", + }, + "query": Object {}, + }, + ], + "type": "kibana_context", + } + `); + }); + + it('removes ungrouped filters and filters matching a group', () => { + const actual = fn(kibanaContext, { group: 'g1', ungrouped: true }, createMockContext()); + expect(actual).toMatchInlineSnapshot(` + Object { + "filters": Array [ + Object { + "meta": Object { + "group": "g2", + }, + "query": Object {}, + }, + ], + "type": "kibana_context", + } + `); + }); + + it('removes filters controlled by specified id', () => { + const actual = fn(kibanaContext, { from: 'i1' }, createMockContext()); + expect(actual).toMatchInlineSnapshot(` + Object { + "filters": Array [ + Object { + "meta": Object { + "group": "g1", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "group": "g2", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "controlledBy": "i2", + "group": "g1", + }, + "query": Object {}, + }, + ], + "type": "kibana_context", + } + `); + }); + + it('removes filters controlled by specified id and matching a group', () => { + const actual = fn(kibanaContext, { group: 'g1', from: 'i1' }, createMockContext()); + expect(actual).toMatchInlineSnapshot(` + Object { + "filters": Array [ + Object { + "meta": Object { + "group": "g1", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "group": "g2", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "controlledBy": "i2", + "group": "g1", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "controlledBy": "i1", + }, + "query": Object {}, + }, + ], + "type": "kibana_context", + } + `); + }); +}); diff --git a/src/plugins/data/common/search/expressions/remove_filter.ts b/src/plugins/data/common/search/expressions/remove_filter.ts new file mode 100644 index 0000000000000..f45cc4c557a73 --- /dev/null +++ b/src/plugins/data/common/search/expressions/remove_filter.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; +import { KibanaContext } from './kibana_context_type'; + +interface Arguments { + group?: string; + from?: string; + ungrouped?: boolean; +} + +export type ExpressionFunctionRemoveFilter = ExpressionFunctionDefinition< + 'removeFilter', + KibanaContext, + Arguments, + KibanaContext +>; + +export const removeFilterFunction: ExpressionFunctionRemoveFilter = { + name: 'removeFilter', + type: 'kibana_context', + inputTypes: ['kibana_context'], + help: i18n.translate('data.search.functions.removeFilter.help', { + defaultMessage: 'Removes filters from context', + }), + args: { + group: { + types: ['string'], + aliases: ['_'], + help: i18n.translate('data.search.functions.removeFilter.group.help', { + defaultMessage: 'Removes only filters belonging to the provided group', + }), + }, + from: { + types: ['string'], + help: i18n.translate('data.search.functions.removeFilter.from.help', { + defaultMessage: 'Removes only filters owned by the provided id', + }), + }, + ungrouped: { + types: ['boolean'], + aliases: ['nogroup', 'nogroups'], + default: false, + help: i18n.translate('data.search.functions.removeFilter.ungrouped.help', { + defaultMessage: 'Should filters without group be removed', + }), + }, + }, + + fn(input, { group, from, ungrouped }) { + return { + ...input, + filters: + input.filters?.filter(({ meta }) => { + const isGroupMatching = + (!group && !ungrouped) || group === meta.group || (ungrouped && !meta.group); + const isOriginMatching = !from || from === meta.controlledBy; + return !isGroupMatching || !isOriginMatching; + }) || [], + }; + }, +}; diff --git a/src/plugins/data/common/search/expressions/select_filter.test.ts b/src/plugins/data/common/search/expressions/select_filter.test.ts new file mode 100644 index 0000000000000..a2515dbcb171d --- /dev/null +++ b/src/plugins/data/common/search/expressions/select_filter.test.ts @@ -0,0 +1,223 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { createMockContext } from '../../../../expressions/common'; +import { functionWrapper } from './utils'; +import { selectFilterFunction } from './select_filter'; +import { KibanaContext } from './kibana_context_type'; + +describe('interpreter/functions#selectFilter', () => { + const fn = functionWrapper(selectFilterFunction); + const kibanaContext: KibanaContext = { + type: 'kibana_context', + filters: [ + { + meta: { + group: 'g1', + }, + query: {}, + }, + { + meta: { + group: 'g2', + }, + query: {}, + }, + { + meta: { + group: 'g1', + controlledBy: 'i1', + }, + query: {}, + }, + { + meta: { + group: 'g1', + controlledBy: 'i2', + }, + query: {}, + }, + { + meta: { + controlledBy: 'i1', + }, + query: {}, + }, + ], + }; + + it('selects all filters when called without arguments', () => { + const actual = fn(kibanaContext, {}, createMockContext()); + expect(actual).toMatchInlineSnapshot(` + Object { + "filters": Array [ + Object { + "meta": Object { + "group": "g1", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "group": "g2", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "controlledBy": "i1", + "group": "g1", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "controlledBy": "i2", + "group": "g1", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "controlledBy": "i1", + }, + "query": Object {}, + }, + ], + "type": "kibana_context", + } + `); + }); + + it('selects filters belonging to certain group', () => { + const actual = fn(kibanaContext, { group: 'g1' }, createMockContext()); + expect(actual).toMatchInlineSnapshot(` + Object { + "filters": Array [ + Object { + "meta": Object { + "group": "g1", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "controlledBy": "i1", + "group": "g1", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "controlledBy": "i2", + "group": "g1", + }, + "query": Object {}, + }, + ], + "type": "kibana_context", + } + `); + }); + + it('selects ungrouped filters', () => { + const actual = fn(kibanaContext, { ungrouped: true }, createMockContext()); + expect(actual).toMatchInlineSnapshot(` + Object { + "filters": Array [ + Object { + "meta": Object { + "controlledBy": "i1", + }, + "query": Object {}, + }, + ], + "type": "kibana_context", + } + `); + }); + + it('selects ungrouped filters and filters matching a group', () => { + const actual = fn(kibanaContext, { group: 'g1', ungrouped: true }, createMockContext()); + expect(actual).toMatchInlineSnapshot(` + Object { + "filters": Array [ + Object { + "meta": Object { + "group": "g1", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "controlledBy": "i1", + "group": "g1", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "controlledBy": "i2", + "group": "g1", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "controlledBy": "i1", + }, + "query": Object {}, + }, + ], + "type": "kibana_context", + } + `); + }); + + it('selects filters controlled by specified id', () => { + const actual = fn(kibanaContext, { from: 'i1' }, createMockContext()); + expect(actual).toMatchInlineSnapshot(` + Object { + "filters": Array [ + Object { + "meta": Object { + "controlledBy": "i1", + "group": "g1", + }, + "query": Object {}, + }, + Object { + "meta": Object { + "controlledBy": "i1", + }, + "query": Object {}, + }, + ], + "type": "kibana_context", + } + `); + }); + + it('selects filters controlled by specified id and matching a group', () => { + const actual = fn(kibanaContext, { group: 'g1', from: 'i1' }, createMockContext()); + expect(actual).toMatchInlineSnapshot(` + Object { + "filters": Array [ + Object { + "meta": Object { + "controlledBy": "i1", + "group": "g1", + }, + "query": Object {}, + }, + ], + "type": "kibana_context", + } + `); + }); +}); diff --git a/src/plugins/data/common/search/expressions/select_filter.ts b/src/plugins/data/common/search/expressions/select_filter.ts new file mode 100644 index 0000000000000..3e76f3a6426c2 --- /dev/null +++ b/src/plugins/data/common/search/expressions/select_filter.ts @@ -0,0 +1,69 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; +import { KibanaContext } from './kibana_context_type'; + +interface Arguments { + group?: string; + from?: string; + ungrouped?: boolean; +} + +export type ExpressionFunctionSelectFilter = ExpressionFunctionDefinition< + 'selectFilter', + KibanaContext, + Arguments, + KibanaContext +>; + +export const selectFilterFunction: ExpressionFunctionSelectFilter = { + name: 'selectFilter', + type: 'kibana_context', + inputTypes: ['kibana_context'], + help: i18n.translate('data.search.functions.selectFilter.help', { + defaultMessage: 'Selects filters from context', + }), + args: { + group: { + types: ['string'], + aliases: ['_'], + help: i18n.translate('data.search.functions.selectFilter.group.help', { + defaultMessage: 'Select only filters belonging to the provided group', + }), + }, + from: { + types: ['string'], + help: i18n.translate('data.search.functions.selectFilter.from.help', { + defaultMessage: 'Select only filters owned by the provided id', + }), + }, + ungrouped: { + types: ['boolean'], + aliases: ['nogroup', 'nogroups'], + default: false, + help: i18n.translate('data.search.functions.selectFilter.ungrouped.help', { + defaultMessage: 'Should filters without group be included', + }), + }, + }, + + fn(input, { group, ungrouped, from }) { + return { + ...input, + filters: + input.filters?.filter(({ meta }) => { + const isGroupMatching = + (!group && !ungrouped) || group === meta.group || (ungrouped && !meta.group); + const isOriginMatching = !from || from === meta.controlledBy; + return isGroupMatching && isOriginMatching; + }) || [], + }; + }, +}; diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index 7cba1ec17c322..ecc0e84917251 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -39,6 +39,8 @@ import { geoPointFunction, queryFilterFunction, rangeFilterFunction, + removeFilterFunction, + selectFilterFunction, kibanaFilterFunction, phraseFilterFunction, esRawResponse, @@ -139,6 +141,8 @@ export class SearchService implements Plugin { expressions.registerFunction(existsFilterFunction); expressions.registerFunction(queryFilterFunction); expressions.registerFunction(rangeFilterFunction); + expressions.registerFunction(removeFilterFunction); + expressions.registerFunction(selectFilterFunction); expressions.registerFunction(phraseFilterFunction); expressions.registerType(kibanaContext); diff --git a/src/plugins/data/server/search/search_service.ts b/src/plugins/data/server/search/search_service.ts index fa7296c822467..04db51fdce7fb 100644 --- a/src/plugins/data/server/search/search_service.ts +++ b/src/plugins/data/server/search/search_service.ts @@ -66,6 +66,8 @@ import { numericalRangeFunction, queryFilterFunction, rangeFilterFunction, + removeFilterFunction, + selectFilterFunction, rangeFunction, SearchSourceDependencies, searchSourceRequiredUiSettings, @@ -205,6 +207,8 @@ export class SearchService implements Plugin { expressions.registerFunction(existsFilterFunction); expressions.registerFunction(queryFilterFunction); expressions.registerFunction(rangeFilterFunction); + expressions.registerFunction(removeFilterFunction); + expressions.registerFunction(selectFilterFunction); expressions.registerFunction(phraseFilterFunction); expressions.registerType(kibanaContext); expressions.registerType(esRawResponse); From 43e0043fcb07cfb2232b6ba81350eb4337e7ba8e Mon Sep 17 00:00:00 2001 From: Christos Nasikas Date: Thu, 21 Oct 2021 12:41:56 +0300 Subject: [PATCH 06/18] [Connectors][ServiceNow] Default `isLegacy` to true for API consumers (#115367) --- .../action-types/servicenow-sir.asciidoc | 4 ++ .../action-types/servicenow.asciidoc | 4 ++ .../builtin_action_types/servicenow/schema.ts | 2 +- .../servicenow/servicenow_connectors.test.tsx | 38 +++++++++++++++++++ .../servicenow/servicenow_connectors.tsx | 14 ++++++- .../builtin_action_types/servicenow_itsm.ts | 5 ++- .../builtin_action_types/servicenow_sir.ts | 5 ++- .../case_api_integration/common/lib/utils.ts | 2 + 8 files changed, 68 insertions(+), 6 deletions(-) diff --git a/docs/management/connectors/action-types/servicenow-sir.asciidoc b/docs/management/connectors/action-types/servicenow-sir.asciidoc index 40fb07897d206..b924837c97bc3 100644 --- a/docs/management/connectors/action-types/servicenow-sir.asciidoc +++ b/docs/management/connectors/action-types/servicenow-sir.asciidoc @@ -37,6 +37,7 @@ Use the <> to customize connecto actionTypeId: .servicenow-sir config: apiUrl: https://example.service-now.com/ + isLegacy: false secrets: username: testuser password: passwordkeystorevalue @@ -45,6 +46,9 @@ Use the <> to customize connecto Config defines information for the connector type. `apiUrl`:: An address that corresponds to *URL*. +`isLegacy`:: A boolean that indicates if the connector should use the Table API (legacy) or the Import Set API. + +Note: If `isLegacy` is set to false the Elastic application should be installed in ServiceNow. Secrets defines sensitive information for the connector type. diff --git a/docs/management/connectors/action-types/servicenow.asciidoc b/docs/management/connectors/action-types/servicenow.asciidoc index eae1fce75731d..73da93e57dae9 100644 --- a/docs/management/connectors/action-types/servicenow.asciidoc +++ b/docs/management/connectors/action-types/servicenow.asciidoc @@ -37,6 +37,7 @@ Use the <> to customize connecto actionTypeId: .servicenow config: apiUrl: https://example.service-now.com/ + isLegacy: false secrets: username: testuser password: passwordkeystorevalue @@ -45,6 +46,9 @@ Use the <> to customize connecto Config defines information for the connector type. `apiUrl`:: An address that corresponds to *URL*. +`isLegacy`:: A boolean that indicates if the connector should use the Table API (legacy) or the Import Set API. + +Note: If `isLegacy` is set to false the Elastic application should be installed in ServiceNow. Secrets defines sensitive information for the connector type. diff --git a/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts b/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts index 5f57555a8f9e1..af8d1b9f38b17 100644 --- a/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts +++ b/x-pack/plugins/actions/server/builtin_action_types/servicenow/schema.ts @@ -14,7 +14,7 @@ export const ExternalIncidentServiceConfigurationBase = { export const ExternalIncidentServiceConfiguration = { ...ExternalIncidentServiceConfigurationBase, - isLegacy: schema.boolean({ defaultValue: false }), + isLegacy: schema.boolean({ defaultValue: true }), }; export const ExternalIncidentServiceConfigurationBaseSchema = schema.object( diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.test.tsx index 7c720148780a4..f491376e5078c 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.test.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.test.tsx @@ -414,5 +414,43 @@ describe('ServiceNowActionConnectorFields renders', () => { .includes(errorMessage) ).toBeTruthy(); }); + + test('should set the isLegacy to false when creating a connector', async () => { + const newConnector = { ...usesTableApiConnector, config: {}, secrets: {} }; + const editActionConfig = jest.fn(); + + mountWithIntl( + {}} + readOnly={false} + setCallbacks={setCallbacks} + isEdit={false} + /> + ); + + expect(editActionConfig).toHaveBeenCalledWith('isLegacy', false); + }); + + test('it should set the legacy attribute if it is not undefined', async () => { + const editActionConfig = jest.fn(); + + mountWithIntl( + {}} + readOnly={false} + setCallbacks={setCallbacks} + isEdit={false} + /> + ); + + expect(editActionConfig).not.toHaveBeenCalled(); + }); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx index 20d38cfc7cea8..a0b4bdca47ff5 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/servicenow/servicenow_connectors.tsx @@ -36,7 +36,7 @@ const ServiceNowConnectorFields: React.FC { + if (isLegacy == null) { + editActionConfig('isLegacy', false); + } + }); + return ( <> {showUpdateConnector && ( diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itsm.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itsm.ts index 82f43ed4a3040..97c2a77a8f074 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itsm.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_itsm.ts @@ -91,6 +91,7 @@ export default function serviceNowITSMTest({ getService }: FtrProviderContext) { connector_type_id: '.servicenow', config: { apiUrl: serviceNowSimulatorURL, + isLegacy: false, }, secrets: mockServiceNow.secrets, }) @@ -125,7 +126,7 @@ export default function serviceNowITSMTest({ getService }: FtrProviderContext) { }); }); - it('should set the isLegacy to false when not provided', async () => { + it('should set the isLegacy to true when not provided', async () => { const { body: createdAction } = await supertest .post('/api/actions/connector') .set('kbn-xsrf', 'foo') @@ -143,7 +144,7 @@ export default function serviceNowITSMTest({ getService }: FtrProviderContext) { .get(`/api/actions/connector/${createdAction.id}`) .expect(200); - expect(fetchedAction.config.isLegacy).to.be(false); + expect(fetchedAction.config.isLegacy).to.be(true); }); it('should respond with a 400 Bad Request when creating a servicenow action with no apiUrl', async () => { diff --git a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_sir.ts b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_sir.ts index 0cdb279ac0746..3a13e055e7aeb 100644 --- a/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_sir.ts +++ b/x-pack/test/alerting_api_integration/security_and_spaces/tests/actions/builtin_action_types/servicenow_sir.ts @@ -95,6 +95,7 @@ export default function serviceNowSIRTest({ getService }: FtrProviderContext) { connector_type_id: '.servicenow-sir', config: { apiUrl: serviceNowSimulatorURL, + isLegacy: false, }, secrets: mockServiceNow.secrets, }) @@ -129,7 +130,7 @@ export default function serviceNowSIRTest({ getService }: FtrProviderContext) { }); }); - it('should set the isLegacy to false when not provided', async () => { + it('should set the isLegacy to true when not provided', async () => { const { body: createdAction } = await supertest .post('/api/actions/connector') .set('kbn-xsrf', 'foo') @@ -147,7 +148,7 @@ export default function serviceNowSIRTest({ getService }: FtrProviderContext) { .get(`/api/actions/connector/${createdAction.id}`) .expect(200); - expect(fetchedAction.config.isLegacy).to.be(false); + expect(fetchedAction.config.isLegacy).to.be(true); }); it('should respond with a 400 Bad Request when creating a servicenow action with no apiUrl', async () => { diff --git a/x-pack/test/case_api_integration/common/lib/utils.ts b/x-pack/test/case_api_integration/common/lib/utils.ts index f34d7398db0c2..0a875f9f1e822 100644 --- a/x-pack/test/case_api_integration/common/lib/utils.ts +++ b/x-pack/test/case_api_integration/common/lib/utils.ts @@ -329,6 +329,7 @@ export const getServiceNowConnector = () => ({ }, config: { apiUrl: 'http://some.non.existent.com', + isLegacy: false, }, }); @@ -385,6 +386,7 @@ export const getServiceNowSIRConnector = () => ({ }, config: { apiUrl: 'http://some.non.existent.com', + isLegacy: false, }, }); From c89bb30b04c918564c71e4f5a02c8a93e24b5a0f Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Thu, 21 Oct 2021 11:50:06 +0200 Subject: [PATCH 07/18] [Lens] Formula: better messages for unsupported field types (#114528) * :bug: Fix wrong error message for wrong data types * :ok_hand: Add one more special case to show error message Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../formula/editor/formula_editor.tsx | 29 ++++++++- .../definitions/formula/formula.test.tsx | 60 +++++++++++++++++-- .../definitions/formula/validation.ts | 55 ++++++++++++++++- .../definitions/last_value.test.tsx | 16 ++++- .../operations/definitions/last_value.tsx | 3 +- 5 files changed, 152 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx index 7abe80003ea0e..15562a999bdb4 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/editor/formula_editor.tsx @@ -49,6 +49,18 @@ import { regenerateLayerFromAst } from '../parse'; import { filterByVisibleOperation } from '../util'; import { getColumnTimeShiftWarnings, getDateHistogramInterval } from '../../../../time_shift_utils'; +function tableHasData( + activeData: ParamEditorProps['activeData'], + layerId: string, + columnId: string +) { + const table = activeData?.[layerId]; + if (!table || table.rows.length === 0) { + return false; + } + return table.rows.some((row) => row[columnId] != null); +} + export const WrappedFormulaEditor = ({ activeData, ...rest @@ -59,7 +71,13 @@ export const WrappedFormulaEditor = ({ activeData, rest.layerId ); - return ; + return ( + + ); }; const MemoizedFormulaEditor = React.memo(FormulaEditor); @@ -76,8 +94,10 @@ export function FormulaEditor({ isFullscreen, setIsCloseable, dateHistogramInterval, + hasData, }: Omit, 'activeData'> & { dateHistogramInterval: ReturnType; + hasData: boolean; }) { const [text, setText] = useState(currentColumn.params.formula); const [warnings, setWarnings] = useState< @@ -180,7 +200,12 @@ export function FormulaEditor({ } if (errors.length) { - if (currentColumn.params.isFormulaBroken) { + // Replace the previous error with the new one + const previousFormulaWasBroken = currentColumn.params.isFormulaBroken; + // If the user is changing a previous formula and there are currently no result + // show the most up-to-date state with the error message. + const previousFormulaWasOkButNoData = !currentColumn.params.isFormulaBroken && !hasData; + if (previousFormulaWasBroken || previousFormulaWasOkButNoData) { // If the formula is already broken, show the latest error message in the workspace if (currentColumn.params.formula !== text) { updateLayer( diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx index 499170349c3d5..d3dc8e95933a3 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/formula.test.tsx @@ -33,11 +33,27 @@ const operationDefinitionMap: Record = { scale: 'ratio', timeScale: false, }), + getPossibleOperationForField: () => ({ scale: 'ratio' }), + } as unknown as GenericOperationDefinition, + terms: { + input: 'field', + getPossibleOperationForField: () => ({ scale: 'ordinal' }), + } as unknown as GenericOperationDefinition, + sum: { + input: 'field', + filterable: true, + getPossibleOperationForField: () => ({ scale: 'ratio' }), + } as unknown as GenericOperationDefinition, + last_value: { + input: 'field', + getPossibleOperationForField: ({ type }) => ({ + scale: type === 'string' ? 'ordinal' : 'ratio', + }), + } as GenericOperationDefinition, + max: { + input: 'field', + getPossibleOperationForField: () => ({ scale: 'ratio' }), } as unknown as GenericOperationDefinition, - terms: { input: 'field' } as GenericOperationDefinition, - sum: { input: 'field', filterable: true } as GenericOperationDefinition, - last_value: { input: 'field' } as GenericOperationDefinition, - max: { input: 'field' } as GenericOperationDefinition, count: { input: 'field', filterable: true, @@ -50,8 +66,12 @@ const operationDefinitionMap: Record = { scale: 'ratio', timeScale: false, }), + getPossibleOperationForField: () => ({ scale: 'ratio' }), + } as unknown as GenericOperationDefinition, + derivative: { + input: 'fullReference', + getPossibleOperationForField: () => ({ scale: 'ratio' }), } as unknown as GenericOperationDefinition, - derivative: { input: 'fullReference' } as GenericOperationDefinition, moving_average: { input: 'fullReference', operationParams: [{ name: 'window', type: 'number', required: true }], @@ -66,8 +86,12 @@ const operationDefinitionMap: Record = { references, }), getErrorMessage: () => ['mock error'], + getPossibleOperationForField: () => ({ scale: 'ratio' }), + } as unknown as GenericOperationDefinition, + cumulative_sum: { + input: 'fullReference', + getPossibleOperationForField: () => ({ scale: 'ratio' }), } as unknown as GenericOperationDefinition, - cumulative_sum: { input: 'fullReference' } as GenericOperationDefinition, }; describe('formula', () => { @@ -1230,6 +1254,30 @@ invalid: " } }); + it('returns errors if the returned type of an operation is not supported by Formula', () => { + // check only "valid" operations which are strictly not supported by Formula + // as for last_value with ordinal data + const formulas = [ + { formula: 'last_value(dest)' }, + { formula: 'terms(dest)' }, + { formula: 'moving_average(last_value(dest), window=7)', errorFormula: 'last_value(dest)' }, + ]; + for (const { formula, errorFormula } of formulas) { + expect( + formulaOperation.getErrorMessage!( + getNewLayerWithFormula(formula), + 'col1', + indexPattern, + operationDefinitionMap + ) + ).toEqual([ + `The return value type of the operation ${ + errorFormula ?? formula + } is not supported in Formula.`, + ]); + } + }); + // there are 4 types of errors for math functions: // * no argument passed // * too many arguments passed diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/validation.ts b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/validation.ts index 8749ed4b690f4..1a8d8529a9b90 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/validation.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/formula/validation.ts @@ -76,6 +76,10 @@ interface ValidationErrors { message: string; type: { operation: string; text: string; type: string }; }; + wrongReturnedType: { + message: string; + type: { text: string }; + }; } type ErrorTypes = keyof ValidationErrors; @@ -308,6 +312,13 @@ function getMessageFromId({ values: { operation: out.operation, text: out.text, type: out.type }, }); break; + case 'wrongReturnedType': + message = i18n.translate('xpack.lens.indexPattern.formulaOperationWrongReturnedType', { + defaultMessage: + 'The return value type of the operation {text} is not supported in Formula.', + values: { text: out.text }, + }); + break; // case 'mathRequiresFunction': // message = i18n.translate('xpack.lens.indexPattern.formulaMathRequiresFunctionLabel', { // defaultMessage; 'The function {name} requires an Elasticsearch function', @@ -602,6 +613,7 @@ function runFullASTValidation( const fieldErrors = validateFieldArguments(node, variables, { isFieldOperation: true, firstArg, + returnedType: getReturnedType(nodeOperation, indexPattern, firstArg), }); if (fieldErrors.length) { errors.push(...fieldErrors); @@ -711,6 +723,7 @@ function runFullASTValidation( const fieldErrors = validateFieldArguments(node, variables, { isFieldOperation: false, firstArg, + returnedType: undefined, }); if (fieldErrors.length) { errors.push(...fieldErrors); @@ -775,6 +788,25 @@ export function getWrongTypeParams( ); } +function getReturnedType( + operation: OperationDefinition, + indexPattern: IndexPattern, + firstArg: TinymathAST +) { + const variables = findVariables(firstArg); + if (variables.length !== 1) { + return; + } + const field = indexPattern.getFieldByName(getValueOrName(variables[0]) as string); + // while usually this is used where it is safe, as generic function it should check anyway + if (!field) { + return; + } + // here we're validating the support of the returned type for Formula, not for the operation itself + // that is already handled indipendently by the operation. So return the scale type + return operation.getPossibleOperationForField(field)?.scale; +} + function getDuplicateParams(params: TinymathNamedArgument[] = []) { const uniqueArgs = Object.create(null); for (const { name } of params) { @@ -898,7 +930,15 @@ export function validateMathNodes(root: TinymathAST, missingVariableSet: Set, - { isFieldOperation, firstArg }: { isFieldOperation: boolean; firstArg: TinymathAST } + { + isFieldOperation, + firstArg, + returnedType, + }: { + isFieldOperation: boolean; + firstArg: TinymathAST; + returnedType: 'ratio' | 'ordinal' | 'interval' | undefined; + } ) { const fields = variables.filter( (arg) => isArgumentValidType(arg, 'variable') && !isMathNode(arg) @@ -920,6 +960,19 @@ function validateFieldArguments( }) ); } + if (isFieldOperation && fields.length === 1 && fields[0] === firstArg) { + if (returnedType === 'ordinal') { + errors.push( + getMessageFromId({ + messageId: 'wrongReturnedType', + values: { + text: node.text ?? `${node.name}(${getValueOrName(firstArg)})`, + }, + locations: node.location ? [node.location] : [], + }) + ); + } + } if (!isFieldOperation && fields.length) { errors.push( getMessageFromId({ diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx index d0dd8a438ed1c..13e16fe1af4d0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.test.tsx @@ -492,7 +492,7 @@ describe('last_value', () => { 'Field notExisting was not found', ]); }); - it('shows error message if the sortField does not exist in index pattern', () => { + it('shows error message if the sortField does not exist in index pattern', () => { errorLayer = { ...errorLayer, columns: { @@ -509,6 +509,20 @@ describe('last_value', () => { 'Field notExisting was not found', ]); }); + it('shows error message if the sourceField is of unsupported type', () => { + errorLayer = { + ...errorLayer, + columns: { + col1: { + ...errorLayer.columns.col1, + sourceField: 'timestamp', + } as LastValueIndexPatternColumn, + }, + }; + expect(lastValueOperation.getErrorMessage!(errorLayer, 'col1', indexPattern)).toEqual([ + 'Field timestamp is of the wrong type', + ]); + }); it('shows error message if the sortField is not date', () => { errorLayer = { ...errorLayer, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx index 9a3ba9a044148..ec949f9f9bc94 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/last_value.tsx @@ -206,7 +206,8 @@ export const lastValueOperation: OperationDefinition Date: Thu, 21 Oct 2021 11:59:00 +0200 Subject: [PATCH 08/18] [Logs UI] Change index pattern config callout into info panel (#115737) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../settings/index_names_configuration_panel.tsx | 14 +++++++------- .../plugins/translations/translations/ja-JP.json | 2 -- .../plugins/translations/translations/zh-CN.json | 2 -- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/x-pack/plugins/infra/public/pages/logs/settings/index_names_configuration_panel.tsx b/x-pack/plugins/infra/public/pages/logs/settings/index_names_configuration_panel.tsx index 2d2909f42bae6..d91c70f09cd3d 100644 --- a/x-pack/plugins/infra/public/pages/logs/settings/index_names_configuration_panel.tsx +++ b/x-pack/plugins/infra/public/pages/logs/settings/index_names_configuration_panel.tsx @@ -48,13 +48,13 @@ export const IndexNamesConfigurationPanel: React.FC<{ - + - + ( ({ indexName }) => indexName ); -const deprecationCalloutTitle = i18n.translate( - 'xpack.infra.logSourceConfiguration.indexNameReferenceDeprecationTitle', +const indexPatternInformationCalloutTitle = i18n.translate( + 'xpack.infra.logSourceConfiguration.indexPatternInformationCalloutTitle', { - defaultMessage: 'Deprecated configuration option', + defaultMessage: 'New configuration option', } ); diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 28f1a0ea81b70..9e4249986c6a9 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -13098,8 +13098,6 @@ "xpack.infra.logSourceConfiguration.childFormElementErrorMessage": "1つ以上のフォームフィールドが無効な状態です。", "xpack.infra.logSourceConfiguration.emptyColumnListErrorMessage": "列リストは未入力のままにできません。", "xpack.infra.logSourceConfiguration.emptyFieldErrorMessage": "フィールド'{fieldName}'は未入力のままにできません。", - "xpack.infra.logSourceConfiguration.indexNameReferenceDeprecationDescription": "ログソースを構成する目的で、Elasticsearchインデックスを直接参照するのは推奨されません。ログソースはKibanaインデックスパターンと統合し、使用されているインデックスを構成します。", - "xpack.infra.logSourceConfiguration.indexNameReferenceDeprecationTitle": "廃止予定の構成オプション", "xpack.infra.logSourceConfiguration.indexPatternManagementLinkText": "インデックスパターン管理画面", "xpack.infra.logSourceConfiguration.indexPatternSectionTitle": "インデックスパターン", "xpack.infra.logSourceConfiguration.indexPatternSelectorPlaceholder": "インデックスパターンを選択", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 7b90a6d6afdaf..db6d19d7550a9 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -13284,8 +13284,6 @@ "xpack.infra.logSourceConfiguration.childFormElementErrorMessage": "至少一个表单字段处于无效状态。", "xpack.infra.logSourceConfiguration.emptyColumnListErrorMessage": "列列表不得为空。", "xpack.infra.logSourceConfiguration.emptyFieldErrorMessage": "字段“{fieldName}”不得为空。", - "xpack.infra.logSourceConfiguration.indexNameReferenceDeprecationDescription": "直接引用 Elasticsearch 索引是配置日志源的方式,但已弃用。现在,日志源与 Kibana 索引模式集成以配置使用的索引。", - "xpack.infra.logSourceConfiguration.indexNameReferenceDeprecationTitle": "弃用的配置选项", "xpack.infra.logSourceConfiguration.indexPatternManagementLinkText": "索引模式管理模式", "xpack.infra.logSourceConfiguration.indexPatternSectionTitle": "索引模式", "xpack.infra.logSourceConfiguration.indexPatternSelectorPlaceholder": "选择索引模式", From d18e7c9158185ded709b70fd190fab88bfc44be8 Mon Sep 17 00:00:00 2001 From: Kerry Gallagher <471693+Kerry350@users.noreply.github.com> Date: Thu, 21 Oct 2021 11:07:43 +0100 Subject: [PATCH 09/18] [Observability] [Alerts] Regenerate archive data and amend tests (#115603) * Regenerate archive data and amend test assertions --- .../observability/alerts/data.json.gz | Bin 2080 -> 3909 bytes .../observability/alerts/mappings.json | 24 +++++++++++++----- .../services/observability/alerts/common.ts | 12 +++++++-- .../apps/observability/alerts/index.ts | 24 ++++++++++-------- .../observability/alerts/workflow_status.ts | 14 +++++----- .../with_rac_write.config.ts | 5 ++++ 6 files changed, 54 insertions(+), 25 deletions(-) diff --git a/x-pack/test/functional/es_archives/observability/alerts/data.json.gz b/x-pack/test/functional/es_archives/observability/alerts/data.json.gz index bcccaeda999c0903d945b4bbeea6c27074dd3c75..544888178cb092cd9e71f1b82f7dd8967ac77d48 100644 GIT binary patch literal 3909 zcmV-L54!LliwFP!000026YZUAZ`;VZ$KUU#5DXO9EjsMnFM4s%qG*8vU7$E;`=(7{ z%+*mNi!LQOO&9y!&oh)AUm{y%LdldgaT1v1aQJX8zyCaQYv=2H=4Uv7!&%5zwNt{SXppB#Agp6^(M_@8`JHCwD= zv#|5Z&SSG$qS`HEb8FpfK3jdN?9KK4g%Fcn&sPZNKjc25Wxa0vgDG(4cdOZTT(0c( zO^%;pN)W+_n4gH4Qaok4Qr!Ig?tTa#Z{uQBS-+aqi+o4^?KVEY{sAn`SC_LZF!tf< zhnceryRoqf>&E72s`V1W5@HlF9IV|QI_6Z|gkQaH;_`hx&yNs0L*J)9!ntboqO#_KOXag~AkG#2lEolDlGFQ^e zT{-+(EbH~=Zb69)#A3v742Y2Ek!Y(Tr4^L3X7y#Y!+6R`C4~6<%lEC? zI`c*CdlKy4!XBh~9>vGH`H<%I$IFLD);DqSMd;-XyeK4R4)Kq>YjE`rLKk;u{`vg2 z+t@7cIHD_D;fHu3JAGU(>!xafl0!VoqK9yqelw`6adA6?5`KLbc2~u_zwN&0y;@OL z_UiF_J-o#ui>iC*Kd%3q_x|O({O_0VzWcemIB>5Olq!N!VgzxE1=1FCq#27eSi`l@ zMI43{2VNOW8H4u`#wgC@w~9l3<04!@H=kDT;fjxN|EJ3hrk@NPL2aBK*JXt-ZW&@*fcJ1V00OHl*PF?C{Q_3fb+de24_Vvieh}Wum@&NOfg=n zcVicZpgq@&TEwJrh%*zAbrB;^C}%Ncc$M*iLH!DOC@CO@Rz&KGRFtU0$N`Z>4vHLn zwy+0WPD~D7t9xUY1e#prI2t4fv4~?wkoCf#Bs~u-5FhDV&nF^!iUmjlc|{WZyj~8W z2#g49$jgGTiEy`lU1Wsza4<+`9qFJQUop4nk2%(fuh$>|&*g!NZN38eW^H)<%suPD z=23rWAGL1{=*Rs)aY%u+-1mcUpU|nml@xe4_nYWzfM*Ol(BA_hBS!zDXxlvcGSclb z@aVo5YS)Vq-Ic+|Pu@~rzLbM!I`P%?cagCWg+weci8#d`*<=LbQEP{3@?M%Zra#u% zArSf?^j8`a+K>L*>>HT=R2>`rsVejzC;c%67rPSD3`hSS5GnK@6aAGkf#4`n&_aX9 z#w~JDS%jss(mE1FeCxJ9IH`n#lZp>Oe?lcDJ=^}<>>HZ?^62O<3;oAPe?Ar9szPIh z4M_hU5IKTbqGzZAG+GWqEfM8Df_y8Cj0_A#6M_v|$;5gO3e4-P5+(|EpwlBWg*67p6?3ZY} z*mKXbCy_(Tx?bN*+^2rc(1UW$c>_PPZIY&8x}()pm`a+#W~Fqm=cflb!7=<$gQRTN z>}qI?J8&QrGg>-t2_(gNLCy%qh}u~~1IVm~Y_|$F? zpPH*FqZL0!@hJ%w1ny8X^hqw;`Ip#d#$#HB*kjCH<0PJv6)F@VlYPH8H zYGx>WK};{v>$u=G+SV6m*2FxgYB+d_-1_2@4z-G;d;L8dUL^2-U|A-QJ?IGKwDrS&Y6i|MMAD zu0BMRhpHeA;2rPZ^`@@phzaaC4-v6jezOxkfB5U)ChMiW`f~2Qebg|i6L_23=33%P z-%Z8;R*RmK>(7gvkojP6YW=t=DU;d#BRVv^Y-6w6n)dU%i$xvcPf*U^T}rG7S<*L5^@%|C;xYfNqn@eq|S!mC+uYoq_(Y;x4InCmH6QVt|SCIWz zZGqg}c6;^i;+J2tL7Q6h=S|?dtc7b`$v<19#x7UQ+OO74?4tos>U8xvzp74>_CQ(@ z6XS!mUB#4`DP@(^Y^eQ?y5sQlv;f=+pS%e`Y6?P_FhRy@iiA(bDoQOiMGL-F3vSh| z+p`u3R`DKM@R~(M3yKz;s}}UY;pu6CQlT+UGCC2Teed6>gX;l+tRW2OG)OxJyl{?ip|o+F zjzL3MUhvHgIgle_a8D80V&4&rvU1T&M9L^@5s@MyFHFGf%+V9<%_3B?Lcw>w196iB} zEvPX}CJ&Ms72?D&Zwc?KR{OLn5pq#StkQa&8*J+RuMTKBObk8KbACA&z*HQc_5m$iVkf z0cnL5QZguPlH|gWF=&WtKcYm$fqJu;?kOT$>^lZWg!UQ|DUL`Hk&_S+G3C4xYQPD; zJve%TBH}fVR#3#az=(5ekx32|G+}Hh)f=e+aLZighwxOx$h zb6X`M^0+!w@c~7oJ4a7&Me5TdRo4xpiylsbG~Pdcc{!-8tGpOj?v3 zlZ<#qFtte_FY_1tlEKIsg^>)>$cU37M3j!LH|f|q%v|5H>afU2>Dc=AA@=SZJzbwf zIF6?dBcB+~0nmzbkWpi-weGTU#}x2S!g%L!@wor+|@29=tH7 zDbfyGByC{cQ66a2W6%(qch40<9za8w(pdET%*ZzTM)S=iMm~?Gmv1JG>YGVzH>iL% z@sLBlJ*xBxcC0Yxg%KVF4XqyTh($(Hi=tIYDiecrWB8&cHd7%hi4U@ZZ;>t#|rBQr#(@%G8r1KRWE8RJIg4;U@LR^RzQOFC#IlR0MN+E}%)hrU}WID4o z43ZkD-hK(gVLqEwI#!B?oZWOGoIfxu`H*u@yJPh9G(@qei3V4Z=4ItvV}Z1G9#P7q zi#*|^bkUHv(2zr{sm5iCz@i~VL%v-@dSLVfznWx(qc#NOHFJnl2|2{0L7EBagn(l+ zirt;alnKc8L+w?r`mS5pV&7;w=P4^)^JDCqZx{9Ep++^}hNnF$^y$$ITP6)932~8E z58x!h6Oa-m&V+W(8R17UT_vxY1zO0%Y?`fc&z|`$=8Xqt{0Oli6wTvpy~hs3V@F; literal 2080 zcmV+*2;cV~iwFP!000026YZN@Z{xTXfZyj=2mxBmEJ`i!7eBQa^r1i(SQPEcEEWN8 za@5$8A<4<6gZ=L%WoMH(wj(97V>=Fz#1eVPW0L3N;W^UxPf2po+|B&OD7kQ@z4%MK zaib^mc;|b#2$#8wrNm-vm|=i3=>YX!1H+jHOnbxywwdyz6^{~y^S?!*G%p%o6?&5D ziLaU(l&1F8jW+otZ|+h(o8A{VpXvFe0Wkic`-bXrUfBm#!kxc1`PA2qp3Y+X7-9sV z0ucWjkr9+5EK))6x21fj?{9q3q}n!lSwxBW>Bg^?Uk8n2X`PRQvJa)#xzUAQ=$N{B zrDHScybg5{gc3>`v|V4@FIrsDP>e851ybKO^x@;!FM4UNDqmlflh_P;vG)C(;q^81 zB8*vKeY%*;IPkuz%IY$FC4X7YjMqgCs`>P?38R}A;|=;%Ue}9({CgBnY&kEQB(IZk zC<#-YG*`MvQ1aQ%t166a(&`l};4euQ1Riy@sf+oT_0=)8t=&{`zH`^sFk~+%Ef?ZT(L1&t){W%XO2m zO>#b8NHE8EMjZsucnc5;f0XtZFoU4g3R4Q{hIYdASIz2z#3P1AM5Ua{Z=2N=a0x0erHmNQ>Gt6k5ujYAk~{IVZ0ux-P4>i0cHjQUujUGS# z!oRlvfBzJJfB*F9dk>DwOfslAuugeE71cmtM*$Sn39n61o`w1y@*dP?%x-5KQvfO0j0Fy)fIjPlCcGVsv5muTLd6{k(} zN7nQTptlyXJEBYLm86*k3dEU85*dw--8 z5{Rkx5p3ZELPmPWS_n)9Mm>=ZcN=T*6CF;57T6L5goPO^+~rl%%<9pPKi=Nnrpxkx z$v06RZVth$(O^HY-wxpIqz`uwhxD9NA+c>q?}j1096=JCcRo-YrUyVy8}l1Xs|WJe znafM$PZo>t&HDf+;Q*$X4tq4SfJ&r+reQ+4!&q~JGK|&+?r#XV4@LA0X2=DC0m!)v^Ce`MFV29VS}Mk<@cT3Uh6VbYVm0G(XFOjC(~QeA&fgX1VJ)&Yp^pcQ(DWe0ndlpcJ}&nLbY>Nl zTxI}sF92np0ga^s&Pk=LHJVEHfq?!h5B;!#K4d->9#TH_%=IDM^sZd*0g4V>Z|&8c z)2Tc6*PY@>NCTK)9#FwCkUFd{u8~0mdP@yECs*G)xrz_KVejzJgYBT)TV_dNAAr}d|p*+hcK5YLDxN+6BdXdjhtGC@GVIq<+3{~#yrCgk8vEe+ zVfCiLbmq~-({>Z;LTafTkw^_j>P42G2j6F?7m<1x{K1fVFxm;mF@>H1g);*v!x=D8 zdSD&#&{>AUI3ED*7)pbU?t^wxVkmfD+y&=2#jJafSuxQGc|W{Jx!BYutSz*953;?4 zm~C8(vWLksXRtQH4(WB$5mYG`bRd@XFz3@_86jCvjS%1^!GLOpfTZ37rD&ERYAI6m z-C5SYdL;?Hf0B8v$NpJ}P(R{EnI&{xzMlr||MaCR>i$v0(Tx{_WA3NL@=5;a;uWaN zi>%UhQ_XENuY4cqJz2bA(v~`;ow)u7TAY-I59z#7HKI7>WGLsYhuxo^^X81DLPHOv z6&g@(88GoYuBCxkTdar@M*_XUGU%^j-WmmmV_xO1e%m$kV9L(SI|1f(&4~A~`~%8} z4+g$WJIYa*luU6Cs8<$98Do$PBg~alPG*MzKFU&o_xAD@7Cpho+nfj$M*uz|*MN`6 zl$eq{zU z37-Tv(py70wD`=yPKVz3AcImzaOhpjK}J(~=HN%uw`S+QA!{P77dAxHs@C?7hhhK+%w*N`E2Lg_QfBy#q K;u$YATmS%fJ^>T} diff --git a/x-pack/test/functional/es_archives/observability/alerts/mappings.json b/x-pack/test/functional/es_archives/observability/alerts/mappings.json index 63750ddafe329..d8916d052b11e 100644 --- a/x-pack/test/functional/es_archives/observability/alerts/mappings.json +++ b/x-pack/test/functional/es_archives/observability/alerts/mappings.json @@ -14,7 +14,7 @@ }, "namespace": "default" }, - "dynamic": "strict", + "dynamic": "false", "properties": { "@timestamp": { "type": "date" @@ -99,7 +99,7 @@ "type": "keyword" }, "from": { - "type": "date" + "type": "keyword" }, "interval": { "type": "keyword" @@ -113,6 +113,10 @@ "note": { "type": "keyword" }, + "params": { + "index": false, + "type": "keyword" + }, "producer": { "type": "keyword" }, @@ -278,7 +282,7 @@ }, "namespace": "default" }, - "dynamic": "strict", + "dynamic": "false", "properties": { "@timestamp": { "type": "date" @@ -363,7 +367,7 @@ "type": "keyword" }, "from": { - "type": "date" + "type": "keyword" }, "interval": { "type": "keyword" @@ -377,6 +381,10 @@ "note": { "type": "keyword" }, + "params": { + "index": false, + "type": "keyword" + }, "producer": { "type": "keyword" }, @@ -518,7 +526,7 @@ }, "namespace": "default" }, - "dynamic": "strict", + "dynamic": "false", "properties": { "@timestamp": { "type": "date" @@ -603,7 +611,7 @@ "type": "keyword" }, "from": { - "type": "date" + "type": "keyword" }, "interval": { "type": "keyword" @@ -617,6 +625,10 @@ "note": { "type": "keyword" }, + "params": { + "index": false, + "type": "keyword" + }, "producer": { "type": "keyword" }, diff --git a/x-pack/test/functional/services/observability/alerts/common.ts b/x-pack/test/functional/services/observability/alerts/common.ts index 676704f849d3f..f7fe110411895 100644 --- a/x-pack/test/functional/services/observability/alerts/common.ts +++ b/x-pack/test/functional/services/observability/alerts/common.ts @@ -11,8 +11,8 @@ import { WebElementWrapper } from '../../../../../../test/functional/services/li // Based on the x-pack/test/functional/es_archives/observability/alerts archive. const DATE_WITH_DATA = { - rangeFrom: '2021-09-01T13:36:22.109Z', - rangeTo: '2021-09-03T13:36:22.109Z', + rangeFrom: '2021-10-18T13:36:22.109Z', + rangeTo: '2021-10-20T13:36:22.109Z', }; const ALERTS_FLYOUT_SELECTOR = 'alertsFlyout'; @@ -32,6 +32,7 @@ export function ObservabilityAlertsCommonProvider({ const pageObjects = getPageObjects(['common']); const retry = getService('retry'); const toasts = getService('toasts'); + const kibanaServer = getService('kibanaServer'); const navigateToTimeWithData = async () => { return await pageObjects.common.navigateToUrlWithBrowserHistory( @@ -42,6 +43,12 @@ export function ObservabilityAlertsCommonProvider({ ); }; + const setKibanaTimeZoneToUTC = async () => { + await kibanaServer.uiSettings.update({ + 'dateFormat:tz': 'UTC', + }); + }; + const getTableColumnHeaders = async () => { const table = await testSubjects.find(ALERTS_TABLE_CONTAINER_SELECTOR); const tableHeaderRow = await testSubjects.findDescendant('dataGridHeader', table); @@ -220,6 +227,7 @@ export function ObservabilityAlertsCommonProvider({ getTableColumnHeaders, getTableOrFail, navigateToTimeWithData, + setKibanaTimeZoneToUTC, openAlertsFlyout, setWorkflowStatusForRow, setWorkflowStatusFilter, diff --git a/x-pack/test/observability_functional/apps/observability/alerts/index.ts b/x-pack/test/observability_functional/apps/observability/alerts/index.ts index 003cebb968479..9b17cb141a255 100644 --- a/x-pack/test/observability_functional/apps/observability/alerts/index.ts +++ b/x-pack/test/observability_functional/apps/observability/alerts/index.ts @@ -14,9 +14,9 @@ async function asyncForEach(array: T[], callback: (item: T, index: number) => } } -const ACTIVE_ALERTS_CELL_COUNT = 48; -const RECOVERED_ALERTS_CELL_COUNT = 24; -const TOTAL_ALERTS_CELL_COUNT = 72; +const ACTIVE_ALERTS_CELL_COUNT = 78; +const RECOVERED_ALERTS_CELL_COUNT = 120; +const TOTAL_ALERTS_CELL_COUNT = 198; export default ({ getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); @@ -30,7 +30,11 @@ export default ({ getService }: FtrProviderContext) => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); - await observability.alerts.common.navigateToTimeWithData(); + const setup = async () => { + await observability.alerts.common.setKibanaTimeZoneToUTC(); + await observability.alerts.common.navigateToTimeWithData(); + }; + await setup(); }); after(async () => { @@ -120,7 +124,7 @@ export default ({ getService }: FtrProviderContext) => { const titleText = await ( await observability.alerts.common.getAlertsFlyoutTitle() ).getVisibleText(); - expect(titleText).to.contain('Log threshold'); + expect(titleText).to.contain('APM Failed Transaction Rate (one)'); }); }); @@ -140,11 +144,11 @@ export default ({ getService }: FtrProviderContext) => { ]; const expectedDescriptions = [ 'Active', - 'Sep 2, 2021 @ 12:54:09.674', - '15 minutes', - '100.25', - '1957', - 'Log threshold', + 'Oct 19, 2021 @ 15:00:41.555', + '20 minutes', + '5', + '30.73', + 'Failed transaction rate threshold', ]; await asyncForEach(flyoutTitles, async (title, index) => { diff --git a/x-pack/test/observability_functional/apps/observability/alerts/workflow_status.ts b/x-pack/test/observability_functional/apps/observability/alerts/workflow_status.ts index a68636b8cb0c0..3637b29a30a26 100644 --- a/x-pack/test/observability_functional/apps/observability/alerts/workflow_status.ts +++ b/x-pack/test/observability_functional/apps/observability/alerts/workflow_status.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; -const OPEN_ALERTS_ROWS_COUNT = 12; +const OPEN_ALERTS_ROWS_COUNT = 33; export default ({ getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); @@ -40,7 +40,7 @@ export default ({ getService }: FtrProviderContext) => { await retry.try(async () => { const tableRows = await observability.alerts.common.getTableCellsInRows(); - expect(tableRows.length).to.be(11); + expect(tableRows.length).to.be(32); }); }); @@ -49,7 +49,7 @@ export default ({ getService }: FtrProviderContext) => { await retry.try(async () => { const tableRows = await observability.alerts.common.getTableCellsInRows(); - expect(tableRows.length).to.be(3); + expect(tableRows.length).to.be(6); }); }); @@ -58,7 +58,7 @@ export default ({ getService }: FtrProviderContext) => { await retry.try(async () => { const tableRows = await observability.alerts.common.getTableCellsInRows(); - expect(tableRows.length).to.be(2); + expect(tableRows.length).to.be(5); }); }); @@ -67,7 +67,7 @@ export default ({ getService }: FtrProviderContext) => { await retry.try(async () => { const tableRows = await observability.alerts.common.getTableCellsInRows(); - expect(tableRows.length).to.be(4); + expect(tableRows.length).to.be(3); }); }); @@ -76,7 +76,7 @@ export default ({ getService }: FtrProviderContext) => { await retry.try(async () => { const tableRows = await observability.alerts.common.getTableCellsInRows(); - expect(tableRows.length).to.be(3); + expect(tableRows.length).to.be(2); }); }); @@ -85,7 +85,7 @@ export default ({ getService }: FtrProviderContext) => { await retry.try(async () => { const tableRows = await observability.alerts.common.getTableCellsInRows(); - expect(tableRows.length).to.be(12); + expect(tableRows.length).to.be(3); }); }); }); diff --git a/x-pack/test/observability_functional/with_rac_write.config.ts b/x-pack/test/observability_functional/with_rac_write.config.ts index 89a0da7857333..5bb7d9926af54 100644 --- a/x-pack/test/observability_functional/with_rac_write.config.ts +++ b/x-pack/test/observability_functional/with_rac_write.config.ts @@ -42,6 +42,11 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { '--xpack.ruleRegistry.write.enabled=true', ], }, + uiSettings: { + defaults: { + 'dateFormat:tz': 'UTC', + }, + }, testFiles: [resolve(__dirname, './apps/observability')], junit: { ...xpackFunctionalConfig.get('junit'), From d831c5035d1e80d9bd3119bef85328961b9a837b Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Thu, 21 Oct 2021 11:23:19 +0100 Subject: [PATCH 10/18] [ML] Adding edit job detector descriptions title (#115765) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../components/edit_job_flyout/tabs/detectors.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/tabs/detectors.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/tabs/detectors.js index d9223e433fb65..3fe7eb2bfb732 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/tabs/detectors.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/tabs/detectors.js @@ -7,8 +7,9 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiFieldText, EuiForm, EuiFormRow, EuiSpacer } from '@elastic/eui'; +import { EuiFieldText, EuiForm, EuiFormRow, EuiSpacer, EuiTitle } from '@elastic/eui'; import { mlJobService } from '../../../../../services/job_service'; import { detectorToString } from '../../../../../util/string_utils'; @@ -43,7 +44,16 @@ export class Detectors extends Component { render() { const { detectors, detectorDescriptions } = this.state; return ( - + <> + + +

+ +

+
{detectorDescriptions.map((d, i) => ( @@ -52,7 +62,7 @@ export class Detectors extends Component { ))} -
+ ); } } From d2dea6816c36d3b0f96404ff18a82da88a104652 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Thu, 21 Oct 2021 05:42:13 -0500 Subject: [PATCH 11/18] [data view mgmt] change urls from indexPatterns to dataViews (#114912) * index pattern management to data view url changes --- .../common/data_views/data_views.ts | 6 +-- .../data_views/redirect_no_index_pattern.tsx | 2 +- .../server/saved_objects/data_views.ts | 4 +- .../create_edit_field/create_edit_field.tsx | 2 +- .../edit_index_pattern/tabs/utils.test.ts | 2 +- .../edit_index_pattern/tabs/utils.ts | 2 +- .../mount_management_section.tsx | 7 +-- .../index_pattern_management/public/plugin.ts | 4 +- .../management_app/management_router.tsx | 8 ++- .../public/management_sections_service.ts | 6 ++- src/plugins/management/public/types.ts | 2 + .../public/utils/management_item.ts | 15 +++++- .../object_view/components/inspect.test.tsx | 4 +- .../object_view/saved_object_view.test.tsx | 10 ++-- .../saved_objects_table.test.tsx.snap | 4 +- .../__snapshots__/relationships.test.tsx.snap | 4 +- .../__snapshots__/table.test.tsx.snap | 8 +-- .../components/relationships.test.tsx | 12 ++--- .../objects_table/components/table.test.tsx | 8 +-- .../saved_objects_table.test.tsx | 4 +- .../apis/saved_objects_management/find.ts | 5 +- .../saved_objects_management/relationships.ts | 10 ++-- .../_index_pattern_create_delete.js | 2 +- .../apps/management/_legacy_url_redirect.ts | 53 +++++++++++++++++++ test/functional/apps/management/index.ts | 1 + test/functional/page_objects/settings_page.ts | 12 ++--- .../components/import_complete_view.tsx | 2 +- .../geo_index_pattern_select.test.tsx.snap | 2 +- .../components/geo_index_pattern_select.tsx | 2 +- .../index_pattern_prompt.tsx | 2 +- ...ndex_patterns_missing_prompt.test.tsx.snap | 2 +- .../index_patterns_missing_prompt.tsx | 2 +- .../geo_index_pattern_select.tsx | 4 +- .../alert_types/geo_containment/readme.md | 2 +- .../feature_controls/management_security.ts | 9 +--- 35 files changed, 145 insertions(+), 79 deletions(-) create mode 100644 test/functional/apps/management/_legacy_url_redirect.ts diff --git a/src/plugins/data_views/common/data_views/data_views.ts b/src/plugins/data_views/common/data_views/data_views.ts index e5c83b28dee96..7882f8d7ca43e 100644 --- a/src/plugins/data_views/common/data_views/data_views.ts +++ b/src/plugins/data_views/common/data_views/data_views.ts @@ -423,11 +423,7 @@ export class DataViewsService { ); if (!savedObject.version) { - throw new SavedObjectNotFound( - DATA_VIEW_SAVED_OBJECT_TYPE, - id, - 'management/kibana/indexPatterns' - ); + throw new SavedObjectNotFound(DATA_VIEW_SAVED_OBJECT_TYPE, id, 'management/kibana/dataViews'); } return this.initFromSavedObject(savedObject); diff --git a/src/plugins/data_views/public/data_views/redirect_no_index_pattern.tsx b/src/plugins/data_views/public/data_views/redirect_no_index_pattern.tsx index 456af90a3c6dd..086cd92a92d82 100644 --- a/src/plugins/data_views/public/data_views/redirect_no_index_pattern.tsx +++ b/src/plugins/data_views/public/data_views/redirect_no_index_pattern.tsx @@ -22,7 +22,7 @@ export const onRedirectNoIndexPattern = ) => () => { const canManageIndexPatterns = capabilities.management.kibana.indexPatterns; - const redirectTarget = canManageIndexPatterns ? '/management/kibana/indexPatterns' : '/home'; + const redirectTarget = canManageIndexPatterns ? '/management/kibana/dataViews' : '/home'; let timeoutId: NodeJS.Timeout | undefined; if (timeoutId) { diff --git a/src/plugins/data_views/server/saved_objects/data_views.ts b/src/plugins/data_views/server/saved_objects/data_views.ts index 68443609f3fc6..5bb85a9bb6e98 100644 --- a/src/plugins/data_views/server/saved_objects/data_views.ts +++ b/src/plugins/data_views/server/saved_objects/data_views.ts @@ -23,11 +23,11 @@ export const dataViewSavedObjectType: SavedObjectsType = { return obj.attributes.title; }, getEditUrl(obj) { - return `/management/kibana/indexPatterns/patterns/${encodeURIComponent(obj.id)}`; + return `/management/kibana/dataViews/dataView/${encodeURIComponent(obj.id)}`; }, getInAppUrl(obj) { return { - path: `/app/management/kibana/indexPatterns/patterns/${encodeURIComponent(obj.id)}`, + path: `/app/management/kibana/dataViews/dataView/${encodeURIComponent(obj.id)}`, uiCapabilitiesPath: 'management.kibana.indexPatterns', }; }, diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/create_edit_field/create_edit_field.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/create_edit_field/create_edit_field.tsx index 37ec0c921b925..843838d9e0fbc 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/create_edit_field/create_edit_field.tsx +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/create_edit_field/create_edit_field.tsx @@ -45,7 +45,7 @@ export const CreateEditField = withRouter( name: undefined, } as unknown as IndexPatternField); - const url = `/patterns/${indexPattern.id}`; + const url = `/dataView/${indexPattern.id}`; if (mode === 'edit' && !spec) { const message = i18n.translate('indexPatternManagement.editDataView.scripted.noFieldLabel', { diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.test.ts b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.test.ts index 6bc51c1d67db5..98502776616ae 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.test.ts +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.test.ts @@ -15,5 +15,5 @@ test('getPath() should encode "fieldName"', () => { { name: 'Memory: Allocated Bytes/sec' } as unknown as IndexPatternField, { id: 'id' } as unknown as IndexPattern ) - ).toMatchInlineSnapshot(`"/patterns/id/field/Memory%3A%20Allocated%20Bytes%2Fsec"`); + ).toMatchInlineSnapshot(`"/dataView/id/field/Memory%3A%20Allocated%20Bytes%2Fsec"`); }); diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.ts b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.ts index 76bb86eb88d9b..5f7e989cc5753 100644 --- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.ts +++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/utils.ts @@ -102,7 +102,7 @@ export function getTabs(indexPattern: IndexPattern, fieldFilter: string) { } export function getPath(field: IndexPatternField, indexPattern: IndexPattern) { - return `/patterns/${indexPattern?.id}/field/${encodeURIComponent(field.name)}`; + return `/dataView/${indexPattern?.id}/field/${encodeURIComponent(field.name)}`; } const allTypesDropDown = i18n.translate( diff --git a/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx b/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx index e4022b524337b..12080fed933f3 100644 --- a/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx +++ b/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx @@ -8,7 +8,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { Router, Switch, Route } from 'react-router-dom'; +import { Router, Switch, Route, Redirect } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import { I18nProvider } from '@kbn/i18n/react'; @@ -73,12 +73,13 @@ export async function mountManagementSection( - + - + + diff --git a/src/plugins/index_pattern_management/public/plugin.ts b/src/plugins/index_pattern_management/public/plugin.ts index e7fd0942a36a1..47290bfdedb8d 100644 --- a/src/plugins/index_pattern_management/public/plugin.ts +++ b/src/plugins/index_pattern_management/public/plugin.ts @@ -36,7 +36,7 @@ const sectionsHeader = i18n.translate('indexPatternManagement.dataView.sectionsH defaultMessage: 'Data Views', }); -const IPM_APP_ID = 'indexPatterns'; +const IPM_APP_ID = 'dataViews'; export class IndexPatternManagementPlugin implements @@ -72,6 +72,8 @@ export class IndexPatternManagementPlugin id: IPM_APP_ID, title: sectionsHeader, order: 0, + capabilitiesId: 'indexPatterns', + redirectFrom: 'kibana/indexPatterns', mount: async (params) => { const { mountManagementSection } = await import('./management_app'); diff --git a/src/plugins/management/public/components/management_app/management_router.tsx b/src/plugins/management/public/components/management_app/management_router.tsx index 2ff6fce6b630d..f09bd4597142d 100644 --- a/src/plugins/management/public/components/management_app/management_router.tsx +++ b/src/plugins/management/public/components/management_app/management_router.tsx @@ -7,7 +7,7 @@ */ import React, { memo } from 'react'; -import { Route, Router, Switch } from 'react-router-dom'; +import { Route, Router, Switch, Redirect } from 'react-router-dom'; import { AppMountParameters, ChromeBreadcrumb, ScopedHistory } from 'kibana/public'; import { ManagementAppWrapper } from '../management_app_wrapper'; import { ManagementLandingPage } from '../landing'; @@ -43,6 +43,12 @@ export const ManagementRouter = memo( /> )) )} + {sections.map((section) => + section + .getAppsEnabled() + .filter((app) => app.redirectFrom) + .map((app) => ) + )} ( diff --git a/src/plugins/management/public/management_sections_service.ts b/src/plugins/management/public/management_sections_service.ts index 219de9b0d8a74..1b14a661159a5 100644 --- a/src/plugins/management/public/management_sections_service.ts +++ b/src/plugins/management/public/management_sections_service.ts @@ -74,7 +74,11 @@ export class ManagementSectionsService { if (capabilities.management.hasOwnProperty(section.id)) { const sectionCapabilities = capabilities.management[section.id]; section.apps.forEach((app) => { - if (sectionCapabilities.hasOwnProperty(app.id) && sectionCapabilities[app.id] !== true) { + const capabilitiesId = app.capabilitiesId || app.id; + if ( + sectionCapabilities.hasOwnProperty(capabilitiesId) && + sectionCapabilities[capabilitiesId] !== true + ) { app.disable(); } }); diff --git a/src/plugins/management/public/types.ts b/src/plugins/management/public/types.ts index 486bece98fc26..6a165c812b474 100644 --- a/src/plugins/management/public/types.ts +++ b/src/plugins/management/public/types.ts @@ -72,4 +72,6 @@ export interface CreateManagementItemArgs { order?: number; euiIconType?: string; // takes precedence over `icon` property. icon?: string; // URL to image file; fallback if no `euiIconType` + capabilitiesId?: string; // overrides app id + redirectFrom?: string; // redirects from an old app id to the current app id } diff --git a/src/plugins/management/public/utils/management_item.ts b/src/plugins/management/public/utils/management_item.ts index 0ae71be559708..e8d13b78460f2 100644 --- a/src/plugins/management/public/utils/management_item.ts +++ b/src/plugins/management/public/utils/management_item.ts @@ -15,16 +15,29 @@ export class ManagementItem { public readonly order: number; public readonly euiIconType?: string; public readonly icon?: string; + public readonly capabilitiesId?: string; + public readonly redirectFrom?: string; public enabled: boolean = true; - constructor({ id, title, tip, order = 100, euiIconType, icon }: CreateManagementItemArgs) { + constructor({ + id, + title, + tip, + order = 100, + euiIconType, + icon, + capabilitiesId, + redirectFrom, + }: CreateManagementItemArgs) { this.id = id; this.title = title; this.tip = tip; this.order = order; this.euiIconType = euiIconType; this.icon = icon; + this.capabilitiesId = capabilitiesId; + this.redirectFrom = redirectFrom; } disable() { diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/components/inspect.test.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/components/inspect.test.tsx index 433728baf6fc1..843468b78307c 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/components/inspect.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/components/inspect.test.tsx @@ -30,9 +30,9 @@ describe('Inspect component', () => { meta: { title: `MyIndexPattern*`, icon: 'indexPatternApp', - editUrl: '#/management/kibana/indexPatterns/patterns/1', + editUrl: '#/management/kibana/dataViews/dataView/1', inAppUrl: { - path: '/management/kibana/indexPatterns/patterns/1', + path: '/management/kibana/dataViews/dataView/1', uiCapabilitiesPath: 'management.kibana.indexPatterns', }, }, diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.test.tsx b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.test.tsx index 13a806361e543..554e34a9fc55a 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/object_view/saved_object_view.test.tsx @@ -158,9 +158,9 @@ describe('SavedObjectEdition', () => { meta: { title: `MyIndexPattern*`, icon: 'indexPatternApp', - editUrl: '#/management/kibana/indexPatterns/patterns/1', + editUrl: '#/management/kibana/dataViews/dataView/1', inAppUrl: { - path: '/management/kibana/indexPatterns/patterns/1', + path: '/management/kibana/dataViews/dataView/1', uiCapabilitiesPath: 'management.kibana.indexPatterns', }, hiddenType: false, @@ -187,7 +187,7 @@ describe('SavedObjectEdition', () => { const headerComponent = component.find('Header'); expect(headerComponent.prop('canViewInApp')).toBe(true); expect(headerComponent.prop('canDelete')).toBe(true); - expect(headerComponent.prop('viewUrl')).toEqual('/management/kibana/indexPatterns/patterns/1'); + expect(headerComponent.prop('viewUrl')).toEqual('/management/kibana/dataViews/dataView/1'); const inspectComponent = component.find('Inspect'); expect(inspectComponent.prop('object')).toEqual(savedObjectItem); }); @@ -225,9 +225,9 @@ describe('SavedObjectEdition', () => { meta: { title: `MyIndexPattern*`, icon: 'indexPatternApp', - editUrl: '#/management/kibana/indexPatterns/patterns/1', + editUrl: '#/management/kibana/dataViews/dataView/1', inAppUrl: { - path: '/management/kibana/indexPatterns/patterns/1', + path: '/management/kibana/dataViews/dataView/1', uiCapabilitiesPath: 'management.kibana.indexPatterns', }, hiddenType: false, diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap index f4325b277faba..03f30d65cbaf8 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap @@ -235,10 +235,10 @@ exports[`SavedObjectsTable should render normally 1`] = ` Object { "id": "1", "meta": Object { - "editUrl": "#/management/kibana/indexPatterns/patterns/1", + "editUrl": "#/management/kibana/dataViews/dataView/1", "icon": "indexPatternApp", "inAppUrl": Object { - "path": "/management/kibana/indexPatterns/patterns/1", + "path": "/management/kibana/dataViews/dataView/1", "uiCapabilitiesPath": "management.kibana.indexPatterns", }, "title": "MyIndexPattern*", diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/relationships.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/relationships.test.tsx.snap index 21bc9c5a00bd7..f9579693eb135 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/relationships.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/relationships.test.tsx.snap @@ -627,10 +627,10 @@ exports[`Relationships should render searches normally 1`] = ` Object { "id": "1", "meta": Object { - "editUrl": "/management/kibana/indexPatterns/patterns/1", + "editUrl": "/management/kibana/dataViews/dataView/1", "icon": "indexPatternApp", "inAppUrl": Object { - "path": "/app/management/kibana/indexPatterns/patterns/1", + "path": "/app/management/kibana/dataViews/dataView/1", "uiCapabilitiesPath": "management.kibana.indexPatterns", }, "title": "My Index Pattern", diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/table.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/table.test.tsx.snap index bca54ff67591c..2515a8ce6d788 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/table.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/table.test.tsx.snap @@ -174,10 +174,10 @@ exports[`Table prevents saved objects from being deleted 1`] = ` "attributes": Object {}, "id": "1", "meta": Object { - "editUrl": "#/management/kibana/indexPatterns/patterns/1", + "editUrl": "#/management/kibana/dataViews/dataView/1", "icon": "indexPatternApp", "inAppUrl": Object { - "path": "/management/kibana/indexPatterns/patterns/1", + "path": "/management/kibana/dataViews/dataView/1", "uiCapabilitiesPath": "management.kibana.indexPatterns", }, "title": "MyIndexPattern*", @@ -394,10 +394,10 @@ exports[`Table should render normally 1`] = ` "attributes": Object {}, "id": "1", "meta": Object { - "editUrl": "#/management/kibana/indexPatterns/patterns/1", + "editUrl": "#/management/kibana/dataViews/dataView/1", "icon": "indexPatternApp", "inAppUrl": Object { - "path": "/management/kibana/indexPatterns/patterns/1", + "path": "/management/kibana/dataViews/dataView/1", "uiCapabilitiesPath": "management.kibana.indexPatterns", }, "title": "MyIndexPattern*", diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.test.tsx index 8f859cd594f92..a3db8af627d2f 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/relationships.test.tsx @@ -74,9 +74,9 @@ describe('Relationships', () => { meta: { title: 'MyIndexPattern*', icon: 'indexPatternApp', - editUrl: '#/management/kibana/indexPatterns/patterns/1', + editUrl: '#/management/kibana/dataViews/dataView/1', inAppUrl: { - path: '/management/kibana/indexPatterns/patterns/1', + path: '/management/kibana/dataViews/dataView/1', uiCapabilitiesPath: 'management.kibana.indexPatterns', }, }, @@ -111,10 +111,10 @@ describe('Relationships', () => { id: '1', relationship: 'child', meta: { - editUrl: '/management/kibana/indexPatterns/patterns/1', + editUrl: '/management/kibana/dataViews/dataView/1', icon: 'indexPatternApp', inAppUrl: { - path: '/app/management/kibana/indexPatterns/patterns/1', + path: '/app/management/kibana/dataViews/dataView/1', uiCapabilitiesPath: 'management.kibana.indexPatterns', }, title: 'My Index Pattern', @@ -365,9 +365,9 @@ describe('Relationships', () => { meta: { title: 'MyIndexPattern*', icon: 'indexPatternApp', - editUrl: '#/management/kibana/indexPatterns/patterns/1', + editUrl: '#/management/kibana/dataViews/dataView/1', inAppUrl: { - path: '/management/kibana/indexPatterns/patterns/1', + path: '/management/kibana/dataViews/dataView/1', uiCapabilitiesPath: 'management.kibana.indexPatterns', }, }, diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.test.tsx index a736c7a8a7c5e..6fb153cb980ff 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.test.tsx @@ -28,9 +28,9 @@ const defaultProps: TableProps = { meta: { title: `MyIndexPattern*`, icon: 'indexPatternApp', - editUrl: '#/management/kibana/indexPatterns/patterns/1', + editUrl: '#/management/kibana/dataViews/dataView/1', inAppUrl: { - path: '/management/kibana/indexPatterns/patterns/1', + path: '/management/kibana/dataViews/dataView/1', uiCapabilitiesPath: 'management.kibana.indexPatterns', }, }, @@ -59,9 +59,9 @@ const defaultProps: TableProps = { meta: { title: `MyIndexPattern*`, icon: 'indexPatternApp', - editUrl: '#/management/kibana/indexPatterns/patterns/1', + editUrl: '#/management/kibana/dataViews/dataView/1', inAppUrl: { - path: '/management/kibana/indexPatterns/patterns/1', + path: '/management/kibana/dataViews/dataView/1', uiCapabilitiesPath: 'management.kibana.indexPatterns', }, }, diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx index 9ef28e1121965..6a98fada416f0 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx @@ -152,9 +152,9 @@ describe('SavedObjectsTable', () => { meta: { title: `MyIndexPattern*`, icon: 'indexPatternApp', - editUrl: '#/management/kibana/indexPatterns/patterns/1', + editUrl: '#/management/kibana/dataViews/dataView/1', inAppUrl: { - path: '/management/kibana/indexPatterns/patterns/1', + path: '/management/kibana/dataViews/dataView/1', uiCapabilitiesPath: 'management.kibana.indexPatterns', }, }, diff --git a/test/api_integration/apis/saved_objects_management/find.ts b/test/api_integration/apis/saved_objects_management/find.ts index bb1840d6d4e87..0b744b7991b38 100644 --- a/test/api_integration/apis/saved_objects_management/find.ts +++ b/test/api_integration/apis/saved_objects_management/find.ts @@ -244,10 +244,9 @@ export default function ({ getService }: FtrProviderContext) { icon: 'indexPatternApp', title: 'saved_objects*', hiddenType: false, - editUrl: - '/management/kibana/indexPatterns/patterns/8963ca30-3224-11e8-a572-ffca06da1357', + editUrl: '/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357', inAppUrl: { - path: '/app/management/kibana/indexPatterns/patterns/8963ca30-3224-11e8-a572-ffca06da1357', + path: '/app/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'management.kibana.indexPatterns', }, namespaceType: 'single', diff --git a/test/api_integration/apis/saved_objects_management/relationships.ts b/test/api_integration/apis/saved_objects_management/relationships.ts index cab323ca028ae..518ec29947016 100644 --- a/test/api_integration/apis/saved_objects_management/relationships.ts +++ b/test/api_integration/apis/saved_objects_management/relationships.ts @@ -86,10 +86,9 @@ export default function ({ getService }: FtrProviderContext) { meta: { title: 'saved_objects*', icon: 'indexPatternApp', - editUrl: - '/management/kibana/indexPatterns/patterns/8963ca30-3224-11e8-a572-ffca06da1357', + editUrl: '/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357', inAppUrl: { - path: '/app/management/kibana/indexPatterns/patterns/8963ca30-3224-11e8-a572-ffca06da1357', + path: '/app/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'management.kibana.indexPatterns', }, namespaceType: 'single', @@ -128,10 +127,9 @@ export default function ({ getService }: FtrProviderContext) { meta: { icon: 'indexPatternApp', title: 'saved_objects*', - editUrl: - '/management/kibana/indexPatterns/patterns/8963ca30-3224-11e8-a572-ffca06da1357', + editUrl: '/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357', inAppUrl: { - path: '/app/management/kibana/indexPatterns/patterns/8963ca30-3224-11e8-a572-ffca06da1357', + path: '/app/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357', uiCapabilitiesPath: 'management.kibana.indexPatterns', }, namespaceType: 'single', diff --git a/test/functional/apps/management/_index_pattern_create_delete.js b/test/functional/apps/management/_index_pattern_create_delete.js index 3e5bf354a48ff..acb554fa7310b 100644 --- a/test/functional/apps/management/_index_pattern_create_delete.js +++ b/test/functional/apps/management/_index_pattern_create_delete.js @@ -133,7 +133,7 @@ export default function ({ getService, getPageObjects }) { return retry.try(function tryingForTime() { return browser.getCurrentUrl().then(function (currentUrl) { log.debug('currentUrl = ' + currentUrl); - expect(currentUrl).to.contain('management/kibana/indexPatterns'); + expect(currentUrl).to.contain('management/kibana/dataViews'); }); }); }); diff --git a/test/functional/apps/management/_legacy_url_redirect.ts b/test/functional/apps/management/_legacy_url_redirect.ts new file mode 100644 index 0000000000000..1dfeb0ec492fd --- /dev/null +++ b/test/functional/apps/management/_legacy_url_redirect.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +export default function ({ getPageObjects, getService }: FtrProviderContext) { + const browser = getService('browser'); + const PageObjects = getPageObjects(['settings', 'common', 'header']); + const kibanaServer = getService('kibanaServer'); + + describe('legacy urls redirect correctly', () => { + before(async function () { + await browser.setWindowSize(1200, 800); + await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover'); + await kibanaServer.uiSettings.replace({}); + }); + + after(async function afterAll() { + await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover'); + }); + + it('redirects correctly to index pattern management', async () => { + await PageObjects.settings.navigateTo(); + await PageObjects.settings.clickKibanaIndexPatterns(); + await PageObjects.settings.clickIndexPatternLogstash(); + + const url = await (await browser.getCurrentUrl()).split('#')[0]; + const modifiedUrl = url.replace('indexPatterns', 'dataViews'); + await browser.navigateTo(modifiedUrl); + await PageObjects.header.waitUntilLoadingHasFinished(); + const newUrl = (await browser.getCurrentUrl()).split('#')[0]; + expect(newUrl).to.equal(url); + }); + + it('redirects correctly to specific index pattern', async () => { + await PageObjects.settings.clickKibanaIndexPatterns(); + await PageObjects.settings.clickIndexPatternLogstash(); + + const url = await (await browser.getCurrentUrl()).split('#')[0]; + const modifiedUrl = url.replace('patterns', 'dataView').replace('indexPatterns', 'dataViews'); + await browser.navigateTo(modifiedUrl); + await PageObjects.header.waitUntilLoadingHasFinished(); + const newUrl = (await browser.getCurrentUrl()).split('#')[0]; + expect(newUrl).to.equal(url); + }); + }); +} diff --git a/test/functional/apps/management/index.ts b/test/functional/apps/management/index.ts index c8049d2db4b28..4787d7b9ee532 100644 --- a/test/functional/apps/management/index.ts +++ b/test/functional/apps/management/index.ts @@ -35,6 +35,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./_scripted_fields')); loadTestFile(require.resolve('./_runtime_fields')); loadTestFile(require.resolve('./_field_formatter')); + loadTestFile(require.resolve('./_legacy_url_redirect')); }); describe('', function () { diff --git a/test/functional/page_objects/settings_page.ts b/test/functional/page_objects/settings_page.ts index 633040221ed36..d3443f9cf4925 100644 --- a/test/functional/page_objects/settings_page.ts +++ b/test/functional/page_objects/settings_page.ts @@ -43,10 +43,10 @@ export class SettingsPageObject extends FtrService { } async clickKibanaIndexPatterns() { - this.log.debug('clickKibanaIndexPatterns link'); + this.log.debug('clickKibanaDataViews link'); const currentUrl = await this.browser.getCurrentUrl(); - if (!currentUrl.endsWith('indexPatterns')) { - await this.testSubjects.click('indexPatterns'); + if (!currentUrl.endsWith('dataViews')) { + await this.testSubjects.click('dataViews'); } await this.header.waitUntilLoadingHasFinished(); @@ -384,10 +384,10 @@ export class SettingsPageObject extends FtrService { await this.retry.try(async () => { const currentUrl = await this.browser.getCurrentUrl(); this.log.info('currentUrl', currentUrl); - if (!currentUrl.match(/indexPatterns\/.+\?/)) { - throw new Error('Index pattern not created'); + if (!currentUrl.match(/dataViews\/.+\?/)) { + throw new Error('Data view not created'); } else { - this.log.debug('Index pattern created: ' + currentUrl); + this.log.debug('Data view created: ' + currentUrl); } }); diff --git a/x-pack/plugins/file_upload/public/components/import_complete_view.tsx b/x-pack/plugins/file_upload/public/components/import_complete_view.tsx index 7f180e6eb774a..59862aea24701 100644 --- a/x-pack/plugins/file_upload/public/components/import_complete_view.tsx +++ b/x-pack/plugins/file_upload/public/components/import_complete_view.tsx @@ -183,7 +183,7 @@ export class ImportCompleteView extends Component { { id="xpack.maps.noIndexPattern.doThisPrefixDescription" defaultMessage="You'll need to " /> - + = ({ destIndex }) => { destIndex, linkToDataViewManagement: ( Configure index patterns diff --git a/x-pack/plugins/security_solution/public/network/components/embeddables/index_patterns_missing_prompt.tsx b/x-pack/plugins/security_solution/public/network/components/embeddables/index_patterns_missing_prompt.tsx index b0e2e6cd7f186..a0ed555b6cec2 100644 --- a/x-pack/plugins/security_solution/public/network/components/embeddables/index_patterns_missing_prompt.tsx +++ b/x-pack/plugins/security_solution/public/network/components/embeddables/index_patterns_missing_prompt.tsx @@ -62,7 +62,7 @@ export const IndexPatternsMissingPromptComponent = () => { } actions={ { id="xpack.stackAlerts.geoContainment.noIndexPattern.doThisPrefixDescription" defaultMessage="You'll need to " /> - + /app/`: -- Stack Management > Data Views: `management/kibana/indexPatterns` +- Stack Management > Data Views: `management/kibana/dataViews` - Stack Management > Alerts & Actions: `management/insightsAndAlerting/triggersActions/alerts` - Maps: `maps` diff --git a/x-pack/test/functional/apps/management/feature_controls/management_security.ts b/x-pack/test/functional/apps/management/feature_controls/management_security.ts index b47afc4cda6e4..66dc697804ce2 100644 --- a/x-pack/test/functional/apps/management/feature_controls/management_security.ts +++ b/x-pack/test/functional/apps/management/feature_controls/management_security.ts @@ -68,14 +68,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); expect(sections[1]).to.eql({ sectionId: 'kibana', - sectionLinks: [ - 'indexPatterns', - 'objects', - 'tags', - 'search_sessions', - 'spaces', - 'settings', - ], + sectionLinks: ['dataViews', 'objects', 'tags', 'search_sessions', 'spaces', 'settings'], }); }); }); From 941a6c9bb90bdd7c7543acf6a85b1a00672fb6b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Thu, 21 Oct 2021 12:54:07 +0200 Subject: [PATCH 12/18] [RAC][Observability] Use no data screen for the alerts and cases pages (#115178) Co-authored-by: Chris Cowan Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/pages/alerts/index.tsx | 20 ++++++++++++++++ .../public/pages/cases/all_cases.tsx | 23 +++++++++++++++++-- .../page_objects/observability_page.ts | 7 ++++-- .../services/observability/alerts/common.ts | 5 ++++ .../apps/observability/alerts/add_to_case.ts | 2 ++ .../apps/observability/alerts/index.ts | 15 ++++++++++++ .../observability/alerts/workflow_status.ts | 2 ++ .../observability_security.ts | 9 ++++++++ 8 files changed, 79 insertions(+), 4 deletions(-) diff --git a/x-pack/plugins/observability/public/pages/alerts/index.tsx b/x-pack/plugins/observability/public/pages/alerts/index.tsx index 590c87707aa3d..e8b550ac353a6 100644 --- a/x-pack/plugins/observability/public/pages/alerts/index.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/index.tsx @@ -15,9 +15,12 @@ import type { AlertWorkflowStatus } from '../../../common/typings'; import { ExperimentalBadge } from '../../components/shared/experimental_badge'; import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; import { useFetcher } from '../../hooks/use_fetcher'; +import { useHasData } from '../../hooks/use_has_data'; import { usePluginContext } from '../../hooks/use_plugin_context'; import { useTimefilterService } from '../../hooks/use_timefilter_service'; import { callObservabilityApi } from '../../services/call_observability_api'; +import { getNoDataConfig } from '../../utils/no_data_config'; +import { LoadingObservability } from '../overview/loading_observability'; import { AlertsSearchBar } from './alerts_search_bar'; import { AlertsTableTGrid } from './alerts_table_t_grid'; import { Provider, alertsPageStateContainer, useAlertsPageStateContainer } from './state_container'; @@ -137,8 +140,25 @@ function AlertsPage() { refetch.current = ref; }, []); + const { hasAnyData, isAllRequestsComplete } = useHasData(); + + // If there is any data, set hasData to true otherwise we need to wait till all the data is loaded before setting hasData to true or false; undefined indicates the data is still loading. + const hasData = hasAnyData === true || (isAllRequestsComplete === false ? undefined : false); + + if (!hasAnyData && !isAllRequestsComplete) { + return ; + } + + const noDataConfig = getNoDataConfig({ + hasData, + basePath: core.http.basePath, + docsLink: core.docLinks.links.observability.guide, + }); + return ( diff --git a/x-pack/plugins/observability/public/pages/cases/all_cases.tsx b/x-pack/plugins/observability/public/pages/cases/all_cases.tsx index 442104a710601..4ac7c4cfd92a5 100644 --- a/x-pack/plugins/observability/public/pages/cases/all_cases.tsx +++ b/x-pack/plugins/observability/public/pages/cases/all_cases.tsx @@ -16,16 +16,35 @@ import { usePluginContext } from '../../hooks/use_plugin_context'; import { useReadonlyHeader } from '../../hooks/use_readonly_header'; import { casesBreadcrumbs } from './links'; import { useBreadcrumbs } from '../../hooks/use_breadcrumbs'; +import { useHasData } from '../../hooks/use_has_data'; +import { LoadingObservability } from '../overview/loading_observability'; +import { getNoDataConfig } from '../../utils/no_data_config'; export const AllCasesPage = React.memo(() => { const userPermissions = useGetUserCasesPermissions(); - const { ObservabilityPageTemplate } = usePluginContext(); + const { core, ObservabilityPageTemplate } = usePluginContext(); useReadonlyHeader(); - useBreadcrumbs([casesBreadcrumbs.cases]); + const { hasAnyData, isAllRequestsComplete } = useHasData(); + + if (!hasAnyData && !isAllRequestsComplete) { + return ; + } + + // If there is any data, set hasData to true otherwise we need to wait till all the data is loaded before setting hasData to true or false; undefined indicates the data is still loading. + const hasData = hasAnyData === true || (isAllRequestsComplete === false ? undefined : false); + + const noDataConfig = getNoDataConfig({ + hasData, + basePath: core.http.basePath, + docsLink: core.docLinks.links.observability.guide, + }); + return userPermissions == null || userPermissions?.read ? ( {i18n.PAGE_TITLE}, }} diff --git a/x-pack/test/functional/page_objects/observability_page.ts b/x-pack/test/functional/page_objects/observability_page.ts index d9e413d473adf..f89dafe4f3a73 100644 --- a/x-pack/test/functional/page_objects/observability_page.ts +++ b/x-pack/test/functional/page_objects/observability_page.ts @@ -11,7 +11,6 @@ import { FtrProviderContext } from '../ftr_provider_context'; export function ObservabilityPageProvider({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); - const find = getService('find'); return { async expectCreateCaseButtonEnabled() { @@ -32,6 +31,10 @@ export function ObservabilityPageProvider({ getService, getPageObjects }: FtrPro await testSubjects.missingOrFail('case-callout-e41900b01c9ef0fa81dd6ff326083fb3'); }, + async expectNoDataPage() { + await testSubjects.existOrFail('noDataPage'); + }, + async expectCreateCase() { await testSubjects.existOrFail('case-creation-form-steps'); }, @@ -47,7 +50,7 @@ export function ObservabilityPageProvider({ getService, getPageObjects }: FtrPro }, async expectForbidden() { - const h2 = await find.byCssSelector('body', 20000); + const h2 = await testSubjects.find('no_feature_permissions', 20000); const text = await h2.getVisibleText(); expect(text).to.contain('Kibana feature privileges required'); }, diff --git a/x-pack/test/functional/services/observability/alerts/common.ts b/x-pack/test/functional/services/observability/alerts/common.ts index f7fe110411895..f47d17039b5ae 100644 --- a/x-pack/test/functional/services/observability/alerts/common.ts +++ b/x-pack/test/functional/services/observability/alerts/common.ts @@ -74,6 +74,10 @@ export function ObservabilityAlertsCommonProvider({ return await testSubjects.existOrFail(ALERTS_TABLE_CONTAINER_SELECTOR); }; + const getNoDataPageOrFail = async () => { + return await testSubjects.existOrFail('noDataPage'); + }; + const getNoDataStateOrFail = async () => { return await testSubjects.existOrFail('tGridEmptyState'); }; @@ -221,6 +225,7 @@ export function ObservabilityAlertsCommonProvider({ getCopyToClipboardButton, getFilterForValueButton, copyToClipboardButtonExists, + getNoDataPageOrFail, getNoDataStateOrFail, getTableCells, getTableCellsInRows, diff --git a/x-pack/test/observability_functional/apps/observability/alerts/add_to_case.ts b/x-pack/test/observability_functional/apps/observability/alerts/add_to_case.ts index f29111f2cb66b..5e80a5769b44d 100644 --- a/x-pack/test/observability_functional/apps/observability/alerts/add_to_case.ts +++ b/x-pack/test/observability_functional/apps/observability/alerts/add_to_case.ts @@ -17,9 +17,11 @@ export default ({ getService, getPageObjects }: FtrProviderContext) => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); + await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs'); }); after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs'); await esArchiver.unload('x-pack/test/functional/es_archives/observability/alerts'); }); diff --git a/x-pack/test/observability_functional/apps/observability/alerts/index.ts b/x-pack/test/observability_functional/apps/observability/alerts/index.ts index 9b17cb141a255..112c24f7c3a88 100644 --- a/x-pack/test/observability_functional/apps/observability/alerts/index.ts +++ b/x-pack/test/observability_functional/apps/observability/alerts/index.ts @@ -41,7 +41,22 @@ export default ({ getService }: FtrProviderContext) => { await esArchiver.unload('x-pack/test/functional/es_archives/observability/alerts'); }); + describe('With no data', () => { + it('Shows the no data screen', async () => { + await observability.alerts.common.getNoDataPageOrFail(); + }); + }); + describe('Alerts table', () => { + before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs'); + await observability.alerts.common.navigateToTimeWithData(); + }); + + after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs'); + }); + it('Renders the table', async () => { await observability.alerts.common.getTableOrFail(); }); diff --git a/x-pack/test/observability_functional/apps/observability/alerts/workflow_status.ts b/x-pack/test/observability_functional/apps/observability/alerts/workflow_status.ts index 3637b29a30a26..4976c1c225aba 100644 --- a/x-pack/test/observability_functional/apps/observability/alerts/workflow_status.ts +++ b/x-pack/test/observability_functional/apps/observability/alerts/workflow_status.ts @@ -21,11 +21,13 @@ export default ({ getService }: FtrProviderContext) => { before(async () => { await esArchiver.load('x-pack/test/functional/es_archives/observability/alerts'); + await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs'); await observability.alerts.common.navigateToTimeWithData(); }); after(async () => { await esArchiver.unload('x-pack/test/functional/es_archives/observability/alerts'); + await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs'); }); it('is filtered to only show "open" alerts by default', async () => { diff --git a/x-pack/test/observability_functional/apps/observability/feature_controls/observability_security.ts b/x-pack/test/observability_functional/apps/observability/feature_controls/observability_security.ts index 69bf995c49ae4..85512f8333038 100644 --- a/x-pack/test/observability_functional/apps/observability/feature_controls/observability_security.ts +++ b/x-pack/test/observability_functional/apps/observability/feature_controls/observability_security.ts @@ -31,8 +31,14 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await esArchiver.unload('x-pack/test/functional/es_archives/cases/default'); }); + it('Shows the no data page on load', async () => { + await PageObjects.common.navigateToActualUrl('observabilityCases'); + await PageObjects.observability.expectNoDataPage(); + }); + describe('observability cases all privileges', () => { before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs'); await observability.users.setTestUserRole( observability.users.defineBasicObservabilityRole({ observabilityCases: ['all'], @@ -42,6 +48,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs'); await observability.users.restoreDefaultTestUserRole(); }); @@ -83,6 +90,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { describe('observability cases read-only privileges', () => { before(async () => { + await esArchiver.load('x-pack/test/functional/es_archives/infra/metrics_and_logs'); await observability.users.setTestUserRole( observability.users.defineBasicObservabilityRole({ observabilityCases: ['read'], @@ -92,6 +100,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); after(async () => { + await esArchiver.unload('x-pack/test/functional/es_archives/infra/metrics_and_logs'); await observability.users.restoreDefaultTestUserRole(); }); From c274d391526c762aee3e3375a4a5e2516a9ebeee Mon Sep 17 00:00:00 2001 From: Pablo Machado Date: Thu, 21 Oct 2021 13:20:11 +0200 Subject: [PATCH 13/18] Standardize page header layouts in Security (#115393) * Standardize page header layouts in Security * Update Observability create case layout --- .../all_cases/all_cases_generic.tsx | 83 ++++---- .../public/components/all_cases/header.tsx | 2 +- .../public/components/panel/index.test.tsx | 17 -- .../cases/public/components/panel/index.tsx | 37 ---- .../components/app/cases/create/index.tsx | 17 +- .../public/cases/components/create/index.tsx | 35 ++-- .../public/cases/pages/create_case.tsx | 2 +- .../rules/all/exceptions/exceptions_table.tsx | 117 ++++++----- .../rules/all/rules_tables.tsx | 198 ++++++++---------- .../public/hosts/pages/hosts.tsx | 1 + .../public/network/pages/network.tsx | 1 + .../open_timeline/open_timeline.tsx | 6 +- 12 files changed, 217 insertions(+), 299 deletions(-) delete mode 100644 x-pack/plugins/cases/public/components/panel/index.test.tsx delete mode 100644 x-pack/plugins/cases/public/components/panel/index.tsx diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_generic.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_generic.tsx index 9cbb13f7227a3..4a4e31230030e 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_generic.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_generic.tsx @@ -28,7 +28,7 @@ import { useGetActionLicense } from '../../containers/use_get_action_license'; import { useGetCases } from '../../containers/use_get_cases'; import { usePostComment } from '../../containers/use_post_comment'; import { CaseDetailsHrefSchema, CasesNavigation } from '../links'; -import { Panel } from '../panel'; + import { getActionLicenseError } from '../use_push_to_service/helpers'; import { useCasesColumns } from './columns'; import { getExpandedRowMap } from './expanded_row'; @@ -240,8 +240,6 @@ export const AllCasesGeneric = React.memo( const isCasesLoading = useMemo(() => loading.indexOf('cases') > -1, [loading]); const isDataEmpty = useMemo(() => data.total === 0, [data]); - const TableWrap = useMemo(() => (isSelectorView ? 'span' : Panel), [isSelectorView]); - const tableRowProps = useCallback( (theCase: Case) => { const onTableRowClick = memoize(async () => { @@ -289,48 +287,43 @@ export const AllCasesGeneric = React.memo( className="essentialAnimation" $isShow={(isCasesLoading || isLoading || isCommentUpdating) && !isDataEmpty} /> - - - - + + ); } diff --git a/x-pack/plugins/cases/public/components/all_cases/header.tsx b/x-pack/plugins/cases/public/components/all_cases/header.tsx index 862f4a72c8cb5..b2e5dafe061cf 100644 --- a/x-pack/plugins/cases/public/components/all_cases/header.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/header.tsx @@ -49,7 +49,7 @@ export const CasesTableHeader: FunctionComponent = ({ showTitle = true, userCanCrud, }) => ( - + {userCanCrud ? ( <> diff --git a/x-pack/plugins/cases/public/components/panel/index.test.tsx b/x-pack/plugins/cases/public/components/panel/index.test.tsx deleted file mode 100644 index 81c80158ae577..0000000000000 --- a/x-pack/plugins/cases/public/components/panel/index.test.tsx +++ /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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { mount } from 'enzyme'; -import { Panel } from '.'; -import React from 'react'; - -describe('Panel', () => { - test('it does not have the boolean loading as a Eui Property', () => { - const wrapper = mount(); - expect(Object.keys(wrapper.find('EuiPanel').props())).not.toContain('loading'); - }); -}); diff --git a/x-pack/plugins/cases/public/components/panel/index.tsx b/x-pack/plugins/cases/public/components/panel/index.tsx deleted file mode 100644 index 802fd4c7f44a6..0000000000000 --- a/x-pack/plugins/cases/public/components/panel/index.tsx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import styled from 'styled-components'; -import React from 'react'; -import { EuiPanel } from '@elastic/eui'; - -/** - * The reason for the type of syntax below of: - * `styled(({ loading, ...props })` - * is filter out the "loading" attribute from being put on the DOM - * and getting one of the stack traces from - * ``` - * ReactJS about non-standard HTML such as this one: - * Warning: Received `true` for a non-boolean attribute `loading`. - * If you want to write it to the DOM, pass a string instead: loading="true" or loading={value.toString()}. - * ``` - * - * Ref: https://github.com/styled-components/styled-components/issues/1198#issuecomment-425650423 - * Ref: https://github.com/elastic/kibana/pull/41596#issuecomment-514418978 - * Ref: https://www.styled-components.com/docs/faqs#why-am-i-getting-html-attribute-warnings - * Ref: https://reactjs.org/blog/2017/09/08/dom-attributes-in-react-16.html - */ -export const Panel = styled(({ loading, ...props }) => )` - position: relative; - ${({ loading }) => - loading && - ` - overflow: hidden; - `} -`; - -Panel.displayName = 'Panel'; diff --git a/x-pack/plugins/observability/public/components/app/cases/create/index.tsx b/x-pack/plugins/observability/public/components/app/cases/create/index.tsx index f4749fed5116d..153797df7cd83 100644 --- a/x-pack/plugins/observability/public/components/app/cases/create/index.tsx +++ b/x-pack/plugins/observability/public/components/app/cases/create/index.tsx @@ -6,7 +6,6 @@ */ import React, { useCallback } from 'react'; -import { EuiPanel } from '@elastic/eui'; import { useKibana } from '../../../../utils/kibana_react'; import { getCaseDetailsUrl } from '../../../../pages/cases/links'; @@ -29,16 +28,12 @@ export const Create = React.memo(() => { [casesUrl, navigateToUrl] ); - return ( - - {cases.getCreateCase({ - disableAlerts: true, - onCancel: handleSetIsCancel, - onSuccess, - owner: [CASES_OWNER], - })} - - ); + return cases.getCreateCase({ + disableAlerts: true, + onCancel: handleSetIsCancel, + onSuccess, + owner: [CASES_OWNER], + }); }); Create.displayName = 'Create'; diff --git a/x-pack/plugins/security_solution/public/cases/components/create/index.tsx b/x-pack/plugins/security_solution/public/cases/components/create/index.tsx index 5e2b7e27fb1e5..72a41acf1d456 100644 --- a/x-pack/plugins/security_solution/public/cases/components/create/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/create/index.tsx @@ -6,7 +6,6 @@ */ import React, { useCallback } from 'react'; -import { EuiPanel } from '@elastic/eui'; import { getCaseDetailsUrl, getCaseUrl } from '../../../common/components/link_to'; import { useKibana } from '../../../common/lib/kibana'; @@ -40,25 +39,21 @@ export const Create = React.memo(() => { [navigateToApp, search] ); - return ( - - {cases.getCreateCase({ - onCancel: handleSetIsCancel, - onSuccess, - timelineIntegration: { - editor_plugins: { - parsingPlugin: timelineMarkdownPlugin.parser, - processingPluginRenderer: timelineMarkdownPlugin.renderer, - uiPlugin: timelineMarkdownPlugin.plugin, - }, - hooks: { - useInsertTimeline, - }, - }, - owner: [APP_ID], - })} - - ); + return cases.getCreateCase({ + onCancel: handleSetIsCancel, + onSuccess, + timelineIntegration: { + editor_plugins: { + parsingPlugin: timelineMarkdownPlugin.parser, + processingPluginRenderer: timelineMarkdownPlugin.renderer, + uiPlugin: timelineMarkdownPlugin.plugin, + }, + hooks: { + useInsertTimeline, + }, + }, + owner: [APP_ID], + }); }); Create.displayName = 'Create'; diff --git a/x-pack/plugins/security_solution/public/cases/pages/create_case.tsx b/x-pack/plugins/security_solution/public/cases/pages/create_case.tsx index 4a59fe3fdcabd..933e890ea2d9f 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/create_case.tsx +++ b/x-pack/plugins/security_solution/public/cases/pages/create_case.tsx @@ -47,7 +47,7 @@ export const CreateCasePage = React.memo(() => { return ( <> - + diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx index 8528d64b7261d..7c8b2ddf636c0 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/exceptions/exceptions_table.tsx @@ -23,7 +23,7 @@ import { AutoDownload } from '../../../../../../common/components/auto_download/ import { useKibana } from '../../../../../../common/lib/kibana'; import { useFormatUrl } from '../../../../../../common/components/link_to'; import { Loader } from '../../../../../../common/components/loader'; -import { Panel } from '../../../../../../common/components/panel'; + import { DetectionEngineHeaderPage } from '../../../../../components/detection_engine_header_page'; import * as i18n from './translations'; @@ -343,67 +343,68 @@ export const ExceptionListsTable = React.memo(() => { <> - - <> - {loadingTableInfo && ( - + {loadingTableInfo && ( + + )} + {!initLoading && } + + + {loadingTableInfo && !initLoading && !showReferenceErrorModal && ( + + )} + + {initLoading ? ( + + ) : ( + <> + - )} - {!initLoading && } - - - {loadingTableInfo && !initLoading && !showReferenceErrorModal && ( - - )} - - {initLoading ? ( - - ) : ( - <> - - - data-test-subj="exceptions-table" - columns={exceptionsColumns} - isSelectable={hasPermissions} - itemId="id" - items={tableItems} - noItemsMessage={emptyPrompt} - onChange={handlePaginationChange} - pagination={paginationMemo} - /> - - )} - - - - + + data-test-subj="exceptions-table" + columns={exceptionsColumns} + isSelectable={hasPermissions} + itemId="id" + items={tableItems} + noItemsMessage={emptyPrompt} + onChange={handlePaginationChange} + pagination={paginationMemo} + /> + + )} + + + + ); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx index 9d9425cdabe63..bc2172a257635 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/all/rules_tables.tsx @@ -32,7 +32,6 @@ import { HeaderSection } from '../../../../../common/components/header_section'; import { useKibana, useUiSetting$ } from '../../../../../common/lib/kibana'; import { useStateToaster } from '../../../../../common/components/toasters'; import { Loader } from '../../../../../common/components/loader'; -import { Panel } from '../../../../../common/components/panel'; import { PrePackagedRulesPrompt } from '../../../../components/rules/pre_packaged_rules/load_empty_prompt'; import { AllRulesTables, SortingType } from '../../../../components/rules/all_rules_tables'; import { getPrePackagedRuleStatus } from '../helpers'; @@ -456,114 +455,101 @@ export const RulesTables = React.memo( - + )} + + {shouldShowRulesTable && ( + + )} + + {!initLoading && + (loading || isLoadingRules || isLoadingAnActionOnRule) && + !isRefreshing && } + {shouldShowPrepackagedRulesPrompt && ( + + )} + {initLoading && ( + + )} + {showIdleModal && ( + +

{i18n.REFRESH_PROMPT_BODY}

+
+ )} + {isDeleteConfirmationVisible && ( + +

{i18n.DELETE_CONFIRMATION_BODY}

+
+ )} + {shouldShowRulesTable && ( <> - {!initLoading && - (loading || isLoadingRules || isLoadingAnActionOnRule) && - isRefreshing && ( - - )} - - {shouldShowRulesTable && ( - - )} - - - {!initLoading && - (loading || isLoadingRules || isLoadingAnActionOnRule) && - !isRefreshing && ( - - )} - - {shouldShowPrepackagedRulesPrompt && ( - - )} - {initLoading && ( - - )} - {showIdleModal && ( - -

{i18n.REFRESH_PROMPT_BODY}

-
- )} - {isDeleteConfirmationVisible && ( - -

{i18n.DELETE_CONFIRMATION_BODY}

-
- )} - {shouldShowRulesTable && ( - <> - - - - )} + + -
+ )} ); } diff --git a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx index 8e6a635624bfe..b1c7c25c794c1 100644 --- a/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx +++ b/x-pack/plugins/security_solution/public/hosts/pages/hosts.tsx @@ -179,6 +179,7 @@ const HostsComponent = () => { /> } title={i18n.PAGE_TITLE} + border /> ( /> } title={i18n.PAGE_TITLE} + border /> ( title={i18n.IMPORT_TIMELINE} /> - +
{!!timelineFilter && timelineFilter} ( tableRef={tableRef} totalSearchResultsCount={totalSearchResultsCount} /> - +
); } From 9e76bcb00d98fa6e85c2268e32ffdf6cc9c96436 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 21 Oct 2021 12:52:55 +0100 Subject: [PATCH 14/18] skip flaky suite (#115529) --- test/functional/apps/visualize/_tsvb_time_series.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/visualize/_tsvb_time_series.ts b/test/functional/apps/visualize/_tsvb_time_series.ts index 009e4a07cd42a..4354e8bb44172 100644 --- a/test/functional/apps/visualize/_tsvb_time_series.ts +++ b/test/functional/apps/visualize/_tsvb_time_series.ts @@ -193,7 +193,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { }); }); - describe('Elastic charts', () => { + // FLAKY: https://github.com/elastic/kibana/issues/115529 + describe.skip('Elastic charts', () => { beforeEach(async () => { await visualBuilder.toggleNewChartsLibraryWithDebug(true); await visualBuilder.clickPanelOptions('timeSeries'); From f2c35a0115161ef15e6a8a69bb8cd037b10f98d9 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 21 Oct 2021 12:55:00 +0100 Subject: [PATCH 15/18] skip flaky suite (#115883) --- x-pack/test/functional/apps/canvas/expression.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/canvas/expression.ts b/x-pack/test/functional/apps/canvas/expression.ts index ecde3a24be423..f346615b771a6 100644 --- a/x-pack/test/functional/apps/canvas/expression.ts +++ b/x-pack/test/functional/apps/canvas/expression.ts @@ -17,7 +17,8 @@ export default function canvasExpressionTest({ getService, getPageObjects }: Ftr const kibanaServer = getService('kibanaServer'); const archive = 'x-pack/test/functional/fixtures/kbn_archiver/canvas/default'; - describe('expression editor', function () { + // FLAKY: https://github.com/elastic/kibana/issues/115883 + describe.skip('expression editor', function () { // there is an issue with FF not properly clicking on workpad elements this.tags('skipFirefox'); From d01ba7d6856e10e22e1eea2e8d5641609bdacb94 Mon Sep 17 00:00:00 2001 From: Robert Oskamp Date: Thu, 21 Oct 2021 13:55:28 +0200 Subject: [PATCH 16/18] [ML] Functional tests - re-activate a11y suite (#115900) This PR re-activates the ML accessibility test suite. --- x-pack/test/accessibility/apps/ml.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/x-pack/test/accessibility/apps/ml.ts b/x-pack/test/accessibility/apps/ml.ts index e06661b000203..4babe0bd6ff88 100644 --- a/x-pack/test/accessibility/apps/ml.ts +++ b/x-pack/test/accessibility/apps/ml.ts @@ -13,8 +13,7 @@ export default function ({ getService }: FtrProviderContext) { const a11y = getService('a11y'); const ml = getService('ml'); - // Failing: See https://github.com/elastic/kibana/issues/115666 - describe.skip('ml', () => { + describe('ml', () => { const esArchiver = getService('esArchiver'); before(async () => { From f76e5df353e8b5f94dd5766d249819ac7add4ce1 Mon Sep 17 00:00:00 2001 From: Tiago Costa Date: Thu, 21 Oct 2021 12:57:53 +0100 Subject: [PATCH 17/18] skip flaky suite (#115320) --- .../saved_object_tagging/functional/tests/som_integration.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test/saved_object_tagging/functional/tests/som_integration.ts b/x-pack/test/saved_object_tagging/functional/tests/som_integration.ts index e35b33b3b4475..79f7c74db2a0d 100644 --- a/x-pack/test/saved_object_tagging/functional/tests/som_integration.ts +++ b/x-pack/test/saved_object_tagging/functional/tests/som_integration.ts @@ -36,7 +36,8 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) { await testSubjects.click('savedObjectSearchBar'); }; - describe('saved objects management integration', () => { + // FLAKY: https://github.com/elastic/kibana/issues/115320 + describe.skip('saved objects management integration', () => { before(async () => { await esArchiver.load( 'x-pack/test/saved_object_tagging/common/fixtures/es_archiver/so_management' From 6c52cce0b854b144794d3278b23358d77c1e0592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loix?= Date: Thu, 21 Oct 2021 12:59:44 +0100 Subject: [PATCH 18/18] [UA] Align server config on master --- .../upgrade_assistant/server/config.ts | 50 ++++++++----------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/x-pack/plugins/upgrade_assistant/server/config.ts b/x-pack/plugins/upgrade_assistant/server/config.ts index 4183ea337def1..99e5d486e30c3 100644 --- a/x-pack/plugins/upgrade_assistant/server/config.ts +++ b/x-pack/plugins/upgrade_assistant/server/config.ts @@ -17,20 +17,17 @@ const kibanaVersion = new SemVer(MAJOR_VERSION); // ------------------------------- // >= 8.x // ------------------------------- -const schemaLatest = schema.object( - { - ui: schema.object({ - enabled: schema.boolean({ defaultValue: true }), - }), - /* - * This will default to true up until the last minor before the next major. - * In readonly mode, the user will not be able to perform any actions in the UI - * and will be presented with a message indicating as such. - */ - readonly: schema.boolean({ defaultValue: true }), - }, - { defaultValue: undefined } -); +const schemaLatest = schema.object({ + ui: schema.object({ + enabled: schema.boolean({ defaultValue: true }), + }), + /* + * This will default to true up until the last minor before the next major. + * In readonly mode, the user will not be able to perform any actions in the UI + * and will be presented with a message indicating as such. + */ + readonly: schema.boolean({ defaultValue: true }), +}); const configLatest: PluginConfigDescriptor = { exposeToBrowser: { @@ -46,21 +43,18 @@ export type UpgradeAssistantConfig = TypeOf; // ------------------------------- // 7.x // ------------------------------- -const schema7x = schema.object( - { +const schema7x = schema.object({ + enabled: schema.boolean({ defaultValue: true }), + ui: schema.object({ enabled: schema.boolean({ defaultValue: true }), - ui: schema.object({ - enabled: schema.boolean({ defaultValue: true }), - }), - /* - * This will default to true up until the last minor before the next major. - * In readonly mode, the user will not be able to perform any actions in the UI - * and will be presented with a message indicating as such. - */ - readonly: schema.boolean({ defaultValue: true }), - }, - { defaultValue: undefined } -); + }), + /* + * This will default to true up until the last minor before the next major. + * In readonly mode, the user will not be able to perform any actions in the UI + * and will be presented with a message indicating as such. + */ + readonly: schema.boolean({ defaultValue: false }), +}); export type UpgradeAssistantConfig7x = TypeOf;