Ability to get scoped call cluster from alerting and action executors (#64432)
* Initial work * Rename to getScopedCallCluster * Fix typecheck * Fix more type check issues * Add tests * Add docs Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
a32d7b1344
commit
db374fc950
|
@ -143,7 +143,8 @@ This is the primary function for an action type. Whenever the action needs to ex
|
||||||
| actionId | The action saved object id that the action type is executing for. |
|
| actionId | The action saved object id that the action type is executing for. |
|
||||||
| config | The decrypted configuration given to an action. This comes from the action saved object that is partially or fully encrypted within the data store. If you would like to validate the config before being passed to the executor, define `validate.config` within the action type. |
|
| config | The decrypted configuration given to an action. This comes from the action saved object that is partially or fully encrypted within the data store. If you would like to validate the config before being passed to the executor, define `validate.config` within the action type. |
|
||||||
| params | Parameters for the execution. These will be given at execution time by either an alert or manually provided when calling the plugin provided execute function. |
|
| params | Parameters for the execution. These will be given at execution time by either an alert or manually provided when calling the plugin provided execute function. |
|
||||||
| services.callCluster(path, opts) | Use this to do Elasticsearch queries on the cluster Kibana connects to. This function is the same as any other `callCluster` in Kibana.<br><br>**NOTE**: This currently authenticates as the Kibana internal user, but will change in a future PR. |
|
| services.callCluster(path, opts) | Use this to do Elasticsearch queries on the cluster Kibana connects to. This function is the same as any other `callCluster` in Kibana but runs in the context of the user who is calling the action when security is enabled.|
|
||||||
|
| services.getScopedCallCluster | This function scopes an instance of CallCluster by returning a `callCluster(path, opts)` function that runs in the context of the user who is calling the action when security is enabled. This must only be called with instances of CallCluster provided by core.|
|
||||||
| services.savedObjectsClient | This is an instance of the saved objects client. This provides the ability to do CRUD on any saved objects within the same space the alert lives in.<br><br>The scope of the saved objects client is tied to the user in context calling the execute API or the API key provided to the execute plugin function (only when security isenabled). |
|
| services.savedObjectsClient | This is an instance of the saved objects client. This provides the ability to do CRUD on any saved objects within the same space the alert lives in.<br><br>The scope of the saved objects client is tied to the user in context calling the execute API or the API key provided to the execute plugin function (only when security isenabled). |
|
||||||
| services.log(tags, [data], [timestamp]) | Use this to create server logs. (This is the same function as server.log) |
|
| services.log(tags, [data], [timestamp]) | Use this to create server logs. (This is the same function as server.log) |
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,13 @@ jest.mock('./lib/send_email', () => ({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { Logger } from '../../../../../src/core/server';
|
import { Logger } from '../../../../../src/core/server';
|
||||||
import { savedObjectsClientMock } from '../../../../../src/core/server/mocks';
|
|
||||||
|
|
||||||
import { ActionType, ActionTypeExecutorOptions } from '../types';
|
import { ActionType, ActionTypeExecutorOptions } from '../types';
|
||||||
import { actionsConfigMock } from '../actions_config.mock';
|
import { actionsConfigMock } from '../actions_config.mock';
|
||||||
import { validateConfig, validateSecrets, validateParams } from '../lib';
|
import { validateConfig, validateSecrets, validateParams } from '../lib';
|
||||||
import { createActionTypeRegistry } from './index.test';
|
import { createActionTypeRegistry } from './index.test';
|
||||||
import { sendEmail } from './lib/send_email';
|
import { sendEmail } from './lib/send_email';
|
||||||
|
import { actionsMock } from '../mocks';
|
||||||
import {
|
import {
|
||||||
ActionParamsType,
|
ActionParamsType,
|
||||||
ActionTypeConfigType,
|
ActionTypeConfigType,
|
||||||
|
@ -26,13 +26,8 @@ import {
|
||||||
const sendEmailMock = sendEmail as jest.Mock;
|
const sendEmailMock = sendEmail as jest.Mock;
|
||||||
|
|
||||||
const ACTION_TYPE_ID = '.email';
|
const ACTION_TYPE_ID = '.email';
|
||||||
const NO_OP_FN = () => {};
|
|
||||||
|
|
||||||
const services = {
|
const services = actionsMock.createServices();
|
||||||
log: NO_OP_FN,
|
|
||||||
callCluster: async (path: string, opts: unknown) => {},
|
|
||||||
savedObjectsClient: savedObjectsClientMock.create(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let actionType: ActionType;
|
let actionType: ActionType;
|
||||||
let mockedLogger: jest.Mocked<Logger>;
|
let mockedLogger: jest.Mocked<Logger>;
|
||||||
|
|
|
@ -10,18 +10,13 @@ jest.mock('./lib/send_email', () => ({
|
||||||
|
|
||||||
import { ActionType, ActionTypeExecutorOptions } from '../types';
|
import { ActionType, ActionTypeExecutorOptions } from '../types';
|
||||||
import { validateConfig, validateParams } from '../lib';
|
import { validateConfig, validateParams } from '../lib';
|
||||||
import { savedObjectsClientMock } from '../../../../../src/core/server/mocks';
|
|
||||||
import { createActionTypeRegistry } from './index.test';
|
import { createActionTypeRegistry } from './index.test';
|
||||||
import { ActionParamsType, ActionTypeConfigType } from './es_index';
|
import { ActionParamsType, ActionTypeConfigType } from './es_index';
|
||||||
|
import { actionsMock } from '../mocks';
|
||||||
|
|
||||||
const ACTION_TYPE_ID = '.index';
|
const ACTION_TYPE_ID = '.index';
|
||||||
const NO_OP_FN = () => {};
|
|
||||||
|
|
||||||
const services = {
|
const services = actionsMock.createServices();
|
||||||
log: NO_OP_FN,
|
|
||||||
callCluster: jest.fn(),
|
|
||||||
savedObjectsClient: savedObjectsClientMock.create(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let actionType: ActionType;
|
let actionType: ActionType;
|
||||||
|
|
||||||
|
@ -196,9 +191,9 @@ describe('execute()', () => {
|
||||||
await actionType.executor(executorOptions);
|
await actionType.executor(executorOptions);
|
||||||
|
|
||||||
const calls = services.callCluster.mock.calls;
|
const calls = services.callCluster.mock.calls;
|
||||||
const timeValue = calls[0][1].body[1].field_to_use_for_time;
|
const timeValue = calls[0][1]?.body[1].field_to_use_for_time;
|
||||||
expect(timeValue).toBeInstanceOf(Date);
|
expect(timeValue).toBeInstanceOf(Date);
|
||||||
delete calls[0][1].body[1].field_to_use_for_time;
|
delete calls[0][1]?.body[1].field_to_use_for_time;
|
||||||
expect(calls).toMatchInlineSnapshot(`
|
expect(calls).toMatchInlineSnapshot(`
|
||||||
Array [
|
Array [
|
||||||
Array [
|
Array [
|
||||||
|
|
|
@ -72,7 +72,7 @@ async function executor(
|
||||||
bulkBody.push(document);
|
bulkBody.push(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
const bulkParams: unknown = {
|
const bulkParams = {
|
||||||
index,
|
index,
|
||||||
body: bulkBody,
|
body: bulkBody,
|
||||||
refresh: config.refresh,
|
refresh: config.refresh,
|
||||||
|
|
|
@ -11,20 +11,17 @@ jest.mock('./lib/post_pagerduty', () => ({
|
||||||
import { getActionType } from './pagerduty';
|
import { getActionType } from './pagerduty';
|
||||||
import { ActionType, Services, ActionTypeExecutorOptions } from '../types';
|
import { ActionType, Services, ActionTypeExecutorOptions } from '../types';
|
||||||
import { validateConfig, validateSecrets, validateParams } from '../lib';
|
import { validateConfig, validateSecrets, validateParams } from '../lib';
|
||||||
import { savedObjectsClientMock } from '../../../../../src/core/server/mocks';
|
|
||||||
import { postPagerduty } from './lib/post_pagerduty';
|
import { postPagerduty } from './lib/post_pagerduty';
|
||||||
import { createActionTypeRegistry } from './index.test';
|
import { createActionTypeRegistry } from './index.test';
|
||||||
import { Logger } from '../../../../../src/core/server';
|
import { Logger } from '../../../../../src/core/server';
|
||||||
import { actionsConfigMock } from '../actions_config.mock';
|
import { actionsConfigMock } from '../actions_config.mock';
|
||||||
|
import { actionsMock } from '../mocks';
|
||||||
|
|
||||||
const postPagerdutyMock = postPagerduty as jest.Mock;
|
const postPagerdutyMock = postPagerduty as jest.Mock;
|
||||||
|
|
||||||
const ACTION_TYPE_ID = '.pagerduty';
|
const ACTION_TYPE_ID = '.pagerduty';
|
||||||
|
|
||||||
const services: Services = {
|
const services: Services = actionsMock.createServices();
|
||||||
callCluster: async (path: string, opts: unknown) => {},
|
|
||||||
savedObjectsClient: savedObjectsClientMock.create(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let actionType: ActionType;
|
let actionType: ActionType;
|
||||||
let mockedLogger: jest.Mocked<Logger>;
|
let mockedLogger: jest.Mocked<Logger>;
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
import { ActionType } from '../types';
|
import { ActionType } from '../types';
|
||||||
import { validateParams } from '../lib';
|
import { validateParams } from '../lib';
|
||||||
import { Logger } from '../../../../../src/core/server';
|
import { Logger } from '../../../../../src/core/server';
|
||||||
import { savedObjectsClientMock } from '../../../../../src/core/server/mocks';
|
|
||||||
import { createActionTypeRegistry } from './index.test';
|
import { createActionTypeRegistry } from './index.test';
|
||||||
|
import { actionsMock } from '../mocks';
|
||||||
|
|
||||||
const ACTION_TYPE_ID = '.server-log';
|
const ACTION_TYPE_ID = '.server-log';
|
||||||
|
|
||||||
|
@ -90,10 +90,7 @@ describe('execute()', () => {
|
||||||
const actionId = 'some-id';
|
const actionId = 'some-id';
|
||||||
await actionType.executor({
|
await actionType.executor({
|
||||||
actionId,
|
actionId,
|
||||||
services: {
|
services: actionsMock.createServices(),
|
||||||
callCluster: async (path: string, opts: unknown) => {},
|
|
||||||
savedObjectsClient: savedObjectsClientMock.create(),
|
|
||||||
},
|
|
||||||
params: { message: 'message text here', level: 'info' },
|
params: { message: 'message text here', level: 'info' },
|
||||||
config: {},
|
config: {},
|
||||||
secrets: {},
|
secrets: {},
|
||||||
|
|
|
@ -7,9 +7,9 @@
|
||||||
import { getActionType } from '.';
|
import { getActionType } from '.';
|
||||||
import { ActionType, Services, ActionTypeExecutorOptions } from '../../types';
|
import { ActionType, Services, ActionTypeExecutorOptions } from '../../types';
|
||||||
import { validateConfig, validateSecrets, validateParams } from '../../lib';
|
import { validateConfig, validateSecrets, validateParams } from '../../lib';
|
||||||
import { savedObjectsClientMock } from '../../../../../../src/core/server/mocks';
|
|
||||||
import { createActionTypeRegistry } from '../index.test';
|
import { createActionTypeRegistry } from '../index.test';
|
||||||
import { actionsConfigMock } from '../../actions_config.mock';
|
import { actionsConfigMock } from '../../actions_config.mock';
|
||||||
|
import { actionsMock } from '../../mocks';
|
||||||
|
|
||||||
import { ACTION_TYPE_ID } from './constants';
|
import { ACTION_TYPE_ID } from './constants';
|
||||||
import * as i18n from './translations';
|
import * as i18n from './translations';
|
||||||
|
@ -21,10 +21,7 @@ jest.mock('./action_handlers');
|
||||||
|
|
||||||
const handleIncidentMock = handleIncident as jest.Mock;
|
const handleIncidentMock = handleIncident as jest.Mock;
|
||||||
|
|
||||||
const services: Services = {
|
const services: Services = actionsMock.createServices();
|
||||||
callCluster: async (path: string, opts: unknown) => {},
|
|
||||||
savedObjectsClient: savedObjectsClientMock.create(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let actionType: ActionType;
|
let actionType: ActionType;
|
||||||
|
|
||||||
|
|
|
@ -10,17 +10,14 @@ import {
|
||||||
ActionTypeExecutorOptions,
|
ActionTypeExecutorOptions,
|
||||||
ActionTypeExecutorResult,
|
ActionTypeExecutorResult,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { savedObjectsClientMock } from '../../../../../src/core/server/mocks';
|
|
||||||
import { validateParams, validateSecrets } from '../lib';
|
import { validateParams, validateSecrets } from '../lib';
|
||||||
import { getActionType } from './slack';
|
import { getActionType } from './slack';
|
||||||
import { actionsConfigMock } from '../actions_config.mock';
|
import { actionsConfigMock } from '../actions_config.mock';
|
||||||
|
import { actionsMock } from '../mocks';
|
||||||
|
|
||||||
const ACTION_TYPE_ID = '.slack';
|
const ACTION_TYPE_ID = '.slack';
|
||||||
|
|
||||||
const services: Services = {
|
const services: Services = actionsMock.createServices();
|
||||||
callCluster: async (path: string, opts: unknown) => {},
|
|
||||||
savedObjectsClient: savedObjectsClientMock.create(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let actionType: ActionType;
|
let actionType: ActionType;
|
||||||
|
|
||||||
|
|
|
@ -11,20 +11,17 @@ jest.mock('axios', () => ({
|
||||||
import { getActionType } from './webhook';
|
import { getActionType } from './webhook';
|
||||||
import { ActionType, Services } from '../types';
|
import { ActionType, Services } from '../types';
|
||||||
import { validateConfig, validateSecrets, validateParams } from '../lib';
|
import { validateConfig, validateSecrets, validateParams } from '../lib';
|
||||||
import { savedObjectsClientMock } from '../../../../../src/core/server/mocks';
|
|
||||||
import { actionsConfigMock } from '../actions_config.mock';
|
import { actionsConfigMock } from '../actions_config.mock';
|
||||||
import { createActionTypeRegistry } from './index.test';
|
import { createActionTypeRegistry } from './index.test';
|
||||||
import { Logger } from '../../../../../src/core/server';
|
import { Logger } from '../../../../../src/core/server';
|
||||||
|
import { actionsMock } from '../mocks';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
const axiosRequestMock = axios.request as jest.Mock;
|
const axiosRequestMock = axios.request as jest.Mock;
|
||||||
|
|
||||||
const ACTION_TYPE_ID = '.webhook';
|
const ACTION_TYPE_ID = '.webhook';
|
||||||
|
|
||||||
const services: Services = {
|
const services: Services = actionsMock.createServices();
|
||||||
callCluster: async (path: string, opts: unknown) => {},
|
|
||||||
savedObjectsClient: savedObjectsClientMock.create(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let actionType: ActionType;
|
let actionType: ActionType;
|
||||||
let mockedLogger: jest.Mocked<Logger>;
|
let mockedLogger: jest.Mocked<Logger>;
|
||||||
|
|
|
@ -9,21 +9,15 @@ import { schema } from '@kbn/config-schema';
|
||||||
import { ActionExecutor } from './action_executor';
|
import { ActionExecutor } from './action_executor';
|
||||||
import { actionTypeRegistryMock } from '../action_type_registry.mock';
|
import { actionTypeRegistryMock } from '../action_type_registry.mock';
|
||||||
import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks';
|
import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks';
|
||||||
import { savedObjectsClientMock, loggingServiceMock } from '../../../../../src/core/server/mocks';
|
import { loggingServiceMock } from '../../../../../src/core/server/mocks';
|
||||||
import { eventLoggerMock } from '../../../event_log/server/mocks';
|
import { eventLoggerMock } from '../../../event_log/server/mocks';
|
||||||
import { spacesServiceMock } from '../../../spaces/server/spaces_service/spaces_service.mock';
|
import { spacesServiceMock } from '../../../spaces/server/spaces_service/spaces_service.mock';
|
||||||
import { ActionType } from '../types';
|
import { ActionType } from '../types';
|
||||||
|
import { actionsMock } from '../mocks';
|
||||||
|
|
||||||
const actionExecutor = new ActionExecutor({ isESOUsingEphemeralEncryptionKey: false });
|
const actionExecutor = new ActionExecutor({ isESOUsingEphemeralEncryptionKey: false });
|
||||||
const savedObjectsClient = savedObjectsClientMock.create();
|
const services = actionsMock.createServices();
|
||||||
|
const savedObjectsClient = services.savedObjectsClient;
|
||||||
function getServices() {
|
|
||||||
return {
|
|
||||||
savedObjectsClient,
|
|
||||||
log: jest.fn(),
|
|
||||||
callCluster: jest.fn(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const encryptedSavedObjectsPlugin = encryptedSavedObjectsMock.createStart();
|
const encryptedSavedObjectsPlugin = encryptedSavedObjectsMock.createStart();
|
||||||
const actionTypeRegistry = actionTypeRegistryMock.create();
|
const actionTypeRegistry = actionTypeRegistryMock.create();
|
||||||
|
|
||||||
|
@ -39,7 +33,7 @@ const spacesMock = spacesServiceMock.createSetupContract();
|
||||||
actionExecutor.initialize({
|
actionExecutor.initialize({
|
||||||
logger: loggingServiceMock.create().get(),
|
logger: loggingServiceMock.create().get(),
|
||||||
spaces: spacesMock,
|
spaces: spacesMock,
|
||||||
getServices,
|
getServices: () => services,
|
||||||
actionTypeRegistry,
|
actionTypeRegistry,
|
||||||
encryptedSavedObjectsPlugin,
|
encryptedSavedObjectsPlugin,
|
||||||
eventLogger: eventLoggerMock.create(),
|
eventLogger: eventLoggerMock.create(),
|
||||||
|
@ -273,7 +267,7 @@ test('throws an error when passing isESOUsingEphemeralEncryptionKey with value o
|
||||||
customActionExecutor.initialize({
|
customActionExecutor.initialize({
|
||||||
logger: loggingServiceMock.create().get(),
|
logger: loggingServiceMock.create().get(),
|
||||||
spaces: spacesMock,
|
spaces: spacesMock,
|
||||||
getServices,
|
getServices: () => services,
|
||||||
actionTypeRegistry,
|
actionTypeRegistry,
|
||||||
encryptedSavedObjectsPlugin,
|
encryptedSavedObjectsPlugin,
|
||||||
eventLogger: eventLoggerMock.create(),
|
eventLogger: eventLoggerMock.create(),
|
||||||
|
|
|
@ -6,6 +6,11 @@
|
||||||
|
|
||||||
import { actionsClientMock } from './actions_client.mock';
|
import { actionsClientMock } from './actions_client.mock';
|
||||||
import { PluginSetupContract, PluginStartContract } from './plugin';
|
import { PluginSetupContract, PluginStartContract } from './plugin';
|
||||||
|
import { Services } from './types';
|
||||||
|
import {
|
||||||
|
elasticsearchServiceMock,
|
||||||
|
savedObjectsClientMock,
|
||||||
|
} from '../../../../src/core/server/mocks';
|
||||||
|
|
||||||
export { actionsClientMock };
|
export { actionsClientMock };
|
||||||
|
|
||||||
|
@ -27,7 +32,19 @@ const createStartMock = () => {
|
||||||
return mock;
|
return mock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createServicesMock = () => {
|
||||||
|
const mock: jest.Mocked<Services & {
|
||||||
|
savedObjectsClient: ReturnType<typeof savedObjectsClientMock.create>;
|
||||||
|
}> = {
|
||||||
|
callCluster: elasticsearchServiceMock.createScopedClusterClient().callAsCurrentUser,
|
||||||
|
getScopedCallCluster: jest.fn(),
|
||||||
|
savedObjectsClient: savedObjectsClientMock.create(),
|
||||||
|
};
|
||||||
|
return mock;
|
||||||
|
};
|
||||||
|
|
||||||
export const actionsMock = {
|
export const actionsMock = {
|
||||||
|
createServices: createServicesMock,
|
||||||
createSetup: createSetupMock,
|
createSetup: createSetupMock,
|
||||||
createStart: createStartMock,
|
createStart: createStartMock,
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
IContextProvider,
|
IContextProvider,
|
||||||
SavedObjectsServiceStart,
|
SavedObjectsServiceStart,
|
||||||
ElasticsearchServiceStart,
|
ElasticsearchServiceStart,
|
||||||
|
IClusterClient,
|
||||||
} from '../../../../src/core/server';
|
} from '../../../../src/core/server';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -302,6 +303,9 @@ export class ActionsPlugin implements Plugin<Promise<PluginSetupContract>, Plugi
|
||||||
return request => ({
|
return request => ({
|
||||||
callCluster: elasticsearch.legacy.client.asScoped(request).callAsCurrentUser,
|
callCluster: elasticsearch.legacy.client.asScoped(request).callAsCurrentUser,
|
||||||
savedObjectsClient: savedObjects.getScopedClient(request),
|
savedObjectsClient: savedObjects.getScopedClient(request),
|
||||||
|
getScopedCallCluster(clusterClient: IClusterClient) {
|
||||||
|
return clusterClient.asScoped(request).callAsCurrentUser;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,15 +4,17 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
|
||||||
SavedObjectsClientContract,
|
|
||||||
SavedObjectAttributes,
|
|
||||||
KibanaRequest,
|
|
||||||
} from '../../../../src/core/server';
|
|
||||||
import { ActionTypeRegistry } from './action_type_registry';
|
import { ActionTypeRegistry } from './action_type_registry';
|
||||||
import { PluginSetupContract, PluginStartContract } from './plugin';
|
import { PluginSetupContract, PluginStartContract } from './plugin';
|
||||||
import { ActionsClient } from './actions_client';
|
import { ActionsClient } from './actions_client';
|
||||||
import { LicenseType } from '../../licensing/common/types';
|
import { LicenseType } from '../../licensing/common/types';
|
||||||
|
import {
|
||||||
|
IClusterClient,
|
||||||
|
IScopedClusterClient,
|
||||||
|
KibanaRequest,
|
||||||
|
SavedObjectsClientContract,
|
||||||
|
SavedObjectAttributes,
|
||||||
|
} from '../../../../src/core/server';
|
||||||
|
|
||||||
export type WithoutQueryAndParams<T> = Pick<T, Exclude<keyof T, 'query' | 'params'>>;
|
export type WithoutQueryAndParams<T> = Pick<T, Exclude<keyof T, 'query' | 'params'>>;
|
||||||
export type GetServicesFunction = (request: KibanaRequest) => Services;
|
export type GetServicesFunction = (request: KibanaRequest) => Services;
|
||||||
|
@ -21,8 +23,9 @@ export type GetBasePathFunction = (spaceId?: string) => string;
|
||||||
export type SpaceIdToNamespaceFunction = (spaceId?: string) => string | undefined;
|
export type SpaceIdToNamespaceFunction = (spaceId?: string) => string | undefined;
|
||||||
|
|
||||||
export interface Services {
|
export interface Services {
|
||||||
callCluster(path: string, opts: unknown): Promise<unknown>;
|
callCluster: IScopedClusterClient['callAsCurrentUser'];
|
||||||
savedObjectsClient: SavedObjectsClientContract;
|
savedObjectsClient: SavedObjectsClientContract;
|
||||||
|
getScopedCallCluster(clusterClient: IClusterClient): IScopedClusterClient['callAsCurrentUser'];
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module 'src/core/server' {
|
declare module 'src/core/server' {
|
||||||
|
|
|
@ -101,6 +101,7 @@ This is the primary function for an alert type. Whenever the alert needs to exec
|
||||||
|---|---|
|
|---|---|
|
||||||
|services.callCluster(path, opts)|Use this to do Elasticsearch queries on the cluster Kibana connects to. This function is the same as any other `callCluster` in Kibana but in the context of the user who created the alert when security is enabled.|
|
|services.callCluster(path, opts)|Use this to do Elasticsearch queries on the cluster Kibana connects to. This function is the same as any other `callCluster` in Kibana but in the context of the user who created the alert when security is enabled.|
|
||||||
|services.savedObjectsClient|This is an instance of the saved objects client. This provides the ability to do CRUD on any saved objects within the same space the alert lives in.<br><br>The scope of the saved objects client is tied to the user who created the alert (only when security isenabled).|
|
|services.savedObjectsClient|This is an instance of the saved objects client. This provides the ability to do CRUD on any saved objects within the same space the alert lives in.<br><br>The scope of the saved objects client is tied to the user who created the alert (only when security isenabled).|
|
||||||
|
|services.getScopedCallCluster|This function scopes an instance of CallCluster by returning a `callCluster(path, opts)` function that runs in the context of the user who created the alert when security is enabled. This must only be called with instances of CallCluster provided by core.|
|
||||||
|services.log(tags, [data], [timestamp])|Use this to create server logs. (This is the same function as server.log)|
|
|services.log(tags, [data], [timestamp])|Use this to create server logs. (This is the same function as server.log)|
|
||||||
|startedAt|The date and time the alert type started execution.|
|
|startedAt|The date and time the alert type started execution.|
|
||||||
|previousStartedAt|The previous date and time the alert type started a successful execution.|
|
|previousStartedAt|The previous date and time the alert type started a successful execution.|
|
||||||
|
|
|
@ -6,8 +6,11 @@
|
||||||
|
|
||||||
import { alertsClientMock } from './alerts_client.mock';
|
import { alertsClientMock } from './alerts_client.mock';
|
||||||
import { PluginSetupContract, PluginStartContract } from './plugin';
|
import { PluginSetupContract, PluginStartContract } from './plugin';
|
||||||
import { savedObjectsClientMock } from '../../../../src/core/server/mocks';
|
|
||||||
import { AlertInstance } from './alert_instance';
|
import { AlertInstance } from './alert_instance';
|
||||||
|
import {
|
||||||
|
elasticsearchServiceMock,
|
||||||
|
savedObjectsClientMock,
|
||||||
|
} from '../../../../src/core/server/mocks';
|
||||||
|
|
||||||
export { alertsClientMock };
|
export { alertsClientMock };
|
||||||
|
|
||||||
|
@ -55,7 +58,8 @@ const createAlertServicesMock = () => {
|
||||||
alertInstanceFactory: jest
|
alertInstanceFactory: jest
|
||||||
.fn<jest.Mocked<AlertInstance>, [string]>()
|
.fn<jest.Mocked<AlertInstance>, [string]>()
|
||||||
.mockReturnValue(alertInstanceFactoryMock),
|
.mockReturnValue(alertInstanceFactoryMock),
|
||||||
callCluster: jest.fn(),
|
callCluster: elasticsearchServiceMock.createScopedClusterClient().callAsCurrentUser,
|
||||||
|
getScopedCallCluster: jest.fn(),
|
||||||
savedObjectsClient: savedObjectsClientMock.create(),
|
savedObjectsClient: savedObjectsClientMock.create(),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -29,6 +29,7 @@ import {
|
||||||
RequestHandler,
|
RequestHandler,
|
||||||
SharedGlobalConfig,
|
SharedGlobalConfig,
|
||||||
ElasticsearchServiceStart,
|
ElasticsearchServiceStart,
|
||||||
|
IClusterClient,
|
||||||
} from '../../../../src/core/server';
|
} from '../../../../src/core/server';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -270,6 +271,9 @@ export class AlertingPlugin {
|
||||||
return request => ({
|
return request => ({
|
||||||
callCluster: elasticsearch.legacy.client.asScoped(request).callAsCurrentUser,
|
callCluster: elasticsearch.legacy.client.asScoped(request).callAsCurrentUser,
|
||||||
savedObjectsClient: savedObjects.getScopedClient(request),
|
savedObjectsClient: savedObjects.getScopedClient(request),
|
||||||
|
getScopedCallCluster(clusterClient: IClusterClient) {
|
||||||
|
return clusterClient.asScoped(request).callAsCurrentUser;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,10 @@ import { ConcreteTaskInstance, TaskStatus } from '../../../../plugins/task_manag
|
||||||
import { TaskRunnerContext } from './task_runner_factory';
|
import { TaskRunnerContext } from './task_runner_factory';
|
||||||
import { TaskRunner } from './task_runner';
|
import { TaskRunner } from './task_runner';
|
||||||
import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks';
|
import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks';
|
||||||
import { savedObjectsClientMock, loggingServiceMock } from '../../../../../src/core/server/mocks';
|
import { loggingServiceMock } from '../../../../../src/core/server/mocks';
|
||||||
import { PluginStartContract as ActionsPluginStart } from '../../../actions/server';
|
import { PluginStartContract as ActionsPluginStart } from '../../../actions/server';
|
||||||
import { actionsMock } from '../../../actions/server/mocks';
|
import { actionsMock } from '../../../actions/server/mocks';
|
||||||
|
import { alertsMock } from '../mocks';
|
||||||
import { eventLoggerMock } from '../../../event_log/server/event_logger.mock';
|
import { eventLoggerMock } from '../../../event_log/server/event_logger.mock';
|
||||||
import { IEventLogger } from '../../../event_log/server';
|
import { IEventLogger } from '../../../event_log/server';
|
||||||
import { SavedObjectsErrorHelpers } from '../../../../../src/core/server';
|
import { SavedObjectsErrorHelpers } from '../../../../../src/core/server';
|
||||||
|
@ -52,13 +53,9 @@ describe('Task Runner', () => {
|
||||||
|
|
||||||
afterAll(() => fakeTimer.restore());
|
afterAll(() => fakeTimer.restore());
|
||||||
|
|
||||||
const savedObjectsClient = savedObjectsClientMock.create();
|
|
||||||
const encryptedSavedObjectsPlugin = encryptedSavedObjectsMock.createStart();
|
const encryptedSavedObjectsPlugin = encryptedSavedObjectsMock.createStart();
|
||||||
const services = {
|
const services = alertsMock.createAlertServices();
|
||||||
log: jest.fn(),
|
const savedObjectsClient = services.savedObjectsClient;
|
||||||
callCluster: jest.fn(),
|
|
||||||
savedObjectsClient,
|
|
||||||
};
|
|
||||||
|
|
||||||
const taskRunnerFactoryInitializerParams: jest.Mocked<TaskRunnerContext> & {
|
const taskRunnerFactoryInitializerParams: jest.Mocked<TaskRunnerContext> & {
|
||||||
actionsPlugin: jest.Mocked<ActionsPluginStart>;
|
actionsPlugin: jest.Mocked<ActionsPluginStart>;
|
||||||
|
|
|
@ -8,8 +8,9 @@ import sinon from 'sinon';
|
||||||
import { ConcreteTaskInstance, TaskStatus } from '../../../../plugins/task_manager/server';
|
import { ConcreteTaskInstance, TaskStatus } from '../../../../plugins/task_manager/server';
|
||||||
import { TaskRunnerContext, TaskRunnerFactory } from './task_runner_factory';
|
import { TaskRunnerContext, TaskRunnerFactory } from './task_runner_factory';
|
||||||
import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks';
|
import { encryptedSavedObjectsMock } from '../../../../plugins/encrypted_saved_objects/server/mocks';
|
||||||
import { savedObjectsClientMock, loggingServiceMock } from '../../../../../src/core/server/mocks';
|
import { loggingServiceMock } from '../../../../../src/core/server/mocks';
|
||||||
import { actionsMock } from '../../../actions/server/mocks';
|
import { actionsMock } from '../../../actions/server/mocks';
|
||||||
|
import { alertsMock } from '../mocks';
|
||||||
import { eventLoggerMock } from '../../../event_log/server/event_logger.mock';
|
import { eventLoggerMock } from '../../../event_log/server/event_logger.mock';
|
||||||
|
|
||||||
const alertType = {
|
const alertType = {
|
||||||
|
@ -48,13 +49,8 @@ describe('Task Runner Factory', () => {
|
||||||
|
|
||||||
afterAll(() => fakeTimer.restore());
|
afterAll(() => fakeTimer.restore());
|
||||||
|
|
||||||
const savedObjectsClient = savedObjectsClientMock.create();
|
|
||||||
const encryptedSavedObjectsPlugin = encryptedSavedObjectsMock.createStart();
|
const encryptedSavedObjectsPlugin = encryptedSavedObjectsMock.createStart();
|
||||||
const services = {
|
const services = alertsMock.createAlertServices();
|
||||||
log: jest.fn(),
|
|
||||||
callCluster: jest.fn(),
|
|
||||||
savedObjectsClient,
|
|
||||||
};
|
|
||||||
|
|
||||||
const taskRunnerFactoryInitializerParams: jest.Mocked<TaskRunnerContext> = {
|
const taskRunnerFactoryInitializerParams: jest.Mocked<TaskRunnerContext> = {
|
||||||
getServices: jest.fn().mockReturnValue(services),
|
getServices: jest.fn().mockReturnValue(services),
|
||||||
|
|
|
@ -7,14 +7,16 @@
|
||||||
import { AlertInstance } from './alert_instance';
|
import { AlertInstance } from './alert_instance';
|
||||||
import { AlertTypeRegistry as OrigAlertTypeRegistry } from './alert_type_registry';
|
import { AlertTypeRegistry as OrigAlertTypeRegistry } from './alert_type_registry';
|
||||||
import { PluginSetupContract, PluginStartContract } from './plugin';
|
import { PluginSetupContract, PluginStartContract } from './plugin';
|
||||||
import {
|
|
||||||
SavedObjectAttributes,
|
|
||||||
SavedObjectsClientContract,
|
|
||||||
KibanaRequest,
|
|
||||||
} from '../../../../src/core/server';
|
|
||||||
import { Alert, AlertActionParams, ActionGroup } from '../common';
|
import { Alert, AlertActionParams, ActionGroup } from '../common';
|
||||||
import { AlertsClient } from './alerts_client';
|
import { AlertsClient } from './alerts_client';
|
||||||
export * from '../common';
|
export * from '../common';
|
||||||
|
import {
|
||||||
|
IClusterClient,
|
||||||
|
IScopedClusterClient,
|
||||||
|
KibanaRequest,
|
||||||
|
SavedObjectAttributes,
|
||||||
|
SavedObjectsClientContract,
|
||||||
|
} from '../../../../src/core/server';
|
||||||
|
|
||||||
// This will have to remain `any` until we can extend Alert Executors with generics
|
// This will have to remain `any` until we can extend Alert Executors with generics
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
@ -36,10 +38,9 @@ declare module 'src/core/server' {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Services {
|
export interface Services {
|
||||||
// This will have to remain `any` until we can extend Alert Services with generics
|
callCluster: IScopedClusterClient['callAsCurrentUser'];
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
callCluster(path: string, opts: any): Promise<any>;
|
|
||||||
savedObjectsClient: SavedObjectsClientContract;
|
savedObjectsClient: SavedObjectsClientContract;
|
||||||
|
getScopedCallCluster(clusterClient: IClusterClient): IScopedClusterClient['callAsCurrentUser'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AlertServices extends Services {
|
export interface AlertServices extends Services {
|
||||||
|
|
|
@ -20,7 +20,7 @@ const executor = createMetricThresholdExecutor('test') as (opts: {
|
||||||
}) => Promise<void>;
|
}) => Promise<void>;
|
||||||
|
|
||||||
const services: AlertServicesMock = alertsMock.createAlertServices();
|
const services: AlertServicesMock = alertsMock.createAlertServices();
|
||||||
services.callCluster.mockImplementation((_: string, { body, index }: any) => {
|
services.callCluster.mockImplementation(async (_: string, { body, index }: any) => {
|
||||||
if (index === 'alternatebeat-*') return mocks.changedSourceIdResponse;
|
if (index === 'alternatebeat-*') return mocks.changedSourceIdResponse;
|
||||||
const metric = body.query.bool.filter[1]?.exists.field;
|
const metric = body.query.bool.filter[1]?.exists.field;
|
||||||
if (body.aggs.groupings) {
|
if (body.aggs.groupings) {
|
||||||
|
|
|
@ -14,6 +14,7 @@ export default function(kibana: any) {
|
||||||
require: ['xpack_main', 'actions', 'alerting', 'elasticsearch'],
|
require: ['xpack_main', 'actions', 'alerting', 'elasticsearch'],
|
||||||
name: 'alerts',
|
name: 'alerts',
|
||||||
init(server: any) {
|
init(server: any) {
|
||||||
|
const clusterClient = server.newPlatform.start.core.elasticsearch.legacy.client;
|
||||||
server.plugins.xpack_main.registerFeature({
|
server.plugins.xpack_main.registerFeature({
|
||||||
id: 'alerting',
|
id: 'alerting',
|
||||||
name: 'Alerting',
|
name: 'Alerting',
|
||||||
|
@ -165,6 +166,22 @@ export default function(kibana: any) {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callClusterError = e;
|
callClusterError = e;
|
||||||
}
|
}
|
||||||
|
// Call scoped cluster
|
||||||
|
const callScopedCluster = services.getScopedCallCluster(clusterClient);
|
||||||
|
let callScopedClusterSuccess = false;
|
||||||
|
let callScopedClusterError;
|
||||||
|
try {
|
||||||
|
await callScopedCluster('index', {
|
||||||
|
index: params.callClusterAuthorizationIndex,
|
||||||
|
refresh: 'wait_for',
|
||||||
|
body: {
|
||||||
|
param1: 'test',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
callScopedClusterSuccess = true;
|
||||||
|
} catch (e) {
|
||||||
|
callScopedClusterError = e;
|
||||||
|
}
|
||||||
// Saved objects client
|
// Saved objects client
|
||||||
let savedObjectsClientSuccess = false;
|
let savedObjectsClientSuccess = false;
|
||||||
let savedObjectsClientError;
|
let savedObjectsClientError;
|
||||||
|
@ -185,6 +202,8 @@ export default function(kibana: any) {
|
||||||
state: {
|
state: {
|
||||||
callClusterSuccess,
|
callClusterSuccess,
|
||||||
callClusterError,
|
callClusterError,
|
||||||
|
callScopedClusterSuccess,
|
||||||
|
callScopedClusterError,
|
||||||
savedObjectsClientSuccess,
|
savedObjectsClientSuccess,
|
||||||
savedObjectsClientError,
|
savedObjectsClientError,
|
||||||
},
|
},
|
||||||
|
@ -376,6 +395,22 @@ export default function(kibana: any) {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
callClusterError = e;
|
callClusterError = e;
|
||||||
}
|
}
|
||||||
|
// Call scoped cluster
|
||||||
|
const callScopedCluster = services.getScopedCallCluster(clusterClient);
|
||||||
|
let callScopedClusterSuccess = false;
|
||||||
|
let callScopedClusterError;
|
||||||
|
try {
|
||||||
|
await callScopedCluster('index', {
|
||||||
|
index: params.callClusterAuthorizationIndex,
|
||||||
|
refresh: 'wait_for',
|
||||||
|
body: {
|
||||||
|
param1: 'test',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
callScopedClusterSuccess = true;
|
||||||
|
} catch (e) {
|
||||||
|
callScopedClusterError = e;
|
||||||
|
}
|
||||||
// Saved objects client
|
// Saved objects client
|
||||||
let savedObjectsClientSuccess = false;
|
let savedObjectsClientSuccess = false;
|
||||||
let savedObjectsClientError;
|
let savedObjectsClientError;
|
||||||
|
@ -396,6 +431,8 @@ export default function(kibana: any) {
|
||||||
state: {
|
state: {
|
||||||
callClusterSuccess,
|
callClusterSuccess,
|
||||||
callClusterError,
|
callClusterError,
|
||||||
|
callScopedClusterSuccess,
|
||||||
|
callScopedClusterError,
|
||||||
savedObjectsClientSuccess,
|
savedObjectsClientSuccess,
|
||||||
savedObjectsClientError,
|
savedObjectsClientError,
|
||||||
},
|
},
|
||||||
|
|
|
@ -436,11 +436,16 @@ export default function({ getService }: FtrProviderContext) {
|
||||||
indexedRecord = searchResult.hits.hits[0];
|
indexedRecord = searchResult.hits.hits[0];
|
||||||
expect(indexedRecord._source.state).to.eql({
|
expect(indexedRecord._source.state).to.eql({
|
||||||
callClusterSuccess: false,
|
callClusterSuccess: false,
|
||||||
|
callScopedClusterSuccess: false,
|
||||||
savedObjectsClientSuccess: false,
|
savedObjectsClientSuccess: false,
|
||||||
callClusterError: {
|
callClusterError: {
|
||||||
...indexedRecord._source.state.callClusterError,
|
...indexedRecord._source.state.callClusterError,
|
||||||
statusCode: 403,
|
statusCode: 403,
|
||||||
},
|
},
|
||||||
|
callScopedClusterError: {
|
||||||
|
...indexedRecord._source.state.callScopedClusterError,
|
||||||
|
statusCode: 403,
|
||||||
|
},
|
||||||
savedObjectsClientError: {
|
savedObjectsClientError: {
|
||||||
...indexedRecord._source.state.savedObjectsClientError,
|
...indexedRecord._source.state.savedObjectsClientError,
|
||||||
output: {
|
output: {
|
||||||
|
@ -457,6 +462,7 @@ export default function({ getService }: FtrProviderContext) {
|
||||||
indexedRecord = searchResult.hits.hits[0];
|
indexedRecord = searchResult.hits.hits[0];
|
||||||
expect(indexedRecord._source.state).to.eql({
|
expect(indexedRecord._source.state).to.eql({
|
||||||
callClusterSuccess: true,
|
callClusterSuccess: true,
|
||||||
|
callScopedClusterSuccess: true,
|
||||||
savedObjectsClientSuccess: false,
|
savedObjectsClientSuccess: false,
|
||||||
savedObjectsClientError: {
|
savedObjectsClientError: {
|
||||||
...indexedRecord._source.state.savedObjectsClientError,
|
...indexedRecord._source.state.savedObjectsClientError,
|
||||||
|
|
|
@ -469,11 +469,16 @@ instanceStateValue: true
|
||||||
expect(searchResult.hits.total.value).to.eql(1);
|
expect(searchResult.hits.total.value).to.eql(1);
|
||||||
expect(searchResult.hits.hits[0]._source.state).to.eql({
|
expect(searchResult.hits.hits[0]._source.state).to.eql({
|
||||||
callClusterSuccess: false,
|
callClusterSuccess: false,
|
||||||
|
callScopedClusterSuccess: false,
|
||||||
savedObjectsClientSuccess: false,
|
savedObjectsClientSuccess: false,
|
||||||
callClusterError: {
|
callClusterError: {
|
||||||
...searchResult.hits.hits[0]._source.state.callClusterError,
|
...searchResult.hits.hits[0]._source.state.callClusterError,
|
||||||
statusCode: 403,
|
statusCode: 403,
|
||||||
},
|
},
|
||||||
|
callScopedClusterError: {
|
||||||
|
...searchResult.hits.hits[0]._source.state.callScopedClusterError,
|
||||||
|
statusCode: 403,
|
||||||
|
},
|
||||||
savedObjectsClientError: {
|
savedObjectsClientError: {
|
||||||
...searchResult.hits.hits[0]._source.state.savedObjectsClientError,
|
...searchResult.hits.hits[0]._source.state.savedObjectsClientError,
|
||||||
output: {
|
output: {
|
||||||
|
@ -497,6 +502,7 @@ instanceStateValue: true
|
||||||
expect(searchResult.hits.total.value).to.eql(1);
|
expect(searchResult.hits.total.value).to.eql(1);
|
||||||
expect(searchResult.hits.hits[0]._source.state).to.eql({
|
expect(searchResult.hits.hits[0]._source.state).to.eql({
|
||||||
callClusterSuccess: true,
|
callClusterSuccess: true,
|
||||||
|
callScopedClusterSuccess: true,
|
||||||
savedObjectsClientSuccess: false,
|
savedObjectsClientSuccess: false,
|
||||||
savedObjectsClientError: {
|
savedObjectsClientError: {
|
||||||
...searchResult.hits.hits[0]._source.state.savedObjectsClientError,
|
...searchResult.hits.hits[0]._source.state.savedObjectsClientError,
|
||||||
|
@ -577,11 +583,16 @@ instanceStateValue: true
|
||||||
expect(searchResult.hits.total.value).to.eql(1);
|
expect(searchResult.hits.total.value).to.eql(1);
|
||||||
expect(searchResult.hits.hits[0]._source.state).to.eql({
|
expect(searchResult.hits.hits[0]._source.state).to.eql({
|
||||||
callClusterSuccess: false,
|
callClusterSuccess: false,
|
||||||
|
callScopedClusterSuccess: false,
|
||||||
savedObjectsClientSuccess: false,
|
savedObjectsClientSuccess: false,
|
||||||
callClusterError: {
|
callClusterError: {
|
||||||
...searchResult.hits.hits[0]._source.state.callClusterError,
|
...searchResult.hits.hits[0]._source.state.callClusterError,
|
||||||
statusCode: 403,
|
statusCode: 403,
|
||||||
},
|
},
|
||||||
|
callScopedClusterError: {
|
||||||
|
...searchResult.hits.hits[0]._source.state.callScopedClusterError,
|
||||||
|
statusCode: 403,
|
||||||
|
},
|
||||||
savedObjectsClientError: {
|
savedObjectsClientError: {
|
||||||
...searchResult.hits.hits[0]._source.state.savedObjectsClientError,
|
...searchResult.hits.hits[0]._source.state.savedObjectsClientError,
|
||||||
output: {
|
output: {
|
||||||
|
@ -605,6 +616,7 @@ instanceStateValue: true
|
||||||
expect(searchResult.hits.total.value).to.eql(1);
|
expect(searchResult.hits.total.value).to.eql(1);
|
||||||
expect(searchResult.hits.hits[0]._source.state).to.eql({
|
expect(searchResult.hits.hits[0]._source.state).to.eql({
|
||||||
callClusterSuccess: true,
|
callClusterSuccess: true,
|
||||||
|
callScopedClusterSuccess: true,
|
||||||
savedObjectsClientSuccess: false,
|
savedObjectsClientSuccess: false,
|
||||||
savedObjectsClientError: {
|
savedObjectsClientError: {
|
||||||
...searchResult.hits.hits[0]._source.state.savedObjectsClientError,
|
...searchResult.hits.hits[0]._source.state.savedObjectsClientError,
|
||||||
|
|
|
@ -186,6 +186,7 @@ export default function({ getService }: FtrProviderContext) {
|
||||||
const indexedRecord = searchResult.hits.hits[0];
|
const indexedRecord = searchResult.hits.hits[0];
|
||||||
expect(indexedRecord._source.state).to.eql({
|
expect(indexedRecord._source.state).to.eql({
|
||||||
callClusterSuccess: true,
|
callClusterSuccess: true,
|
||||||
|
callScopedClusterSuccess: true,
|
||||||
savedObjectsClientSuccess: false,
|
savedObjectsClientSuccess: false,
|
||||||
savedObjectsClientError: {
|
savedObjectsClientError: {
|
||||||
...indexedRecord._source.state.savedObjectsClientError,
|
...indexedRecord._source.state.savedObjectsClientError,
|
||||||
|
|
|
@ -277,6 +277,7 @@ instanceStateValue: true
|
||||||
)[0];
|
)[0];
|
||||||
expect(alertTestRecord._source.state).to.eql({
|
expect(alertTestRecord._source.state).to.eql({
|
||||||
callClusterSuccess: true,
|
callClusterSuccess: true,
|
||||||
|
callScopedClusterSuccess: true,
|
||||||
savedObjectsClientSuccess: false,
|
savedObjectsClientSuccess: false,
|
||||||
savedObjectsClientError: {
|
savedObjectsClientError: {
|
||||||
...alertTestRecord._source.state.savedObjectsClientError,
|
...alertTestRecord._source.state.savedObjectsClientError,
|
||||||
|
@ -332,6 +333,7 @@ instanceStateValue: true
|
||||||
)[0];
|
)[0];
|
||||||
expect(actionTestRecord._source.state).to.eql({
|
expect(actionTestRecord._source.state).to.eql({
|
||||||
callClusterSuccess: true,
|
callClusterSuccess: true,
|
||||||
|
callScopedClusterSuccess: true,
|
||||||
savedObjectsClientSuccess: false,
|
savedObjectsClientSuccess: false,
|
||||||
savedObjectsClientError: {
|
savedObjectsClientError: {
|
||||||
...actionTestRecord._source.state.savedObjectsClientError,
|
...actionTestRecord._source.state.savedObjectsClientError,
|
||||||
|
|
Loading…
Reference in a new issue