Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Alerting] License Errors on Alert List View #89920

Merged
merged 28 commits into from
Feb 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f82f5b3
Adding tooltips to alert list and modal for license upgrade
ymao1 Jan 29, 2021
c3e350b
Merge branch 'master' of https://github.com/elastic/kibana into alert…
ymao1 Feb 1, 2021
c817594
Fixing typings
ymao1 Feb 1, 2021
4546870
Merge branch 'master' of https://github.com/elastic/kibana into alert…
ymao1 Feb 1, 2021
9e3055c
Custom License Error status. Moving modal to alerts list page
ymao1 Feb 1, 2021
09af8f1
Adding unit test
ymao1 Feb 2, 2021
bcde1da
Cleanup
ymao1 Feb 2, 2021
0c2ad1b
Merge branch 'master' of https://github.com/elastic/kibana into alert…
ymao1 Feb 2, 2021
5188f14
Unit tests
ymao1 Feb 2, 2021
bd42c6f
Merge branch 'master' of https://github.com/elastic/kibana into alert…
ymao1 Feb 2, 2021
e5acc75
Removing tooltip from alert name
ymao1 Feb 2, 2021
e985bd8
Merge branch 'master' of https://github.com/elastic/kibana into alert…
ymao1 Feb 4, 2021
aa05bb5
License
ymao1 Feb 4, 2021
a31895c
Merge branch 'master' of https://github.com/elastic/kibana into alert…
ymao1 Feb 5, 2021
95f6333
PR fixes
ymao1 Feb 5, 2021
391ad6e
Merge branch 'master' into alerting/licensing-ux
kibanamachine Feb 8, 2021
e84d0ae
Merge branch 'master' of https://github.com/elastic/kibana into alert…
ymao1 Feb 9, 2021
6f85e7d
Merge branch 'alerting/licensing-ux' of https://github.com/ymao1/kiba…
ymao1 Feb 9, 2021
4a134a7
Updating modal wording
ymao1 Feb 9, 2021
50910cd
Merge branch 'master' into alerting/licensing-ux
kibanamachine Feb 9, 2021
dadd4f2
Merge branch 'master' of https://github.com/elastic/kibana into alert…
ymao1 Feb 9, 2021
9a1fccc
Updating license state error message
ymao1 Feb 10, 2021
48db829
Merge branch 'alerting/licensing-ux' of https://github.com/ymao1/kiba…
ymao1 Feb 10, 2021
a1df060
Merge branch 'master' into alerting/licensing-ux
kibanamachine Feb 10, 2021
f836c28
Merge branch 'master' of https://github.com/elastic/kibana into alert…
ymao1 Feb 10, 2021
ab2bb14
i18n fix
ymao1 Feb 10, 2021
5c8aa76
Fixing functional test
ymao1 Feb 10, 2021
44fedc4
Merge branch 'master' of https://github.com/elastic/kibana into alert…
ymao1 Feb 10, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion x-pack/plugins/alerts/server/lib/license_state.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ describe('ensureLicenseForAlertType()', () => {
expect(() =>
licenseState.ensureLicenseForAlertType(alertType)
).toThrowErrorMatchingInlineSnapshot(
`"Alert test is disabled because it requires a Gold license. Contact your administrator to upgrade your license."`
`"Alert test is disabled because it requires a Gold license. Go to License Management to view upgrade options."`
);
});

Expand Down
8 changes: 6 additions & 2 deletions x-pack/plugins/alerts/server/lib/license_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Boom from '@hapi/boom';
import { i18n } from '@kbn/i18n';
import type { PublicMethodsOf } from '@kbn/utility-types';
import { assertNever } from '@kbn/std';
import { capitalize } from 'lodash';
import { Observable, Subscription } from 'rxjs';
import { LicensingPluginStart } from '../../../licensing/server';
import { ILicense, LicenseType } from '../../../licensing/common/types';
Expand Down Expand Up @@ -190,8 +191,11 @@ export class LicenseState {
throw new AlertTypeDisabledError(
i18n.translate('xpack.alerts.serverSideErrors.invalidLicenseErrorMessage', {
defaultMessage:
'Alert {alertTypeId} is disabled because it requires a Gold license. Contact your administrator to upgrade your license.',
values: { alertTypeId: alertType.id },
'Alert {alertTypeId} is disabled because it requires a {licenseType} license. Go to License Management to view upgrade options.',
values: {
alertTypeId: alertType.id,
licenseType: capitalize(alertType.minimumLicenseRequired),
},
}),
'license_invalid'
);
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -4830,7 +4830,6 @@
"xpack.alerts.server.healthStatus.degraded": "アラートフレームワークは劣化しました",
"xpack.alerts.server.healthStatus.unavailable": "アラートフレームワークを使用できません",
"xpack.alerts.serverSideErrors.expirerdLicenseErrorMessage": "{licenseType} ライセンスの期限が切れたのでアラートタイプ {alertTypeId} は無効です。",
"xpack.alerts.serverSideErrors.invalidLicenseErrorMessage": "アラート {alertTypeId} は無効です。Gold ライセンスが必要です。ライセンスをアップグレードするには、管理者に問い合わせてください。",
"xpack.alerts.serverSideErrors.unavailableLicenseErrorMessage": "現時点でライセンス情報を入手できないため、アラートタイプ {alertTypeId} は無効です。",
"xpack.alerts.serverSideErrors.unavailableLicenseInformationErrorMessage": "アラートを利用できません。現在ライセンス情報が利用できません。",
"xpack.apm.a.thresholdMet": "しきい値一致",
Expand Down
1 change: 0 additions & 1 deletion x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -4836,7 +4836,6 @@
"xpack.alerts.server.healthStatus.degraded": "告警框架已降级",
"xpack.alerts.server.healthStatus.unavailable": "告警框架不可用",
"xpack.alerts.serverSideErrors.expirerdLicenseErrorMessage": "告警类型 {alertTypeId} 已禁用,因为您的{licenseType}许可证已过期。",
"xpack.alerts.serverSideErrors.invalidLicenseErrorMessage": "告警 {alertTypeId} 已禁用,因为它需要黄金级许可证。请联系管理员升级您的许可证。",
"xpack.alerts.serverSideErrors.unavailableLicenseErrorMessage": "告警类型 {alertTypeId} 已禁用,因为许可证信息当前不可用。",
"xpack.alerts.serverSideErrors.unavailableLicenseInformationErrorMessage": "告警不可用 - 许可信息当前不可用。",
"xpack.apm.a.thresholdMet": "已达到阈值",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,116 +127,148 @@ describe('alerts_list component empty', () => {

wrapper.find('button[data-test-subj="createFirstAlertButton"]').simulate('click');

// When the AlertAdd component is rendered, it waits for the healthcheck to resolve
await new Promise((resolve) => {
setTimeout(resolve, 1000);
await act(async () => {
// When the AlertAdd component is rendered, it waits for the healthcheck to resolve
await new Promise((resolve) => {
setTimeout(resolve, 1000);
pmuellr marked this conversation as resolved.
Show resolved Hide resolved
});

await nextTick();
wrapper.update();
});
wrapper.update();

expect(wrapper.find('AlertAdd').exists()).toEqual(true);
});
});

describe('alerts_list component with items', () => {
let wrapper: ReactWrapper<any>;

const mockedAlertsData = [
pmuellr marked this conversation as resolved.
Show resolved Hide resolved
{
id: '1',
name: 'test alert',
tags: ['tag1'],
enabled: true,
alertTypeId: 'test_alert_type',
schedule: { interval: '5d' },
actions: [],
params: { name: 'test alert type name' },
scheduledTaskId: null,
createdBy: null,
updatedBy: null,
apiKeyOwner: null,
throttle: '1m',
muteAll: false,
mutedInstanceIds: [],
executionStatus: {
status: 'active',
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
error: null,
},
},
{
id: '2',
name: 'test alert ok',
tags: ['tag1'],
enabled: true,
alertTypeId: 'test_alert_type',
schedule: { interval: '5d' },
actions: [],
params: { name: 'test alert type name' },
scheduledTaskId: null,
createdBy: null,
updatedBy: null,
apiKeyOwner: null,
throttle: '1m',
muteAll: false,
mutedInstanceIds: [],
executionStatus: {
status: 'ok',
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
error: null,
},
},
{
id: '3',
name: 'test alert pending',
tags: ['tag1'],
enabled: true,
alertTypeId: 'test_alert_type',
schedule: { interval: '5d' },
actions: [],
params: { name: 'test alert type name' },
scheduledTaskId: null,
createdBy: null,
updatedBy: null,
apiKeyOwner: null,
throttle: '1m',
muteAll: false,
mutedInstanceIds: [],
executionStatus: {
status: 'pending',
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
error: null,
},
},
{
id: '4',
name: 'test alert error',
tags: ['tag1'],
enabled: true,
alertTypeId: 'test_alert_type',
schedule: { interval: '5d' },
actions: [{ id: 'test', group: 'alert', params: { message: 'test' } }],
params: { name: 'test alert type name' },
scheduledTaskId: null,
createdBy: null,
updatedBy: null,
apiKeyOwner: null,
throttle: '1m',
muteAll: false,
mutedInstanceIds: [],
executionStatus: {
status: 'error',
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
error: {
reason: AlertExecutionStatusErrorReasons.Unknown,
message: 'test',
},
},
},
{
id: '5',
name: 'test alert license error',
tags: ['tag1'],
enabled: true,
alertTypeId: 'test_alert_type',
schedule: { interval: '5d' },
actions: [{ id: 'test', group: 'alert', params: { message: 'test' } }],
params: { name: 'test alert type name' },
scheduledTaskId: null,
createdBy: null,
updatedBy: null,
apiKeyOwner: null,
throttle: '1m',
muteAll: false,
mutedInstanceIds: [],
executionStatus: {
status: 'error',
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
error: {
reason: AlertExecutionStatusErrorReasons.License,
message: 'test',
},
},
},
];

async function setup() {
loadAlerts.mockResolvedValue({
page: 1,
perPage: 10000,
total: 4,
data: [
{
id: '1',
name: 'test alert',
tags: ['tag1'],
enabled: true,
alertTypeId: 'test_alert_type',
schedule: { interval: '5d' },
actions: [],
params: { name: 'test alert type name' },
scheduledTaskId: null,
createdBy: null,
updatedBy: null,
apiKeyOwner: null,
throttle: '1m',
muteAll: false,
mutedInstanceIds: [],
executionStatus: {
status: 'active',
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
error: null,
},
},
{
id: '2',
name: 'test alert ok',
tags: ['tag1'],
enabled: true,
alertTypeId: 'test_alert_type',
schedule: { interval: '5d' },
actions: [],
params: { name: 'test alert type name' },
scheduledTaskId: null,
createdBy: null,
updatedBy: null,
apiKeyOwner: null,
throttle: '1m',
muteAll: false,
mutedInstanceIds: [],
executionStatus: {
status: 'ok',
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
error: null,
},
},
{
id: '3',
name: 'test alert pending',
tags: ['tag1'],
enabled: true,
alertTypeId: 'test_alert_type',
schedule: { interval: '5d' },
actions: [],
params: { name: 'test alert type name' },
scheduledTaskId: null,
createdBy: null,
updatedBy: null,
apiKeyOwner: null,
throttle: '1m',
muteAll: false,
mutedInstanceIds: [],
executionStatus: {
status: 'pending',
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
error: null,
},
},
{
id: '4',
name: 'test alert error',
tags: ['tag1'],
enabled: true,
alertTypeId: 'test_alert_type',
schedule: { interval: '5d' },
actions: [{ id: 'test', group: 'alert', params: { message: 'test' } }],
params: { name: 'test alert type name' },
scheduledTaskId: null,
createdBy: null,
updatedBy: null,
apiKeyOwner: null,
throttle: '1m',
muteAll: false,
mutedInstanceIds: [],
executionStatus: {
status: 'error',
lastExecutionDate: new Date('2020-08-20T19:23:38Z'),
error: {
reason: AlertExecutionStatusErrorReasons.Unknown,
message: 'test',
},
},
},
],
data: mockedAlertsData,
});
loadActionTypes.mockResolvedValue([
{
Expand Down Expand Up @@ -271,21 +303,66 @@ describe('alerts_list component with items', () => {
it('renders table of alerts', async () => {
await setup();
expect(wrapper.find('EuiBasicTable')).toHaveLength(1);
expect(wrapper.find('EuiTableRow')).toHaveLength(4);
expect(wrapper.find('[data-test-subj="alertsTableCell-status"]').length).toBeGreaterThan(0);
expect(wrapper.find('[data-test-subj="alertStatus-active"]').length).toBeGreaterThan(0);
expect(wrapper.find('[data-test-subj="alertStatus-error"]').length).toBeGreaterThan(0);
expect(wrapper.find('[data-test-subj="alertStatus-ok"]').length).toBeGreaterThan(0);
expect(wrapper.find('[data-test-subj="alertStatus-pending"]').length).toBeGreaterThan(0);
expect(wrapper.find('[data-test-subj="alertStatus-unknown"]').length).toBe(0);
expect(wrapper.find('EuiTableRow')).toHaveLength(mockedAlertsData.length);
expect(wrapper.find('EuiTableRowCell[data-test-subj="alertsTableCell-status"]').length).toEqual(
mockedAlertsData.length
);
expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-active"]').length).toEqual(1);
expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-ok"]').length).toEqual(1);
expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-pending"]').length).toEqual(1);
expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-unknown"]').length).toEqual(0);

expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-error"]').length).toEqual(2);
expect(wrapper.find('[data-test-subj="alertStatus-error-tooltip"]').length).toEqual(2);
expect(
wrapper.find('EuiButtonEmpty[data-test-subj="alertStatus-error-license-fix"]').length
).toEqual(1);

expect(wrapper.find('[data-test-subj="refreshAlertsButton"]').exists()).toBeTruthy();

expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-error"]').first().text()).toEqual(
'Error'
);
expect(wrapper.find('EuiHealth[data-test-subj="alertStatus-error"]').last().text()).toEqual(
'License Error'
);
});

it('loads alerts when refresh button is clicked', async () => {
await setup();
wrapper.find('[data-test-subj="refreshAlertsButton"]').first().simulate('click');

await act(async () => {
await nextTick();
wrapper.update();
});

expect(loadAlerts).toHaveBeenCalled();
});

it('renders license errors and manage license modal on click', async () => {
global.open = jest.fn();
await setup();
expect(wrapper.find('ManageLicenseModal').exists()).toBeFalsy();
expect(
wrapper.find('EuiButtonEmpty[data-test-subj="alertStatus-error-license-fix"]').length
).toEqual(1);
wrapper
.find('EuiButtonEmpty[data-test-subj="alertStatus-error-license-fix"]')
.simulate('click');

await act(async () => {
await nextTick();
wrapper.update();
});

expect(wrapper.find('ManageLicenseModal').exists()).toBeTruthy();
expect(wrapper.find('EuiButton[data-test-subj="confirmModalConfirmButton"]').text()).toEqual(
'Manage license'
);
wrapper.find('EuiButton[data-test-subj="confirmModalConfirmButton"]').simulate('click');
expect(global.open).toHaveBeenCalled();
});
});

describe('alerts_list component empty with show only capability', () => {
Expand All @@ -308,7 +385,9 @@ describe('alerts_list component empty with show only capability', () => {
name: 'Test2',
},
]);
loadAlertTypes.mockResolvedValue([{ id: 'test_alert_type', name: 'some alert type' }]);
loadAlertTypes.mockResolvedValue([
{ id: 'test_alert_type', name: 'some alert type', authorizedConsumers: {} },
]);
loadAllActions.mockResolvedValue([]);
// eslint-disable-next-line react-hooks/rules-of-hooks
useKibanaMock().services.alertTypeRegistry = alertTypeRegistry;
Expand Down
Loading