Skip to content

Commit 9ba3ee3

Browse files
authored
[Alerting UI] Fixed a bad UX for xpack.actions.enabled is set as false. UI should show the proper message instead of the endless spinner. (#89043)
* [Alerts][Actions] Changed isESOUsingEphemeralEncryptionKey determination. Set ESO plugin as an optional dependancy for actions and alerts plugins. * fixed faling typechecks * fixed faling typechecks * fixed health framework status message * fixed due to comments * fixed faling test * changed approach * fixed due to comments * fixed due to comments * fixed tests * fixed tests * fixed tests * fixed wrong commit * fixed lang issue * Fixed to remove eso check * Fixed tests * Fixed due to comments.
1 parent 46c9e64 commit 9ba3ee3

15 files changed

Lines changed: 280 additions & 88 deletions

File tree

x-pack/plugins/translations/translations/ja-JP.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21446,9 +21446,6 @@
2144621446
"xpack.triggersActionsUI.components.healthCheck.encryptionErrorAfterKey": " kibana.ymlファイルで",
2144721447
"xpack.triggersActionsUI.components.healthCheck.encryptionErrorBeforeKey": "アラートを作成するには、値を設定します ",
2144821448
"xpack.triggersActionsUI.components.healthCheck.encryptionErrorTitle": "暗号化鍵を設定する必要があります",
21449-
"xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionError": "KibanaとElasticsearchの間でトランスポートレイヤーセキュリティを有効にし、kibana.ymlファイルで暗号化鍵を構成する必要があります。",
21450-
"xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionErrorAction": "方法を学習",
21451-
"xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionErrorTitle": "追加の設定が必要です",
2145221449
"xpack.triggersActionsUI.components.healthCheck.tlsError": "アラートはAPIキーに依存し、キーを使用するにはElasticsearchとKibanaの間にTLSが必要です。",
2145321450
"xpack.triggersActionsUI.components.healthCheck.tlsErrorAction": "TLSを有効にする方法をご覧ください。",
2145421451
"xpack.triggersActionsUI.components.healthCheck.tlsErrorTitle": "トランスポートレイヤーセキュリティを有効にする必要があります",

x-pack/plugins/translations/translations/zh-CN.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21496,9 +21496,6 @@
2149621496
"xpack.triggersActionsUI.components.healthCheck.encryptionErrorAfterKey": " 。",
2149721497
"xpack.triggersActionsUI.components.healthCheck.encryptionErrorBeforeKey": "要创建告警,请在 kibana.yml 文件中设置以下项的值: ",
2149821498
"xpack.triggersActionsUI.components.healthCheck.encryptionErrorTitle": "必须设置加密密钥",
21499-
"xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionError": "必须在 Kibana 和 Elasticsearch 之间启用传输层安全并在 kibana.yml 文件中配置加密密钥。",
21500-
"xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionErrorAction": "了解操作方法",
21501-
"xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionErrorTitle": "需要其他设置",
2150221499
"xpack.triggersActionsUI.components.healthCheck.tlsError": "Alerting 功能依赖于 API 密钥,这需要在 Elasticsearch 与 Kibana 之间启用 TLS。",
2150321500
"xpack.triggersActionsUI.components.healthCheck.tlsErrorAction": "了解如何启用 TLS。",
2150421501
"xpack.triggersActionsUI.components.healthCheck.tlsErrorTitle": "必须启用传输层安全",

x-pack/plugins/triggers_actions_ui/public/application/components/health_check.test.tsx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,11 @@ describe('health check', () => {
5656
});
5757

5858
it('renders children if keys are enabled', async () => {
59-
useKibanaMock().services.http.get = jest
60-
.fn()
61-
.mockResolvedValue({ isSufficientlySecure: true, hasPermanentEncryptionKey: true });
59+
useKibanaMock().services.http.get = jest.fn().mockResolvedValue({
60+
isSufficientlySecure: true,
61+
hasPermanentEncryptionKey: true,
62+
isAlertsAvailable: true,
63+
});
6264
const { queryByText } = render(
6365
<HealthContextProvider>
6466
<HealthCheck waitForCheck={true}>
@@ -72,10 +74,11 @@ describe('health check', () => {
7274
expect(queryByText('should render')).toBeInTheDocument();
7375
});
7476

75-
test('renders warning if keys are disabled', async () => {
76-
useKibanaMock().services.http.get = jest.fn().mockImplementationOnce(async () => ({
77+
test('renders warning if TLS is required', async () => {
78+
useKibanaMock().services.http.get = jest.fn().mockImplementation(async () => ({
7779
isSufficientlySecure: false,
7880
hasPermanentEncryptionKey: true,
81+
isAlertsAvailable: true,
7982
}));
8083
const { queryAllByText } = render(
8184
<HealthContextProvider>
@@ -104,9 +107,10 @@ describe('health check', () => {
104107
});
105108

106109
test('renders warning if encryption key is ephemeral', async () => {
107-
useKibanaMock().services.http.get = jest.fn().mockImplementationOnce(async () => ({
110+
useKibanaMock().services.http.get = jest.fn().mockImplementation(async () => ({
108111
isSufficientlySecure: true,
109112
hasPermanentEncryptionKey: false,
113+
isAlertsAvailable: true,
110114
}));
111115
const { queryByText, queryByRole } = render(
112116
<HealthContextProvider>
@@ -121,7 +125,7 @@ describe('health check', () => {
121125

122126
const description = queryByRole(/banner/i);
123127
expect(description!.textContent).toMatchInlineSnapshot(
124-
`"To create an alert, set a value for xpack.encryptedSavedObjects.encryptionKey in your kibana.yml file. Learn how.(opens in a new tab or window)"`
128+
`"To create an alert, set a value for xpack.encryptedSavedObjects.encryptionKey in your kibana.yml file and ensure the Encrypted Saved Objects plugin is enabled. Learn how.(opens in a new tab or window)"`
125129
);
126130

127131
const action = queryByText(/Learn/i);
@@ -132,9 +136,10 @@ describe('health check', () => {
132136
});
133137

134138
test('renders warning if encryption key is ephemeral and keys are disabled', async () => {
135-
useKibanaMock().services.http.get = jest.fn().mockImplementationOnce(async () => ({
139+
useKibanaMock().services.http.get = jest.fn().mockImplementation(async () => ({
136140
isSufficientlySecure: false,
137141
hasPermanentEncryptionKey: false,
142+
isAlertsAvailable: true,
138143
}));
139144

140145
const { queryByText } = render(

x-pack/plugins/triggers_actions_ui/public/application/components/health_check.tsx

Lines changed: 106 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,49 @@ import { i18n } from '@kbn/i18n';
1414

1515
import { EuiEmptyPrompt, EuiCode } from '@elastic/eui';
1616
import { DocLinksStart } from 'kibana/public';
17-
import { AlertingFrameworkHealth } from '../../types';
18-
import { health } from '../lib/alert_api';
17+
import { alertingFrameworkHealth } from '../lib/alert_api';
1918
import './health_check.scss';
2019
import { useHealthContext } from '../context/health_context';
2120
import { useKibana } from '../../common/lib/kibana';
2221
import { CenterJustifiedSpinner } from './center_justified_spinner';
22+
import { triggersActionsUiHealth } from '../../common/lib/health_api';
2323

2424
interface Props {
2525
inFlyout?: boolean;
2626
waitForCheck: boolean;
2727
}
2828

29+
interface HealthStatus {
30+
isAlertsAvailable: boolean;
31+
isSufficientlySecure: boolean;
32+
hasPermanentEncryptionKey: boolean;
33+
}
34+
2935
export const HealthCheck: React.FunctionComponent<Props> = ({
3036
children,
3137
waitForCheck,
3238
inFlyout = false,
3339
}) => {
3440
const { http, docLinks } = useKibana().services;
3541
const { setLoadingHealthCheck } = useHealthContext();
36-
const [alertingHealth, setAlertingHealth] = React.useState<Option<AlertingFrameworkHealth>>(none);
42+
const [alertingHealth, setAlertingHealth] = React.useState<Option<HealthStatus>>(none);
3743

3844
React.useEffect(() => {
3945
(async function () {
4046
setLoadingHealthCheck(true);
41-
setAlertingHealth(some(await health({ http })));
47+
const triggersActionsUiHealthStatus = await triggersActionsUiHealth({ http });
48+
const healthStatus: HealthStatus = {
49+
...triggersActionsUiHealthStatus,
50+
isSufficientlySecure: false,
51+
hasPermanentEncryptionKey: false,
52+
};
53+
if (healthStatus.isAlertsAvailable) {
54+
const alertingHealthResult = await alertingFrameworkHealth({ http });
55+
healthStatus.isSufficientlySecure = alertingHealthResult.isSufficientlySecure;
56+
healthStatus.hasPermanentEncryptionKey = alertingHealthResult.hasPermanentEncryptionKey;
57+
}
58+
59+
setAlertingHealth(some(healthStatus));
4260
setLoadingHealthCheck(false);
4361
})();
4462
}, [http, setLoadingHealthCheck]);
@@ -60,6 +78,8 @@ export const HealthCheck: React.FunctionComponent<Props> = ({
6078
(healthCheck) => {
6179
return healthCheck?.isSufficientlySecure && healthCheck?.hasPermanentEncryptionKey ? (
6280
<Fragment>{children}</Fragment>
81+
) : !healthCheck.isAlertsAvailable ? (
82+
<AlertsError docLinks={docLinks} className={className} />
6383
) : !healthCheck.isSufficientlySecure && !healthCheck.hasPermanentEncryptionKey ? (
6484
<TlsAndEncryptionError docLinks={docLinks} className={className} />
6585
) : !healthCheck.hasPermanentEncryptionKey ? (
@@ -77,7 +97,7 @@ interface PromptErrorProps {
7797
className?: string;
7898
}
7999

80-
const TlsAndEncryptionError = ({
100+
const EncryptionError = ({
81101
// eslint-disable-next-line @typescript-eslint/naming-convention
82102
docLinks: { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION },
83103
className,
@@ -90,27 +110,37 @@ const TlsAndEncryptionError = ({
90110
title={
91111
<h2>
92112
<FormattedMessage
93-
id="xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionErrorTitle"
94-
defaultMessage="Additional setup required"
113+
id="xpack.triggersActionsUI.components.healthCheck.encryptionErrorTitle"
114+
defaultMessage="Encrypted saved objects are not available"
95115
/>
96116
</h2>
97117
}
98118
body={
99119
<div className={`${className}__body`}>
100120
<p role="banner">
101-
{i18n.translate('xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionError', {
102-
defaultMessage:
103-
'You must enable Transport Layer Security between Kibana and Elasticsearch and configure an encryption key in your kibana.yml file. ',
104-
})}
121+
{i18n.translate(
122+
'xpack.triggersActionsUI.components.healthCheck.encryptionErrorBeforeKey',
123+
{
124+
defaultMessage: 'To create an alert, set a value for ',
125+
}
126+
)}
127+
<EuiCode>{'xpack.encryptedSavedObjects.encryptionKey'}</EuiCode>
128+
{i18n.translate(
129+
'xpack.triggersActionsUI.components.healthCheck.encryptionErrorAfterKey',
130+
{
131+
defaultMessage:
132+
' in your kibana.yml file and ensure the Encrypted Saved Objects plugin is enabled. ',
133+
}
134+
)}
105135
<EuiLink
106-
href={`${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/alerting-getting-started.html#alerting-setup-prerequisites`}
136+
href={`${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/alert-action-settings-kb.html#general-alert-action-settings`}
107137
external
108138
target="_blank"
109139
>
110140
{i18n.translate(
111-
'xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionErrorAction',
141+
'xpack.triggersActionsUI.components.healthCheck.encryptionErrorAction',
112142
{
113-
defaultMessage: 'Learn how',
143+
defaultMessage: 'Learn how.',
114144
}
115145
)}
116146
</EuiLink>
@@ -120,7 +150,7 @@ const TlsAndEncryptionError = ({
120150
/>
121151
);
122152

123-
const EncryptionError = ({
153+
const TlsError = ({
124154
// eslint-disable-next-line @typescript-eslint/naming-convention
125155
docLinks: { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION },
126156
className,
@@ -133,46 +163,73 @@ const EncryptionError = ({
133163
title={
134164
<h2>
135165
<FormattedMessage
136-
id="xpack.triggersActionsUI.components.healthCheck.encryptionErrorTitle"
137-
defaultMessage="You must set an encryption key"
166+
id="xpack.triggersActionsUI.components.healthCheck.tlsErrorTitle"
167+
defaultMessage="You must enable Transport Layer Security"
138168
/>
139169
</h2>
140170
}
141171
body={
142172
<div className={`${className}__body`}>
143173
<p role="banner">
144-
{i18n.translate(
145-
'xpack.triggersActionsUI.components.healthCheck.encryptionErrorBeforeKey',
146-
{
147-
defaultMessage: 'To create an alert, set a value for ',
148-
}
149-
)}
150-
<EuiCode>{'xpack.encryptedSavedObjects.encryptionKey'}</EuiCode>
151-
{i18n.translate(
152-
'xpack.triggersActionsUI.components.healthCheck.encryptionErrorAfterKey',
153-
{
154-
defaultMessage: ' in your kibana.yml file. ',
155-
}
156-
)}
174+
{i18n.translate('xpack.triggersActionsUI.components.healthCheck.tlsError', {
175+
defaultMessage:
176+
'Alerting relies on API keys, which require TLS between Elasticsearch and Kibana. ',
177+
})}
157178
<EuiLink
158-
href={`${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/alert-action-settings-kb.html#general-alert-action-settings`}
179+
href={`${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/configuring-tls.html`}
159180
external
160181
target="_blank"
161182
>
162-
{i18n.translate(
163-
'xpack.triggersActionsUI.components.healthCheck.encryptionErrorAction',
164-
{
165-
defaultMessage: 'Learn how.',
166-
}
167-
)}
183+
{i18n.translate('xpack.triggersActionsUI.components.healthCheck.tlsErrorAction', {
184+
defaultMessage: 'Learn how to enable TLS.',
185+
})}
168186
</EuiLink>
169187
</p>
170188
</div>
171189
}
172190
/>
173191
);
174192

175-
const TlsError = ({
193+
const AlertsError = ({
194+
// eslint-disable-next-line @typescript-eslint/naming-convention
195+
docLinks: { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION },
196+
className,
197+
}: PromptErrorProps) => (
198+
<EuiEmptyPrompt
199+
iconType="watchesApp"
200+
data-test-subj="alertsNeededEmptyPrompt"
201+
className={className}
202+
titleSize="xs"
203+
title={
204+
<h2>
205+
<FormattedMessage
206+
id="xpack.triggersActionsUI.components.healthCheck.alertsErrorTitle"
207+
defaultMessage="You must enable Alerts and Actions"
208+
/>
209+
</h2>
210+
}
211+
body={
212+
<div className={`${className}__body`}>
213+
<p role="banner">
214+
{i18n.translate('xpack.triggersActionsUI.components.healthCheck.alertsError', {
215+
defaultMessage: 'To create an alert, set alerts and actions plugins enabled. ',
216+
})}
217+
<EuiLink
218+
href={`${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/alert-action-settings-kb.html`}
219+
external
220+
target="_blank"
221+
>
222+
{i18n.translate('xpack.triggersActionsUI.components.healthCheck.alertsErrorAction', {
223+
defaultMessage: 'Learn how to enable Alerts and Actions.',
224+
})}
225+
</EuiLink>
226+
</p>
227+
</div>
228+
}
229+
/>
230+
);
231+
232+
const TlsAndEncryptionError = ({
176233
// eslint-disable-next-line @typescript-eslint/naming-convention
177234
docLinks: { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION },
178235
className,
@@ -185,26 +242,29 @@ const TlsError = ({
185242
title={
186243
<h2>
187244
<FormattedMessage
188-
id="xpack.triggersActionsUI.components.healthCheck.tlsErrorTitle"
189-
defaultMessage="You must enable Transport Layer Security"
245+
id="xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionErrorTitle"
246+
defaultMessage="Additional setup required"
190247
/>
191248
</h2>
192249
}
193250
body={
194251
<div className={`${className}__body`}>
195252
<p role="banner">
196-
{i18n.translate('xpack.triggersActionsUI.components.healthCheck.tlsError', {
253+
{i18n.translate('xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionError', {
197254
defaultMessage:
198-
'Alerting relies on API keys, which require TLS between Elasticsearch and Kibana. ',
255+
'You must enable Transport Layer Security between Kibana and Elasticsearch and configure an encryption key in your kibana.yml file. ',
199256
})}
200257
<EuiLink
201-
href={`${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/configuring-tls.html`}
258+
href={`${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/alerting-getting-started.html#alerting-setup-prerequisites`}
202259
external
203260
target="_blank"
204261
>
205-
{i18n.translate('xpack.triggersActionsUI.components.healthCheck.tlsErrorAction', {
206-
defaultMessage: 'Learn how to enable TLS.',
207-
})}
262+
{i18n.translate(
263+
'xpack.triggersActionsUI.components.healthCheck.tlsAndEncryptionErrorAction',
264+
{
265+
defaultMessage: 'Learn how',
266+
}
267+
)}
208268
</EuiLink>
209269
</p>
210270
</div>

x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
updateAlert,
2626
muteAlertInstance,
2727
unmuteAlertInstance,
28-
health,
28+
alertingFrameworkHealth,
2929
mapFiltersToKql,
3030
} from './alert_api';
3131
import uuid from 'uuid';
@@ -801,9 +801,9 @@ describe('unmuteAlerts', () => {
801801
});
802802
});
803803

804-
describe('health', () => {
805-
test('should call health API', async () => {
806-
const result = await health({ http });
804+
describe('alertingFrameworkHealth', () => {
805+
test('should call alertingFrameworkHealth API', async () => {
806+
const result = await alertingFrameworkHealth({ http });
807807
expect(result).toEqual(undefined);
808808
expect(http.get.mock.calls).toMatchInlineSnapshot(`
809809
Array [

x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,10 @@ export async function unmuteAlerts({
282282
await Promise.all(ids.map((id) => unmuteAlert({ id, http })));
283283
}
284284

285-
export async function health({ http }: { http: HttpSetup }): Promise<AlertingFrameworkHealth> {
285+
export async function alertingFrameworkHealth({
286+
http,
287+
}: {
288+
http: HttpSetup;
289+
}): Promise<AlertingFrameworkHealth> {
286290
return await http.get(`${BASE_ALERT_API_PATH}/_health`);
287291
}

x-pack/plugins/triggers_actions_ui/public/application/sections/alert_form/alert_add.test.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,14 @@ jest.mock('../../../common/lib/kibana');
2525

2626
jest.mock('../../lib/alert_api', () => ({
2727
loadAlertTypes: jest.fn(),
28-
health: jest.fn(() => ({ isSufficientlySecure: true, hasPermanentEncryptionKey: true })),
28+
alertingFrameworkHealth: jest.fn(() => ({
29+
isSufficientlySecure: true,
30+
hasPermanentEncryptionKey: true,
31+
})),
32+
}));
33+
34+
jest.mock('../../../common/lib/health_api', () => ({
35+
triggersActionsUiHealth: jest.fn(() => ({ isAlertsAvailable: true })),
2936
}));
3037

3138
const actionTypeRegistry = actionTypeRegistryMock.create();

0 commit comments

Comments
 (0)