mirror of
https://github.com/elastic/kibana.git
synced 2025-06-28 03:01:21 -04:00
[Alerting] Export rules and connectors (#98802)
* Adding importableAndExportable but hidden saved object types to saved object feature privilege * Adding helper function for transforming rule for export. Added audit logging * Adding helper function for transforming rule for export. Added audit logging * Adding unit test for transforming rules for export * Exporting connectors * Removing auditing during export * Adding import/export to docs * PR fixes * Using action type validation onExport * Fixing logic for connectors with optional secrets * Fixing logic for connectors with optional secrets Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
0d7a5826a4
commit
4ab86c77d4
11 changed files with 495 additions and 7 deletions
|
@ -106,6 +106,12 @@ New connectors can be created by clicking the *Create connector* button, which w
|
||||||
[role="screenshot"]
|
[role="screenshot"]
|
||||||
image::images/connector-select-type.png[Connector select type]
|
image::images/connector-select-type.png[Connector select type]
|
||||||
|
|
||||||
|
[float]
|
||||||
|
[[importing-and-exporting-connectors]]
|
||||||
|
=== Importing and exporting connectors
|
||||||
|
|
||||||
|
To import and export rules, use the <<managing-saved-objects, Saved Objects Management UI>>.
|
||||||
|
|
||||||
[float]
|
[float]
|
||||||
[[create-connectors]]
|
[[create-connectors]]
|
||||||
=== Preconfigured connectors
|
=== Preconfigured connectors
|
||||||
|
|
|
@ -57,6 +57,12 @@ These operations can also be performed in bulk by multi-selecting rules and clic
|
||||||
[role="screenshot"]
|
[role="screenshot"]
|
||||||
image:images/bulk-mute-disable.png[The Manage rules button lets you mute/unmute, enable/disable, and delete in bulk]
|
image:images/bulk-mute-disable.png[The Manage rules button lets you mute/unmute, enable/disable, and delete in bulk]
|
||||||
|
|
||||||
|
[float]
|
||||||
|
[[importing-and-exporting-rules]]
|
||||||
|
=== Importing and exporting rules
|
||||||
|
|
||||||
|
To import and export rules, use the <<managing-saved-objects, Saved Objects Management UI>>.
|
||||||
|
|
||||||
[float]
|
[float]
|
||||||
=== Required permissions
|
=== Required permissions
|
||||||
|
|
||||||
|
|
|
@ -181,7 +181,6 @@ export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartCon
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.features.registerKibanaFeature(ACTIONS_FEATURE);
|
plugins.features.registerKibanaFeature(ACTIONS_FEATURE);
|
||||||
setupSavedObjects(core.savedObjects, plugins.encryptedSavedObjects);
|
|
||||||
|
|
||||||
this.eventLogService = plugins.eventLog;
|
this.eventLogService = plugins.eventLog;
|
||||||
plugins.eventLog.registerProviderActions(EVENT_LOG_PROVIDER, Object.values(EVENT_LOG_ACTIONS));
|
plugins.eventLog.registerProviderActions(EVENT_LOG_PROVIDER, Object.values(EVENT_LOG_ACTIONS));
|
||||||
|
@ -228,6 +227,8 @@ export class ActionsPlugin implements Plugin<PluginSetupContract, PluginStartCon
|
||||||
this.actionExecutor = actionExecutor;
|
this.actionExecutor = actionExecutor;
|
||||||
this.security = plugins.security;
|
this.security = plugins.security;
|
||||||
|
|
||||||
|
setupSavedObjects(core.savedObjects, plugins.encryptedSavedObjects, this.actionTypeRegistry!);
|
||||||
|
|
||||||
registerBuiltInActionTypes({
|
registerBuiltInActionTypes({
|
||||||
logger: this.logger,
|
logger: this.logger,
|
||||||
actionTypeRegistry,
|
actionTypeRegistry,
|
||||||
|
|
|
@ -5,12 +5,18 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { SavedObject, SavedObjectsServiceSetup } from 'kibana/server';
|
import {
|
||||||
|
SavedObject,
|
||||||
|
SavedObjectsExportTransformContext,
|
||||||
|
SavedObjectsServiceSetup,
|
||||||
|
} from 'kibana/server';
|
||||||
import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server';
|
import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server';
|
||||||
import mappings from './mappings.json';
|
import mappings from './mappings.json';
|
||||||
import { getMigrations } from './migrations';
|
import { getMigrations } from './migrations';
|
||||||
import { RawAction } from '../types';
|
import { RawAction } from '../types';
|
||||||
import { getImportResultMessage, GO_TO_CONNECTORS_BUTTON_LABLE } from './get_import_result_message';
|
import { getImportResultMessage, GO_TO_CONNECTORS_BUTTON_LABLE } from './get_import_result_message';
|
||||||
|
import { transformConnectorsForExport } from './transform_connectors_for_export';
|
||||||
|
import { ActionTypeRegistry } from '../action_type_registry';
|
||||||
|
|
||||||
export const ACTION_SAVED_OBJECT_TYPE = 'action';
|
export const ACTION_SAVED_OBJECT_TYPE = 'action';
|
||||||
export const ALERT_SAVED_OBJECT_TYPE = 'alert';
|
export const ALERT_SAVED_OBJECT_TYPE = 'alert';
|
||||||
|
@ -18,7 +24,8 @@ export const ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE = 'action_task_params';
|
||||||
|
|
||||||
export function setupSavedObjects(
|
export function setupSavedObjects(
|
||||||
savedObjects: SavedObjectsServiceSetup,
|
savedObjects: SavedObjectsServiceSetup,
|
||||||
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup
|
encryptedSavedObjects: EncryptedSavedObjectsPluginSetup,
|
||||||
|
actionTypeRegistry: ActionTypeRegistry
|
||||||
) {
|
) {
|
||||||
savedObjects.registerType({
|
savedObjects.registerType({
|
||||||
name: ACTION_SAVED_OBJECT_TYPE,
|
name: ACTION_SAVED_OBJECT_TYPE,
|
||||||
|
@ -32,6 +39,12 @@ export function setupSavedObjects(
|
||||||
getTitle(obj) {
|
getTitle(obj) {
|
||||||
return `Connector: [${obj.attributes.name}]`;
|
return `Connector: [${obj.attributes.name}]`;
|
||||||
},
|
},
|
||||||
|
onExport<RawAction>(
|
||||||
|
context: SavedObjectsExportTransformContext,
|
||||||
|
objects: Array<SavedObject<RawAction>>
|
||||||
|
) {
|
||||||
|
return transformConnectorsForExport(objects, actionTypeRegistry);
|
||||||
|
},
|
||||||
onImport(connectors) {
|
onImport(connectors) {
|
||||||
return {
|
return {
|
||||||
warnings: [
|
warnings: [
|
||||||
|
|
|
@ -0,0 +1,253 @@
|
||||||
|
/*
|
||||||
|
* 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 { transformConnectorsForExport } from './transform_connectors_for_export';
|
||||||
|
import { ActionTypeRegistry, ActionTypeRegistryOpts } from '../action_type_registry';
|
||||||
|
import { loggingSystemMock } from '../../../../../src/core/server/mocks';
|
||||||
|
import { actionsConfigMock } from '../actions_config.mock';
|
||||||
|
import { licensingMock } from '../../../licensing/server/mocks';
|
||||||
|
import { licenseStateMock } from '../lib/license_state.mock';
|
||||||
|
import { taskManagerMock } from '../../../task_manager/server/mocks';
|
||||||
|
import { ActionExecutor, TaskRunnerFactory } from '../lib';
|
||||||
|
import { registerBuiltInActionTypes } from '../builtin_action_types';
|
||||||
|
|
||||||
|
describe('transform connector for export', () => {
|
||||||
|
const actionTypeRegistryParams: ActionTypeRegistryOpts = {
|
||||||
|
licensing: licensingMock.createSetup(),
|
||||||
|
taskManager: taskManagerMock.createSetup(),
|
||||||
|
taskRunnerFactory: new TaskRunnerFactory(new ActionExecutor({ isESOCanEncrypt: true })),
|
||||||
|
actionsConfigUtils: actionsConfigMock.create(),
|
||||||
|
licenseState: licenseStateMock.create(),
|
||||||
|
preconfiguredActions: [],
|
||||||
|
};
|
||||||
|
const actionTypeRegistry: ActionTypeRegistry = new ActionTypeRegistry(actionTypeRegistryParams);
|
||||||
|
|
||||||
|
registerBuiltInActionTypes({
|
||||||
|
logger: loggingSystemMock.create().get(),
|
||||||
|
actionTypeRegistry,
|
||||||
|
actionsConfigUtils: actionsConfigMock.create(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const connectorsWithNoSecrets = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
type: 'action',
|
||||||
|
attributes: {
|
||||||
|
actionTypeId: '.email',
|
||||||
|
name: 'email connector without auth',
|
||||||
|
isMissingSecrets: false,
|
||||||
|
config: {
|
||||||
|
hasAuth: false,
|
||||||
|
from: 'me@me.com',
|
||||||
|
host: 'host',
|
||||||
|
port: 22,
|
||||||
|
service: null,
|
||||||
|
secure: null,
|
||||||
|
},
|
||||||
|
secrets: 'asbqw4tqbef',
|
||||||
|
},
|
||||||
|
references: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
type: 'action',
|
||||||
|
attributes: {
|
||||||
|
actionTypeId: '.index',
|
||||||
|
name: 'index connector',
|
||||||
|
isMissingSecrets: false,
|
||||||
|
config: {
|
||||||
|
index: 'test-index',
|
||||||
|
refresh: false,
|
||||||
|
executionTimeField: null,
|
||||||
|
},
|
||||||
|
secrets: 'asbqw4tqbef',
|
||||||
|
},
|
||||||
|
references: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
type: 'action',
|
||||||
|
attributes: {
|
||||||
|
actionTypeId: '.server-log',
|
||||||
|
name: 'server log connector',
|
||||||
|
isMissingSecrets: false,
|
||||||
|
config: {},
|
||||||
|
secrets: 'asbqw4tqbef',
|
||||||
|
},
|
||||||
|
references: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
type: 'action',
|
||||||
|
attributes: {
|
||||||
|
actionTypeId: '.webhook',
|
||||||
|
name: 'webhook connector without auth',
|
||||||
|
isMissingSecrets: false,
|
||||||
|
config: {
|
||||||
|
method: 'post',
|
||||||
|
hasAuth: false,
|
||||||
|
url: 'https://webhook',
|
||||||
|
headers: {},
|
||||||
|
},
|
||||||
|
secrets: 'asbqw4tqbef',
|
||||||
|
},
|
||||||
|
references: [],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const connectorsWithSecrets = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
type: 'action',
|
||||||
|
attributes: {
|
||||||
|
actionTypeId: '.email',
|
||||||
|
name: 'email connector with auth',
|
||||||
|
isMissingSecrets: false,
|
||||||
|
config: {
|
||||||
|
hasAuth: true,
|
||||||
|
from: 'me@me.com',
|
||||||
|
host: 'host',
|
||||||
|
port: 22,
|
||||||
|
service: null,
|
||||||
|
secure: null,
|
||||||
|
},
|
||||||
|
secrets: 'asbqw4tqbef',
|
||||||
|
},
|
||||||
|
references: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
type: 'action',
|
||||||
|
attributes: {
|
||||||
|
actionTypeId: '.resilient',
|
||||||
|
name: 'resilient connector',
|
||||||
|
isMissingSecrets: false,
|
||||||
|
config: {
|
||||||
|
apiUrl: 'https://resilient',
|
||||||
|
orgId: 'origId',
|
||||||
|
},
|
||||||
|
secrets: 'asbqw4tqbef',
|
||||||
|
},
|
||||||
|
references: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
type: 'action',
|
||||||
|
attributes: {
|
||||||
|
actionTypeId: '.servicenow',
|
||||||
|
name: 'servicenow itsm connector',
|
||||||
|
isMissingSecrets: false,
|
||||||
|
config: {
|
||||||
|
apiUrl: 'https://servicenow',
|
||||||
|
},
|
||||||
|
secrets: 'asbqw4tqbef',
|
||||||
|
},
|
||||||
|
references: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
type: 'action',
|
||||||
|
attributes: {
|
||||||
|
actionTypeId: '.pagerduty',
|
||||||
|
name: 'pagerduty connector',
|
||||||
|
isMissingSecrets: false,
|
||||||
|
config: {
|
||||||
|
apiUrl: 'https://pagerduty',
|
||||||
|
},
|
||||||
|
secrets: 'asbqw4tqbef',
|
||||||
|
},
|
||||||
|
references: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
type: 'action',
|
||||||
|
attributes: {
|
||||||
|
actionTypeId: '.jira',
|
||||||
|
name: 'jira connector',
|
||||||
|
isMissingSecrets: false,
|
||||||
|
config: {
|
||||||
|
apiUrl: 'https://jira',
|
||||||
|
projectKey: 'foo',
|
||||||
|
},
|
||||||
|
secrets: 'asbqw4tqbef',
|
||||||
|
},
|
||||||
|
references: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6',
|
||||||
|
type: 'action',
|
||||||
|
attributes: {
|
||||||
|
actionTypeId: '.teams',
|
||||||
|
name: 'teams connector',
|
||||||
|
isMissingSecrets: false,
|
||||||
|
config: {},
|
||||||
|
secrets: 'asbqw4tqbef',
|
||||||
|
},
|
||||||
|
references: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '7',
|
||||||
|
type: 'action',
|
||||||
|
attributes: {
|
||||||
|
actionTypeId: '.slack',
|
||||||
|
name: 'slack connector',
|
||||||
|
isMissingSecrets: false,
|
||||||
|
config: {},
|
||||||
|
secrets: 'asbqw4tqbef',
|
||||||
|
},
|
||||||
|
references: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '8',
|
||||||
|
type: 'action',
|
||||||
|
attributes: {
|
||||||
|
actionTypeId: '.servicenow-sir',
|
||||||
|
name: 'servicenow sir connector',
|
||||||
|
isMissingSecrets: false,
|
||||||
|
config: {
|
||||||
|
apiUrl: 'https://servicenow-sir',
|
||||||
|
},
|
||||||
|
secrets: 'asbqw4tqbef',
|
||||||
|
},
|
||||||
|
references: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '8',
|
||||||
|
type: 'action',
|
||||||
|
attributes: {
|
||||||
|
actionTypeId: '.webhook',
|
||||||
|
name: 'webhook connector with auth',
|
||||||
|
isMissingSecrets: false,
|
||||||
|
config: {
|
||||||
|
method: 'post',
|
||||||
|
hasAuth: true,
|
||||||
|
url: 'https://webhook',
|
||||||
|
headers: {},
|
||||||
|
},
|
||||||
|
secrets: 'asbqw4tqbef',
|
||||||
|
},
|
||||||
|
references: [],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
it('should not change connectors without secrets', () => {
|
||||||
|
expect(transformConnectorsForExport(connectorsWithNoSecrets, actionTypeRegistry)).toEqual(
|
||||||
|
connectorsWithNoSecrets
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove secrets for connectors with secrets', () => {
|
||||||
|
expect(transformConnectorsForExport(connectorsWithSecrets, actionTypeRegistry)).toEqual(
|
||||||
|
connectorsWithSecrets.map((connector) => ({
|
||||||
|
...connector,
|
||||||
|
attributes: {
|
||||||
|
...connector.attributes,
|
||||||
|
isMissingSecrets: true,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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 { SavedObject } from 'kibana/server';
|
||||||
|
import { ActionTypeRegistry } from '../action_type_registry';
|
||||||
|
import { validateSecrets } from '../lib';
|
||||||
|
import { RawAction, ActionType } from '../types';
|
||||||
|
|
||||||
|
export function transformConnectorsForExport(
|
||||||
|
connectors: SavedObject[],
|
||||||
|
actionTypeRegistry: ActionTypeRegistry
|
||||||
|
): Array<SavedObject<RawAction>> {
|
||||||
|
return connectors.map((c) => {
|
||||||
|
const connector = c as SavedObject<RawAction>;
|
||||||
|
return transformConnectorForExport(
|
||||||
|
connector,
|
||||||
|
actionTypeRegistry.get(connector.attributes.actionTypeId)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformConnectorForExport(
|
||||||
|
connector: SavedObject<RawAction>,
|
||||||
|
actionType: ActionType
|
||||||
|
): SavedObject<RawAction> {
|
||||||
|
let isMissingSecrets = false;
|
||||||
|
try {
|
||||||
|
// If connector requires secrets, this will throw an error
|
||||||
|
validateSecrets(actionType, {});
|
||||||
|
|
||||||
|
// If connector has optional (or no) secrets, set isMissingSecrets value to value of hasAuth
|
||||||
|
// If connector doesn't have hasAuth value, default to isMissingSecrets: false
|
||||||
|
isMissingSecrets = (connector?.attributes?.config?.hasAuth as boolean) ?? false;
|
||||||
|
} catch (err) {
|
||||||
|
isMissingSecrets = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip connectors
|
||||||
|
return {
|
||||||
|
...connector,
|
||||||
|
attributes: {
|
||||||
|
...connector.attributes,
|
||||||
|
isMissingSecrets,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
|
@ -5,11 +5,15 @@
|
||||||
* 2.0.
|
* 2.0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { SavedObjectsServiceSetup } from 'kibana/server';
|
import {
|
||||||
|
SavedObject,
|
||||||
|
SavedObjectsExportTransformContext,
|
||||||
|
SavedObjectsServiceSetup,
|
||||||
|
} from 'kibana/server';
|
||||||
import mappings from './mappings.json';
|
import mappings from './mappings.json';
|
||||||
import { getMigrations } from './migrations';
|
import { getMigrations } from './migrations';
|
||||||
import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server';
|
import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server';
|
||||||
|
import { transformRulesForExport } from './transform_rule_for_export';
|
||||||
export { partiallyUpdateAlert } from './partially_update_alert';
|
export { partiallyUpdateAlert } from './partially_update_alert';
|
||||||
|
|
||||||
export const AlertAttributesExcludedFromAAD = [
|
export const AlertAttributesExcludedFromAAD = [
|
||||||
|
@ -43,6 +47,18 @@ export function setupSavedObjects(
|
||||||
namespaceType: 'single',
|
namespaceType: 'single',
|
||||||
migrations: getMigrations(encryptedSavedObjects),
|
migrations: getMigrations(encryptedSavedObjects),
|
||||||
mappings: mappings.alert,
|
mappings: mappings.alert,
|
||||||
|
management: {
|
||||||
|
importableAndExportable: true,
|
||||||
|
getTitle(obj) {
|
||||||
|
return `Rule: [${obj.attributes.name}]`;
|
||||||
|
},
|
||||||
|
onExport<RawAlert>(
|
||||||
|
context: SavedObjectsExportTransformContext,
|
||||||
|
objects: Array<SavedObject<RawAlert>>
|
||||||
|
) {
|
||||||
|
return transformRulesForExport(objects);
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
savedObjects.registerType({
|
savedObjects.registerType({
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
/*
|
||||||
|
* 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 { transformRulesForExport } from './transform_rule_for_export';
|
||||||
|
|
||||||
|
describe('transform rule for export', () => {
|
||||||
|
const date = new Date().toISOString();
|
||||||
|
const mockRules = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
type: 'alert',
|
||||||
|
attributes: {
|
||||||
|
enabled: true,
|
||||||
|
name: 'rule-name',
|
||||||
|
tags: ['tag-1', 'tag-2'],
|
||||||
|
alertTypeId: '123',
|
||||||
|
consumer: 'alert-consumer',
|
||||||
|
schedule: { interval: '1m' },
|
||||||
|
actions: [],
|
||||||
|
params: {},
|
||||||
|
createdBy: 'me',
|
||||||
|
updatedBy: 'me',
|
||||||
|
createdAt: date,
|
||||||
|
updatedAt: date,
|
||||||
|
apiKey: '4tndskbuhewotw4klrhgjewrt9u',
|
||||||
|
apiKeyOwner: 'me',
|
||||||
|
throttle: null,
|
||||||
|
notifyWhen: 'onActionGroupChange',
|
||||||
|
muteAll: false,
|
||||||
|
mutedInstanceIds: [],
|
||||||
|
executionStatus: {
|
||||||
|
status: 'active',
|
||||||
|
lastExecutionDate: '2020-08-20T19:23:38Z',
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
scheduledTaskId: '2q5tjbf3q45twer',
|
||||||
|
},
|
||||||
|
references: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
type: 'alert',
|
||||||
|
attributes: {
|
||||||
|
enabled: false,
|
||||||
|
name: 'disabled-rule',
|
||||||
|
tags: ['tag-1'],
|
||||||
|
alertTypeId: '456',
|
||||||
|
consumer: 'alert-consumer',
|
||||||
|
schedule: { interval: '1h' },
|
||||||
|
actions: [],
|
||||||
|
params: {},
|
||||||
|
createdBy: 'you',
|
||||||
|
updatedBy: 'you',
|
||||||
|
createdAt: date,
|
||||||
|
updatedAt: date,
|
||||||
|
apiKey: null,
|
||||||
|
apiKeyOwner: null,
|
||||||
|
throttle: null,
|
||||||
|
notifyWhen: 'onActionGroupChange',
|
||||||
|
muteAll: false,
|
||||||
|
mutedInstanceIds: [],
|
||||||
|
executionStatus: {
|
||||||
|
status: 'pending',
|
||||||
|
lastExecutionDate: '2020-08-20T19:23:38Z',
|
||||||
|
error: null,
|
||||||
|
},
|
||||||
|
scheduledTaskId: null,
|
||||||
|
},
|
||||||
|
references: [],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
it('should disable rule and clear sensitive values', () => {
|
||||||
|
expect(transformRulesForExport(mockRules)).toEqual(
|
||||||
|
mockRules.map((rule) => ({
|
||||||
|
...rule,
|
||||||
|
attributes: {
|
||||||
|
...rule.attributes,
|
||||||
|
enabled: false,
|
||||||
|
apiKey: null,
|
||||||
|
apiKeyOwner: null,
|
||||||
|
scheduledTaskId: null,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* 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 { SavedObject } from 'kibana/server';
|
||||||
|
import { RawAlert } from '../types';
|
||||||
|
|
||||||
|
export function transformRulesForExport(rules: SavedObject[]): Array<SavedObject<RawAlert>> {
|
||||||
|
return rules.map((rule) => transformRuleForExport(rule as SavedObject<RawAlert>));
|
||||||
|
}
|
||||||
|
|
||||||
|
function transformRuleForExport(rule: SavedObject<RawAlert>): SavedObject<RawAlert> {
|
||||||
|
return {
|
||||||
|
...rule,
|
||||||
|
attributes: {
|
||||||
|
...rule.attributes,
|
||||||
|
enabled: false,
|
||||||
|
apiKey: null,
|
||||||
|
apiKeyOwner: null,
|
||||||
|
scheduledTaskId: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
|
@ -27,6 +27,20 @@ describe('Features Plugin', () => {
|
||||||
namespaceType: 'single' as 'single',
|
namespaceType: 'single' as 'single',
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
typeRegistry.getImportableAndExportableTypes.mockReturnValue([
|
||||||
|
{
|
||||||
|
name: 'hidden-importableAndExportable',
|
||||||
|
hidden: true,
|
||||||
|
mappings: { properties: {} },
|
||||||
|
namespaceType: 'single' as 'single',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'not-hidden-importableAndExportable',
|
||||||
|
hidden: false,
|
||||||
|
mappings: { properties: {} },
|
||||||
|
namespaceType: 'single' as 'single',
|
||||||
|
},
|
||||||
|
]);
|
||||||
coreStart.savedObjects.getTypeRegistry.mockReturnValue(typeRegistry);
|
coreStart.savedObjects.getTypeRegistry.mockReturnValue(typeRegistry);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -87,7 +101,9 @@ describe('Features Plugin', () => {
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('registers kibana features with not hidden saved objects types', async () => {
|
it('registers kibana features with visible saved objects types and hidden saved object types that are importable and exportable', async () => {
|
||||||
|
typeRegistry.isHidden.mockReturnValueOnce(true);
|
||||||
|
typeRegistry.isHidden.mockReturnValueOnce(false);
|
||||||
const plugin = new FeaturesPlugin(initContext);
|
const plugin = new FeaturesPlugin(initContext);
|
||||||
await plugin.setup(coreSetup, {});
|
await plugin.setup(coreSetup, {});
|
||||||
const { getKibanaFeatures } = plugin.start(coreStart);
|
const { getKibanaFeatures } = plugin.start(coreStart);
|
||||||
|
@ -98,6 +114,8 @@ describe('Features Plugin', () => {
|
||||||
|
|
||||||
expect(soTypes.includes('foo')).toBe(true);
|
expect(soTypes.includes('foo')).toBe(true);
|
||||||
expect(soTypes.includes('bar')).toBe(false);
|
expect(soTypes.includes('bar')).toBe(false);
|
||||||
|
expect(soTypes.includes('hidden-importableAndExportable')).toBe(true);
|
||||||
|
expect(soTypes.includes('not-hidden-importableAndExportable')).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns registered elasticsearch features', async () => {
|
it('returns registered elasticsearch features', async () => {
|
||||||
|
|
|
@ -128,7 +128,15 @@ export class FeaturesPlugin
|
||||||
|
|
||||||
private registerOssFeatures(savedObjects: SavedObjectsServiceStart) {
|
private registerOssFeatures(savedObjects: SavedObjectsServiceStart) {
|
||||||
const registry = savedObjects.getTypeRegistry();
|
const registry = savedObjects.getTypeRegistry();
|
||||||
const savedObjectTypes = registry.getVisibleTypes().map((t) => t.name);
|
const savedObjectVisibleTypes = registry.getVisibleTypes().map((t) => t.name);
|
||||||
|
const savedObjectImportableAndExportableHiddenTypes = registry
|
||||||
|
.getImportableAndExportableTypes()
|
||||||
|
.filter((t) => registry.isHidden(t.name))
|
||||||
|
.map((t) => t.name);
|
||||||
|
|
||||||
|
const savedObjectTypes = Array.from(
|
||||||
|
new Set([...savedObjectVisibleTypes, ...savedObjectImportableAndExportableHiddenTypes])
|
||||||
|
);
|
||||||
|
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
`Registering OSS features with SO types: ${savedObjectTypes.join(', ')}. "includeTimelion": ${
|
`Registering OSS features with SO types: ${savedObjectTypes.join(', ')}. "includeTimelion": ${
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue