diff --git a/x-pack/plugins/synthetics/server/feature.ts b/x-pack/plugins/synthetics/server/feature.ts index 4c2fa529f4690..ac5bce8a6b790 100644 --- a/x-pack/plugins/synthetics/server/feature.ts +++ b/x-pack/plugins/synthetics/server/feature.ts @@ -11,7 +11,7 @@ import { SYNTHETICS_RULE_TYPES } from '../common/constants/synthetics_alerts'; import { privateLocationsSavedObjectName } from '../common/saved_objects/private_locations'; import { PLUGIN } from '../common/constants/plugin'; import { UPTIME_RULE_TYPES } from '../common/constants/uptime_alerts'; -import { umDynamicSettings } from './saved_objects/uptime_settings'; +import { settingsObjectType } from './saved_objects/uptime_settings'; import { syntheticsApiKeyObjectType } from './saved_objects/service_api_key'; export const uptimeFeature = { @@ -32,7 +32,7 @@ export const uptimeFeature = { api: ['uptime-read', 'uptime-write', 'lists-all', 'rac'], savedObject: { all: [ - umDynamicSettings.name, + settingsObjectType, syntheticsMonitorType, syntheticsApiKeyObjectType, privateLocationsSavedObjectName, @@ -61,7 +61,7 @@ export const uptimeFeature = { all: [], read: [ syntheticsParamType, - umDynamicSettings.name, + settingsObjectType, syntheticsMonitorType, syntheticsApiKeyObjectType, privateLocationsSavedObjectName, diff --git a/x-pack/plugins/synthetics/server/lib.test.ts b/x-pack/plugins/synthetics/server/lib.test.ts new file mode 100644 index 0000000000000..0ce1f79607846 --- /dev/null +++ b/x-pack/plugins/synthetics/server/lib.test.ts @@ -0,0 +1,140 @@ +/* + * 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 { UptimeEsClient } from './lib'; +import { savedObjectsClientMock, uiSettingsServiceMock } from '@kbn/core/server/mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; + +describe('UptimeEsClient', () => { + let uptimeEsClient: UptimeEsClient; + const savedObjectsClient = savedObjectsClientMock.create(); + const esClient = elasticsearchClientMock.createClusterClient().asInternalUser; + + beforeEach(() => { + uptimeEsClient = new UptimeEsClient(savedObjectsClient, esClient); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('search', () => { + it('should call baseESClient.search with correct parameters', async () => { + const mockSearchParams = { + body: { + query: { + match_all: {}, + }, + }, + }; + + const result = await uptimeEsClient.search({ + body: { + query: { + match_all: {}, + }, + }, + }); + + expect(esClient.search).toHaveBeenCalledWith( + { + index: 'heartbeat-8*,heartbeat-7*', + ...mockSearchParams, + }, + { meta: true } + ); + expect(result).toEqual({ + body: {}, + headers: { + 'x-elastic-product': 'Elasticsearch', + }, + meta: {}, + statusCode: 200, + warnings: [], + }); + }); + + it('should throw an error if baseESClient.search throws an error', async () => { + const mockSearchParams = { + body: { + query: { + match_all: {}, + }, + }, + }; + const mockError = new Error('Search error'); + esClient.search.mockRejectedValueOnce(mockError); + + await expect(uptimeEsClient.search(mockSearchParams)).rejects.toThrow(mockError); + expect(esClient.search).toHaveBeenCalledWith( + { + index: 'heartbeat-8*,heartbeat-7*', + ...mockSearchParams, + }, + { meta: true } + ); + }); + }); + + describe('count', () => { + it('should call baseESClient.count with correct parameters', async () => { + const mockCountParams = { + index: 'example', + }; + + const result = await uptimeEsClient.count(mockCountParams); + + expect(esClient.count).toHaveBeenCalledWith(mockCountParams, { meta: true }); + expect(result).toEqual({ + indices: 'heartbeat-8*,heartbeat-7*', + result: { + body: {}, + headers: { + 'x-elastic-product': 'Elasticsearch', + }, + meta: {}, + statusCode: 200, + warnings: [], + }, + }); + }); + + it('should throw an error if baseESClient.count throws an error', async () => { + const mockCountParams = { + index: 'example', + }; + const mockError = new Error('Count error'); + esClient.count.mockRejectedValueOnce(mockError); + + await expect(uptimeEsClient.count(mockCountParams)).rejects.toThrow(mockError); + expect(esClient.count).toHaveBeenCalledWith(mockCountParams, { meta: true }); + }); + }); + + describe('getInspectEnabled', () => { + it('should return false if uiSettings is not available', async () => { + const result = await uptimeEsClient.getInspectEnabled(); + + expect(result).toBe(false); + }); + + it('should return the value from uiSettings if available', async () => { + const mockUiSettings = uiSettingsServiceMock.createClient(); + uptimeEsClient.uiSettings = { + client: mockUiSettings, + } as any; + + // @ts-expect-error + mockUiSettings.get.mockReturnValue(true); + + await uptimeEsClient.getInspectEnabled(); + + expect(uptimeEsClient.isInspectorEnabled).toBe(true); + expect(mockUiSettings.get).toHaveBeenCalledWith('observability:enableInspectEsQueries'); + }); + }); +}); diff --git a/x-pack/plugins/synthetics/server/saved_objects/migrations.test.ts b/x-pack/plugins/synthetics/server/saved_objects/migrations.test.ts deleted file mode 100644 index ba97a9753f86a..0000000000000 --- a/x-pack/plugins/synthetics/server/saved_objects/migrations.test.ts +++ /dev/null @@ -1,165 +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 { add820Indices, remove890Indices } from './migrations'; -import { SavedObject, SavedObjectMigrationContext } from '@kbn/core/server'; -import { DynamicSettingsAttributes } from '../runtime_types/settings'; - -describe('add820Indices migration', () => { - const context = { log: { warning: () => {} } } as unknown as SavedObjectMigrationContext; - - const makeSettings = (heartbeatIndices: string): SavedObject => { - return { - id: '1', - type: 't', - references: [], - attributes: { - heartbeatIndices, - certAgeThreshold: 1, - certExpirationThreshold: 2, - defaultConnectors: ['example'], - }, - }; - }; - - it("adds the synthetics-* index if it's not in the indices settings", () => { - const doc = makeSettings('heartbeat-8*,something_else'); - const result = add820Indices(doc, context); - expect(result).toEqual({ - ...doc, - attributes: { - ...doc.attributes, - heartbeatIndices: 'heartbeat-8*,something_else,synthetics-*', - }, - }); - }); - - it("adds the heartbeat-8* index if it's not in the indices settings", () => { - const doc = makeSettings('synthetics-*,something_else'); - const result = add820Indices(doc, context); - expect(result).toEqual({ - ...doc, - attributes: { - ...doc.attributes, - heartbeatIndices: 'synthetics-*,something_else,heartbeat-8*', - }, - }); - }); - - it("adds both synthetics-* and heartbeat-8* index if they're not present in the indices", () => { - const doc = makeSettings('something-*,something_else'); - const result = add820Indices(doc, context); - expect(result).toEqual({ - ...doc, - attributes: { - ...doc.attributes, - heartbeatIndices: 'something-*,something_else,synthetics-*,heartbeat-8*', - }, - }); - }); - - it('works for empty heartbeat indices fields', () => { - const doc = makeSettings(''); - const result = add820Indices(doc, context); - expect(result).toEqual({ - ...doc, - attributes: { - ...doc.attributes, - heartbeatIndices: 'synthetics-*,heartbeat-8*', - }, - }); - }); - - it('works for undefined heartbeat indices fields', () => { - const doc = makeSettings(''); - - // We must TS ignore this so that we can delete this - // non-optional field and test that the migration still works - // when the field does not exist in the document - // @ts-expect-error - delete doc.attributes.heartbeatIndices; - const result = add820Indices(doc, context); - expect(result).toEqual({ - ...doc, - attributes: { - ...doc.attributes, - heartbeatIndices: 'synthetics-*,heartbeat-8*', - }, - }); - }); -}); - -describe('remove890Indices migration', () => { - const context = { log: { warning: () => {} } } as unknown as SavedObjectMigrationContext; - - const makeSettings = (heartbeatIndices: string): SavedObject => { - return { - id: '1', - type: 't', - references: [], - attributes: { - heartbeatIndices, - certAgeThreshold: 1, - certExpirationThreshold: 2, - defaultConnectors: ['example'], - }, - }; - }; - - it("removes the synthetics-* index if it's in the indices settings", () => { - const doc = makeSettings('heartbeat-8*,synthetics-*,something_else'); - const result = remove890Indices(doc, context); - expect(result).toEqual({ - ...doc, - attributes: { - ...doc.attributes, - heartbeatIndices: 'heartbeat-8*,something_else', - }, - }); - }); - - it("adds the heartbeat-8* index if it's not in the indices settings", () => { - const doc = makeSettings('synthetics-*,something_else'); - const result = remove890Indices(doc, context); - expect(result).toEqual({ - ...doc, - attributes: { - ...doc.attributes, - heartbeatIndices: 'something_else,heartbeat-8*', - }, - }); - }); - - it('works for empty heartbeat indices fields', () => { - const doc = makeSettings(''); - const result = remove890Indices(doc, context); - expect(result).toEqual({ - ...doc, - attributes: { - ...doc.attributes, - heartbeatIndices: 'heartbeat-8*', - }, - }); - }); - - it('works for undefined heartbeat indices fields', () => { - const doc = makeSettings(''); - - // We must TS ignore this so that we can delete this - // non-optional field and test that the migration still works - // when the field does not exist in the document - // @ts-expect-error - delete doc.attributes.heartbeatIndices; - const result = remove890Indices(doc, context); - expect(result).toEqual({ - ...doc, - attributes: { - ...doc.attributes, - heartbeatIndices: 'heartbeat-8*', - }, - }); - }); -}); diff --git a/x-pack/plugins/synthetics/server/saved_objects/migrations.ts b/x-pack/plugins/synthetics/server/saved_objects/migrations.ts deleted file mode 100644 index 2b7e15e37d83a..0000000000000 --- a/x-pack/plugins/synthetics/server/saved_objects/migrations.ts +++ /dev/null @@ -1,63 +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 { SavedObjectMigrationFn } from '@kbn/core/server'; -import { DynamicSettingsAttributes } from '../runtime_types/settings'; - -export const add820Indices: SavedObjectMigrationFn< - DynamicSettingsAttributes, - DynamicSettingsAttributes -> = (doc) => { - const heartbeatIndices = doc.attributes?.heartbeatIndices; - - const indicesArr = !heartbeatIndices ? [] : heartbeatIndices.split(','); - - if (!indicesArr.includes('synthetics-*')) { - indicesArr.push('synthetics-*'); - } - - if (!indicesArr.includes('heartbeat-8*')) { - indicesArr.push('heartbeat-8*'); - } - - const migratedObj = { - ...doc, - attributes: { - ...doc.attributes, - heartbeatIndices: indicesArr.join(','), - }, - }; - - return migratedObj; -}; - -export const remove890Indices: SavedObjectMigrationFn< - DynamicSettingsAttributes, - DynamicSettingsAttributes -> = (doc) => { - const heartbeatIndices = doc.attributes?.heartbeatIndices; - - const indicesArr = !heartbeatIndices ? [] : heartbeatIndices.split(','); - - // remove synthetics-* from the array - const indexToRemove = indicesArr.indexOf('synthetics-*'); - if (indexToRemove > -1) { - indicesArr.splice(indexToRemove, 1); - } - - if (!indicesArr.includes('heartbeat-8*')) { - indicesArr.push('heartbeat-8*'); - } - - return { - ...doc, - attributes: { - ...doc.attributes, - heartbeatIndices: indicesArr.join(','), - }, - }; -}; diff --git a/x-pack/plugins/synthetics/server/saved_objects/saved_objects.ts b/x-pack/plugins/synthetics/server/saved_objects/saved_objects.ts index 2ae631214e9e1..ae2ad1c712bc2 100644 --- a/x-pack/plugins/synthetics/server/saved_objects/saved_objects.ts +++ b/x-pack/plugins/synthetics/server/saved_objects/saved_objects.ts @@ -21,7 +21,7 @@ import { DYNAMIC_SETTINGS_DEFAULT_ATTRIBUTES } from '../constants/settings'; import { DynamicSettingsAttributes } from '../runtime_types/settings'; import { ConfigKey } from '../../common/runtime_types'; import { UptimeConfig } from '../../common/config'; -import { settingsObjectId, umDynamicSettings } from './uptime_settings'; +import { settingsObjectId, settingsObjectType } from './uptime_settings'; import { getSyntheticsMonitorSavedObjectType, SYNTHETICS_MONITOR_ENCRYPTED_TYPE, @@ -63,10 +63,7 @@ export const savedObjectsAdapter: UMSavedObjectsAdapter = { config: null, getUptimeDynamicSettings: async (client) => { try { - const obj = await client.get( - umDynamicSettings.name, - settingsObjectId - ); + const obj = await client.get(settingsObjectType, settingsObjectId); return obj?.attributes ?? DYNAMIC_SETTINGS_DEFAULT_ATTRIBUTES; } catch (getErr) { const config = savedObjectsAdapter.config; @@ -80,7 +77,7 @@ export const savedObjectsAdapter: UMSavedObjectsAdapter = { } }, setUptimeDynamicSettings: async (client, settings: DynamicSettingsAttributes | undefined) => { - await client.create(umDynamicSettings.name, settings, { + await client.create(settingsObjectType, settings, { id: settingsObjectId, overwrite: true, }); diff --git a/x-pack/plugins/synthetics/server/saved_objects/uptime_settings.ts b/x-pack/plugins/synthetics/server/saved_objects/uptime_settings.ts index 7e6d94c3b4ebd..93cde59a25369 100644 --- a/x-pack/plugins/synthetics/server/saved_objects/uptime_settings.ts +++ b/x-pack/plugins/synthetics/server/saved_objects/uptime_settings.ts @@ -5,49 +5,5 @@ * 2.0. */ -import { SavedObjectsType } from '@kbn/core/server'; -import { i18n } from '@kbn/i18n'; -import { add820Indices, remove890Indices } from './migrations'; - export const settingsObjectType = 'uptime-dynamic-settings'; export const settingsObjectId = 'uptime-dynamic-settings-singleton'; - -export const umDynamicSettings: SavedObjectsType = { - name: settingsObjectType, - hidden: false, - namespaceType: 'single', - mappings: { - dynamic: false, - properties: { - /* Leaving these commented to make it clear that these fields exist, even though we don't want them indexed. - When adding new fields please add them here. If they need to be searchable put them in the uncommented - part of properties. - heartbeatIndices: { - type: 'keyword', - }, - certAgeThreshold: { - type: 'long', - }, - certExpirationThreshold: { - type: 'long', - }, - defaultConnectors: { - type: 'keyword', - }, - */ - }, - }, - management: { - importableAndExportable: true, - icon: 'uptimeApp', - getTitle: () => - i18n.translate('xpack.synthetics.uptimeSettings.index', { - defaultMessage: 'Uptime Settings - Index', - }), - }, - migrations: { - // Takes a pre 8.2.0 doc, and converts it to 8.2.0 - '8.2.0': add820Indices, - '8.9.0': remove890Indices, - }, -}; diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index f3f5713ea7357..8d312cf306da9 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -36383,7 +36383,6 @@ "xpack.synthetics.totalDuration.metrics": "Durée de l’étape", "xpack.synthetics.totalDuration.transferSize": "Taille du transfert", "xpack.synthetics.uptimeFeatureCatalogueTitle": "Uptime", - "xpack.synthetics.uptimeSettings.index": "Paramètres Uptime - Index", "xpack.synthetics.wait": "Attendre", "xpack.synthetics.waterfall.applyFilters.label": "Sélectionner un élément auquel appliquer le filtre", "xpack.synthetics.waterfall.applyFilters.message": "Cliquer pour ajouter ou retirer le filtre", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index cf26f95341638..d9ca725bbd4dd 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -36368,7 +36368,6 @@ "xpack.synthetics.totalDuration.metrics": "ステップ期間", "xpack.synthetics.totalDuration.transferSize": "転送サイズ", "xpack.synthetics.uptimeFeatureCatalogueTitle": "アップタイム", - "xpack.synthetics.uptimeSettings.index": "アップタイム設定 - インデックス", "xpack.synthetics.wait": "待機", "xpack.synthetics.waterfall.applyFilters.label": "フィルターを適用するアイテムを選択", "xpack.synthetics.waterfall.applyFilters.message": "クリックして、フィルターを追加または削除します", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 6f5b3e721c83d..a778e8193a149 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -36362,7 +36362,6 @@ "xpack.synthetics.totalDuration.metrics": "步骤持续时间", "xpack.synthetics.totalDuration.transferSize": "传输大小", "xpack.synthetics.uptimeFeatureCatalogueTitle": "运行时间", - "xpack.synthetics.uptimeSettings.index": "Uptime 设置 - 索引", "xpack.synthetics.wait": "等待", "xpack.synthetics.waterfall.applyFilters.label": "选择要应用筛选的项目", "xpack.synthetics.waterfall.applyFilters.message": "单击以添加或移除筛选", diff --git a/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/duration_anomaly.ts b/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/duration_anomaly.ts index 1ee03a5976ea8..a378f2d2a7fc5 100644 --- a/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/duration_anomaly.ts +++ b/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/duration_anomaly.ts @@ -142,7 +142,10 @@ export const durationAnomalyAlertFactory: UptimeAlertTypeFactory }) { const uptimeEsClient = new UptimeEsClient( savedObjectsClient, - scopedClusterClient.asCurrentUser + scopedClusterClient.asCurrentUser, + { + isLegacyAlert: true, + } ); const { share, basePath } = server; const alertsLocator: LocatorPublic | undefined = diff --git a/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/status_check.ts b/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/status_check.ts index e9afea1504d97..dd713718a2c12 100644 --- a/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/status_check.ts +++ b/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/status_check.ts @@ -377,7 +377,10 @@ export const statusCheckAlertFactory: UptimeAlertTypeFactory = ( share.url.locators.get(alertsLocatorID); const uptimeEsClient = new UptimeEsClient( savedObjectsClient, - scopedClusterClient.asCurrentUser + scopedClusterClient.asCurrentUser, + { + isLegacyAlert: true, + } ); const filterString = await formatFilterString(uptimeEsClient, filters, search, libs); diff --git a/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/tls.ts b/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/tls.ts index 11c274feca20f..eb40a709174e7 100644 --- a/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/tls.ts +++ b/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/tls.ts @@ -155,6 +155,7 @@ export const tlsAlertFactory: UptimeAlertTypeFactory = ( spaceId, startedAt, state, + rule, }) { const { share, basePath } = _server; const alertsLocator: LocatorPublic | undefined = @@ -163,7 +164,10 @@ export const tlsAlertFactory: UptimeAlertTypeFactory = ( const uptimeEsClient = new UptimeEsClient( savedObjectsClient, - scopedClusterClient.asCurrentUser + scopedClusterClient.asCurrentUser, + { + isLegacyAlert: true, + } ); const certExpirationThreshold = diff --git a/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/tls_legacy.ts b/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/tls_legacy.ts index addb2dbbbdbe6..871157229b0a3 100644 --- a/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/tls_legacy.ts +++ b/x-pack/plugins/uptime/server/legacy_uptime/lib/alerts/tls_legacy.ts @@ -117,7 +117,10 @@ export const tlsLegacyAlertFactory: UptimeAlertTypeFactory = (_s const uptimeEsClient = new UptimeEsClient( savedObjectsClient, - scopedClusterClient.asCurrentUser + scopedClusterClient.asCurrentUser, + { + isLegacyAlert: true, + } ); const { certs, total }: CertResult = await libs.requests.getCerts({ uptimeEsClient, diff --git a/x-pack/plugins/uptime/server/legacy_uptime/lib/lib.test.ts b/x-pack/plugins/uptime/server/legacy_uptime/lib/lib.test.ts new file mode 100644 index 0000000000000..13a151da2e635 --- /dev/null +++ b/x-pack/plugins/uptime/server/legacy_uptime/lib/lib.test.ts @@ -0,0 +1,176 @@ +/* + * 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 { UptimeEsClient } from './lib'; +import { savedObjectsClientMock, uiSettingsServiceMock } from '@kbn/core/server/mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import { savedObjectsAdapter } from './saved_objects'; + +describe('UptimeEsClient', () => { + let uptimeEsClient: UptimeEsClient; + const savedObjectsClient = savedObjectsClientMock.create(); + const esClient = elasticsearchClientMock.createClusterClient().asInternalUser; + + beforeEach(() => { + uptimeEsClient = new UptimeEsClient(savedObjectsClient, esClient); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('search', () => { + it('should call baseESClient.search with correct parameters', async () => { + const mockSearchParams = { + body: { + query: { + match_all: {}, + }, + }, + }; + + const result = await uptimeEsClient.search({ + body: { + query: { + match_all: {}, + }, + }, + }); + + expect(esClient.search).toHaveBeenCalledWith( + { + index: 'heartbeat-8*,heartbeat-7*', + ...mockSearchParams, + }, + { meta: true } + ); + expect(result).toEqual({ + body: {}, + headers: { + 'x-elastic-product': 'Elasticsearch', + }, + meta: {}, + statusCode: 200, + warnings: [], + }); + }); + + it('should throw an error if baseESClient.search throws an error', async () => { + const mockSearchParams = { + body: { + query: { + match_all: {}, + }, + }, + }; + const mockError = new Error('Search error'); + esClient.search.mockRejectedValueOnce(mockError); + + await expect(uptimeEsClient.search(mockSearchParams)).rejects.toThrow(mockError); + expect(esClient.search).toHaveBeenCalledWith( + { + index: 'heartbeat-8*,heartbeat-7*', + ...mockSearchParams, + }, + { meta: true } + ); + }); + }); + + describe('count', () => { + it('should call baseESClient.count with correct parameters', async () => { + const mockCountParams = { + index: 'example', + }; + + const result = await uptimeEsClient.count(mockCountParams); + + expect(esClient.count).toHaveBeenCalledWith(mockCountParams, { meta: true }); + expect(result).toEqual({ + indices: 'heartbeat-8*,heartbeat-7*', + result: { + body: {}, + headers: { + 'x-elastic-product': 'Elasticsearch', + }, + meta: {}, + statusCode: 200, + warnings: [], + }, + }); + }); + + it('should throw an error if baseESClient.count throws an error', async () => { + const mockCountParams = { + index: 'example', + }; + const mockError = new Error('Count error'); + esClient.count.mockRejectedValueOnce(mockError); + + await expect(uptimeEsClient.count(mockCountParams)).rejects.toThrow(mockError); + expect(esClient.count).toHaveBeenCalledWith(mockCountParams, { meta: true }); + }); + }); + + describe('getInspectEnabled', () => { + it('should return false if uiSettings is not available', async () => { + const result = await uptimeEsClient.getInspectEnabled(); + + expect(result).toBe(false); + }); + + it('should return the value from uiSettings if available', async () => { + const mockUiSettings = uiSettingsServiceMock.createClient(); + uptimeEsClient.uiSettings = { + client: mockUiSettings, + } as any; + + // @ts-expect-error + mockUiSettings.get.mockReturnValue(true); + + await uptimeEsClient.getInspectEnabled(); + + expect(uptimeEsClient.isInspectorEnabled).toBe(true); + expect(mockUiSettings.get).toHaveBeenCalledWith('observability:enableInspectEsQueries'); + }); + }); + describe('heartbeatIndices', () => { + it('appends synthetics-* in index for legacy alerts', async () => { + savedObjectsAdapter.getUptimeDynamicSettings = jest.fn().mockResolvedValue({ + heartbeatIndices: 'heartbeat-8*,heartbeat-7*', + syntheticsIndexRemoved: true, + }); + uptimeEsClient = new UptimeEsClient(savedObjectsClient, esClient, { isLegacyAlert: true }); + + const mockSearchParams = { + body: { + query: { + match_all: {}, + }, + }, + }; + + await uptimeEsClient.search({ + body: { + query: { + match_all: {}, + }, + }, + }); + + expect(esClient.search).toHaveBeenCalledWith( + { + index: 'heartbeat-8*,heartbeat-7*,synthetics-*', + ...mockSearchParams, + }, + { meta: true } + ); + }); + }); + + // Add more tests for other methods and edge cases +}); diff --git a/x-pack/plugins/uptime/server/legacy_uptime/lib/lib.ts b/x-pack/plugins/uptime/server/legacy_uptime/lib/lib.ts index 8880e809861a4..e00104974014f 100644 --- a/x-pack/plugins/uptime/server/legacy_uptime/lib/lib.ts +++ b/x-pack/plugins/uptime/server/legacy_uptime/lib/lib.ts @@ -20,15 +20,8 @@ import { enableInspectEsQueries } from '@kbn/observability-plugin/common'; import { getInspectResponse } from '@kbn/observability-shared-plugin/common'; import { API_URLS } from '../../../common/constants'; import { UptimeServerSetup } from './adapters'; -import { UMLicenseCheck } from './domains'; -import { UptimeRequests } from './requests'; import { savedObjectsAdapter } from './saved_objects/saved_objects'; -export interface UMDomainLibs { - requests: UptimeRequests; - license: UMLicenseCheck; -} - export type { UMServerLibs } from '../uptime_server'; export interface CountResponse { @@ -55,6 +48,7 @@ export class UptimeEsClient { inspectableEsQueries: InspectResponse = []; uiSettings?: CoreRequestHandlerContext['uiSettings']; savedObjectsClient: SavedObjectsClientContract; + isLegacyAlert?: boolean; constructor( savedObjectsClient: SavedObjectsClientContract, @@ -64,9 +58,17 @@ export class UptimeEsClient { uiSettings?: CoreRequestHandlerContext['uiSettings']; request?: KibanaRequest; heartbeatIndices?: string; + isLegacyAlert?: boolean; } ) { - const { isDev = false, uiSettings, request, heartbeatIndices = '' } = options ?? {}; + const { + isLegacyAlert, + isDev = false, + uiSettings, + request, + heartbeatIndices = '', + } = options ?? {}; + this.isLegacyAlert = isLegacyAlert; this.uiSettings = uiSettings; this.baseESClient = esClient; this.savedObjectsClient = savedObjectsClient; @@ -197,11 +199,20 @@ export class UptimeEsClient { } async getIndices() { + // if isLegacyAlert appends synthetics-* if it's not already there + let indices = ''; + let syntheticsIndexRemoved = false; if (this.heartbeatIndices) { - return this.heartbeatIndices; + indices = this.heartbeatIndices; + } else { + const settings = await savedObjectsAdapter.getUptimeDynamicSettings(this.savedObjectsClient); + indices = settings?.heartbeatIndices || ''; + syntheticsIndexRemoved = settings.syntheticsIndexRemoved ?? false; + } + if (this.isLegacyAlert && !indices.includes('synthetics-') && syntheticsIndexRemoved) { + indices = indices + ',synthetics-*'; } - const settings = await savedObjectsAdapter.getUptimeDynamicSettings(this.savedObjectsClient); - return settings?.heartbeatIndices || ''; + return indices; } } diff --git a/x-pack/plugins/uptime/server/legacy_uptime/lib/saved_objects/migrations.test.ts b/x-pack/plugins/uptime/server/legacy_uptime/lib/saved_objects/migrations.test.ts index cccde17d79667..49c0a27b8be7d 100644 --- a/x-pack/plugins/uptime/server/legacy_uptime/lib/saved_objects/migrations.test.ts +++ b/x-pack/plugins/uptime/server/legacy_uptime/lib/saved_objects/migrations.test.ts @@ -117,6 +117,7 @@ describe('remove890Indices migration', () => { attributes: { ...doc.attributes, heartbeatIndices: 'heartbeat-8*,something_else', + syntheticsIndexRemoved: true, }, }); }); @@ -129,6 +130,7 @@ describe('remove890Indices migration', () => { attributes: { ...doc.attributes, heartbeatIndices: 'something_else,heartbeat-8*', + syntheticsIndexRemoved: true, }, }); }); diff --git a/x-pack/plugins/uptime/server/legacy_uptime/lib/saved_objects/migrations.ts b/x-pack/plugins/uptime/server/legacy_uptime/lib/saved_objects/migrations.ts index 12ed237e1b569..45ae98a776daf 100644 --- a/x-pack/plugins/uptime/server/legacy_uptime/lib/saved_objects/migrations.ts +++ b/x-pack/plugins/uptime/server/legacy_uptime/lib/saved_objects/migrations.ts @@ -53,11 +53,14 @@ export const remove890Indices: SavedObjectMigrationFn< indicesArr.push('heartbeat-8*'); } + const syntheticsIndexRemoved = indexToRemove > -1; + return { ...doc, attributes: { ...doc.attributes, heartbeatIndices: indicesArr.join(','), + ...(syntheticsIndexRemoved && { syntheticsIndexRemoved }), }, }; }; diff --git a/x-pack/plugins/uptime/server/runtime_types/settings.ts b/x-pack/plugins/uptime/server/runtime_types/settings.ts index 8f5ad64910558..152aced59fbb2 100644 --- a/x-pack/plugins/uptime/server/runtime_types/settings.ts +++ b/x-pack/plugins/uptime/server/runtime_types/settings.ts @@ -26,6 +26,7 @@ export const DynamicSettingsAttributesCodec = t.intersection([ }), t.partial({ defaultEmail: DefaultEmailCodec, + syntheticsIndexRemoved: t.boolean, }), ]);