From 8de96ea548c5a6a972cbf2a42a162d63b37804ef Mon Sep 17 00:00:00 2001 From: Pierre Gayvallet Date: Thu, 6 Apr 2023 08:07:52 -0400 Subject: [PATCH] Fix SO management fulltext search (#154409) ## Summary Fix https://github.com/elastic/kibana/issues/154244 Related / workaround for https://github.com/elastic/kibana/issues/130616 Very old issue that happened again, this time for the `slo` type because of mapping incompatibilities with default searchable fields (and a limitation in our code we still did not address) - Fix the `slo` SO type mapping for the `name` field (keyword => text) - Add a integration test failing if any management type has incorrect mappings for searchable fields (mostly a workaround rather than fixing the root problem, but at least it will avoid that kind of scenario for the time being) --- .../group2/check_registered_types.test.ts | 2 +- .../group3/default_search_fields.test.ts | 75 +++++++++++++++++++ .../group3/type_registrations.test.ts | 12 ++- .../observability/server/saved_objects/slo.ts | 2 +- 4 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 src/core/server/integration_tests/saved_objects/migrations/group3/default_search_fields.test.ts diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts index 86198df9b1d27..124d8703d044d 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/check_registered_types.test.ts @@ -134,7 +134,7 @@ describe('checking migration metadata changes on all registered SO types', () => "siem-ui-timeline": "e9d6b3a9fd7af6dc502293c21cbdb309409f3996", "siem-ui-timeline-note": "13c9d4c142f96624a93a623c6d7cba7e1ae9b5a6", "siem-ui-timeline-pinned-event": "96a43d59b9e2fc11f12255a0cb47ef0a3d83af4c", - "slo": "ee0e16abebba5779c37277bc3fe8da1fe1207b7a", + "slo": "aefffabdb35d15a6c388634af2cee1fa59ede83c", "space": "7fc578a1f9f7708cb07479f03953d664ad9f1dae", "spaces-usage-stats": "084bd0f080f94fb5735d7f3cf12f13ec92f36bad", "synthetics-monitor": "7136a2669a65323c56da849f26c369cdeeb3b381", diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/default_search_fields.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/default_search_fields.test.ts new file mode 100644 index 0000000000000..4101c22c23d50 --- /dev/null +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/default_search_fields.test.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 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 { createRoot } from '@kbn/core-test-helpers-kbn-server'; + +describe('SO default search fields', () => { + let root: ReturnType; + + afterEach(() => { + try { + root?.shutdown(); + } catch (e) { + /* trap */ + } + }); + + interface InvalidMappingTuple { + type: string; + field: string; + } + + // identify / avoid scenarios of https://github.com/elastic/kibana/issues/130616 + it('make sure management types have the correct mappings for default search fields', async () => { + root = createRoot({}, { oss: false }); + await root.preboot(); + const setup = await root.setup(); + + const allTypes = setup.savedObjects.getTypeRegistry().getAllTypes(); + + const defaultSearchFields = [ + ...allTypes.reduce((fieldSet, type) => { + if (type.management?.defaultSearchField) { + fieldSet.add(type.management.defaultSearchField); + } + return fieldSet; + }, new Set()), + ]; + + const invalidMappings: InvalidMappingTuple[] = []; + + const managementTypes = setup.savedObjects + .getTypeRegistry() + .getImportableAndExportableTypes() + .filter((type) => type.management!.visibleInManagement ?? true); + + managementTypes.forEach((type) => { + const mappingProps = type.mappings.properties; + defaultSearchFields.forEach((searchField) => { + if (mappingProps[searchField]) { + const fieldDef = mappingProps[searchField]; + if (fieldDef.type !== 'text') { + invalidMappings.push({ + type: type.name, + field: searchField, + }); + } + } + }); + }); + + if (invalidMappings.length > 0) { + // `fail()` no longer exists... + expect( + `fields registered as defaultSearchField by any type must be registered as text. Invalid mappings found: ${JSON.stringify( + invalidMappings + )}` + ).toEqual(''); + } + }); +}); diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts index 914c825597774..dbcdbf2f9928c 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts @@ -132,8 +132,18 @@ const previouslyRegisteredTypes = [ ].sort(); describe('SO type registrations', () => { + let root: ReturnType; + + afterEach(() => { + try { + root?.shutdown(); + } catch (e) { + /* trap */ + } + }); + it('does not remove types from registrations without updating excludeOnUpgradeQuery', async () => { - const root = createRoot({}, { oss: false }); + root = createRoot({}, { oss: false }); await root.preboot(); const setup = await root.setup(); const currentlyRegisteredTypes = setup.savedObjects diff --git a/x-pack/plugins/observability/server/saved_objects/slo.ts b/x-pack/plugins/observability/server/saved_objects/slo.ts index e2c8fe22336a8..1e6d108088b8e 100644 --- a/x-pack/plugins/observability/server/saved_objects/slo.ts +++ b/x-pack/plugins/observability/server/saved_objects/slo.ts @@ -20,7 +20,7 @@ export const slo: SavedObjectsType = { dynamic: false, properties: { id: { type: 'keyword' }, - name: { type: 'keyword' }, + name: { type: 'text' }, description: { type: 'text' }, indicator: { properties: {