diff --git a/x-pack/plugins/alerts/server/alerts_client.mock.ts b/x-pack/plugins/alerts/server/alerts_client.mock.ts index 240c33b8849b3..b2ce2a1b356fb 100644 --- a/x-pack/plugins/alerts/server/alerts_client.mock.ts +++ b/x-pack/plugins/alerts/server/alerts_client.mock.ts @@ -27,7 +27,6 @@ const createAlertsClientMock = () => { unmuteInstance: jest.fn(), listAlertTypes: jest.fn(), getAlertInstanceSummary: jest.fn(), - getHealth: jest.fn(), }; return mocked; }; diff --git a/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts index f108c30c62b1f..742572a565664 100644 --- a/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts +++ b/x-pack/plugins/alerts/server/alerts_client/alerts_client.ts @@ -420,77 +420,6 @@ export class AlertsClient { }; } - public async getHealth(): Promise { - const { saved_objects: data } = await this.unsecuredSavedObjectsClient.find({ - filter: 'alert.attributes.executionStatus.status:error', - fields: ['executionStatus'], - type: 'alert', - }); - - const healthStatuses = data.reduce( - (prevItem: AlertsHealth, item: SavedObjectsFindResult) => { - switch (item.attributes.executionStatus.error?.reason) { - case AlertExecutionStatusErrorReasons.Decrypt: - prevItem.decryptionHealth = { - status: HealthStatus.Warning, - timestamp: item.attributes.executionStatus.lastExecutionDate, - }; - break; - case AlertExecutionStatusErrorReasons.Execute: - prevItem.executionHealth = { - status: HealthStatus.Warning, - timestamp: item.attributes.executionStatus.lastExecutionDate, - }; - break; - case AlertExecutionStatusErrorReasons.Read: - prevItem.readHealth = { - status: HealthStatus.Warning, - timestamp: item.attributes.executionStatus.lastExecutionDate, - }; - break; - } - return prevItem; - }, - { - decryptionHealth: { - status: HealthStatus.OK, - timestamp: '', - }, - executionHealth: { - status: HealthStatus.OK, - timestamp: '', - }, - readHealth: { - status: HealthStatus.OK, - timestamp: '', - }, - } - ); - - const { saved_objects: noErrorData } = await this.unsecuredSavedObjectsClient.find({ - filter: - 'alert.attributes.executionStatus.status:ok or alert.attributes.executionStatus.status:active or alert.attributes.executionStatus.status:pending', - fields: ['executionStatus'], - type: 'alert', - }); - const lastExecutionDate = noErrorData.reduce( - (prev: Date, item) => - prev > new Date(item.attributes.executionStatus.lastExecutionDate) - ? prev - : new Date(item.attributes.executionStatus.lastExecutionDate), - new Date('0001/01/01') - ); - - Object.entries(healthStatuses).map(([healthType, statusItem]) => { - if (statusItem.status === HealthStatus.OK) { - statusItem.timestamp = lastExecutionDate.toISOString(); - } - return [healthType, statusItem]; - }); - - return healthStatuses; - } - public async aggregate({ options: { fields, ...options } = {}, }: { options?: AggregateOptions } = {}): Promise { diff --git a/x-pack/plugins/alerts/server/alerts_client/tests/get_health.test.ts b/x-pack/plugins/alerts/server/health/get_health.test.ts similarity index 63% rename from x-pack/plugins/alerts/server/alerts_client/tests/get_health.test.ts rename to x-pack/plugins/alerts/server/health/get_health.test.ts index fad96ff0549db..36bd04a042606 100644 --- a/x-pack/plugins/alerts/server/alerts_client/tests/get_health.test.ts +++ b/x-pack/plugins/alerts/server/health/get_health.test.ts @@ -3,67 +3,17 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { AlertsClient, ConstructorOptions } from '../alerts_client'; -import { savedObjectsClientMock, loggingSystemMock } from '../../../../../../src/core/server/mocks'; -import { alertTypeRegistryMock } from '../../alert_type_registry.mock'; -import { alertsAuthorizationMock } from '../../authorization/alerts_authorization.mock'; -import { encryptedSavedObjectsMock } from '../../../../encrypted_saved_objects/server/mocks'; -import { actionsAuthorizationMock } from '../../../../actions/server/mocks'; -import { AlertsAuthorization } from '../../authorization/alerts_authorization'; -import { ActionsAuthorization } from '../../../../actions/server'; -import { getBeforeSetup } from './lib'; -import { AlertExecutionStatusErrorReasons, HealthStatus } from '../../types'; -import { taskManagerMock } from '../../../../task_manager/server/mocks'; +import { savedObjectsRepositoryMock } from '../../../../../src/core/server/mocks'; +import { AlertExecutionStatusErrorReasons, HealthStatus } from '../types'; +import { getHealth } from './get_health'; -const taskManager = taskManagerMock.createStart(); -const alertTypeRegistry = alertTypeRegistryMock.create(); -const unsecuredSavedObjectsClient = savedObjectsClientMock.create(); -const encryptedSavedObjects = encryptedSavedObjectsMock.createClient(); -const authorization = alertsAuthorizationMock.create(); -const actionsAuthorization = actionsAuthorizationMock.create(); - -const kibanaVersion = 'v7.10.0'; -const alertsClientParams: jest.Mocked = { - taskManager, - alertTypeRegistry, - unsecuredSavedObjectsClient, - authorization: (authorization as unknown) as AlertsAuthorization, - actionsAuthorization: (actionsAuthorization as unknown) as ActionsAuthorization, - spaceId: 'default', - namespace: 'default', - getUserName: jest.fn(), - createAPIKey: jest.fn(), - invalidateAPIKey: jest.fn(), - logger: loggingSystemMock.create().get(), - encryptedSavedObjectsClient: encryptedSavedObjects, - getActionsClient: jest.fn(), - getEventLogClient: jest.fn(), - kibanaVersion, -}; - -beforeEach(() => { - getBeforeSetup(alertsClientParams, taskManager, alertTypeRegistry); -}); +const savedObjectsRepository = savedObjectsRepositoryMock.create(); describe('getHealth()', () => { - const listedTypes = new Set([ - { - actionGroups: [], - actionVariables: undefined, - defaultActionGroupId: 'default', - id: 'myType', - name: 'myType', - producer: 'myApp', - }, - ]); - beforeAll(() => { - alertTypeRegistry.list.mockReturnValue(listedTypes); - }); - test('return true if some of alerts has a decryption error', async () => { const lastExecutionDateError = new Date().toISOString(); const lastExecutionDate = new Date().toISOString(); - unsecuredSavedObjectsClient.find.mockResolvedValue({ + savedObjectsRepository.find.mockResolvedValue({ total: 2, per_page: 10, page: 1, @@ -126,8 +76,7 @@ describe('getHealth()', () => { }, ], }); - const alertsClient = new AlertsClient(alertsClientParams); - const result = await alertsClient.getHealth(); + const result = await getHealth(savedObjectsRepository); expect(result).toStrictEqual({ executionHealth: { status: HealthStatus.OK, @@ -142,13 +91,13 @@ describe('getHealth()', () => { timestamp: lastExecutionDateError, }, }); - expect(unsecuredSavedObjectsClient.find).toHaveBeenCalledTimes(2); + expect(savedObjectsRepository.find).toHaveBeenCalledTimes(2); }); test('return false if no alerts with a decryption error', async () => { const lastExecutionDateError = new Date().toISOString(); const lastExecutionDate = new Date().toISOString(); - unsecuredSavedObjectsClient.find.mockResolvedValue({ + savedObjectsRepository.find.mockResolvedValue({ total: 2, per_page: 10, page: 1, @@ -211,8 +160,7 @@ describe('getHealth()', () => { }, ], }); - const alertsClient = new AlertsClient(alertsClientParams); - const result = await alertsClient.getHealth(); + const result = await getHealth(savedObjectsRepository); expect(result).toStrictEqual({ executionHealth: { status: HealthStatus.Warning, diff --git a/x-pack/plugins/alerts/server/health/get_health.ts b/x-pack/plugins/alerts/server/health/get_health.ts new file mode 100644 index 0000000000000..144c1bacb11d7 --- /dev/null +++ b/x-pack/plugins/alerts/server/health/get_health.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; + * you may not use this file except in compliance with the Elastic License. + */ +import { ISavedObjectsRepository, SavedObjectsFindResult } from 'src/core/server'; +import { AlertsHealth, HealthStatus, RawAlert, AlertExecutionStatusErrorReasons } from '../types'; + +export const getHealth = async ( + internalSavedObjectsRepository: ISavedObjectsRepository +): Promise => { + const { saved_objects: data } = await internalSavedObjectsRepository.find({ + filter: 'alert.attributes.executionStatus.status:error', + fields: ['executionStatus'], + type: 'alert', + }); + + const healthStatuses = data.reduce( + (prevItem: AlertsHealth, item: SavedObjectsFindResult) => { + switch (item.attributes.executionStatus.error?.reason) { + case AlertExecutionStatusErrorReasons.Decrypt: + prevItem.decryptionHealth = { + status: HealthStatus.Warning, + timestamp: item.attributes.executionStatus.lastExecutionDate, + }; + break; + case AlertExecutionStatusErrorReasons.Execute: + prevItem.executionHealth = { + status: HealthStatus.Warning, + timestamp: item.attributes.executionStatus.lastExecutionDate, + }; + break; + case AlertExecutionStatusErrorReasons.Read: + prevItem.readHealth = { + status: HealthStatus.Warning, + timestamp: item.attributes.executionStatus.lastExecutionDate, + }; + break; + } + return prevItem; + }, + { + decryptionHealth: { + status: HealthStatus.OK, + timestamp: '', + }, + executionHealth: { + status: HealthStatus.OK, + timestamp: '', + }, + readHealth: { + status: HealthStatus.OK, + timestamp: '', + }, + } + ); + + const { saved_objects: noErrorData } = await internalSavedObjectsRepository.find({ + filter: 'not alert.attributes.executionStatus.status:error', + fields: ['executionStatus'], + type: 'alert', + }); + const lastExecutionDate = noErrorData.reduce( + (prev: Date, item) => + prev > new Date(item.attributes.executionStatus.lastExecutionDate) + ? prev + : new Date(item.attributes.executionStatus.lastExecutionDate), + new Date('0001/01/01') + ); + + for (const [, statusItem] of Object.entries(healthStatuses)) { + if (statusItem.status === HealthStatus.OK) { + statusItem.timestamp = lastExecutionDate.toISOString(); + } + } + + return healthStatuses; +}; diff --git a/x-pack/plugins/alerts/server/health/get_state.test.ts b/x-pack/plugins/alerts/server/health/get_state.test.ts index 445fb2340eb2c..86981c486da0f 100644 --- a/x-pack/plugins/alerts/server/health/get_state.test.ts +++ b/x-pack/plugins/alerts/server/health/get_state.test.ts @@ -4,11 +4,11 @@ * you may not use this file except in compliance with the Elastic License. */ import { taskManagerMock } from '../../../task_manager/server/mocks'; -import { healthStatus$ } from '.'; +import { getHealthStatusStream } from '.'; import { TaskStatus } from '../../../task_manager/server'; import { HealthStatus } from '../types'; -describe('healthStatus$()', () => { +describe('getHealthStatusStream()', () => { const mockTaskManager = taskManagerMock.createStart(); it('should return an object with the "unavailable" level and proper summary of "Alerting framework is unhealthy"', async () => { @@ -35,7 +35,7 @@ describe('healthStatus$()', () => { }; }) ); - healthStatus$(mockTaskManager).subscribe( + getHealthStatusStream(mockTaskManager).subscribe( (val: { level: Readonly; summary: string }) => { expect(val.level).toBe(false); } @@ -66,7 +66,7 @@ describe('healthStatus$()', () => { }; }) ); - healthStatus$(mockTaskManager).subscribe( + getHealthStatusStream(mockTaskManager).subscribe( (val: { level: Readonly; summary: string }) => { expect(val.level).toBe(true); } diff --git a/x-pack/plugins/alerts/server/health/get_state.ts b/x-pack/plugins/alerts/server/health/get_state.ts index aa1251a696d5a..476456ecad88a 100644 --- a/x-pack/plugins/alerts/server/health/get_state.ts +++ b/x-pack/plugins/alerts/server/health/get_state.ts @@ -4,9 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ +import { i18n } from '@kbn/i18n'; import { interval, Observable } from 'rxjs'; import { catchError, switchMap } from 'rxjs/operators'; -import { get } from 'lodash'; import { ServiceStatus, ServiceStatusLevels } from '../../../../../src/core/server'; import { TaskManagerStartContract } from '../../../task_manager/server'; import { HEALTH_TASK_ID } from './task'; @@ -26,33 +26,47 @@ async function getLatestTaskState(taskManager: TaskManagerStartContract) { return null; } -export const healthStatus$ = ( +const LEVEL_SUMMARY = { + [ServiceStatusLevels.available.toString()]: i18n.translate( + 'xpack.alerts.server.healthStatus.available', + { + defaultMessage: 'Alerting framework is available', + } + ), + [ServiceStatusLevels.degraded.toString()]: i18n.translate( + 'xpack.alerts.server.healthStatus.degraded', + { + defaultMessage: 'Alerting framework is degraded', + } + ), + [ServiceStatusLevels.unavailable.toString()]: i18n.translate( + 'xpack.alerts.server.healthStatus.unavailable', + { + defaultMessage: 'Alerting framework is unavailable', + } + ), +}; + +export const getHealthStatusStream = ( taskManager: TaskManagerStartContract ): Observable> => { return interval(60000 * 5).pipe( switchMap(async () => { const doc = await getLatestTaskState(taskManager); - const body = get(doc, 'state'); - if (body?.health_status === HealthStatus.OK) { - return { - level: ServiceStatusLevels.available, - summary: 'Alerting framework is available', - }; - } else if (body?.health_status === HealthStatus.Warning) { - return { - level: ServiceStatusLevels.degraded, - summary: 'Alerting framework is degraded', - }; - } else { - return { - level: ServiceStatusLevels.unavailable, - summary: 'Alerting framework is unavailable', - }; - } + const level = + doc?.state?.health_status === HealthStatus.OK + ? ServiceStatusLevels.available + : doc?.state?.health_status === HealthStatus.Warning + ? ServiceStatusLevels.degraded + : ServiceStatusLevels.unavailable; + return { + level, + summary: LEVEL_SUMMARY[level.toString()], + }; }), catchError(async (error) => ({ level: ServiceStatusLevels.unavailable, - summary: `Alerting framework is unavailable`, + summary: LEVEL_SUMMARY[ServiceStatusLevels.unavailable.toString()], meta: { error }, })) ); diff --git a/x-pack/plugins/alerts/server/health/index.ts b/x-pack/plugins/alerts/server/health/index.ts index 276b11937cda1..730c4596aa550 100644 --- a/x-pack/plugins/alerts/server/health/index.ts +++ b/x-pack/plugins/alerts/server/health/index.ts @@ -4,5 +4,5 @@ * you may not use this file except in compliance with the Elastic License. */ -export { healthStatus$ } from './get_state'; +export { getHealthStatusStream } from './get_state'; export { scheduleAlertingHealthCheck, initializeAlertingHealth } from './task'; diff --git a/x-pack/plugins/alerts/server/health/task.ts b/x-pack/plugins/alerts/server/health/task.ts index 809f3e90e5268..2113d205cf8e8 100644 --- a/x-pack/plugins/alerts/server/health/task.ts +++ b/x-pack/plugins/alerts/server/health/task.ts @@ -4,15 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Logger, KibanaRequest } from 'kibana/server'; +import { ISavedObjectsRepository, Logger } from 'kibana/server'; import { RunContext, TaskManagerSetupContract, TaskManagerStartContract, } from '../../../task_manager/server'; -import { AlertsClient } from '../alerts_client'; import { AlertsConfig } from '../config'; -import { GetBasePathFunction, HealthStatus } from '../types'; +import { HealthStatus } from '../types'; +import { getHealth } from './get_health'; export const HEALTH_TASK_TYPE = 'alerting_health_check'; @@ -21,15 +21,11 @@ export const HEALTH_TASK_ID = `Alerting-${HEALTH_TASK_TYPE}`; export function initializeAlertingHealth( logger: Logger, taskManager: TaskManagerSetupContract, - getBasePath: GetBasePathFunction, - getAlertsClientWithRequest: ( - request: KibanaRequest - ) => Promise<{ - getAlertsClient: () => AlertsClient; + internalSavedObjectsRepository: Promise<{ + createInternalRepository: () => ISavedObjectsRepository; }> ) { - const request = getFakeKibanaRequest(getBasePath); - registerAlertingHealthCheckTask(logger, taskManager, getAlertsClientWithRequest(request)); + registerAlertingHealthCheckTask(logger, taskManager, internalSavedObjectsRepository); } export function scheduleAlertingHealthCheck( @@ -42,35 +38,17 @@ export function scheduleAlertingHealthCheck( } } -function getFakeKibanaRequest(getBasePath: GetBasePathFunction) { - const requestHeaders: Record = {}; - return ({ - headers: requestHeaders, - getBasePath: () => getBasePath(), - path: '/', - route: { settings: {} }, - url: { - href: '/', - }, - raw: { - req: { - url: '/', - }, - }, - } as unknown) as KibanaRequest; -} - function registerAlertingHealthCheckTask( logger: Logger, taskManager: TaskManagerSetupContract, - alertsClient: Promise<{ - getAlertsClient: () => AlertsClient; + internalSavedObjectsRepository: Promise<{ + createInternalRepository: () => ISavedObjectsRepository; }> ) { taskManager.registerTaskDefinitions({ [HEALTH_TASK_TYPE]: { title: 'Alerting framework health check task', - createTaskRunner: healthCheckTaskRunner(logger, alertsClient), + createTaskRunner: healthCheckTaskRunner(logger, internalSavedObjectsRepository), }, }); } @@ -98,16 +76,17 @@ async function scheduleTasks( export function healthCheckTaskRunner( logger: Logger, - alertsClient: Promise<{ - getAlertsClient: () => AlertsClient; + internalSavedObjectsRepository: Promise<{ + createInternalRepository: () => ISavedObjectsRepository; }> ) { return ({ taskInstance }: RunContext) => { const { state } = taskInstance; return { async run() { + const repository = await (await internalSavedObjectsRepository).createInternalRepository(); try { - const alertingHealthStatus = await (await alertsClient).getAlertsClient().getHealth(); + const alertingHealthStatus = await getHealth(repository); return { state: { runs: (state.runs || 0) + 1, diff --git a/x-pack/plugins/alerts/server/mocks.ts b/x-pack/plugins/alerts/server/mocks.ts index 05d64bdbb77f4..cfae4c650bd42 100644 --- a/x-pack/plugins/alerts/server/mocks.ts +++ b/x-pack/plugins/alerts/server/mocks.ts @@ -25,6 +25,7 @@ const createStartMock = () => { const mock: jest.Mocked = { listTypes: jest.fn(), getAlertsClientWithRequest: jest.fn().mockResolvedValue(alertsClientMock.create()), + getFrameworkHealth: jest.fn(), }; return mock; }; diff --git a/x-pack/plugins/alerts/server/plugin.ts b/x-pack/plugins/alerts/server/plugin.ts index f741b32b4872b..6fff05198c225 100644 --- a/x-pack/plugins/alerts/server/plugin.ts +++ b/x-pack/plugins/alerts/server/plugin.ts @@ -59,14 +59,19 @@ import { PluginSetupContract as ActionsPluginSetupContract, PluginStartContract as ActionsPluginStartContract, } from '../../actions/server'; -import { Services } from './types'; +import { AlertsHealth, Services } from './types'; import { registerAlertsUsageCollector } from './usage'; import { initializeAlertingTelemetry, scheduleAlertingTelemetry } from './usage/task'; import { IEventLogger, IEventLogService, IEventLogClientService } from '../../event_log/server'; import { PluginStartContract as FeaturesPluginStart } from '../../features/server'; import { setupSavedObjects } from './saved_objects'; -import { healthStatus$, scheduleAlertingHealthCheck, initializeAlertingHealth } from './health'; +import { + getHealthStatusStream, + scheduleAlertingHealthCheck, + initializeAlertingHealth, +} from './health'; import { AlertsConfig } from './config'; +import { getHealth } from './health/get_health'; export const EVENT_LOG_PROVIDER = 'alerting'; export const EVENT_LOG_ACTIONS = { @@ -83,6 +88,7 @@ export interface PluginSetupContract { export interface PluginStartContract { listTypes: AlertTypeRegistry['list']; getAlertsClientWithRequest(request: KibanaRequest): PublicMethodsOf; + getFrameworkHealth: () => Promise; } export interface AlertingPluginsSetup { @@ -196,7 +202,10 @@ export class AlertingPlugin { core.getStartServices().then(async ([, startPlugins]) => { core.status.set( - combineLatest([core.status.derivedStatus$, healthStatus$(startPlugins.taskManager)]).pipe( + combineLatest([ + core.status.derivedStatus$, + getHealthStatusStream(startPlugins.taskManager), + ]).pipe( map(([derivedStatus, healthStatus]) => { if (healthStatus.level > derivedStatus.level) { return healthStatus as ServiceStatus; @@ -211,8 +220,7 @@ export class AlertingPlugin { initializeAlertingHealth( this.logger, plugins.taskManager, - this.getBasePath, - this.createAlertClientHandlerContext(core) + this.createSOInternalRepositoryContext(core) ); core.http.registerRouteHandlerContext('alerting', this.createRouteHandlerContext(core)); @@ -309,6 +317,8 @@ export class AlertingPlugin { return { listTypes: alertTypeRegistry!.list.bind(this.alertTypeRegistry!), getAlertsClientWithRequest, + getFrameworkHealth: async () => + await getHealth(core.savedObjects.createInternalRepository(['alert'])), }; } @@ -323,19 +333,18 @@ export class AlertingPlugin { return alertsClientFactory!.create(request, savedObjects); }, listTypes: alertTypeRegistry!.list.bind(alertTypeRegistry!), + getFrameworkHealth: async () => + await getHealth(savedObjects.createInternalRepository(['alert'])), }; }; }; - private createAlertClientHandlerContext = (core: CoreSetup) => { - const { alertsClientFactory } = this; - return async function alertsFakeContext(request: KibanaRequest) { - const [{ savedObjects }] = await core.getStartServices(); - return { - getAlertsClient: () => { - return alertsClientFactory!.create(request, savedObjects); - }, - }; + private createSOInternalRepositoryContext = async (core: CoreSetup) => { + const [{ savedObjects }] = await core.getStartServices(); + return { + createInternalRepository: () => { + return savedObjects.createInternalRepository(['alert']); + }, }; }; diff --git a/x-pack/plugins/alerts/server/routes/_mock_handler_arguments.ts b/x-pack/plugins/alerts/server/routes/_mock_handler_arguments.ts index 3d13fc65ab260..b3f407b20c142 100644 --- a/x-pack/plugins/alerts/server/routes/_mock_handler_arguments.ts +++ b/x-pack/plugins/alerts/server/routes/_mock_handler_arguments.ts @@ -14,7 +14,7 @@ import { identity } from 'lodash'; import type { MethodKeysOf } from '@kbn/utility-types'; import { httpServerMock } from '../../../../../src/core/server/mocks'; import { alertsClientMock, AlertsClientMock } from '../alerts_client.mock'; -import { AlertType } from '../../common'; +import { AlertsHealth, AlertType } from '../../common'; import { elasticsearchServiceMock } from '../../../../../src/core/server/mocks'; export function mockHandlerArguments( @@ -22,10 +22,13 @@ export function mockHandlerArguments( alertsClient = alertsClientMock.create(), listTypes: listTypesRes = [], esClient = elasticsearchServiceMock.createLegacyClusterClient(), + getFrameworkHealth, }: { alertsClient?: AlertsClientMock; listTypes?: AlertType[]; esClient?: jest.Mocked; + getFrameworkHealth?: jest.MockInstance, []> & + (() => Promise); }, req: unknown, res?: Array> @@ -39,6 +42,7 @@ export function mockHandlerArguments( getAlertsClient() { return alertsClient || alertsClientMock.create(); }, + getFrameworkHealth, }, } as unknown) as RequestHandlerContext, req as KibanaRequest, diff --git a/x-pack/plugins/alerts/server/routes/health.test.ts b/x-pack/plugins/alerts/server/routes/health.test.ts index 1474ac3d0bbcb..d1967c6dd9bf8 100644 --- a/x-pack/plugins/alerts/server/routes/health.test.ts +++ b/x-pack/plugins/alerts/server/routes/health.test.ts @@ -13,16 +13,19 @@ import { mockLicenseState } from '../lib/license_state.mock'; import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks'; import { alertsClientMock } from '../alerts_client.mock'; import { HealthStatus } from '../types'; +import { alertsMock } from '../mocks'; const alertsClient = alertsClientMock.create(); jest.mock('../lib/license_api_access.ts', () => ({ verifyApiAccess: jest.fn(), })); +const alerting = alertsMock.createStart(); + const currentDate = new Date().toISOString(); beforeEach(() => { jest.resetAllMocks(); - alertsClient.getHealth.mockResolvedValue({ + alerting.getFrameworkHealth.mockResolvedValue({ decryptionHealth: { status: HealthStatus.OK, timestamp: currentDate, @@ -93,7 +96,11 @@ describe('healthRoute', () => { const esClient = elasticsearchServiceMock.createLegacyClusterClient(); esClient.callAsInternalUser.mockReturnValue(Promise.resolve({})); - const [context, req, res] = mockHandlerArguments({ esClient, alertsClient }, {}, ['ok']); + const [context, req, res] = mockHandlerArguments( + { esClient, alertsClient, getFrameworkHealth: alerting.getFrameworkHealth }, + {}, + ['ok'] + ); expect(await handler(context, req, res)).toStrictEqual({ body: { @@ -129,7 +136,11 @@ describe('healthRoute', () => { const esClient = elasticsearchServiceMock.createLegacyClusterClient(); esClient.callAsInternalUser.mockReturnValue(Promise.resolve({})); - const [context, req, res] = mockHandlerArguments({ esClient, alertsClient }, {}, ['ok']); + const [context, req, res] = mockHandlerArguments( + { esClient, alertsClient, getFrameworkHealth: alerting.getFrameworkHealth }, + {}, + ['ok'] + ); expect(await handler(context, req, res)).toStrictEqual({ body: { @@ -165,7 +176,11 @@ describe('healthRoute', () => { const esClient = elasticsearchServiceMock.createLegacyClusterClient(); esClient.callAsInternalUser.mockReturnValue(Promise.resolve({ security: {} })); - const [context, req, res] = mockHandlerArguments({ esClient, alertsClient }, {}, ['ok']); + const [context, req, res] = mockHandlerArguments( + { esClient, alertsClient, getFrameworkHealth: alerting.getFrameworkHealth }, + {}, + ['ok'] + ); expect(await handler(context, req, res)).toStrictEqual({ body: { @@ -201,7 +216,11 @@ describe('healthRoute', () => { const esClient = elasticsearchServiceMock.createLegacyClusterClient(); esClient.callAsInternalUser.mockReturnValue(Promise.resolve({ security: { enabled: true } })); - const [context, req, res] = mockHandlerArguments({ esClient, alertsClient }, {}, ['ok']); + const [context, req, res] = mockHandlerArguments( + { esClient, alertsClient, getFrameworkHealth: alerting.getFrameworkHealth }, + {}, + ['ok'] + ); expect(await handler(context, req, res)).toStrictEqual({ body: { @@ -239,7 +258,11 @@ describe('healthRoute', () => { Promise.resolve({ security: { enabled: true, ssl: {} } }) ); - const [context, req, res] = mockHandlerArguments({ esClient, alertsClient }, {}, ['ok']); + const [context, req, res] = mockHandlerArguments( + { esClient, alertsClient, getFrameworkHealth: alerting.getFrameworkHealth }, + {}, + ['ok'] + ); expect(await handler(context, req, res)).toStrictEqual({ body: { @@ -277,7 +300,11 @@ describe('healthRoute', () => { Promise.resolve({ security: { enabled: true, ssl: { http: { enabled: true } } } }) ); - const [context, req, res] = mockHandlerArguments({ esClient, alertsClient }, {}, ['ok']); + const [context, req, res] = mockHandlerArguments( + { esClient, alertsClient, getFrameworkHealth: alerting.getFrameworkHealth }, + {}, + ['ok'] + ); expect(await handler(context, req, res)).toStrictEqual({ body: { diff --git a/x-pack/plugins/alerts/server/routes/health.ts b/x-pack/plugins/alerts/server/routes/health.ts index ca97fd012ff43..bfd5b1e272287 100644 --- a/x-pack/plugins/alerts/server/routes/health.ts +++ b/x-pack/plugins/alerts/server/routes/health.ts @@ -60,8 +60,7 @@ export function healthRoute( path: '/_xpack/usage', }); - const alertsClient = context.alerting.getAlertsClient(); - const alertingFrameworkHeath = await alertsClient.getHealth(); + const alertingFrameworkHeath = await context.alerting.getFrameworkHealth(); const frameworkHealth: AlertingFrameworkHealth = { isSufficientlySecure: !isSecurityEnabled || (isSecurityEnabled && isTLSEnabled), diff --git a/x-pack/plugins/alerts/server/types.ts b/x-pack/plugins/alerts/server/types.ts index 6f58ff369ef9e..9226461f6e30a 100644 --- a/x-pack/plugins/alerts/server/types.ts +++ b/x-pack/plugins/alerts/server/types.ts @@ -27,6 +27,7 @@ import { AlertInstanceState, AlertExecutionStatuses, AlertExecutionStatusErrorReasons, + AlertsHealth, } from '../common'; export type WithoutQueryAndParams = Pick>; @@ -39,6 +40,7 @@ declare module 'src/core/server' { alerting?: { getAlertsClient: () => AlertsClient; listTypes: AlertTypeRegistry['list']; + getFrameworkHealth: () => Promise; }; } }