mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[Alerting] Enabling import of rules and connectors (#99857)
* [Alerting] Enabling import of rules and connectors * changed export to set pending executionStatus for rule * fixed tests * added docs * Apply suggestions from code review Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * fixed docs * fixed docs * Update x-pack/plugins/alerting/server/saved_objects/get_import_warnings.ts Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com> * fixed test * fixed test Co-authored-by: gchaps <33642766+gchaps@users.noreply.github.com>
This commit is contained in:
parent
fab96050a4
commit
8a344fa385
16 changed files with 182 additions and 17 deletions
|
@ -111,6 +111,13 @@ image::images/connector-select-type.png[Connector select type]
|
|||
=== Importing and exporting connectors
|
||||
|
||||
To import and export rules, use the <<managing-saved-objects, Saved Objects Management UI>>.
|
||||
After a successful import, the proper banner is displayed:
|
||||
[role="screenshot"]
|
||||
image::images/coonectors-import-banner.png[Connectors import banner, width=50%]
|
||||
|
||||
If a connector is missing user sensitive information because of the import, a **Fix** button appears in the list view.
|
||||
[role="screenshot"]
|
||||
image::images/connectors-with-missing-secrets.png[Connectors with missing secrets]
|
||||
|
||||
[float]
|
||||
[[create-connectors]]
|
||||
|
|
BIN
docs/management/images/connectors-with-missing-secrets.png
Normal file
BIN
docs/management/images/connectors-with-missing-secrets.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 147 KiB |
BIN
docs/management/images/coonectors-import-banner.png
Normal file
BIN
docs/management/images/coonectors-import-banner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 61 KiB |
BIN
docs/user/alerting/images/rules-imported-banner.png
Normal file
BIN
docs/user/alerting/images/rules-imported-banner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 77 KiB |
|
@ -62,6 +62,9 @@ image:images/bulk-mute-disable.png[The Manage rules button lets you mute/unmute,
|
|||
=== Importing and exporting rules
|
||||
|
||||
To import and export rules, use the <<managing-saved-objects, Saved Objects Management UI>>.
|
||||
After the succesful import the proper banner will be displayed:
|
||||
[role="screenshot"]
|
||||
image::images/rules-imported-banner.png[Rules import banner, width=50%]
|
||||
|
||||
[float]
|
||||
=== Required permissions
|
||||
|
|
|
@ -48,7 +48,7 @@ describe('getImportWarnings', () => {
|
|||
const warnings = getImportWarnings(
|
||||
(savedObjectConnectors as unknown) as Array<SavedObject<RawAction>>
|
||||
);
|
||||
expect(warnings[0].message).toBe('1 connector has secrets that require updates.');
|
||||
expect(warnings[0].message).toBe('1 connector has sensitive information that require updates.');
|
||||
});
|
||||
|
||||
it('does not return the warning message if all of the imported connectors do not have secrets to update', () => {
|
||||
|
|
|
@ -20,7 +20,7 @@ export function getImportWarnings(
|
|||
}
|
||||
const message = i18n.translate('xpack.actions.savedObjects.onImportText', {
|
||||
defaultMessage:
|
||||
'{connectorsWithSecretsLength} {connectorsWithSecretsLength, plural, one {connector has} other {connectors have}} secrets that require updates.',
|
||||
'{connectorsWithSecretsLength} {connectorsWithSecretsLength, plural, one {connector has} other {connectors have}} sensitive information that require updates.',
|
||||
values: {
|
||||
connectorsWithSecretsLength: connectorsWithSecrets.length,
|
||||
},
|
||||
|
@ -35,4 +35,7 @@ export function getImportWarnings(
|
|||
];
|
||||
}
|
||||
|
||||
export const GO_TO_CONNECTORS_BUTTON_LABLE = 'Go to connectors';
|
||||
export const GO_TO_CONNECTORS_BUTTON_LABLE = i18n.translate(
|
||||
'xpack.actions.savedObjects.goToConnectorsButtonText',
|
||||
{ defaultMessage: 'Go to connectors' }
|
||||
);
|
||||
|
|
|
@ -36,8 +36,8 @@ export function setupSavedObjects(
|
|||
management: {
|
||||
defaultSearchField: 'name',
|
||||
importableAndExportable: true,
|
||||
getTitle(obj) {
|
||||
return `Connector: [${obj.attributes.name}]`;
|
||||
getTitle(savedObject: SavedObject<RawAction>) {
|
||||
return `Connector: [${savedObject.attributes.name}]`;
|
||||
},
|
||||
onExport<RawAction>(
|
||||
context: SavedObjectsExportTransformContext,
|
||||
|
|
|
@ -59,6 +59,7 @@ import { markApiKeyForInvalidation } from '../invalidate_pending_api_keys/mark_a
|
|||
import { alertAuditEvent, AlertAuditAction } from './audit_events';
|
||||
import { nodeBuilder } from '../../../../../src/plugins/data/common';
|
||||
import { mapSortField } from './lib';
|
||||
import { getAlertExecutionStatusPending } from '../lib/alert_execution_status';
|
||||
|
||||
export interface RegistryAlertTypeWithAuth extends RegistryAlertType {
|
||||
authorizedConsumers: string[];
|
||||
|
@ -288,11 +289,7 @@ export class AlertsClient {
|
|||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
notifyWhen,
|
||||
executionStatus: {
|
||||
status: 'pending',
|
||||
lastExecutionDate: new Date().toISOString(),
|
||||
error: null,
|
||||
},
|
||||
executionStatus: getAlertExecutionStatusPending(new Date().toISOString()),
|
||||
};
|
||||
|
||||
this.auditLogger?.log(
|
||||
|
|
|
@ -9,6 +9,7 @@ import { Logger } from 'src/core/server';
|
|||
import { AlertTaskState, AlertExecutionStatus, RawAlertExecutionStatus } from '../types';
|
||||
import { getReasonFromError } from './error_with_reason';
|
||||
import { getEsErrorMessage } from './errors';
|
||||
import { AlertExecutionStatuses } from '../../common';
|
||||
|
||||
export function executionStatusFromState(state: AlertTaskState): AlertExecutionStatus {
|
||||
const instanceIds = Object.keys(state.alertInstances ?? {});
|
||||
|
@ -66,3 +67,9 @@ export function alertExecutionStatusFromRaw(
|
|||
return { lastExecutionDate: parsedDate, status };
|
||||
}
|
||||
}
|
||||
|
||||
export const getAlertExecutionStatusPending = (lastExecutionDate: string) => ({
|
||||
status: 'pending' as AlertExecutionStatuses,
|
||||
lastExecutionDate,
|
||||
error: null,
|
||||
});
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* 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';
|
||||
import { getImportWarnings } from './get_import_warnings';
|
||||
|
||||
describe('getImportWarnings', () => {
|
||||
it('return warning message with total imported rules that have to be enabled', () => {
|
||||
const savedObjectRules = [
|
||||
{
|
||||
id: '1',
|
||||
type: 'alert',
|
||||
attributes: {
|
||||
enabled: true,
|
||||
name: 'rule-name1',
|
||||
tags: ['tag-1', 'tag-2'],
|
||||
alertTypeId: '123',
|
||||
consumer: 'alert-consumer',
|
||||
schedule: { interval: '1m' },
|
||||
actions: [],
|
||||
params: {},
|
||||
createdBy: 'me',
|
||||
updatedBy: 'me',
|
||||
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: true,
|
||||
name: 'rule-name2',
|
||||
tags: [],
|
||||
alertTypeId: '123',
|
||||
consumer: 'alert-consumer',
|
||||
schedule: { interval: '1m' },
|
||||
actions: [],
|
||||
params: {},
|
||||
createdBy: 'me',
|
||||
updatedBy: 'me',
|
||||
apiKey: '4tndskbuhewotw4klrhgjewrt9u',
|
||||
apiKeyOwner: 'me',
|
||||
throttle: null,
|
||||
notifyWhen: 'onActionGroupChange',
|
||||
muteAll: false,
|
||||
mutedInstanceIds: [],
|
||||
executionStatus: {
|
||||
status: 'pending',
|
||||
lastExecutionDate: '2020-08-20T19:23:38Z',
|
||||
error: null,
|
||||
},
|
||||
scheduledTaskId: '123',
|
||||
},
|
||||
references: [],
|
||||
},
|
||||
];
|
||||
const warnings = getImportWarnings(
|
||||
(savedObjectRules as unknown) as Array<SavedObject<RawAlert>>
|
||||
);
|
||||
expect(warnings[0].message).toBe('2 rules must be enabled after the import.');
|
||||
});
|
||||
|
||||
it('return no warning messages if no rules were imported', () => {
|
||||
const savedObjectRules = [] as Array<SavedObject<RawAlert>>;
|
||||
const warnings = getImportWarnings(
|
||||
(savedObjectRules as unknown) as Array<SavedObject<RawAlert>>
|
||||
);
|
||||
expect(warnings.length).toBe(0);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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 { i18n } from '@kbn/i18n';
|
||||
import { SavedObject, SavedObjectsImportWarning } from 'kibana/server';
|
||||
|
||||
export function getImportWarnings(
|
||||
rulesSavedObjects: Array<SavedObject<unknown>>
|
||||
): SavedObjectsImportWarning[] {
|
||||
if (rulesSavedObjects.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const message = i18n.translate('xpack.alerting.savedObjects.onImportText', {
|
||||
defaultMessage:
|
||||
'{rulesSavedObjectsLength} {rulesSavedObjectsLength, plural, one {rule} other {rules}} must be enabled after the import.',
|
||||
values: {
|
||||
rulesSavedObjectsLength: rulesSavedObjects.length,
|
||||
},
|
||||
});
|
||||
return [
|
||||
{
|
||||
type: 'action_required',
|
||||
message,
|
||||
actionPath: '/app/management/insightsAndAlerting/triggersActions/rules',
|
||||
buttonLabel: GO_TO_RULES_BUTTON_LABLE,
|
||||
} as SavedObjectsImportWarning,
|
||||
];
|
||||
}
|
||||
|
||||
export const GO_TO_RULES_BUTTON_LABLE = i18n.translate(
|
||||
'xpack.alerting.savedObjects.goToRulesButtonText',
|
||||
{ defaultMessage: 'Go to rules' }
|
||||
);
|
|
@ -14,6 +14,8 @@ import mappings from './mappings.json';
|
|||
import { getMigrations } from './migrations';
|
||||
import { EncryptedSavedObjectsPluginSetup } from '../../../encrypted_saved_objects/server';
|
||||
import { transformRulesForExport } from './transform_rule_for_export';
|
||||
import { RawAlert } from '../types';
|
||||
import { getImportWarnings } from './get_import_warnings';
|
||||
export { partiallyUpdateAlert } from './partially_update_alert';
|
||||
|
||||
export const AlertAttributesExcludedFromAAD = [
|
||||
|
@ -49,8 +51,13 @@ export function setupSavedObjects(
|
|||
mappings: mappings.alert,
|
||||
management: {
|
||||
importableAndExportable: true,
|
||||
getTitle(obj) {
|
||||
return `Rule: [${obj.attributes.name}]`;
|
||||
getTitle(ruleSavedObject: SavedObject<RawAlert>) {
|
||||
return `Rule: [${ruleSavedObject.attributes.name}]`;
|
||||
},
|
||||
onImport(ruleSavedObjects) {
|
||||
return {
|
||||
warnings: getImportWarnings(ruleSavedObjects),
|
||||
};
|
||||
},
|
||||
onExport<RawAlert>(
|
||||
context: SavedObjectsExportTransformContext,
|
||||
|
|
|
@ -6,7 +6,13 @@
|
|||
*/
|
||||
|
||||
import { transformRulesForExport } from './transform_rule_for_export';
|
||||
|
||||
jest.mock('../lib/alert_execution_status', () => ({
|
||||
getAlertExecutionStatusPending: () => ({
|
||||
status: 'pending',
|
||||
lastExecutionDate: '2020-08-20T19:23:38Z',
|
||||
error: null,
|
||||
}),
|
||||
}));
|
||||
describe('transform rule for export', () => {
|
||||
const date = new Date().toISOString();
|
||||
const mockRules = [
|
||||
|
@ -84,6 +90,11 @@ describe('transform rule for export', () => {
|
|||
apiKey: null,
|
||||
apiKeyOwner: null,
|
||||
scheduledTaskId: null,
|
||||
executionStatus: {
|
||||
status: 'pending',
|
||||
lastExecutionDate: '2020-08-20T19:23:38Z',
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
}))
|
||||
);
|
||||
|
|
|
@ -6,13 +6,18 @@
|
|||
*/
|
||||
|
||||
import { SavedObject } from 'kibana/server';
|
||||
import { getAlertExecutionStatusPending } from '../lib/alert_execution_status';
|
||||
import { RawAlert } from '../types';
|
||||
|
||||
export function transformRulesForExport(rules: SavedObject[]): Array<SavedObject<RawAlert>> {
|
||||
return rules.map((rule) => transformRuleForExport(rule as SavedObject<RawAlert>));
|
||||
const exportDate = new Date().toISOString();
|
||||
return rules.map((rule) => transformRuleForExport(rule as SavedObject<RawAlert>, exportDate));
|
||||
}
|
||||
|
||||
function transformRuleForExport(rule: SavedObject<RawAlert>): SavedObject<RawAlert> {
|
||||
function transformRuleForExport(
|
||||
rule: SavedObject<RawAlert>,
|
||||
exportDate: string
|
||||
): SavedObject<RawAlert> {
|
||||
return {
|
||||
...rule,
|
||||
attributes: {
|
||||
|
@ -21,6 +26,7 @@ function transformRuleForExport(rule: SavedObject<RawAlert>): SavedObject<RawAle
|
|||
apiKey: null,
|
||||
apiKeyOwner: null,
|
||||
scheduledTaskId: null,
|
||||
executionStatus: getAlertExecutionStatusPending(exportDate),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ export const AddConnectorInline = ({
|
|||
<EuiText color="danger">
|
||||
<FormattedMessage
|
||||
id="xpack.triggersActionsUI.sections.alertForm.unableToLoadConnectorTitle"
|
||||
defaultMessage="Unable to load connector."
|
||||
defaultMessage="Unable to load connector"
|
||||
/>
|
||||
</EuiText>
|
||||
);
|
||||
|
@ -196,7 +196,7 @@ export const AddConnectorInline = ({
|
|||
data-test-subj={`alertActionAccordionErrorTooltip`}
|
||||
content={
|
||||
<FormattedMessage
|
||||
defaultMessage="Unable to load connector."
|
||||
defaultMessage="Unable to load connector"
|
||||
id="xpack.triggersActionsUI.sections.alertForm.unableToLoadConnectorTitle'"
|
||||
/>
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue