* Extracting saved object references before saving action_task_params saved object * Injecting saved object ids from references when reading action_task_param * Adding migration * Adding unit test for migrations * Not differentiating between preconfigured or not * Adding functional test for migration * Skip extracting action id if action is preconfigured * Only migrating action task params for non preconfigured connectors * Simplifying related saved objects * Fixing functional test * Fixing migration * Javascript is sometimes magical * Updating functional test * PR feedback Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: ymao1 <ying.mao@elastic.co>
This commit is contained in:
parent
66ed66ae8b
commit
a0fd8e2089
|
@ -76,7 +76,15 @@ describe('execute()', () => {
|
|||
params: { baz: false },
|
||||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
},
|
||||
{}
|
||||
{
|
||||
references: [
|
||||
{
|
||||
id: '123',
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
expect(actionTypeRegistry.isActionExecutable).toHaveBeenCalledWith('123', 'mock-action', {
|
||||
notifyUsage: true,
|
||||
|
@ -128,14 +136,27 @@ describe('execute()', () => {
|
|||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
id: 'some-id',
|
||||
id: 'related_some-type_0',
|
||||
namespace: 'some-namespace',
|
||||
type: 'some-type',
|
||||
typeId: 'some-typeId',
|
||||
},
|
||||
],
|
||||
},
|
||||
{}
|
||||
{
|
||||
references: [
|
||||
{
|
||||
id: '123',
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: 'some-id',
|
||||
name: 'related_some-type_0',
|
||||
type: 'some-type',
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -214,6 +235,102 @@ describe('execute()', () => {
|
|||
);
|
||||
});
|
||||
|
||||
test('schedules the action with all given parameters with a preconfigured action and relatedSavedObjects', async () => {
|
||||
const executeFn = createExecutionEnqueuerFunction({
|
||||
taskManager: mockTaskManager,
|
||||
actionTypeRegistry: actionTypeRegistryMock.create(),
|
||||
isESOCanEncrypt: true,
|
||||
preconfiguredActions: [
|
||||
{
|
||||
id: '123',
|
||||
actionTypeId: 'mock-action-preconfigured',
|
||||
config: {},
|
||||
isPreconfigured: true,
|
||||
name: 'x',
|
||||
secrets: {},
|
||||
},
|
||||
],
|
||||
});
|
||||
const source = { type: 'alert', id: uuid.v4() };
|
||||
|
||||
savedObjectsClient.get.mockResolvedValueOnce({
|
||||
id: '123',
|
||||
type: 'action',
|
||||
attributes: {
|
||||
actionTypeId: 'mock-action',
|
||||
},
|
||||
references: [],
|
||||
});
|
||||
savedObjectsClient.create.mockResolvedValueOnce({
|
||||
id: '234',
|
||||
type: 'action_task_params',
|
||||
attributes: {},
|
||||
references: [],
|
||||
});
|
||||
await executeFn(savedObjectsClient, {
|
||||
id: '123',
|
||||
params: { baz: false },
|
||||
spaceId: 'default',
|
||||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
source: asSavedObjectExecutionSource(source),
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
id: 'some-id',
|
||||
namespace: 'some-namespace',
|
||||
type: 'some-type',
|
||||
typeId: 'some-typeId',
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(mockTaskManager.schedule).toHaveBeenCalledTimes(1);
|
||||
expect(mockTaskManager.schedule.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"params": Object {
|
||||
"actionTaskParamsId": "234",
|
||||
"spaceId": "default",
|
||||
},
|
||||
"scope": Array [
|
||||
"actions",
|
||||
],
|
||||
"state": Object {},
|
||||
"taskType": "actions:mock-action-preconfigured",
|
||||
},
|
||||
]
|
||||
`);
|
||||
expect(savedObjectsClient.get).not.toHaveBeenCalled();
|
||||
expect(savedObjectsClient.create).toHaveBeenCalledWith(
|
||||
'action_task_params',
|
||||
{
|
||||
actionId: '123',
|
||||
params: { baz: false },
|
||||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
id: 'related_some-type_0',
|
||||
namespace: 'some-namespace',
|
||||
type: 'some-type',
|
||||
typeId: 'some-typeId',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
references: [
|
||||
{
|
||||
id: source.id,
|
||||
name: 'source',
|
||||
type: source.type,
|
||||
},
|
||||
{
|
||||
id: 'some-id',
|
||||
name: 'related_some-type_0',
|
||||
type: 'some-type',
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('throws when passing isESOCanEncrypt with false as a value', async () => {
|
||||
const executeFn = createExecutionEnqueuerFunction({
|
||||
taskManager: mockTaskManager,
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
} from './types';
|
||||
import { ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE } from './constants/saved_objects';
|
||||
import { ExecuteOptions as ActionExecutorOptions } from './lib/action_executor';
|
||||
import { isSavedObjectExecutionSource } from './lib';
|
||||
import { extractSavedObjectReferences, isSavedObjectExecutionSource } from './lib';
|
||||
import { RelatedSavedObjects } from './lib/related_saved_objects';
|
||||
|
||||
interface CreateExecuteFunctionOptions {
|
||||
|
@ -53,7 +53,11 @@ export function createExecutionEnqueuerFunction({
|
|||
);
|
||||
}
|
||||
|
||||
const action = await getAction(unsecuredSavedObjectsClient, preconfiguredActions, id);
|
||||
const { action, isPreconfigured } = await getAction(
|
||||
unsecuredSavedObjectsClient,
|
||||
preconfiguredActions,
|
||||
id
|
||||
);
|
||||
validateCanActionBeUsed(action);
|
||||
|
||||
const { actionTypeId } = action;
|
||||
|
@ -61,15 +65,33 @@ export function createExecutionEnqueuerFunction({
|
|||
actionTypeRegistry.ensureActionTypeEnabled(actionTypeId);
|
||||
}
|
||||
|
||||
// Get saved object references from action ID and relatedSavedObjects
|
||||
const { references, relatedSavedObjectWithRefs } = extractSavedObjectReferences(
|
||||
id,
|
||||
isPreconfigured,
|
||||
relatedSavedObjects
|
||||
);
|
||||
const executionSourceReference = executionSourceAsSavedObjectReferences(source);
|
||||
|
||||
const taskReferences = [];
|
||||
if (executionSourceReference.references) {
|
||||
taskReferences.push(...executionSourceReference.references);
|
||||
}
|
||||
if (references) {
|
||||
taskReferences.push(...references);
|
||||
}
|
||||
|
||||
const actionTaskParamsRecord = await unsecuredSavedObjectsClient.create(
|
||||
ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE,
|
||||
{
|
||||
actionId: id,
|
||||
params,
|
||||
apiKey,
|
||||
relatedSavedObjects,
|
||||
relatedSavedObjects: relatedSavedObjectWithRefs,
|
||||
},
|
||||
executionSourceAsSavedObjectReferences(source)
|
||||
{
|
||||
references: taskReferences,
|
||||
}
|
||||
);
|
||||
|
||||
await taskManager.schedule({
|
||||
|
@ -93,7 +115,7 @@ export function createEphemeralExecutionEnqueuerFunction({
|
|||
unsecuredSavedObjectsClient: SavedObjectsClientContract,
|
||||
{ id, params, spaceId, source, apiKey }: ExecuteOptions
|
||||
): Promise<RunNowResult> {
|
||||
const action = await getAction(unsecuredSavedObjectsClient, preconfiguredActions, id);
|
||||
const { action } = await getAction(unsecuredSavedObjectsClient, preconfiguredActions, id);
|
||||
validateCanActionBeUsed(action);
|
||||
|
||||
const { actionTypeId } = action;
|
||||
|
@ -148,12 +170,12 @@ async function getAction(
|
|||
unsecuredSavedObjectsClient: SavedObjectsClientContract,
|
||||
preconfiguredActions: PreConfiguredAction[],
|
||||
actionId: string
|
||||
): Promise<PreConfiguredAction | RawAction> {
|
||||
): Promise<{ action: PreConfiguredAction | RawAction; isPreconfigured: boolean }> {
|
||||
const pcAction = preconfiguredActions.find((action) => action.id === actionId);
|
||||
if (pcAction) {
|
||||
return pcAction;
|
||||
return { action: pcAction, isPreconfigured: true };
|
||||
}
|
||||
|
||||
const { attributes } = await unsecuredSavedObjectsClient.get<RawAction>('action', actionId);
|
||||
return attributes;
|
||||
return { action: attributes, isPreconfigured: false };
|
||||
}
|
||||
|
|
|
@ -0,0 +1,402 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
extractSavedObjectReferences,
|
||||
injectSavedObjectReferences,
|
||||
} from './action_task_params_utils';
|
||||
|
||||
describe('extractSavedObjectReferences()', () => {
|
||||
test('correctly extracts action id into references array', () => {
|
||||
expect(extractSavedObjectReferences('my-action-id', false)).toEqual({
|
||||
references: [
|
||||
{
|
||||
id: 'my-action-id',
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('correctly extracts related saved object into references array', () => {
|
||||
const relatedSavedObjects = [
|
||||
{
|
||||
id: 'abc',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeA',
|
||||
},
|
||||
{
|
||||
id: 'def',
|
||||
type: 'action',
|
||||
typeId: 'connectorTypeB',
|
||||
},
|
||||
{
|
||||
id: 'xyz',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeB',
|
||||
namespace: 'custom',
|
||||
},
|
||||
];
|
||||
|
||||
expect(extractSavedObjectReferences('my-action-id', false, relatedSavedObjects)).toEqual({
|
||||
references: [
|
||||
{
|
||||
id: 'my-action-id',
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: 'abc',
|
||||
name: 'related_alert_0',
|
||||
type: 'alert',
|
||||
},
|
||||
{
|
||||
id: 'def',
|
||||
name: 'related_action_1',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: 'xyz',
|
||||
name: 'related_alert_2',
|
||||
type: 'alert',
|
||||
},
|
||||
],
|
||||
relatedSavedObjectWithRefs: [
|
||||
{
|
||||
id: 'related_alert_0',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeA',
|
||||
},
|
||||
{
|
||||
id: 'related_action_1',
|
||||
type: 'action',
|
||||
typeId: 'connectorTypeB',
|
||||
},
|
||||
{
|
||||
id: 'related_alert_2',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeB',
|
||||
namespace: 'custom',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('correctly skips extracting action id if action is preconfigured', () => {
|
||||
expect(extractSavedObjectReferences('my-action-id', true)).toEqual({
|
||||
references: [],
|
||||
});
|
||||
});
|
||||
|
||||
test('correctly extracts related saved object into references array if isPreconfigured is true', () => {
|
||||
const relatedSavedObjects = [
|
||||
{
|
||||
id: 'abc',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeA',
|
||||
},
|
||||
{
|
||||
id: 'def',
|
||||
type: 'action',
|
||||
typeId: 'connectorTypeB',
|
||||
},
|
||||
{
|
||||
id: 'xyz',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeB',
|
||||
namespace: 'custom',
|
||||
},
|
||||
];
|
||||
|
||||
expect(extractSavedObjectReferences('my-action-id', true, relatedSavedObjects)).toEqual({
|
||||
references: [
|
||||
{
|
||||
id: 'abc',
|
||||
name: 'related_alert_0',
|
||||
type: 'alert',
|
||||
},
|
||||
{
|
||||
id: 'def',
|
||||
name: 'related_action_1',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: 'xyz',
|
||||
name: 'related_alert_2',
|
||||
type: 'alert',
|
||||
},
|
||||
],
|
||||
relatedSavedObjectWithRefs: [
|
||||
{
|
||||
id: 'related_alert_0',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeA',
|
||||
},
|
||||
{
|
||||
id: 'related_action_1',
|
||||
type: 'action',
|
||||
typeId: 'connectorTypeB',
|
||||
},
|
||||
{
|
||||
id: 'related_alert_2',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeB',
|
||||
namespace: 'custom',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('injectSavedObjectReferences()', () => {
|
||||
test('correctly returns action id from references array', () => {
|
||||
expect(
|
||||
injectSavedObjectReferences([
|
||||
{
|
||||
id: 'my-action-id',
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
])
|
||||
).toEqual({ actionId: 'my-action-id' });
|
||||
});
|
||||
|
||||
test('correctly returns undefined if no action id in references array', () => {
|
||||
expect(injectSavedObjectReferences([])).toEqual({});
|
||||
});
|
||||
|
||||
test('correctly injects related saved object ids from references array', () => {
|
||||
expect(
|
||||
injectSavedObjectReferences(
|
||||
[
|
||||
{
|
||||
id: 'my-action-id',
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: 'abc',
|
||||
name: 'related_alert_0',
|
||||
type: 'alert',
|
||||
},
|
||||
{
|
||||
id: 'def',
|
||||
name: 'related_action_1',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: 'xyz',
|
||||
name: 'related_alert_2',
|
||||
type: 'alert',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
id: 'related_alert_0',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeA',
|
||||
},
|
||||
{
|
||||
id: 'related_action_1',
|
||||
type: 'action',
|
||||
typeId: 'connectorTypeB',
|
||||
},
|
||||
{
|
||||
id: 'related_alert_2',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeB',
|
||||
namespace: 'custom',
|
||||
},
|
||||
]
|
||||
)
|
||||
).toEqual({
|
||||
actionId: 'my-action-id',
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
id: 'abc',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeA',
|
||||
},
|
||||
{
|
||||
id: 'def',
|
||||
type: 'action',
|
||||
typeId: 'connectorTypeB',
|
||||
},
|
||||
{
|
||||
id: 'xyz',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeB',
|
||||
namespace: 'custom',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('correctly injects related saved object ids from references array if no actionRef', () => {
|
||||
expect(
|
||||
injectSavedObjectReferences(
|
||||
[
|
||||
{
|
||||
id: 'abc',
|
||||
name: 'related_alert_0',
|
||||
type: 'alert',
|
||||
},
|
||||
{
|
||||
id: 'def',
|
||||
name: 'related_action_1',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: 'xyz',
|
||||
name: 'related_alert_2',
|
||||
type: 'alert',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
id: 'related_alert_0',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeA',
|
||||
},
|
||||
{
|
||||
id: 'related_action_1',
|
||||
type: 'action',
|
||||
typeId: 'connectorTypeB',
|
||||
},
|
||||
{
|
||||
id: 'related_alert_2',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeB',
|
||||
namespace: 'custom',
|
||||
},
|
||||
]
|
||||
)
|
||||
).toEqual({
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
id: 'abc',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeA',
|
||||
},
|
||||
{
|
||||
id: 'def',
|
||||
type: 'action',
|
||||
typeId: 'connectorTypeB',
|
||||
},
|
||||
{
|
||||
id: 'xyz',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeB',
|
||||
namespace: 'custom',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('correctly keeps related saved object ids if references array is empty', () => {
|
||||
expect(
|
||||
injectSavedObjectReferences(
|
||||
[],
|
||||
[
|
||||
{
|
||||
id: 'abc',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeA',
|
||||
},
|
||||
{
|
||||
id: 'def',
|
||||
type: 'action',
|
||||
typeId: 'connectorTypeB',
|
||||
},
|
||||
{
|
||||
id: 'xyz',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeB',
|
||||
namespace: 'custom',
|
||||
},
|
||||
]
|
||||
)
|
||||
).toEqual({
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
id: 'abc',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeA',
|
||||
},
|
||||
{
|
||||
id: 'def',
|
||||
type: 'action',
|
||||
typeId: 'connectorTypeB',
|
||||
},
|
||||
{
|
||||
id: 'xyz',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeB',
|
||||
namespace: 'custom',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('correctly skips injecting missing related saved object ids in references array', () => {
|
||||
expect(
|
||||
injectSavedObjectReferences(
|
||||
[
|
||||
{
|
||||
id: 'my-action-id',
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: 'abc',
|
||||
name: 'related_alert_0',
|
||||
type: 'alert',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
id: 'related_alert_0',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeA',
|
||||
},
|
||||
{
|
||||
id: 'def',
|
||||
type: 'action',
|
||||
typeId: 'connectorTypeB',
|
||||
},
|
||||
{
|
||||
id: 'xyz',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeB',
|
||||
namespace: 'custom',
|
||||
},
|
||||
]
|
||||
)
|
||||
).toEqual({
|
||||
actionId: 'my-action-id',
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
id: 'abc',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeA',
|
||||
},
|
||||
{
|
||||
id: 'def',
|
||||
type: 'action',
|
||||
typeId: 'connectorTypeB',
|
||||
},
|
||||
{
|
||||
id: 'xyz',
|
||||
type: 'alert',
|
||||
typeId: 'ruleTypeB',
|
||||
namespace: 'custom',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { SavedObjectAttribute, SavedObjectReference } from 'src/core/server/types';
|
||||
import { RelatedSavedObjects } from './related_saved_objects';
|
||||
|
||||
export const ACTION_REF_NAME = `actionRef`;
|
||||
|
||||
export function extractSavedObjectReferences(
|
||||
actionId: string,
|
||||
isPreconfigured: boolean,
|
||||
relatedSavedObjects?: RelatedSavedObjects
|
||||
): {
|
||||
references: SavedObjectReference[];
|
||||
relatedSavedObjectWithRefs?: RelatedSavedObjects;
|
||||
} {
|
||||
const references: SavedObjectReference[] = [];
|
||||
const relatedSavedObjectWithRefs: RelatedSavedObjects = [];
|
||||
|
||||
// Add action saved object to reference if it is not preconfigured
|
||||
if (!isPreconfigured) {
|
||||
references.push({
|
||||
id: actionId,
|
||||
name: ACTION_REF_NAME,
|
||||
type: 'action',
|
||||
});
|
||||
}
|
||||
|
||||
// Add related saved objects, if any
|
||||
(relatedSavedObjects ?? []).forEach((relatedSavedObject, index) => {
|
||||
relatedSavedObjectWithRefs.push({
|
||||
...relatedSavedObject,
|
||||
id: `related_${relatedSavedObject.type}_${index}`,
|
||||
});
|
||||
references.push({
|
||||
id: relatedSavedObject.id,
|
||||
name: `related_${relatedSavedObject.type}_${index}`,
|
||||
type: relatedSavedObject.type,
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
references,
|
||||
...(relatedSavedObjects ? { relatedSavedObjectWithRefs } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function injectSavedObjectReferences(
|
||||
references: SavedObjectReference[],
|
||||
relatedSavedObjects?: RelatedSavedObjects
|
||||
): { actionId?: string; relatedSavedObjects?: SavedObjectAttribute } {
|
||||
references = references ?? [];
|
||||
|
||||
// Look for for the action id
|
||||
const action = references.find((ref) => ref.name === ACTION_REF_NAME);
|
||||
|
||||
const injectedRelatedSavedObjects = (relatedSavedObjects ?? []).flatMap((relatedSavedObject) => {
|
||||
const reference = references.find((ref) => ref.name === relatedSavedObject.id);
|
||||
|
||||
// relatedSavedObjects are used only in the event log document that is written during
|
||||
// action execution. Because they are not critical to the actual execution of the action
|
||||
// we will not throw an error if no reference is found matching this related saved object
|
||||
return reference ? [{ ...relatedSavedObject, id: reference.id }] : [relatedSavedObject];
|
||||
});
|
||||
|
||||
const result: { actionId?: string; relatedSavedObjects?: SavedObjectAttribute } = {};
|
||||
if (action) {
|
||||
result.actionId = action.id;
|
||||
}
|
||||
|
||||
if (relatedSavedObjects) {
|
||||
result.relatedSavedObjects = injectedRelatedSavedObjects;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -13,6 +13,10 @@ export { ILicenseState, LicenseState } from './license_state';
|
|||
export { verifyApiAccess } from './verify_api_access';
|
||||
export { getActionTypeFeatureUsageName } from './get_action_type_feature_usage_name';
|
||||
export { spaceIdToNamespace } from './space_id_to_namespace';
|
||||
export {
|
||||
extractSavedObjectReferences,
|
||||
injectSavedObjectReferences,
|
||||
} from './action_task_params_utils';
|
||||
export {
|
||||
ActionTypeDisabledError,
|
||||
ActionTypeDisabledReason,
|
||||
|
|
|
@ -97,7 +97,7 @@ test(`throws an error if factory is already initialized`, () => {
|
|||
).toThrowErrorMatchingInlineSnapshot(`"TaskRunnerFactory already initialized"`);
|
||||
});
|
||||
|
||||
test('executes the task by calling the executor with proper parameters', async () => {
|
||||
test('executes the task by calling the executor with proper parameters, using given actionId when no actionRef in references', async () => {
|
||||
const taskRunner = taskRunnerFactory.create({
|
||||
taskInstance: mockedTaskInstance,
|
||||
});
|
||||
|
@ -146,6 +146,61 @@ test('executes the task by calling the executor with proper parameters', async (
|
|||
);
|
||||
});
|
||||
|
||||
test('executes the task by calling the executor with proper parameters, using stored actionId when actionRef is in references', async () => {
|
||||
const taskRunner = taskRunnerFactory.create({
|
||||
taskInstance: mockedTaskInstance,
|
||||
});
|
||||
|
||||
mockedActionExecutor.execute.mockResolvedValueOnce({ status: 'ok', actionId: '2' });
|
||||
spaceIdToNamespace.mockReturnValueOnce('namespace-test');
|
||||
mockedEncryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({
|
||||
id: '3',
|
||||
type: 'action_task_params',
|
||||
attributes: {
|
||||
actionId: '2',
|
||||
params: { baz: true },
|
||||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
},
|
||||
references: [
|
||||
{
|
||||
id: '9',
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const runnerResult = await taskRunner.run();
|
||||
|
||||
expect(runnerResult).toBeUndefined();
|
||||
expect(spaceIdToNamespace).toHaveBeenCalledWith('test');
|
||||
expect(
|
||||
mockedEncryptedSavedObjectsClient.getDecryptedAsInternalUser
|
||||
).toHaveBeenCalledWith('action_task_params', '3', { namespace: 'namespace-test' });
|
||||
|
||||
expect(mockedActionExecutor.execute).toHaveBeenCalledWith({
|
||||
actionId: '9',
|
||||
isEphemeral: false,
|
||||
params: { baz: true },
|
||||
relatedSavedObjects: [],
|
||||
request: expect.objectContaining({
|
||||
headers: {
|
||||
// base64 encoded "123:abc"
|
||||
authorization: 'ApiKey MTIzOmFiYw==',
|
||||
},
|
||||
}),
|
||||
taskInfo: {
|
||||
scheduled: new Date(),
|
||||
},
|
||||
});
|
||||
|
||||
const [executeParams] = mockedActionExecutor.execute.mock.calls[0];
|
||||
expect(taskRunnerFactoryInitializerParams.basePathService.set).toHaveBeenCalledWith(
|
||||
executeParams.request,
|
||||
'/s/test'
|
||||
);
|
||||
});
|
||||
|
||||
test('cleans up action_task_params object', async () => {
|
||||
const taskRunner = taskRunnerFactory.create({
|
||||
taskInstance: mockedTaskInstance,
|
||||
|
@ -161,7 +216,13 @@ test('cleans up action_task_params object', async () => {
|
|||
params: { baz: true },
|
||||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
},
|
||||
references: [],
|
||||
references: [
|
||||
{
|
||||
id: '2',
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await taskRunner.run();
|
||||
|
@ -184,7 +245,13 @@ test('runs successfully when cleanup fails and logs the error', async () => {
|
|||
params: { baz: true },
|
||||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
},
|
||||
references: [],
|
||||
references: [
|
||||
{
|
||||
id: '2',
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
],
|
||||
});
|
||||
services.savedObjectsClient.delete.mockRejectedValueOnce(new Error('Fail'));
|
||||
|
||||
|
@ -209,7 +276,13 @@ test('throws an error with suggested retry logic when return status is error', a
|
|||
params: { baz: true },
|
||||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
},
|
||||
references: [],
|
||||
references: [
|
||||
{
|
||||
id: '2',
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
],
|
||||
});
|
||||
mockedActionExecutor.execute.mockResolvedValueOnce({
|
||||
status: 'error',
|
||||
|
@ -244,7 +317,13 @@ test('uses API key when provided', async () => {
|
|||
params: { baz: true },
|
||||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
},
|
||||
references: [],
|
||||
references: [
|
||||
{
|
||||
id: '2',
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await taskRunner.run();
|
||||
|
@ -272,7 +351,7 @@ test('uses API key when provided', async () => {
|
|||
);
|
||||
});
|
||||
|
||||
test('uses relatedSavedObjects when provided', async () => {
|
||||
test('uses relatedSavedObjects merged with references when provided', async () => {
|
||||
const taskRunner = taskRunnerFactory.create({
|
||||
taskInstance: mockedTaskInstance,
|
||||
});
|
||||
|
@ -286,9 +365,20 @@ test('uses relatedSavedObjects when provided', async () => {
|
|||
actionId: '2',
|
||||
params: { baz: true },
|
||||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
relatedSavedObjects: [{ id: 'some-id', type: 'some-type' }],
|
||||
relatedSavedObjects: [{ id: 'related_some-type_0', type: 'some-type' }],
|
||||
},
|
||||
references: [],
|
||||
references: [
|
||||
{
|
||||
id: '2',
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: 'some-id',
|
||||
name: 'related_some-type_0',
|
||||
type: 'some-type',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await taskRunner.run();
|
||||
|
@ -315,6 +405,56 @@ test('uses relatedSavedObjects when provided', async () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('uses relatedSavedObjects as is when references are empty', async () => {
|
||||
const taskRunner = taskRunnerFactory.create({
|
||||
taskInstance: mockedTaskInstance,
|
||||
});
|
||||
|
||||
mockedActionExecutor.execute.mockResolvedValueOnce({ status: 'ok', actionId: '2' });
|
||||
spaceIdToNamespace.mockReturnValueOnce('namespace-test');
|
||||
mockedEncryptedSavedObjectsClient.getDecryptedAsInternalUser.mockResolvedValueOnce({
|
||||
id: '3',
|
||||
type: 'action_task_params',
|
||||
attributes: {
|
||||
actionId: '2',
|
||||
params: { baz: true },
|
||||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
relatedSavedObjects: [{ id: 'abc', type: 'some-type', namespace: 'yo' }],
|
||||
},
|
||||
references: [
|
||||
{
|
||||
id: '2',
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await taskRunner.run();
|
||||
|
||||
expect(mockedActionExecutor.execute).toHaveBeenCalledWith({
|
||||
actionId: '2',
|
||||
isEphemeral: false,
|
||||
params: { baz: true },
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
id: 'abc',
|
||||
type: 'some-type',
|
||||
namespace: 'yo',
|
||||
},
|
||||
],
|
||||
request: expect.objectContaining({
|
||||
headers: {
|
||||
// base64 encoded "123:abc"
|
||||
authorization: 'ApiKey MTIzOmFiYw==',
|
||||
},
|
||||
}),
|
||||
taskInfo: {
|
||||
scheduled: new Date(),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('sanitizes invalid relatedSavedObjects when provided', async () => {
|
||||
const taskRunner = taskRunnerFactory.create({
|
||||
taskInstance: mockedTaskInstance,
|
||||
|
@ -329,9 +469,20 @@ test('sanitizes invalid relatedSavedObjects when provided', async () => {
|
|||
actionId: '2',
|
||||
params: { baz: true },
|
||||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
relatedSavedObjects: [{ Xid: 'some-id', type: 'some-type' }],
|
||||
relatedSavedObjects: [{ Xid: 'related_some-type_0', type: 'some-type' }],
|
||||
},
|
||||
references: [],
|
||||
references: [
|
||||
{
|
||||
id: '2',
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: 'some-id',
|
||||
name: 'related_some-type_0',
|
||||
type: 'some-type',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await taskRunner.run();
|
||||
|
@ -366,7 +517,13 @@ test(`doesn't use API key when not provided`, async () => {
|
|||
actionId: '2',
|
||||
params: { baz: true },
|
||||
},
|
||||
references: [],
|
||||
references: [
|
||||
{
|
||||
id: '2',
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await taskRunner.run();
|
||||
|
@ -404,7 +561,13 @@ test(`throws an error when license doesn't support the action type`, async () =>
|
|||
params: { baz: true },
|
||||
apiKey: Buffer.from('123:abc').toString('base64'),
|
||||
},
|
||||
references: [],
|
||||
references: [
|
||||
{
|
||||
id: '2',
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
],
|
||||
});
|
||||
mockedActionExecutor.execute.mockImplementation(() => {
|
||||
throw new ActionTypeDisabledError('Fail', 'license_invalid');
|
||||
|
|
|
@ -33,7 +33,8 @@ import {
|
|||
} from '../types';
|
||||
import { ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE } from '../constants/saved_objects';
|
||||
import { asSavedObjectExecutionSource } from './action_execution_source';
|
||||
import { validatedRelatedSavedObjects } from './related_saved_objects';
|
||||
import { RelatedSavedObjects, validatedRelatedSavedObjects } from './related_saved_objects';
|
||||
import { injectSavedObjectReferences } from './action_task_params_utils';
|
||||
|
||||
export interface TaskRunnerContext {
|
||||
logger: Logger;
|
||||
|
@ -178,11 +179,30 @@ async function getActionTaskParams(
|
|||
const { spaceId } = executorParams;
|
||||
const namespace = spaceIdToNamespace(spaceId);
|
||||
if (isPersistedActionTask(executorParams)) {
|
||||
return encryptedSavedObjectsClient.getDecryptedAsInternalUser<ActionTaskParams>(
|
||||
const actionTask = await encryptedSavedObjectsClient.getDecryptedAsInternalUser<ActionTaskParams>(
|
||||
ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE,
|
||||
executorParams.actionTaskParamsId,
|
||||
{ namespace }
|
||||
);
|
||||
|
||||
const {
|
||||
attributes: { relatedSavedObjects },
|
||||
references,
|
||||
} = actionTask;
|
||||
|
||||
const {
|
||||
actionId,
|
||||
relatedSavedObjects: injectedRelatedSavedObjects,
|
||||
} = injectSavedObjectReferences(references, relatedSavedObjects as RelatedSavedObjects);
|
||||
|
||||
return {
|
||||
...actionTask,
|
||||
attributes: {
|
||||
...actionTask.attributes,
|
||||
...(actionId ? { actionId } : {}),
|
||||
...(relatedSavedObjects ? { relatedSavedObjects: injectedRelatedSavedObjects } : {}),
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return { attributes: executorParams.taskParams, references: executorParams.references ?? [] };
|
||||
}
|
||||
|
|
|
@ -229,7 +229,8 @@ export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartCon
|
|||
core.savedObjects,
|
||||
plugins.encryptedSavedObjects,
|
||||
this.actionTypeRegistry!,
|
||||
plugins.taskManager.index
|
||||
plugins.taskManager.index,
|
||||
this.preconfiguredActions
|
||||
);
|
||||
|
||||
registerBuiltInActionTypes({
|
||||
|
|
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import uuid from 'uuid';
|
||||
import {
|
||||
getActionTaskParamsMigrations,
|
||||
isPreconfiguredAction,
|
||||
} from './action_task_params_migrations';
|
||||
import { ActionTaskParams } from '../types';
|
||||
import { SavedObjectReference, SavedObjectUnsanitizedDoc } from 'kibana/server';
|
||||
import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks';
|
||||
import { migrationMocks } from 'src/core/server/mocks';
|
||||
|
||||
const context = migrationMocks.createContext();
|
||||
const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup();
|
||||
|
||||
const preconfiguredActions = [
|
||||
{
|
||||
actionTypeId: 'foo',
|
||||
config: {},
|
||||
id: 'my-slack1',
|
||||
name: 'Slack #xyz',
|
||||
secrets: {},
|
||||
isPreconfigured: true,
|
||||
},
|
||||
];
|
||||
|
||||
describe('successful migrations', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
encryptedSavedObjectsSetup.createMigration.mockImplementation(({ migration }) => migration);
|
||||
});
|
||||
|
||||
describe('7.16.0', () => {
|
||||
test('adds actionId to references array if actionId is not preconfigured', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const actionTaskParam = getMockData();
|
||||
const migratedActionTaskParam = migration716(actionTaskParam, context);
|
||||
expect(migratedActionTaskParam).toEqual({
|
||||
...actionTaskParam,
|
||||
references: [
|
||||
{
|
||||
id: actionTaskParam.attributes.actionId,
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('does not add actionId to references array if actionId is preconfigured', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const actionTaskParam = getMockData({ actionId: 'my-slack1' });
|
||||
const migratedActionTaskParam = migration716(actionTaskParam, context);
|
||||
expect(migratedActionTaskParam).toEqual({
|
||||
...actionTaskParam,
|
||||
references: [],
|
||||
});
|
||||
});
|
||||
|
||||
test('handles empty relatedSavedObjects array', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const actionTaskParam = getMockData({ relatedSavedObjects: [] });
|
||||
const migratedActionTaskParam = migration716(actionTaskParam, context);
|
||||
expect(migratedActionTaskParam).toEqual({
|
||||
...actionTaskParam,
|
||||
attributes: {
|
||||
...actionTaskParam.attributes,
|
||||
relatedSavedObjects: [],
|
||||
},
|
||||
references: [
|
||||
{
|
||||
id: actionTaskParam.attributes.actionId,
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('adds actionId and relatedSavedObjects to references array', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const actionTaskParam = getMockData({
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
id: 'some-id',
|
||||
namespace: 'some-namespace',
|
||||
type: 'some-type',
|
||||
typeId: 'some-typeId',
|
||||
},
|
||||
],
|
||||
});
|
||||
const migratedActionTaskParam = migration716(actionTaskParam, context);
|
||||
expect(migratedActionTaskParam).toEqual({
|
||||
...actionTaskParam,
|
||||
attributes: {
|
||||
...actionTaskParam.attributes,
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
id: 'related_some-type_0',
|
||||
namespace: 'some-namespace',
|
||||
type: 'some-type',
|
||||
typeId: 'some-typeId',
|
||||
},
|
||||
],
|
||||
},
|
||||
references: [
|
||||
{
|
||||
id: actionTaskParam.attributes.actionId,
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: 'some-id',
|
||||
name: 'related_some-type_0',
|
||||
type: 'some-type',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('only adds relatedSavedObjects to references array if action is preconfigured', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const actionTaskParam = getMockData({
|
||||
actionId: 'my-slack1',
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
id: 'some-id',
|
||||
namespace: 'some-namespace',
|
||||
type: 'some-type',
|
||||
typeId: 'some-typeId',
|
||||
},
|
||||
],
|
||||
});
|
||||
const migratedActionTaskParam = migration716(actionTaskParam, context);
|
||||
expect(migratedActionTaskParam).toEqual({
|
||||
...actionTaskParam,
|
||||
attributes: {
|
||||
...actionTaskParam.attributes,
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
id: 'related_some-type_0',
|
||||
namespace: 'some-namespace',
|
||||
type: 'some-type',
|
||||
typeId: 'some-typeId',
|
||||
},
|
||||
],
|
||||
},
|
||||
references: [
|
||||
{
|
||||
id: 'some-id',
|
||||
name: 'related_some-type_0',
|
||||
type: 'some-type',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('adds actionId and multiple relatedSavedObjects to references array', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const actionTaskParam = getMockData({
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
id: 'some-id',
|
||||
namespace: 'some-namespace',
|
||||
type: 'some-type',
|
||||
typeId: 'some-typeId',
|
||||
},
|
||||
{
|
||||
id: 'another-id',
|
||||
type: 'another-type',
|
||||
typeId: 'another-typeId',
|
||||
},
|
||||
],
|
||||
});
|
||||
const migratedActionTaskParam = migration716(actionTaskParam, context);
|
||||
expect(migratedActionTaskParam).toEqual({
|
||||
...actionTaskParam,
|
||||
attributes: {
|
||||
...actionTaskParam.attributes,
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
id: 'related_some-type_0',
|
||||
namespace: 'some-namespace',
|
||||
type: 'some-type',
|
||||
typeId: 'some-typeId',
|
||||
},
|
||||
{
|
||||
id: 'related_another-type_1',
|
||||
type: 'another-type',
|
||||
typeId: 'another-typeId',
|
||||
},
|
||||
],
|
||||
},
|
||||
references: [
|
||||
{
|
||||
id: actionTaskParam.attributes.actionId,
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: 'some-id',
|
||||
name: 'related_some-type_0',
|
||||
type: 'some-type',
|
||||
},
|
||||
{
|
||||
id: 'another-id',
|
||||
name: 'related_another-type_1',
|
||||
type: 'another-type',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('does not overwrite existing references', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const actionTaskParam = getMockData(
|
||||
{
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
id: 'some-id',
|
||||
namespace: 'some-namespace',
|
||||
type: 'some-type',
|
||||
typeId: 'some-typeId',
|
||||
},
|
||||
],
|
||||
},
|
||||
[
|
||||
{
|
||||
id: 'existing-ref-id',
|
||||
name: 'existingRef',
|
||||
type: 'existing-ref-type',
|
||||
},
|
||||
]
|
||||
);
|
||||
const migratedActionTaskParam = migration716(actionTaskParam, context);
|
||||
expect(migratedActionTaskParam).toEqual({
|
||||
...actionTaskParam,
|
||||
attributes: {
|
||||
...actionTaskParam.attributes,
|
||||
relatedSavedObjects: [
|
||||
{
|
||||
id: 'related_some-type_0',
|
||||
namespace: 'some-namespace',
|
||||
type: 'some-type',
|
||||
typeId: 'some-typeId',
|
||||
},
|
||||
],
|
||||
},
|
||||
references: [
|
||||
{
|
||||
id: 'existing-ref-id',
|
||||
name: 'existingRef',
|
||||
type: 'existing-ref-type',
|
||||
},
|
||||
{
|
||||
id: actionTaskParam.attributes.actionId,
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
{
|
||||
id: 'some-id',
|
||||
name: 'related_some-type_0',
|
||||
type: 'some-type',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('does not overwrite existing references if relatedSavedObjects is undefined', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const actionTaskParam = getMockData({}, [
|
||||
{
|
||||
id: 'existing-ref-id',
|
||||
name: 'existingRef',
|
||||
type: 'existing-ref-type',
|
||||
},
|
||||
]);
|
||||
const migratedActionTaskParam = migration716(actionTaskParam, context);
|
||||
expect(migratedActionTaskParam).toEqual({
|
||||
...actionTaskParam,
|
||||
references: [
|
||||
{
|
||||
id: 'existing-ref-id',
|
||||
name: 'existingRef',
|
||||
type: 'existing-ref-type',
|
||||
},
|
||||
{
|
||||
id: actionTaskParam.attributes.actionId,
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('does not overwrite existing references if relatedSavedObjects is empty', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const actionTaskParam = getMockData({ relatedSavedObjects: [] }, [
|
||||
{
|
||||
id: 'existing-ref-id',
|
||||
name: 'existingRef',
|
||||
type: 'existing-ref-type',
|
||||
},
|
||||
]);
|
||||
const migratedActionTaskParam = migration716(actionTaskParam, context);
|
||||
expect(migratedActionTaskParam).toEqual({
|
||||
...actionTaskParam,
|
||||
attributes: {
|
||||
...actionTaskParam.attributes,
|
||||
relatedSavedObjects: [],
|
||||
},
|
||||
references: [
|
||||
{
|
||||
id: 'existing-ref-id',
|
||||
name: 'existingRef',
|
||||
type: 'existing-ref-type',
|
||||
},
|
||||
{
|
||||
id: actionTaskParam.attributes.actionId,
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('handles errors during migrations', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
encryptedSavedObjectsSetup.createMigration.mockImplementation(() => () => {
|
||||
throw new Error(`Can't migrate!`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('7.16.0 throws if migration fails', () => {
|
||||
test('should show the proper exception', () => {
|
||||
const migration716 = getActionTaskParamsMigrations(
|
||||
encryptedSavedObjectsSetup,
|
||||
preconfiguredActions
|
||||
)['7.16.0'];
|
||||
const actionTaskParam = getMockData();
|
||||
expect(() => {
|
||||
migration716(actionTaskParam, context);
|
||||
}).toThrowError(`Can't migrate!`);
|
||||
expect(context.log.error).toHaveBeenCalledWith(
|
||||
`encryptedSavedObject 7.16.0 migration failed for action task param ${actionTaskParam.id} with error: Can't migrate!`,
|
||||
{
|
||||
migrations: {
|
||||
actionTaskParamDocument: actionTaskParam,
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isPreconfiguredAction()', () => {
|
||||
test('returns true if actionId is preconfigured action', () => {
|
||||
expect(
|
||||
isPreconfiguredAction(getMockData({ actionId: 'my-slack1' }), preconfiguredActions)
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
test('returns false if actionId is not preconfigured action', () => {
|
||||
expect(isPreconfiguredAction(getMockData(), preconfiguredActions)).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
function getMockData(
|
||||
overwrites: Record<string, unknown> = {},
|
||||
referencesOverwrites: SavedObjectReference[] = []
|
||||
): SavedObjectUnsanitizedDoc<ActionTaskParams> {
|
||||
return {
|
||||
attributes: {
|
||||
actionId: uuid.v4(),
|
||||
params: {},
|
||||
...overwrites,
|
||||
},
|
||||
references: [...referencesOverwrites],
|
||||
id: uuid.v4(),
|
||||
type: 'action_task_param',
|
||||
};
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import {
|
||||
LogMeta,
|
||||
SavedObjectMigrationMap,
|
||||
SavedObjectUnsanitizedDoc,
|
||||
SavedObjectMigrationFn,
|
||||
SavedObjectMigrationContext,
|
||||
SavedObjectReference,
|
||||
} from '../../../../../src/core/server';
|
||||
import { ActionTaskParams, PreConfiguredAction } from '../types';
|
||||
import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server';
|
||||
import type { IsMigrationNeededPredicate } from '../../../encrypted_saved_objects/server';
|
||||
import { RelatedSavedObjects } from '../lib/related_saved_objects';
|
||||
|
||||
interface ActionTaskParamsLogMeta extends LogMeta {
|
||||
migrations: { actionTaskParamDocument: SavedObjectUnsanitizedDoc<ActionTaskParams> };
|
||||
}
|
||||
|
||||
type ActionTaskParamMigration = (
|
||||
doc: SavedObjectUnsanitizedDoc<ActionTaskParams>
|
||||
) => SavedObjectUnsanitizedDoc<ActionTaskParams>;
|
||||
|
||||
function createEsoMigration(
|
||||
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup,
|
||||
isMigrationNeededPredicate: IsMigrationNeededPredicate<ActionTaskParams, ActionTaskParams>,
|
||||
migrationFunc: ActionTaskParamMigration
|
||||
) {
|
||||
return encryptedSavedObjects.createMigration<ActionTaskParams, ActionTaskParams>({
|
||||
isMigrationNeededPredicate,
|
||||
migration: migrationFunc,
|
||||
shouldMigrateIfDecryptionFails: true, // shouldMigrateIfDecryptionFails flag that applies the migration to undecrypted document if decryption fails
|
||||
});
|
||||
}
|
||||
|
||||
export function getActionTaskParamsMigrations(
|
||||
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup,
|
||||
preconfiguredActions: PreConfiguredAction[]
|
||||
): SavedObjectMigrationMap {
|
||||
const migrationActionTaskParamsSixteen = createEsoMigration(
|
||||
encryptedSavedObjects,
|
||||
(doc): doc is SavedObjectUnsanitizedDoc<ActionTaskParams> => true,
|
||||
pipeMigrations(getUseSavedObjectReferencesFn(preconfiguredActions))
|
||||
);
|
||||
|
||||
return {
|
||||
'7.16.0': executeMigrationWithErrorHandling(migrationActionTaskParamsSixteen, '7.16.0'),
|
||||
};
|
||||
}
|
||||
|
||||
function executeMigrationWithErrorHandling(
|
||||
migrationFunc: SavedObjectMigrationFn<ActionTaskParams, ActionTaskParams>,
|
||||
version: string
|
||||
) {
|
||||
return (
|
||||
doc: SavedObjectUnsanitizedDoc<ActionTaskParams>,
|
||||
context: SavedObjectMigrationContext
|
||||
) => {
|
||||
try {
|
||||
return migrationFunc(doc, context);
|
||||
} catch (ex) {
|
||||
context.log.error<ActionTaskParamsLogMeta>(
|
||||
`encryptedSavedObject ${version} migration failed for action task param ${doc.id} with error: ${ex.message}`,
|
||||
{
|
||||
migrations: {
|
||||
actionTaskParamDocument: doc,
|
||||
},
|
||||
}
|
||||
);
|
||||
throw ex;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function isPreconfiguredAction(
|
||||
doc: SavedObjectUnsanitizedDoc<ActionTaskParams>,
|
||||
preconfiguredActions: PreConfiguredAction[]
|
||||
): boolean {
|
||||
return !!preconfiguredActions.find((action) => action.id === doc.attributes.actionId);
|
||||
}
|
||||
|
||||
function getUseSavedObjectReferencesFn(preconfiguredActions: PreConfiguredAction[]) {
|
||||
return (doc: SavedObjectUnsanitizedDoc<ActionTaskParams>) => {
|
||||
return useSavedObjectReferences(doc, preconfiguredActions);
|
||||
};
|
||||
}
|
||||
|
||||
function useSavedObjectReferences(
|
||||
doc: SavedObjectUnsanitizedDoc<ActionTaskParams>,
|
||||
preconfiguredActions: PreConfiguredAction[]
|
||||
): SavedObjectUnsanitizedDoc<ActionTaskParams> {
|
||||
const {
|
||||
attributes: { actionId, relatedSavedObjects },
|
||||
references,
|
||||
} = doc;
|
||||
|
||||
const newReferences: SavedObjectReference[] = [];
|
||||
const relatedSavedObjectRefs: RelatedSavedObjects = [];
|
||||
|
||||
if (!isPreconfiguredAction(doc, preconfiguredActions)) {
|
||||
newReferences.push({
|
||||
id: actionId,
|
||||
name: 'actionRef',
|
||||
type: 'action',
|
||||
});
|
||||
}
|
||||
|
||||
// Add related saved objects, if any
|
||||
((relatedSavedObjects as RelatedSavedObjects) ?? []).forEach((relatedSavedObject, index) => {
|
||||
relatedSavedObjectRefs.push({
|
||||
...relatedSavedObject,
|
||||
id: `related_${relatedSavedObject.type}_${index}`,
|
||||
});
|
||||
newReferences.push({
|
||||
id: relatedSavedObject.id,
|
||||
name: `related_${relatedSavedObject.type}_${index}`,
|
||||
type: relatedSavedObject.type,
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
...doc,
|
||||
attributes: {
|
||||
...doc.attributes,
|
||||
...(relatedSavedObjects ? { relatedSavedObjects: relatedSavedObjectRefs } : {}),
|
||||
},
|
||||
references: [...(references ?? []), ...(newReferences ?? [])],
|
||||
};
|
||||
}
|
||||
|
||||
function pipeMigrations(...migrations: ActionTaskParamMigration[]): ActionTaskParamMigration {
|
||||
return (doc: SavedObjectUnsanitizedDoc<ActionTaskParams>) =>
|
||||
migrations.reduce((migratedDoc, nextMigration) => nextMigration(migratedDoc), doc);
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import uuid from 'uuid';
|
||||
import { getMigrations } from './migrations';
|
||||
import { getActionsMigrations } from './actions_migrations';
|
||||
import { RawAction } from '../types';
|
||||
import { SavedObjectUnsanitizedDoc } from 'kibana/server';
|
||||
import { encryptedSavedObjectsMock } from '../../../encrypted_saved_objects/server/mocks';
|
||||
|
@ -23,7 +23,7 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('7.10.0', () => {
|
||||
test('add hasAuth config property for .email actions', () => {
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0'];
|
||||
const migration710 = getActionsMigrations(encryptedSavedObjectsSetup)['7.10.0'];
|
||||
const action = getMockDataForEmail({});
|
||||
const migratedAction = migration710(action, context);
|
||||
expect(migratedAction.attributes.config).toEqual({
|
||||
|
@ -41,7 +41,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('rename cases configuration object', () => {
|
||||
const migration710 = getMigrations(encryptedSavedObjectsSetup)['7.10.0'];
|
||||
const migration710 = getActionsMigrations(encryptedSavedObjectsSetup)['7.10.0'];
|
||||
const action = getCasesMockData({});
|
||||
const migratedAction = migration710(action, context);
|
||||
expect(migratedAction.attributes.config).toEqual({
|
||||
|
@ -61,7 +61,7 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('7.11.0', () => {
|
||||
test('add hasAuth = true for .webhook actions with user and password', () => {
|
||||
const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const migration711 = getActionsMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const action = getMockDataForWebhook({}, true);
|
||||
expect(migration711(action, context)).toMatchObject({
|
||||
...action,
|
||||
|
@ -75,7 +75,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
|
||||
test('add hasAuth = false for .webhook actions without user and password', () => {
|
||||
const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const migration711 = getActionsMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const action = getMockDataForWebhook({}, false);
|
||||
expect(migration711(action, context)).toMatchObject({
|
||||
...action,
|
||||
|
@ -88,7 +88,7 @@ describe('successful migrations', () => {
|
|||
});
|
||||
});
|
||||
test('remove cases mapping object', () => {
|
||||
const migration711 = getMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const migration711 = getActionsMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const action = getMockData({
|
||||
config: { incidentConfiguration: { mapping: [] }, isCaseOwned: true, another: 'value' },
|
||||
});
|
||||
|
@ -106,7 +106,7 @@ describe('successful migrations', () => {
|
|||
|
||||
describe('7.14.0', () => {
|
||||
test('add isMissingSecrets property for actions', () => {
|
||||
const migration714 = getMigrations(encryptedSavedObjectsSetup)['7.14.0'];
|
||||
const migration714 = getActionsMigrations(encryptedSavedObjectsSetup)['7.14.0'];
|
||||
const action = getMockData({ isMissingSecrets: undefined });
|
||||
const migratedAction = migration714(action, context);
|
||||
expect(migratedAction).toEqual({
|
||||
|
@ -130,7 +130,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 = getActionsMigrations(encryptedSavedObjectsSetup)['7.10.0'];
|
||||
const action = getMockDataForEmail({});
|
||||
expect(() => {
|
||||
migration710(action, context);
|
||||
|
@ -148,7 +148,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 = getActionsMigrations(encryptedSavedObjectsSetup)['7.11.0'];
|
||||
const action = getMockDataForEmail({});
|
||||
expect(() => {
|
||||
migration711(action, context);
|
||||
|
@ -166,7 +166,7 @@ describe('handles errors during migrations', () => {
|
|||
|
||||
describe('7.14.0 throws if migration fails', () => {
|
||||
test('should show the proper exception', () => {
|
||||
const migration714 = getMigrations(encryptedSavedObjectsSetup)['7.14.0'];
|
||||
const migration714 = getActionsMigrations(encryptedSavedObjectsSetup)['7.14.0'];
|
||||
const action = getMockDataForEmail({});
|
||||
expect(() => {
|
||||
migration714(action, context);
|
|
@ -36,7 +36,7 @@ function createEsoMigration(
|
|||
});
|
||||
}
|
||||
|
||||
export function getMigrations(
|
||||
export function getActionsMigrations(
|
||||
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup
|
||||
): SavedObjectMigrationMap {
|
||||
const migrationActionsTen = createEsoMigration(
|
|
@ -13,8 +13,9 @@ import type {
|
|||
} from 'kibana/server';
|
||||
import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server';
|
||||
import mappings from './mappings.json';
|
||||
import { getMigrations } from './migrations';
|
||||
import { RawAction } from '../types';
|
||||
import { getActionsMigrations } from './actions_migrations';
|
||||
import { getActionTaskParamsMigrations } from './action_task_params_migrations';
|
||||
import { PreConfiguredAction, RawAction } from '../types';
|
||||
import { getImportWarnings } from './get_import_warnings';
|
||||
import { transformConnectorsForExport } from './transform_connectors_for_export';
|
||||
import { ActionTypeRegistry } from '../action_type_registry';
|
||||
|
@ -28,14 +29,15 @@ export function setupSavedObjects(
|
|||
savedObjects: SavedObjectsServiceSetup,
|
||||
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup,
|
||||
actionTypeRegistry: ActionTypeRegistry,
|
||||
taskManagerIndex: string
|
||||
taskManagerIndex: string,
|
||||
preconfiguredActions: PreConfiguredAction[]
|
||||
) {
|
||||
savedObjects.registerType({
|
||||
name: ACTION_SAVED_OBJECT_TYPE,
|
||||
hidden: true,
|
||||
namespaceType: 'single',
|
||||
mappings: mappings.action as SavedObjectsTypeMappingDefinition,
|
||||
migrations: getMigrations(encryptedSavedObjects),
|
||||
migrations: getActionsMigrations(encryptedSavedObjects),
|
||||
management: {
|
||||
defaultSearchField: 'name',
|
||||
importableAndExportable: true,
|
||||
|
@ -71,6 +73,7 @@ export function setupSavedObjects(
|
|||
hidden: true,
|
||||
namespaceType: 'single',
|
||||
mappings: mappings.action_task_params as SavedObjectsTypeMappingDefinition,
|
||||
migrations: getActionTaskParamsMigrations(encryptedSavedObjects, preconfiguredActions),
|
||||
excludeOnUpgrade: async ({ readonlyEsClient }) => {
|
||||
const oldestIdleActionTask = await getOldestIdleActionTask(
|
||||
readonlyEsClient,
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { FtrProviderContext } from '../../../common/ftr_provider_context';
|
||||
import { buildUp, tearDown } from '..';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function actionTaskParamsTests({ loadTestFile, getService }: FtrProviderContext) {
|
||||
describe('Action Task Params', () => {
|
||||
before(async () => buildUp(getService));
|
||||
after(async () => tearDown(getService));
|
||||
|
||||
loadTestFile(require.resolve('./migrations'));
|
||||
});
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { SavedObject, SavedObjectReference } from 'src/core/server';
|
||||
import { ActionTaskParams } from '../../../../../plugins/actions/server/types';
|
||||
import { FtrProviderContext } from '../../../common/ftr_provider_context';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function createGetTests({ getService }: FtrProviderContext) {
|
||||
const es = getService('es');
|
||||
const esArchiver = getService('esArchiver');
|
||||
|
||||
describe('migrations', () => {
|
||||
before(async () => {
|
||||
await esArchiver.load('x-pack/test/functional/es_archives/action_task_params');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await esArchiver.unload('x-pack/test/functional/es_archives/action_task_params');
|
||||
});
|
||||
|
||||
it('7.16.0 migrates action_task_params to use references array', async () => {
|
||||
// Inspect migration of non-preconfigured connector ID
|
||||
const response = await es.get<SavedObject<ActionTaskParams>>({
|
||||
index: '.kibana',
|
||||
id: 'action_task_params:b9af6280-0052-11ec-917b-f7aa317691ed',
|
||||
});
|
||||
expect(response.statusCode).to.eql(200);
|
||||
const { actionId, relatedSavedObjects, references } = getActionIdAndRelatedSavedObjects(
|
||||
response.body._source
|
||||
);
|
||||
|
||||
expect(references.find((ref: SavedObjectReference) => ref.name === 'actionRef')).to.eql({
|
||||
name: 'actionRef',
|
||||
id: actionId,
|
||||
type: 'action',
|
||||
});
|
||||
|
||||
// Should have reference entry for each relatedSavedObject entry
|
||||
(relatedSavedObjects ?? []).forEach((relatedSavedObject: any) => {
|
||||
expect(
|
||||
references.find((ref: SavedObjectReference) => ref.name === relatedSavedObject.id)
|
||||
).not.to.be(undefined);
|
||||
});
|
||||
|
||||
// Inspect migration of preconfigured connector ID
|
||||
const preconfiguredConnectorResponse = await es.get<SavedObject<ActionTaskParams>>({
|
||||
index: '.kibana',
|
||||
id: 'action_task_params:0205a520-0054-11ec-917b-f7aa317691ed',
|
||||
});
|
||||
expect(preconfiguredConnectorResponse.statusCode).to.eql(200);
|
||||
|
||||
const {
|
||||
relatedSavedObjects: preconfiguredRelatedSavedObjects,
|
||||
references: preconfiguredReferences,
|
||||
} = getActionIdAndRelatedSavedObjects(preconfiguredConnectorResponse.body._source);
|
||||
|
||||
expect(
|
||||
preconfiguredReferences.find((ref: SavedObjectReference) => ref.name === 'actionRef')
|
||||
).to.eql(undefined);
|
||||
|
||||
// Should have reference entry for each relatedSavedObject entry
|
||||
(preconfiguredRelatedSavedObjects ?? []).forEach((relatedSavedObject: any) => {
|
||||
expect(
|
||||
preconfiguredReferences.find(
|
||||
(ref: SavedObjectReference) => ref.name === relatedSavedObject.id
|
||||
)
|
||||
).not.to.be(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function getActionIdAndRelatedSavedObjects(responseSource: any) {
|
||||
if (!responseSource) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const actionTaskParams = (responseSource as any)?.action_task_params as ActionTaskParams;
|
||||
const actionId = actionTaskParams.actionId;
|
||||
const relatedSavedObjects = actionTaskParams.relatedSavedObjects as unknown[];
|
||||
const references = responseSource?.references ?? [];
|
||||
|
||||
return { actionId, relatedSavedObjects, references };
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ export default function alertingApiIntegrationTests({ loadTestFile }: FtrProvide
|
|||
|
||||
loadTestFile(require.resolve('./actions'));
|
||||
loadTestFile(require.resolve('./alerting'));
|
||||
loadTestFile(require.resolve('./action_task_params'));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"index": ".kibana_1",
|
||||
"id": "action_task_params:b9af6280-0052-11ec-917b-f7aa317691ed",
|
||||
"source": {
|
||||
"type": "action_task_params",
|
||||
"action_task_params" : {
|
||||
"actionId" : "918da460-0052-11ec-917b-f7aa317691ed",
|
||||
"params" : {
|
||||
"level" : "info",
|
||||
"message" : "yo yo"
|
||||
},
|
||||
"relatedSavedObjects" : [
|
||||
{
|
||||
"type" : "alert",
|
||||
"typeId" : "example.always-firing",
|
||||
"id" : "b6db0cd0-0052-11ec-917b-f7aa317691ed"
|
||||
}
|
||||
]
|
||||
},
|
||||
"references" : [
|
||||
{
|
||||
"name" : "source",
|
||||
"id" : "b6db0cd0-0052-11ec-917b-f7aa317691ed",
|
||||
"type" : "alert"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
"type": "doc",
|
||||
"value": {
|
||||
"index": ".kibana_1",
|
||||
"id": "action_task_params:0205a520-0054-11ec-917b-f7aa317691ed",
|
||||
"source": {
|
||||
"type": "action_task_params",
|
||||
"action_task_params" : {
|
||||
"actionId" : "my-slack1",
|
||||
"params" : {
|
||||
"level" : "info",
|
||||
"message" : "hi hi"
|
||||
},
|
||||
"relatedSavedObjects" : [
|
||||
{
|
||||
"type" : "alert",
|
||||
"typeId" : "example.always-firing",
|
||||
"id" : "b50bfcb0-0053-11ec-917b-f7aa317691ed"
|
||||
}
|
||||
]
|
||||
},
|
||||
"references" : [
|
||||
{
|
||||
"name" : "source",
|
||||
"id" : "b50bfcb0-0053-11ec-917b-f7aa317691ed",
|
||||
"type" : "alert"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
2572
x-pack/test/functional/es_archives/action_task_params/mappings.json
Normal file
2572
x-pack/test/functional/es_archives/action_task_params/mappings.json
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue