mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Alerting] Remove predefined connectors from rule reference array (#109437)
* Exposing preconfigured connectors through actions setup contract * Adding stub for migration using preconfigured connectors * Adding isPreconfigured fn to actions client * Updating rules client logic to not extract predefined connector ids * Functional tests * Adding migration * Adding functional test for migration * Adding functional test for migration * Adding note to docs about referenced_by_count if is_preconfigured * Fixing functional test * Changing to isPreconfiguredConnector fn in actions plugin setup contract * Update docs/api/actions-and-connectors/get_all.asciidoc Co-authored-by: Mike Côté <mikecote@users.noreply.github.com> Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Mike Côté <mikecote@users.noreply.github.com>
This commit is contained in:
parent
3854d3a586
commit
a3d03ecbdf
20 changed files with 1475 additions and 66 deletions
|
@ -44,7 +44,7 @@ The API returns the following:
|
|||
"connector_type_id": ".email",
|
||||
"name": "email: preconfigured-mail-connector",
|
||||
"is_preconfigured": true,
|
||||
"referenced_by_count": 1
|
||||
"referenced_by_count": 0 <1>
|
||||
},
|
||||
{
|
||||
"id": "c55b6eb0-6bad-11eb-9f3b-611eebc6c3ad",
|
||||
|
@ -61,3 +61,5 @@ The API returns the following:
|
|||
}
|
||||
]
|
||||
--------------------------------------------------
|
||||
|
||||
<1> `referenced_by_count` - The number of saved-objects referencing this connector. This value is not calculated if `is_preconfigured: true`.
|
|
@ -24,6 +24,7 @@ const createActionsClientMock = () => {
|
|||
ephemeralEnqueuedExecution: jest.fn(),
|
||||
listTypes: jest.fn(),
|
||||
isActionTypeEnabled: jest.fn(),
|
||||
isPreconfigured: jest.fn(),
|
||||
};
|
||||
return mocked;
|
||||
};
|
||||
|
|
|
@ -1823,3 +1823,65 @@ describe('isActionTypeEnabled()', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isPreconfigured()', () => {
|
||||
test('should return true if connector id is in list of preconfigured connectors', () => {
|
||||
actionsClient = new ActionsClient({
|
||||
actionTypeRegistry,
|
||||
unsecuredSavedObjectsClient,
|
||||
scopedClusterClient,
|
||||
defaultKibanaIndex,
|
||||
actionExecutor,
|
||||
executionEnqueuer,
|
||||
ephemeralExecutionEnqueuer,
|
||||
request,
|
||||
authorization: (authorization as unknown) as ActionsAuthorization,
|
||||
preconfiguredActions: [
|
||||
{
|
||||
id: 'testPreconfigured',
|
||||
actionTypeId: 'my-action-type',
|
||||
secrets: {
|
||||
test: 'test1',
|
||||
},
|
||||
isPreconfigured: true,
|
||||
name: 'test',
|
||||
config: {
|
||||
foo: 'bar',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(actionsClient.isPreconfigured('testPreconfigured')).toEqual(true);
|
||||
});
|
||||
|
||||
test('should return false if connector id is not in list of preconfigured connectors', () => {
|
||||
actionsClient = new ActionsClient({
|
||||
actionTypeRegistry,
|
||||
unsecuredSavedObjectsClient,
|
||||
scopedClusterClient,
|
||||
defaultKibanaIndex,
|
||||
actionExecutor,
|
||||
executionEnqueuer,
|
||||
ephemeralExecutionEnqueuer,
|
||||
request,
|
||||
authorization: (authorization as unknown) as ActionsAuthorization,
|
||||
preconfiguredActions: [
|
||||
{
|
||||
id: 'testPreconfigured',
|
||||
actionTypeId: 'my-action-type',
|
||||
secrets: {
|
||||
test: 'test1',
|
||||
},
|
||||
isPreconfigured: true,
|
||||
name: 'test',
|
||||
config: {
|
||||
foo: 'bar',
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(actionsClient.isPreconfigured(uuid.v4())).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -523,6 +523,10 @@ export class ActionsClient {
|
|||
) {
|
||||
return this.actionTypeRegistry.isActionTypeEnabled(actionTypeId, options);
|
||||
}
|
||||
|
||||
public isPreconfigured(connectorId: string): boolean {
|
||||
return !!this.preconfiguredActions.find((preconfigured) => preconfigured.id === connectorId);
|
||||
}
|
||||
}
|
||||
|
||||
function actionFromSavedObject(savedObject: SavedObject<RawAction>): ActionResult {
|
||||
|
|
|
@ -19,6 +19,7 @@ export { actionsClientMock };
|
|||
const createSetupMock = () => {
|
||||
const mock: jest.Mocked<PluginSetupContract> = {
|
||||
registerType: jest.fn(),
|
||||
isPreconfiguredConnector: jest.fn(),
|
||||
};
|
||||
return mock;
|
||||
};
|
||||
|
|
|
@ -185,6 +185,62 @@ describe('Actions Plugin', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isPreconfiguredConnector', () => {
|
||||
function getConfig(overrides = {}) {
|
||||
return {
|
||||
enabled: true,
|
||||
enabledActionTypes: ['*'],
|
||||
allowedHosts: ['*'],
|
||||
preconfiguredAlertHistoryEsIndex: false,
|
||||
preconfigured: {
|
||||
preconfiguredServerLog: {
|
||||
actionTypeId: '.server-log',
|
||||
name: 'preconfigured-server-log',
|
||||
config: {},
|
||||
secrets: {},
|
||||
},
|
||||
},
|
||||
proxyRejectUnauthorizedCertificates: true,
|
||||
proxyBypassHosts: undefined,
|
||||
proxyOnlyHosts: undefined,
|
||||
rejectUnauthorized: true,
|
||||
maxResponseContentLength: new ByteSizeValue(1000000),
|
||||
responseTimeout: moment.duration('60s'),
|
||||
cleanupFailedExecutionsTask: {
|
||||
enabled: true,
|
||||
cleanupInterval: schema.duration().validate('5m'),
|
||||
idleInterval: schema.duration().validate('1h'),
|
||||
pageSize: 100,
|
||||
},
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function setup(config: ActionsConfig) {
|
||||
context = coreMock.createPluginInitializerContext<ActionsConfig>(config);
|
||||
plugin = new ActionsPlugin(context);
|
||||
coreSetup = coreMock.createSetup();
|
||||
pluginsSetup = {
|
||||
taskManager: taskManagerMock.createSetup(),
|
||||
encryptedSavedObjects: encryptedSavedObjectsMock.createSetup(),
|
||||
licensing: licensingMock.createSetup(),
|
||||
eventLog: eventLogMock.createSetup(),
|
||||
usageCollection: usageCollectionPluginMock.createSetupContract(),
|
||||
features: featuresPluginMock.createSetup(),
|
||||
};
|
||||
}
|
||||
|
||||
it('should correctly return whether connector is preconfigured', async () => {
|
||||
setup(getConfig());
|
||||
// coreMock.createSetup doesn't support Plugin generics
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const pluginSetup = await plugin.setup(coreSetup as any, pluginsSetup);
|
||||
|
||||
expect(pluginSetup.isPreconfiguredConnector('preconfiguredServerLog')).toEqual(true);
|
||||
expect(pluginSetup.isPreconfiguredConnector('anotherConnectorId')).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('start()', () => {
|
||||
|
|
|
@ -96,6 +96,7 @@ export interface PluginSetupContract {
|
|||
>(
|
||||
actionType: ActionType<Config, Secrets, Params, ExecutorResultData>
|
||||
): void;
|
||||
isPreconfiguredConnector(connectorId: string): boolean;
|
||||
}
|
||||
|
||||
export interface PluginStartContract {
|
||||
|
@ -289,6 +290,11 @@ export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartCon
|
|||
ensureSufficientLicense(actionType);
|
||||
actionTypeRegistry.register(actionType);
|
||||
},
|
||||
isPreconfiguredConnector: (connectorId: string): boolean => {
|
||||
return !!this.preconfiguredActions.find(
|
||||
(preconfigured) => preconfigured.id === connectorId
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -228,7 +228,8 @@ export class AlertingPlugin {
|
|||
core.savedObjects,
|
||||
plugins.encryptedSavedObjects,
|
||||
this.ruleTypeRegistry,
|
||||
this.logger
|
||||
this.logger,
|
||||
plugins.actions.isPreconfiguredConnector
|
||||
);
|
||||
|
||||
initializeApiKeyInvalidator(
|
||||
|
|
|
@ -189,6 +189,9 @@ export interface GetAlertInstanceSummaryParams {
|
|||
// NOTE: Changing this prefix will require a migration to update the prefix in all existing `rule` saved objects
|
||||
const extractedSavedObjectParamReferenceNamePrefix = 'param:';
|
||||
|
||||
// NOTE: Changing this prefix will require a migration to update the prefix in all existing `rule` saved objects
|
||||
const preconfiguredConnectorActionRefPrefix = 'preconfigured:';
|
||||
|
||||
const alertingAuthorizationFilterOpts: AlertingAuthorizationFilterOpts = {
|
||||
type: AlertingAuthorizationFilterType.KQL,
|
||||
fieldNames: { ruleTypeId: 'alert.attributes.alertTypeId', consumer: 'alert.attributes.consumer' },
|
||||
|
@ -1508,6 +1511,13 @@ export class RulesClient {
|
|||
references: SavedObjectReference[]
|
||||
) {
|
||||
return actions.map((action) => {
|
||||
if (action.actionRef.startsWith(preconfiguredConnectorActionRefPrefix)) {
|
||||
return {
|
||||
...omit(action, 'actionRef'),
|
||||
id: action.actionRef.replace(preconfiguredConnectorActionRefPrefix, ''),
|
||||
};
|
||||
}
|
||||
|
||||
const reference = references.find((ref) => ref.name === action.actionRef);
|
||||
if (!reference) {
|
||||
throw new Error(`Action reference "${action.actionRef}" not found in alert id: ${alertId}`);
|
||||
|
@ -1700,17 +1710,25 @@ export class RulesClient {
|
|||
alertActions.forEach(({ id, ...alertAction }, i) => {
|
||||
const actionResultValue = actionResults.find((action) => action.id === id);
|
||||
if (actionResultValue) {
|
||||
const actionRef = `action_${i}`;
|
||||
references.push({
|
||||
id,
|
||||
name: actionRef,
|
||||
type: 'action',
|
||||
});
|
||||
actions.push({
|
||||
...alertAction,
|
||||
actionRef,
|
||||
actionTypeId: actionResultValue.actionTypeId,
|
||||
});
|
||||
if (actionsClient.isPreconfigured(id)) {
|
||||
actions.push({
|
||||
...alertAction,
|
||||
actionRef: `${preconfiguredConnectorActionRefPrefix}${id}`,
|
||||
actionTypeId: actionResultValue.actionTypeId,
|
||||
});
|
||||
} else {
|
||||
const actionRef = `action_${i}`;
|
||||
references.push({
|
||||
id,
|
||||
name: actionRef,
|
||||
type: 'action',
|
||||
});
|
||||
actions.push({
|
||||
...alertAction,
|
||||
actionRef,
|
||||
actionTypeId: actionResultValue.actionTypeId,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
actions.push({
|
||||
...alertAction,
|
||||
|
|
|
@ -791,6 +791,253 @@ describe('create()', () => {
|
|||
`);
|
||||
});
|
||||
|
||||
test('creates a rule with some actions using preconfigured connectors', async () => {
|
||||
const data = getMockData({
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
id: '1',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
group: 'default',
|
||||
id: 'preconfigured',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
group: 'default',
|
||||
id: '2',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
actionsClient.getBulk.mockReset();
|
||||
actionsClient.getBulk.mockResolvedValue([
|
||||
{
|
||||
id: '1',
|
||||
actionTypeId: 'test',
|
||||
config: {
|
||||
from: 'me@me.com',
|
||||
hasAuth: false,
|
||||
host: 'hello',
|
||||
port: 22,
|
||||
secure: null,
|
||||
service: null,
|
||||
},
|
||||
isMissingSecrets: false,
|
||||
name: 'email connector',
|
||||
isPreconfigured: false,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
actionTypeId: 'test2',
|
||||
config: {
|
||||
from: 'me@me.com',
|
||||
hasAuth: false,
|
||||
host: 'hello',
|
||||
port: 22,
|
||||
secure: null,
|
||||
service: null,
|
||||
},
|
||||
isMissingSecrets: false,
|
||||
name: 'another email connector',
|
||||
isPreconfigured: false,
|
||||
},
|
||||
{
|
||||
id: 'preconfigured',
|
||||
actionTypeId: 'test',
|
||||
config: {
|
||||
from: 'me@me.com',
|
||||
hasAuth: false,
|
||||
host: 'hello',
|
||||
port: 22,
|
||||
secure: null,
|
||||
service: null,
|
||||
},
|
||||
isMissingSecrets: false,
|
||||
name: 'preconfigured email connector',
|
||||
isPreconfigured: true,
|
||||
},
|
||||
]);
|
||||
actionsClient.isPreconfigured.mockReset();
|
||||
actionsClient.isPreconfigured.mockReturnValueOnce(false);
|
||||
actionsClient.isPreconfigured.mockReturnValueOnce(true);
|
||||
actionsClient.isPreconfigured.mockReturnValueOnce(false);
|
||||
unsecuredSavedObjectsClient.create.mockResolvedValueOnce({
|
||||
id: '1',
|
||||
type: 'alert',
|
||||
attributes: {
|
||||
alertTypeId: '123',
|
||||
schedule: { interval: '10s' },
|
||||
params: {
|
||||
bar: true,
|
||||
},
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
notifyWhen: 'onActiveAlert',
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
actionRef: 'action_0',
|
||||
actionTypeId: 'test',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
group: 'default',
|
||||
actionRef: 'preconfigured:preconfigured',
|
||||
actionTypeId: 'test',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
group: 'default',
|
||||
actionRef: 'action_2',
|
||||
actionTypeId: 'test2',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
references: [
|
||||
{
|
||||
name: 'action_0',
|
||||
type: 'action',
|
||||
id: '1',
|
||||
},
|
||||
{
|
||||
name: 'action_2',
|
||||
type: 'action',
|
||||
id: '2',
|
||||
},
|
||||
],
|
||||
});
|
||||
unsecuredSavedObjectsClient.create.mockResolvedValueOnce({
|
||||
id: '1',
|
||||
type: 'alert',
|
||||
attributes: {
|
||||
actions: [],
|
||||
scheduledTaskId: 'task-123',
|
||||
},
|
||||
references: [],
|
||||
});
|
||||
const result = await rulesClient.create({ data });
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"actions": Array [
|
||||
Object {
|
||||
"actionTypeId": "test",
|
||||
"group": "default",
|
||||
"id": "1",
|
||||
"params": Object {
|
||||
"foo": true,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"actionTypeId": "test",
|
||||
"group": "default",
|
||||
"id": "preconfigured",
|
||||
"params": Object {
|
||||
"foo": true,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"actionTypeId": "test2",
|
||||
"group": "default",
|
||||
"id": "2",
|
||||
"params": Object {
|
||||
"foo": true,
|
||||
},
|
||||
},
|
||||
],
|
||||
"alertTypeId": "123",
|
||||
"createdAt": 2019-02-12T21:01:22.479Z,
|
||||
"id": "1",
|
||||
"notifyWhen": "onActiveAlert",
|
||||
"params": Object {
|
||||
"bar": true,
|
||||
},
|
||||
"schedule": Object {
|
||||
"interval": "10s",
|
||||
},
|
||||
"scheduledTaskId": "task-123",
|
||||
"updatedAt": 2019-02-12T21:01:22.479Z,
|
||||
}
|
||||
`);
|
||||
expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith(
|
||||
'alert',
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
actionRef: 'action_0',
|
||||
actionTypeId: 'test',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
group: 'default',
|
||||
actionRef: 'preconfigured:preconfigured',
|
||||
actionTypeId: 'test',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
group: 'default',
|
||||
actionRef: 'action_2',
|
||||
actionTypeId: 'test2',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
alertTypeId: '123',
|
||||
apiKey: null,
|
||||
apiKeyOwner: null,
|
||||
consumer: 'bar',
|
||||
createdAt: '2019-02-12T21:01:22.479Z',
|
||||
createdBy: 'elastic',
|
||||
enabled: true,
|
||||
legacyId: null,
|
||||
executionStatus: {
|
||||
error: null,
|
||||
lastExecutionDate: '2019-02-12T21:01:22.479Z',
|
||||
status: 'pending',
|
||||
},
|
||||
meta: { versionApiKeyLastmodified: kibanaVersion },
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
name: 'abc',
|
||||
notifyWhen: 'onActiveAlert',
|
||||
params: { bar: true },
|
||||
schedule: { interval: '10s' },
|
||||
tags: ['foo'],
|
||||
throttle: null,
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
updatedBy: 'elastic',
|
||||
},
|
||||
{
|
||||
id: 'mock-saved-object-id',
|
||||
references: [
|
||||
{ id: '1', name: 'action_0', type: 'action' },
|
||||
{ id: '2', name: 'action_2', type: 'action' },
|
||||
],
|
||||
}
|
||||
);
|
||||
expect(actionsClient.isPreconfigured).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
test('creates a disabled alert', async () => {
|
||||
const data = getMockData({ enabled: false });
|
||||
unsecuredSavedObjectsClient.create.mockResolvedValueOnce({
|
||||
|
|
|
@ -185,6 +185,106 @@ describe('find()', () => {
|
|||
`);
|
||||
});
|
||||
|
||||
test('finds rules with actions using preconfigured connectors', async () => {
|
||||
unsecuredSavedObjectsClient.find.mockReset();
|
||||
unsecuredSavedObjectsClient.find.mockResolvedValueOnce({
|
||||
total: 1,
|
||||
per_page: 10,
|
||||
page: 1,
|
||||
saved_objects: [
|
||||
{
|
||||
id: '1',
|
||||
type: 'alert',
|
||||
attributes: {
|
||||
alertTypeId: 'myType',
|
||||
schedule: { interval: '10s' },
|
||||
params: {
|
||||
bar: true,
|
||||
},
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
notifyWhen: 'onActiveAlert',
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
actionRef: 'action_0',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
group: 'default',
|
||||
actionRef: 'preconfigured:preconfigured',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
score: 1,
|
||||
references: [
|
||||
{
|
||||
name: 'action_0',
|
||||
type: 'action',
|
||||
id: '1',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
const rulesClient = new RulesClient(rulesClientParams);
|
||||
const result = await rulesClient.find({ options: {} });
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"data": Array [
|
||||
Object {
|
||||
"actions": Array [
|
||||
Object {
|
||||
"group": "default",
|
||||
"id": "1",
|
||||
"params": Object {
|
||||
"foo": true,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"group": "default",
|
||||
"id": "preconfigured",
|
||||
"params": Object {
|
||||
"foo": true,
|
||||
},
|
||||
},
|
||||
],
|
||||
"alertTypeId": "myType",
|
||||
"createdAt": 2019-02-12T21:01:22.479Z,
|
||||
"id": "1",
|
||||
"notifyWhen": "onActiveAlert",
|
||||
"params": Object {
|
||||
"bar": true,
|
||||
},
|
||||
"schedule": Object {
|
||||
"interval": "10s",
|
||||
},
|
||||
"updatedAt": 2019-02-12T21:01:22.479Z,
|
||||
},
|
||||
],
|
||||
"page": 1,
|
||||
"perPage": 10,
|
||||
"total": 1,
|
||||
}
|
||||
`);
|
||||
expect(unsecuredSavedObjectsClient.find).toHaveBeenCalledTimes(1);
|
||||
expect(unsecuredSavedObjectsClient.find.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"fields": undefined,
|
||||
"filter": undefined,
|
||||
"sortField": undefined,
|
||||
"type": "alert",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('calls mapSortField', async () => {
|
||||
const rulesClient = new RulesClient(rulesClientParams);
|
||||
await rulesClient.find({ options: { sortField: 'name' } });
|
||||
|
|
|
@ -119,6 +119,86 @@ describe('get()', () => {
|
|||
`);
|
||||
});
|
||||
|
||||
test('gets rule with actions using preconfigured connectors', async () => {
|
||||
const rulesClient = new RulesClient(rulesClientParams);
|
||||
unsecuredSavedObjectsClient.get.mockResolvedValueOnce({
|
||||
id: '1',
|
||||
type: 'alert',
|
||||
attributes: {
|
||||
alertTypeId: '123',
|
||||
schedule: { interval: '10s' },
|
||||
params: {
|
||||
bar: true,
|
||||
},
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
actionRef: 'action_0',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
group: 'default',
|
||||
actionRef: 'preconfigured:preconfigured',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
notifyWhen: 'onActiveAlert',
|
||||
},
|
||||
references: [
|
||||
{
|
||||
name: 'action_0',
|
||||
type: 'action',
|
||||
id: '1',
|
||||
},
|
||||
],
|
||||
});
|
||||
const result = await rulesClient.get({ id: '1' });
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"actions": Array [
|
||||
Object {
|
||||
"group": "default",
|
||||
"id": "1",
|
||||
"params": Object {
|
||||
"foo": true,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"group": "default",
|
||||
"id": "preconfigured",
|
||||
"params": Object {
|
||||
"foo": true,
|
||||
},
|
||||
},
|
||||
],
|
||||
"alertTypeId": "123",
|
||||
"createdAt": 2019-02-12T21:01:22.479Z,
|
||||
"id": "1",
|
||||
"notifyWhen": "onActiveAlert",
|
||||
"params": Object {
|
||||
"bar": true,
|
||||
},
|
||||
"schedule": Object {
|
||||
"interval": "10s",
|
||||
},
|
||||
"updatedAt": 2019-02-12T21:01:22.479Z,
|
||||
}
|
||||
`);
|
||||
expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1);
|
||||
expect(unsecuredSavedObjectsClient.get.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"alert",
|
||||
"1",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('should call useSavedObjectReferences.injectReferences if defined for rule type', async () => {
|
||||
const injectReferencesFn = jest.fn().mockReturnValue({
|
||||
bar: true,
|
||||
|
|
|
@ -130,7 +130,10 @@ describe('update()', () => {
|
|||
ruleTypeRegistry.get.mockReturnValue({
|
||||
id: 'myType',
|
||||
name: 'Test',
|
||||
actionGroups: [{ id: 'default', name: 'Default' }],
|
||||
actionGroups: [
|
||||
{ id: 'default', name: 'Default' },
|
||||
{ id: 'custom', name: 'Not the Default' },
|
||||
],
|
||||
defaultActionGroupId: 'default',
|
||||
minimumLicenseRequired: 'basic',
|
||||
isExportable: true,
|
||||
|
@ -407,6 +410,252 @@ describe('update()', () => {
|
|||
expect(actionsClient.isActionTypeEnabled).toHaveBeenCalledWith('test2', { notifyUsage: true });
|
||||
});
|
||||
|
||||
test('should update a rule with some preconfigured actions', async () => {
|
||||
actionsClient.getBulk.mockReset();
|
||||
actionsClient.getBulk.mockResolvedValue([
|
||||
{
|
||||
id: '1',
|
||||
actionTypeId: 'test',
|
||||
config: {
|
||||
from: 'me@me.com',
|
||||
hasAuth: false,
|
||||
host: 'hello',
|
||||
port: 22,
|
||||
secure: null,
|
||||
service: null,
|
||||
},
|
||||
isMissingSecrets: false,
|
||||
name: 'email connector',
|
||||
isPreconfigured: false,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
actionTypeId: 'test2',
|
||||
config: {
|
||||
from: 'me@me.com',
|
||||
hasAuth: false,
|
||||
host: 'hello',
|
||||
port: 22,
|
||||
secure: null,
|
||||
service: null,
|
||||
},
|
||||
isMissingSecrets: false,
|
||||
name: 'another email connector',
|
||||
isPreconfigured: false,
|
||||
},
|
||||
{
|
||||
id: 'preconfigured',
|
||||
actionTypeId: 'test',
|
||||
config: {
|
||||
from: 'me@me.com',
|
||||
hasAuth: false,
|
||||
host: 'hello',
|
||||
port: 22,
|
||||
secure: null,
|
||||
service: null,
|
||||
},
|
||||
isMissingSecrets: false,
|
||||
name: 'preconfigured email connector',
|
||||
isPreconfigured: true,
|
||||
},
|
||||
]);
|
||||
actionsClient.isPreconfigured.mockReset();
|
||||
actionsClient.isPreconfigured.mockReturnValueOnce(false);
|
||||
actionsClient.isPreconfigured.mockReturnValueOnce(true);
|
||||
actionsClient.isPreconfigured.mockReturnValueOnce(true);
|
||||
unsecuredSavedObjectsClient.create.mockResolvedValueOnce({
|
||||
id: '1',
|
||||
type: 'alert',
|
||||
attributes: {
|
||||
enabled: true,
|
||||
schedule: { interval: '10s' },
|
||||
params: {
|
||||
bar: true,
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
actionRef: 'action_0',
|
||||
actionTypeId: 'test',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
group: 'default',
|
||||
actionRef: 'preconfigured:preconfigured',
|
||||
actionTypeId: 'test',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
group: 'custom',
|
||||
actionRef: 'preconfigured:preconfigured',
|
||||
actionTypeId: 'test',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
notifyWhen: 'onActiveAlert',
|
||||
scheduledTaskId: 'task-123',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
},
|
||||
references: [
|
||||
{
|
||||
name: 'action_0',
|
||||
type: 'action',
|
||||
id: '1',
|
||||
},
|
||||
{
|
||||
name: 'param:soRef_0',
|
||||
type: 'someSavedObjectType',
|
||||
id: '9',
|
||||
},
|
||||
],
|
||||
});
|
||||
const result = await rulesClient.update({
|
||||
id: '1',
|
||||
data: {
|
||||
schedule: { interval: '10s' },
|
||||
name: 'abc',
|
||||
tags: ['foo'],
|
||||
params: {
|
||||
bar: true,
|
||||
},
|
||||
throttle: null,
|
||||
notifyWhen: 'onActiveAlert',
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
id: '1',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
group: 'default',
|
||||
id: 'preconfigured',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
group: 'custom',
|
||||
id: 'preconfigured',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
expect(unsecuredSavedObjectsClient.create).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'alert',
|
||||
{
|
||||
actions: [
|
||||
{
|
||||
group: 'default',
|
||||
actionRef: 'action_0',
|
||||
actionTypeId: 'test',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
group: 'default',
|
||||
actionRef: 'preconfigured:preconfigured',
|
||||
actionTypeId: 'test',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
group: 'custom',
|
||||
actionRef: 'preconfigured:preconfigured',
|
||||
actionTypeId: 'test',
|
||||
params: {
|
||||
foo: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
alertTypeId: 'myType',
|
||||
apiKey: null,
|
||||
apiKeyOwner: null,
|
||||
consumer: 'myApp',
|
||||
enabled: true,
|
||||
meta: { versionApiKeyLastmodified: 'v7.10.0' },
|
||||
name: 'abc',
|
||||
notifyWhen: 'onActiveAlert',
|
||||
params: { bar: true },
|
||||
schedule: { interval: '10s' },
|
||||
scheduledTaskId: 'task-123',
|
||||
tags: ['foo'],
|
||||
throttle: null,
|
||||
updatedAt: '2019-02-12T21:01:22.479Z',
|
||||
updatedBy: 'elastic',
|
||||
},
|
||||
{
|
||||
id: '1',
|
||||
overwrite: true,
|
||||
references: [{ id: '1', name: 'action_0', type: 'action' }],
|
||||
version: '123',
|
||||
}
|
||||
);
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"actions": Array [
|
||||
Object {
|
||||
"actionTypeId": "test",
|
||||
"group": "default",
|
||||
"id": "1",
|
||||
"params": Object {
|
||||
"foo": true,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"actionTypeId": "test",
|
||||
"group": "default",
|
||||
"id": "preconfigured",
|
||||
"params": Object {
|
||||
"foo": true,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"actionTypeId": "test",
|
||||
"group": "custom",
|
||||
"id": "preconfigured",
|
||||
"params": Object {
|
||||
"foo": true,
|
||||
},
|
||||
},
|
||||
],
|
||||
"createdAt": 2019-02-12T21:01:22.479Z,
|
||||
"enabled": true,
|
||||
"id": "1",
|
||||
"notifyWhen": "onActiveAlert",
|
||||
"params": Object {
|
||||
"bar": true,
|
||||
},
|
||||
"schedule": Object {
|
||||
"interval": "10s",
|
||||
},
|
||||
"scheduledTaskId": "task-123",
|
||||
"updatedAt": 2019-02-12T21:01:22.479Z,
|
||||
}
|
||||
`);
|
||||
expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', {
|
||||
namespace: 'default',
|
||||
});
|
||||
expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled();
|
||||
expect(actionsClient.isPreconfigured).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
test('should call useSavedObjectReferences.extractReferences and useSavedObjectReferences.injectReferences if defined for rule type', async () => {
|
||||
const ruleParams = {
|
||||
bar: true,
|
||||
|
|
|
@ -20,7 +20,6 @@ import { RawAlert } from '../types';
|
|||
import { getImportWarnings } from './get_import_warnings';
|
||||
import { isRuleExportable } from './is_rule_exportable';
|
||||
import { RuleTypeRegistry } from '../rule_type_registry';
|
||||
|
||||
export { partiallyUpdateAlert } from './partially_update_alert';
|
||||
|
||||
export const AlertAttributesExcludedFromAAD = [
|
||||
|
@ -48,13 +47,14 @@ export function setupSavedObjects(
|
|||
savedObjects: SavedObjectsServiceSetup,
|
||||
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup,
|
||||
ruleTypeRegistry: RuleTypeRegistry,
|
||||
logger: Logger
|
||||
logger: Logger,
|
||||
isPreconfigured: (connectorId: string) => boolean
|
||||
) {
|
||||
savedObjects.registerType({
|
||||
name: 'alert',
|
||||
hidden: true,
|
||||
namespaceType: 'single',
|
||||
migrations: getMigrations(encryptedSavedObjects),
|
||||
migrations: getMigrations(encryptedSavedObjects, isPreconfigured),
|
||||
mappings: mappings.alert as SavedObjectsTypeMappingDefinition,
|
||||
management: {
|
||||
importableAndExportable: true,
|
||||
|
|
|
@ -15,6 +15,8 @@ import { migrationMocks } from 'src/core/server/mocks';
|
|||
const migrationContext = migrationMocks.createContext();
|
||||
const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup();
|
||||
|
||||
const isPreconfigured = jest.fn();
|
||||
|
||||
describe('successful migrations', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
|
@ -22,7 +24,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
describe('7.10.0', () => {
|
||||
test('marks alerts as legacy', () => {
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0'];
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
|
||||
const alert = getMockData({});
|
||||
expect(migration710(alert, migrationContext)).toMatchObject({
|
||||
...alert,
|
||||
|
@ -36,7 +38,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('migrates the consumer for metrics', () => {
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0'];
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
|
||||
const alert = getMockData({
|
||||
consumer: 'metrics',
|
||||
});
|
||||
|
@ -53,7 +55,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('migrates the consumer for siem', () => {
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0'];
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
|
||||
const alert = getMockData({
|
||||
consumer: 'securitySolution',
|
||||
});
|
||||
|
@ -70,7 +72,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('migrates the consumer for alerting', () => {
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0'];
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
|
||||
const alert = getMockData({
|
||||
consumer: 'alerting',
|
||||
});
|
||||
|
@ -87,7 +89,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('migrates PagerDuty actions to set a default dedupkey of the AlertId', () => {
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0'];
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
|
||||
const alert = getMockData({
|
||||
actions: [
|
||||
{
|
||||
|
@ -124,7 +126,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('skips PagerDuty actions with a specified dedupkey', () => {
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0'];
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
|
||||
const alert = getMockData({
|
||||
actions: [
|
||||
{
|
||||
|
@ -162,7 +164,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('skips PagerDuty actions with an eventAction of "trigger"', () => {
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0'];
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
|
||||
const alert = getMockData({
|
||||
actions: [
|
||||
{
|
||||
|
@ -201,7 +203,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('creates execution status', () => {
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0'];
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
|
||||
const alert = getMockData();
|
||||
const dateStart = Date.now();
|
||||
const migratedAlert = migration710(alert, migrationContext);
|
||||
|
@ -229,7 +231,7 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('7.11.0', () => {
|
||||
test('add updatedAt field to alert - set to SavedObject updated_at attribute', () => {
|
||||
const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const migration711 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.0'];
|
||||
const alert = getMockData({}, true);
|
||||
expect(migration711(alert, migrationContext)).toEqual({
|
||||
...alert,
|
||||
|
@ -242,7 +244,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('add updatedAt field to alert - set to createdAt when SavedObject updated_at is not defined', () => {
|
||||
const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const migration711 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.0'];
|
||||
const alert = getMockData({});
|
||||
expect(migration711(alert, migrationContext)).toEqual({
|
||||
...alert,
|
||||
|
@ -255,7 +257,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('add notifyWhen=onActiveAlert when throttle is null', () => {
|
||||
const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const migration711 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.0'];
|
||||
const alert = getMockData({});
|
||||
expect(migration711(alert, migrationContext)).toEqual({
|
||||
...alert,
|
||||
|
@ -268,7 +270,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('add notifyWhen=onActiveAlert when throttle is set', () => {
|
||||
const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const migration711 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.0'];
|
||||
const alert = getMockData({ throttle: '5m' });
|
||||
expect(migration711(alert, migrationContext)).toEqual({
|
||||
...alert,
|
||||
|
@ -283,7 +285,7 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('7.11.2', () => {
|
||||
test('transforms connectors that support incident correctly', () => {
|
||||
const migration7112 = getMigrations(encryptedSavedObjectsSetup)['7.11.2'];
|
||||
const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2'];
|
||||
const alert = getMockData({
|
||||
actions: [
|
||||
{
|
||||
|
@ -425,7 +427,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('it transforms only subAction=pushToService', () => {
|
||||
const migration7112 = getMigrations(encryptedSavedObjectsSetup)['7.11.2'];
|
||||
const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2'];
|
||||
const alert = getMockData({
|
||||
actions: [
|
||||
{
|
||||
|
@ -444,7 +446,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('it does not transforms other connectors', () => {
|
||||
const migration7112 = getMigrations(encryptedSavedObjectsSetup)['7.11.2'];
|
||||
const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2'];
|
||||
const alert = getMockData({
|
||||
actions: [
|
||||
{
|
||||
|
@ -523,7 +525,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('it does not transforms alerts when the right structure connectors is already applied', () => {
|
||||
const migration7112 = getMigrations(encryptedSavedObjectsSetup)['7.11.2'];
|
||||
const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2'];
|
||||
const alert = getMockData({
|
||||
actions: [
|
||||
{
|
||||
|
@ -560,7 +562,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('if incident attribute is an empty object, copy back the related attributes from subActionParams back to incident', () => {
|
||||
const migration7112 = getMigrations(encryptedSavedObjectsSetup)['7.11.2'];
|
||||
const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2'];
|
||||
const alert = getMockData({
|
||||
actions: [
|
||||
{
|
||||
|
@ -622,7 +624,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('custom action does not get migrated/loss', () => {
|
||||
const migration7112 = getMigrations(encryptedSavedObjectsSetup)['7.11.2'];
|
||||
const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2'];
|
||||
const alert = getMockData({
|
||||
actions: [
|
||||
{
|
||||
|
@ -651,7 +653,7 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('7.13.0', () => {
|
||||
test('security solution alerts get migrated and remove null values', () => {
|
||||
const migration713 = getMigrations(encryptedSavedObjectsSetup)['7.13.0'];
|
||||
const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0'];
|
||||
const alert = getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
params: {
|
||||
|
@ -747,7 +749,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('non-null values in security solution alerts are not modified', () => {
|
||||
const migration713 = getMigrations(encryptedSavedObjectsSetup)['7.13.0'];
|
||||
const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0'];
|
||||
const alert = getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
params: {
|
||||
|
@ -815,7 +817,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('security solution threshold alert with string in threshold.field is migrated to array', () => {
|
||||
const migration713 = getMigrations(encryptedSavedObjectsSetup)['7.13.0'];
|
||||
const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0'];
|
||||
const alert = getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
params: {
|
||||
|
@ -846,7 +848,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('security solution threshold alert with empty string in threshold.field is migrated to empty array', () => {
|
||||
const migration713 = getMigrations(encryptedSavedObjectsSetup)['7.13.0'];
|
||||
const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0'];
|
||||
const alert = getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
params: {
|
||||
|
@ -877,7 +879,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('security solution threshold alert with array in threshold.field and cardinality is left alone', () => {
|
||||
const migration713 = getMigrations(encryptedSavedObjectsSetup)['7.13.0'];
|
||||
const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0'];
|
||||
const alert = getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
params: {
|
||||
|
@ -919,7 +921,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('security solution ML alert with string in machineLearningJobId is converted to an array', () => {
|
||||
const migration713 = getMigrations(encryptedSavedObjectsSetup)['7.13.0'];
|
||||
const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0'];
|
||||
const alert = getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
params: {
|
||||
|
@ -945,7 +947,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('security solution ML alert with an array in machineLearningJobId is preserved', () => {
|
||||
const migration713 = getMigrations(encryptedSavedObjectsSetup)['7.13.0'];
|
||||
const migration713 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0'];
|
||||
const alert = getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
params: {
|
||||
|
@ -973,7 +975,7 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('7.14.1', () => {
|
||||
test('security solution author field is migrated to array if it is undefined', () => {
|
||||
const migration7141 = getMigrations(encryptedSavedObjectsSetup)['7.14.1'];
|
||||
const migration7141 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.14.1'];
|
||||
const alert = getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
params: {},
|
||||
|
@ -991,7 +993,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('security solution author field does not override existing values if they exist', () => {
|
||||
const migration7141 = getMigrations(encryptedSavedObjectsSetup)['7.14.1'];
|
||||
const migration7141 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.14.1'];
|
||||
const alert = getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
params: {
|
||||
|
@ -1015,7 +1017,7 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('7.15.0', () => {
|
||||
test('security solution is migrated to saved object references if it has 1 exceptionsList', () => {
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup)['7.15.0'];
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
|
||||
const alert = getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
params: {
|
||||
|
@ -1044,7 +1046,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('security solution is migrated to saved object references if it has 2 exceptionsLists', () => {
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup)['7.15.0'];
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
|
||||
const alert = getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
params: {
|
||||
|
@ -1084,7 +1086,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('security solution is migrated to saved object references if it has 3 exceptionsLists', () => {
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup)['7.15.0'];
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
|
||||
const alert = getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
params: {
|
||||
|
@ -1135,7 +1137,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('security solution does not change anything if exceptionsList is missing', () => {
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup)['7.15.0'];
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
|
||||
const alert = getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
params: {
|
||||
|
@ -1147,7 +1149,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('security solution will keep existing references if we do not have an exceptionsList but we do already have references', () => {
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup)['7.15.0'];
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
|
||||
const alert = {
|
||||
...getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
|
@ -1177,7 +1179,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('security solution keep any foreign references if they exist but still migrate other references', () => {
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup)['7.15.0'];
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
|
||||
const alert = {
|
||||
...getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
|
@ -1242,7 +1244,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('security solution is idempotent and if re-run on the same migrated data will keep the same items', () => {
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup)['7.15.0'];
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
|
||||
const alert = {
|
||||
...getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
|
@ -1282,7 +1284,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('security solution will migrate with only missing data if we have partially migrated data', () => {
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup)['7.15.0'];
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
|
||||
const alert = {
|
||||
...getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
|
@ -1331,7 +1333,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('security solution will not migrate if exception list if it is invalid data', () => {
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup)['7.15.0'];
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
|
||||
const alert = {
|
||||
...getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
|
@ -1345,7 +1347,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('security solution will migrate valid data if it is mixed with invalid data', () => {
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup)['7.15.0'];
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
|
||||
const alert = {
|
||||
...getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
|
@ -1387,7 +1389,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('security solution will not migrate if exception list is invalid data but will keep existing references', () => {
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup)['7.15.0'];
|
||||
const migration7150 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.15.0'];
|
||||
const alert = {
|
||||
...getMockData({
|
||||
alertTypeId: 'siem.signals',
|
||||
|
@ -1419,7 +1421,7 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('7.16.0', () => {
|
||||
test('add legacyId field to alert - set to SavedObject id attribute', () => {
|
||||
const migration716 = getMigrations(encryptedSavedObjectsSetup)['7.16.0'];
|
||||
const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
|
||||
const alert = getMockData({}, true);
|
||||
expect(migration716(alert, migrationContext)).toEqual({
|
||||
...alert,
|
||||
|
@ -1429,6 +1431,274 @@ describe('successful migrations', () => {
|
|||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('removes preconfigured connectors from references array', () => {
|
||||
isPreconfigured.mockReset();
|
||||
isPreconfigured.mockReturnValueOnce(true);
|
||||
isPreconfigured.mockReturnValueOnce(false);
|
||||
const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
|
||||
const rule = {
|
||||
...getMockData({
|
||||
actions: [
|
||||
{
|
||||
actionRef: 'action_0',
|
||||
actionTypeId: '.slack',
|
||||
group: 'small',
|
||||
params: {
|
||||
message: 'preconfigured',
|
||||
},
|
||||
},
|
||||
{
|
||||
actionRef: 'action_1',
|
||||
actionTypeId: '.server-log',
|
||||
group: 'small',
|
||||
params: {
|
||||
level: 'info',
|
||||
message: 'boo',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
references: [
|
||||
{
|
||||
id: 'my-slack1',
|
||||
name: 'action_0',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: '997c0120-00ee-11ec-b067-2524946ba327',
|
||||
name: 'action_1',
|
||||
type: 'action',
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(migration716(rule, migrationContext)).toEqual({
|
||||
...rule,
|
||||
attributes: {
|
||||
...rule.attributes,
|
||||
legacyId: rule.id,
|
||||
actions: [
|
||||
{
|
||||
actionRef: 'preconfigured:my-slack1',
|
||||
actionTypeId: '.slack',
|
||||
group: 'small',
|
||||
params: {
|
||||
message: 'preconfigured',
|
||||
},
|
||||
},
|
||||
{
|
||||
actionRef: 'action_1',
|
||||
actionTypeId: '.server-log',
|
||||
group: 'small',
|
||||
params: {
|
||||
level: 'info',
|
||||
message: 'boo',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
references: [
|
||||
{
|
||||
id: '997c0120-00ee-11ec-b067-2524946ba327',
|
||||
name: 'action_1',
|
||||
type: 'action',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('removes preconfigured connectors from references array and maintains non-action references', () => {
|
||||
isPreconfigured.mockReset();
|
||||
isPreconfigured.mockReturnValueOnce(true);
|
||||
isPreconfigured.mockReturnValueOnce(false);
|
||||
isPreconfigured.mockReturnValueOnce(false);
|
||||
const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
|
||||
const rule = {
|
||||
...getMockData({
|
||||
actions: [
|
||||
{
|
||||
actionRef: 'action_0',
|
||||
actionTypeId: '.slack',
|
||||
group: 'small',
|
||||
params: {
|
||||
message: 'preconfigured',
|
||||
},
|
||||
},
|
||||
{
|
||||
actionRef: 'action_1',
|
||||
actionTypeId: '.server-log',
|
||||
group: 'small',
|
||||
params: {
|
||||
level: 'info',
|
||||
message: 'boo',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
references: [
|
||||
{
|
||||
id: 'my-slack1',
|
||||
name: 'action_0',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: '997c0120-00ee-11ec-b067-2524946ba327',
|
||||
name: 'action_1',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: '3838d98c-67d3-49e8-b813-aa8404bb6b1a',
|
||||
name: 'params:something-else',
|
||||
type: 'some-other-type',
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(migration716(rule, migrationContext)).toEqual({
|
||||
...rule,
|
||||
attributes: {
|
||||
...rule.attributes,
|
||||
legacyId: rule.id,
|
||||
actions: [
|
||||
{
|
||||
actionRef: 'preconfigured:my-slack1',
|
||||
actionTypeId: '.slack',
|
||||
group: 'small',
|
||||
params: {
|
||||
message: 'preconfigured',
|
||||
},
|
||||
},
|
||||
{
|
||||
actionRef: 'action_1',
|
||||
actionTypeId: '.server-log',
|
||||
group: 'small',
|
||||
params: {
|
||||
level: 'info',
|
||||
message: 'boo',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
references: [
|
||||
{
|
||||
id: '997c0120-00ee-11ec-b067-2524946ba327',
|
||||
name: 'action_1',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: '3838d98c-67d3-49e8-b813-aa8404bb6b1a',
|
||||
name: 'params:something-else',
|
||||
type: 'some-other-type',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('does nothing to rules with no references', () => {
|
||||
isPreconfigured.mockReset();
|
||||
const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
|
||||
const rule = {
|
||||
...getMockData({
|
||||
actions: [
|
||||
{
|
||||
actionRef: 'action_0',
|
||||
actionTypeId: '.slack',
|
||||
group: 'small',
|
||||
params: {
|
||||
message: 'preconfigured',
|
||||
},
|
||||
},
|
||||
{
|
||||
actionRef: 'action_1',
|
||||
actionTypeId: '.server-log',
|
||||
group: 'small',
|
||||
params: {
|
||||
level: 'info',
|
||||
message: 'boo',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
references: [],
|
||||
};
|
||||
expect(migration716(rule, migrationContext)).toEqual({
|
||||
...rule,
|
||||
attributes: {
|
||||
...rule.attributes,
|
||||
legacyId: rule.id,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('does nothing to rules with no action references', () => {
|
||||
isPreconfigured.mockReset();
|
||||
const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
|
||||
const rule = {
|
||||
...getMockData({
|
||||
actions: [
|
||||
{
|
||||
actionRef: 'action_0',
|
||||
actionTypeId: '.slack',
|
||||
group: 'small',
|
||||
params: {
|
||||
message: 'preconfigured',
|
||||
},
|
||||
},
|
||||
{
|
||||
actionRef: 'action_1',
|
||||
actionTypeId: '.server-log',
|
||||
group: 'small',
|
||||
params: {
|
||||
level: 'info',
|
||||
message: 'boo',
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
references: [
|
||||
{
|
||||
id: '3838d98c-67d3-49e8-b813-aa8404bb6b1a',
|
||||
name: 'params:something-else',
|
||||
type: 'some-other-type',
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(migration716(rule, migrationContext)).toEqual({
|
||||
...rule,
|
||||
attributes: {
|
||||
...rule.attributes,
|
||||
legacyId: rule.id,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('does nothing to rules with references but no actions', () => {
|
||||
isPreconfigured.mockReset();
|
||||
const migration716 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
|
||||
const rule = {
|
||||
...getMockData({
|
||||
actions: [],
|
||||
}),
|
||||
references: [
|
||||
{
|
||||
id: 'my-slack1',
|
||||
name: 'action_0',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: '997c0120-00ee-11ec-b067-2524946ba327',
|
||||
name: 'action_1',
|
||||
type: 'action',
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(migration716(rule, migrationContext)).toEqual({
|
||||
...rule,
|
||||
attributes: {
|
||||
...rule.attributes,
|
||||
legacyId: rule.id,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1441,7 +1711,7 @@ describe('handles errors during migrations', () => {
|
|||
});
|
||||
describe('7.10.0 throws if migration fails', () => {
|
||||
test('should show the proper exception', () => {
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0'];
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.10.0'];
|
||||
const alert = getMockData({
|
||||
consumer: 'alerting',
|
||||
});
|
||||
|
@ -1466,7 +1736,7 @@ describe('handles errors during migrations', () => {
|
|||
|
||||
describe('7.11.0 throws if migration fails', () => {
|
||||
test('should show the proper exception', () => {
|
||||
const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const migration711 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.0'];
|
||||
const alert = getMockData({
|
||||
consumer: 'alerting',
|
||||
});
|
||||
|
@ -1491,7 +1761,7 @@ describe('handles errors during migrations', () => {
|
|||
|
||||
describe('7.11.2 throws if migration fails', () => {
|
||||
test('should show the proper exception', () => {
|
||||
const migration7112 = getMigrations(encryptedSavedObjectsSetup)['7.11.2'];
|
||||
const migration7112 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.11.2'];
|
||||
const alert = getMockData({
|
||||
consumer: 'alerting',
|
||||
});
|
||||
|
@ -1516,7 +1786,7 @@ describe('handles errors during migrations', () => {
|
|||
|
||||
describe('7.13.0 throws if migration fails', () => {
|
||||
test('should show the proper exception', () => {
|
||||
const migration7130 = getMigrations(encryptedSavedObjectsSetup)['7.13.0'];
|
||||
const migration7130 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.13.0'];
|
||||
const alert = getMockData({
|
||||
consumer: 'alerting',
|
||||
});
|
||||
|
@ -1538,6 +1808,29 @@ describe('handles errors during migrations', () => {
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('7.16.0 throws if migration fails', () => {
|
||||
test('should show the proper exception', () => {
|
||||
const migration7160 = getMigrations(encryptedSavedObjectsSetup, isPreconfigured)['7.16.0'];
|
||||
const rule = getMockData();
|
||||
expect(() => {
|
||||
migration7160(rule, migrationContext);
|
||||
}).toThrowError(`Can't migrate!`);
|
||||
expect(migrationContext.log.error).toHaveBeenCalledWith(
|
||||
`encryptedSavedObject 7.16.0 migration failed for alert ${rule.id} with error: Can't migrate!`,
|
||||
{
|
||||
migrations: {
|
||||
alertDocument: {
|
||||
...rule,
|
||||
attributes: {
|
||||
...rule.attributes,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function getUpdatedAt(): string {
|
||||
|
|
|
@ -55,7 +55,8 @@ export const isSecuritySolutionRule = (doc: SavedObjectUnsanitizedDoc<RawAlert>)
|
|||
doc.attributes.alertTypeId === 'siem.signals';
|
||||
|
||||
export function getMigrations(
|
||||
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup
|
||||
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup,
|
||||
isPreconfigured: (connectorId: string) => boolean
|
||||
): SavedObjectMigrationMap {
|
||||
const migrationWhenRBACWasIntroduced = createEsoMigration(
|
||||
encryptedSavedObjects,
|
||||
|
@ -99,10 +100,10 @@ export function getMigrations(
|
|||
pipeMigrations(addExceptionListsToReferences)
|
||||
);
|
||||
|
||||
const migrateLegacyIds716 = createEsoMigration(
|
||||
const migrateRules716 = createEsoMigration(
|
||||
encryptedSavedObjects,
|
||||
(doc): doc is SavedObjectUnsanitizedDoc<RawAlert> => true,
|
||||
pipeMigrations(setLegacyId)
|
||||
pipeMigrations(setLegacyId, getRemovePreconfiguredConnectorsFromReferencesFn(isPreconfigured))
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -112,7 +113,7 @@ export function getMigrations(
|
|||
'7.13.0': executeMigrationWithErrorHandling(migrationSecurityRules713, '7.13.0'),
|
||||
'7.14.1': executeMigrationWithErrorHandling(migrationSecurityRules714, '7.14.1'),
|
||||
'7.15.0': executeMigrationWithErrorHandling(migrationSecurityRules715, '7.15.0'),
|
||||
'7.16.0': executeMigrationWithErrorHandling(migrateLegacyIds716, '7.16.0'),
|
||||
'7.16.0': executeMigrationWithErrorHandling(migrateRules716, '7.16.0'),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -587,6 +588,82 @@ function setLegacyId(
|
|||
};
|
||||
}
|
||||
|
||||
function getRemovePreconfiguredConnectorsFromReferencesFn(
|
||||
isPreconfigured: (connectorId: string) => boolean
|
||||
) {
|
||||
return (doc: SavedObjectUnsanitizedDoc<RawAlert>) => {
|
||||
return removePreconfiguredConnectorsFromReferences(doc, isPreconfigured);
|
||||
};
|
||||
}
|
||||
|
||||
function removePreconfiguredConnectorsFromReferences(
|
||||
doc: SavedObjectUnsanitizedDoc<RawAlert>,
|
||||
isPreconfigured: (connectorId: string) => boolean
|
||||
): SavedObjectUnsanitizedDoc<RawAlert> {
|
||||
const {
|
||||
attributes: { actions },
|
||||
references,
|
||||
} = doc;
|
||||
|
||||
// Look for connector references
|
||||
const connectorReferences = (references ?? []).filter((ref: SavedObjectReference) =>
|
||||
ref.name.startsWith('action_')
|
||||
);
|
||||
if (connectorReferences.length > 0) {
|
||||
const restReferences = (references ?? []).filter(
|
||||
(ref: SavedObjectReference) => !ref.name.startsWith('action_')
|
||||
);
|
||||
|
||||
const updatedConnectorReferences: SavedObjectReference[] = [];
|
||||
const updatedActions: RawAlert['actions'] = [];
|
||||
|
||||
// For each connector reference, check if connector is preconfigured
|
||||
// If yes, we need to remove from the references array and update
|
||||
// the corresponding action so it directly references the preconfigured connector id
|
||||
connectorReferences.forEach((connectorRef: SavedObjectReference) => {
|
||||
// Look for the corresponding entry in the actions array
|
||||
const correspondingAction = getCorrespondingAction(actions, connectorRef.name);
|
||||
if (correspondingAction) {
|
||||
if (isPreconfigured(connectorRef.id)) {
|
||||
updatedActions.push({
|
||||
...correspondingAction,
|
||||
actionRef: `preconfigured:${connectorRef.id}`,
|
||||
});
|
||||
} else {
|
||||
updatedActions.push(correspondingAction);
|
||||
updatedConnectorReferences.push(connectorRef);
|
||||
}
|
||||
} else {
|
||||
// Couldn't find the matching action, leave as is
|
||||
updatedConnectorReferences.push(connectorRef);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
...doc,
|
||||
attributes: {
|
||||
...doc.attributes,
|
||||
actions: [...updatedActions],
|
||||
},
|
||||
references: [...updatedConnectorReferences, ...restReferences],
|
||||
};
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
function getCorrespondingAction(
|
||||
actions: SavedObjectAttribute,
|
||||
connectorRef: string
|
||||
): RawAlertAction | null {
|
||||
if (!Array.isArray(actions)) {
|
||||
return null;
|
||||
} else {
|
||||
return actions.find(
|
||||
(action) => (action as RawAlertAction)?.actionRef === connectorRef
|
||||
) as RawAlertAction;
|
||||
}
|
||||
}
|
||||
|
||||
function pipeMigrations(...migrations: AlertMigration[]): AlertMigration {
|
||||
return (doc: SavedObjectUnsanitizedDoc<RawAlert>) =>
|
||||
migrations.reduce((migratedDoc, nextMigration) => nextMigration(migratedDoc), doc);
|
||||
|
|
|
@ -204,7 +204,7 @@ export default function getAllActionTests({ getService }: FtrProviderContext) {
|
|||
is_preconfigured: true,
|
||||
connector_type_id: '.slack',
|
||||
name: 'Slack#xyz',
|
||||
referenced_by_count: 1,
|
||||
referenced_by_count: 0,
|
||||
},
|
||||
{
|
||||
id: 'custom-system-abc-connector',
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import expect from '@kbn/expect';
|
||||
import type { ApiResponse, estypes } from '@elastic/elasticsearch';
|
||||
import { SavedObject } from 'kibana/server';
|
||||
import { Spaces } from '../../scenarios';
|
||||
import {
|
||||
checkAAD,
|
||||
|
@ -17,6 +18,7 @@ import {
|
|||
TaskManagerDoc,
|
||||
} from '../../../common/lib';
|
||||
import { FtrProviderContext } from '../../../common/ftr_provider_context';
|
||||
import { RawAlert } from '../../../../../plugins/alerting/server/types';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function createAlertTests({ getService }: FtrProviderContext) {
|
||||
|
@ -115,6 +117,112 @@ export default function createAlertTests({ getService }: FtrProviderContext) {
|
|||
});
|
||||
});
|
||||
|
||||
it('should store references correctly for actions', async () => {
|
||||
const { body: createdAction } = await supertest
|
||||
.post(`${getUrlPrefix(Spaces.space1.id)}/api/actions/connector`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send({
|
||||
name: 'MY action',
|
||||
connector_type_id: 'test.noop',
|
||||
config: {},
|
||||
secrets: {},
|
||||
})
|
||||
.expect(200);
|
||||
|
||||
const response = await supertest
|
||||
.post(`${getUrlPrefix(Spaces.space1.id)}/api/alerting/rule`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.send(
|
||||
getTestAlertData({
|
||||
actions: [
|
||||
{
|
||||
id: createdAction.id,
|
||||
group: 'default',
|
||||
params: {},
|
||||
},
|
||||
{
|
||||
id: 'my-slack1',
|
||||
group: 'default',
|
||||
params: {
|
||||
message: 'something important happened!',
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
expect(response.status).to.eql(200);
|
||||
objectRemover.add(Spaces.space1.id, response.body.id, 'rule', 'alerting');
|
||||
expect(response.body).to.eql({
|
||||
id: response.body.id,
|
||||
name: 'abc',
|
||||
tags: ['foo'],
|
||||
actions: [
|
||||
{
|
||||
id: createdAction.id,
|
||||
connector_type_id: createdAction.connector_type_id,
|
||||
group: 'default',
|
||||
params: {},
|
||||
},
|
||||
{
|
||||
id: 'my-slack1',
|
||||
group: 'default',
|
||||
connector_type_id: '.slack',
|
||||
params: {
|
||||
message: 'something important happened!',
|
||||
},
|
||||
},
|
||||
],
|
||||
enabled: true,
|
||||
rule_type_id: 'test.noop',
|
||||
consumer: 'alertsFixture',
|
||||
params: {},
|
||||
created_by: null,
|
||||
schedule: { interval: '1m' },
|
||||
scheduled_task_id: response.body.scheduled_task_id,
|
||||
updated_by: null,
|
||||
api_key_owner: null,
|
||||
throttle: '1m',
|
||||
notify_when: 'onThrottleInterval',
|
||||
mute_all: false,
|
||||
muted_alert_ids: [],
|
||||
created_at: response.body.created_at,
|
||||
updated_at: response.body.updated_at,
|
||||
execution_status: response.body.execution_status,
|
||||
});
|
||||
|
||||
const esResponse = await es.get<SavedObject<RawAlert>>({
|
||||
index: '.kibana',
|
||||
id: `${Spaces.space1.id}:alert:${response.body.id}`,
|
||||
});
|
||||
expect(esResponse.statusCode).to.eql(200);
|
||||
const rawActions = (esResponse.body._source as any)?.alert.actions ?? [];
|
||||
expect(rawActions).to.eql([
|
||||
{
|
||||
actionRef: 'action_0',
|
||||
actionTypeId: 'test.noop',
|
||||
group: 'default',
|
||||
params: {},
|
||||
},
|
||||
{
|
||||
actionRef: 'preconfigured:my-slack1',
|
||||
actionTypeId: '.slack',
|
||||
group: 'default',
|
||||
params: {
|
||||
message: 'something important happened!',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const references = esResponse.body._source?.references ?? [];
|
||||
expect(references.length).to.eql(1);
|
||||
expect(references[0]).to.eql({
|
||||
id: createdAction.id,
|
||||
name: 'action_0',
|
||||
type: 'action',
|
||||
});
|
||||
});
|
||||
|
||||
// see: https://github.com/elastic/kibana/issues/100607
|
||||
// note this fails when the mappings for `params` does not have ignore_above
|
||||
it('should handle alerts with immense params', async () => {
|
||||
|
|
|
@ -9,7 +9,7 @@ import expect from '@kbn/expect';
|
|||
import type { ApiResponse, estypes } from '@elastic/elasticsearch';
|
||||
import { getUrlPrefix } from '../../../common/lib';
|
||||
import { FtrProviderContext } from '../../../common/ftr_provider_context';
|
||||
import type { RawAlert } from '../../../../../plugins/alerting/server/types';
|
||||
import type { RawAlert, RawAlertAction } from '../../../../../plugins/alerting/server/types';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function createGetTests({ getService }: FtrProviderContext) {
|
||||
|
@ -218,5 +218,44 @@ export default function createGetTests({ getService }: FtrProviderContext) {
|
|||
'74f3e6d7-b7bb-477d-ac28-92ee22728e6e'
|
||||
);
|
||||
});
|
||||
|
||||
it('7.16.0 migrates existing rules so predefined connectors are not stored in references', async () => {
|
||||
const searchResult: ApiResponse<estypes.SearchResponse<RawAlert>> = await es.search({
|
||||
index: '.kibana',
|
||||
body: {
|
||||
query: {
|
||||
term: {
|
||||
_id: 'alert:9c003b00-00ee-11ec-b067-2524946ba327',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(searchResult.statusCode).to.equal(200);
|
||||
expect((searchResult.body.hits.total as estypes.SearchTotalHits).value).to.equal(1);
|
||||
const hit = searchResult.body.hits.hits[0];
|
||||
expect((hit!._source!.alert! as RawAlert).actions! as RawAlertAction[]).to.eql([
|
||||
{
|
||||
actionRef: 'action_0',
|
||||
actionTypeId: 'test.noop',
|
||||
group: 'default',
|
||||
params: {},
|
||||
},
|
||||
{
|
||||
actionRef: 'preconfigured:my-slack1',
|
||||
actionTypeId: '.slack',
|
||||
group: 'default',
|
||||
params: {
|
||||
message: 'something happened!',
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(hit!._source!.references!).to.eql([
|
||||
{
|
||||
id: '66a8ab7a-35cf-445e-ade3-215a029c6969',
|
||||
name: 'action_0',
|
||||
type: 'action',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -387,3 +387,68 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"id": "alert:9c003b00-00ee-11ec-b067-2524946ba327",
|
||||
"index": ".kibana_1",
|
||||
"source": {
|
||||
"alert": {
|
||||
"actions": [{
|
||||
"actionRef": "action_0",
|
||||
"actionTypeId": "test.noop",
|
||||
"group": "default",
|
||||
"params": {
|
||||
}
|
||||
},
|
||||
{
|
||||
"actionRef": "action_1",
|
||||
"actionTypeId": ".slack",
|
||||
"group": "default",
|
||||
"params": {
|
||||
"message": "something happened!"
|
||||
}
|
||||
}],
|
||||
"alertTypeId": "test.noop",
|
||||
"apiKey": null,
|
||||
"apiKeyOwner": null,
|
||||
"consumer": "alertsFixture",
|
||||
"createdAt": "2020-09-22T15:16:07.451Z",
|
||||
"createdBy": null,
|
||||
"enabled": true,
|
||||
"muteAll": false,
|
||||
"mutedInstanceIds": [
|
||||
],
|
||||
"name": "test migration of preconfigured connector",
|
||||
"params": {
|
||||
},
|
||||
"schedule": {
|
||||
"interval": "1m"
|
||||
},
|
||||
"scheduledTaskId": "329798f0-b0b0-11ea-9510-fdf248d5f2a4",
|
||||
"tags": [
|
||||
],
|
||||
"throttle": null,
|
||||
"updatedBy": "elastic"
|
||||
},
|
||||
"migrationVersion": {
|
||||
"alert": "7.15.0"
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"id": "66a8ab7a-35cf-445e-ade3-215a029c6969",
|
||||
"name": "action_0",
|
||||
"type": "action"
|
||||
},
|
||||
{
|
||||
"id": "my-slack1",
|
||||
"name": "action_1",
|
||||
"type": "action"
|
||||
}
|
||||
],
|
||||
"type": "alert",
|
||||
"updated_at": "2020-06-17T15:35:39.839Z"
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue