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 UI] Don't wait for health check before showing Create Alert flyout #80996

Merged
merged 11 commits into from
Oct 26, 2020
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/ja-JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -20272,15 +20272,13 @@
"xpack.triggersActionsUI.sections.addModalConnectorForm.saveButtonLabel": "保存",
"xpack.triggersActionsUI.sections.addModalConnectorForm.updateSuccessNotificationText": "「{connectorName}」を作成しました",
"xpack.triggersActionsUI.sections.alertAdd.betaBadgeTooltipContent": "{pluginName} はベータ段階で、変更される可能性があります。デザインとコードはオフィシャル GA 機能よりも完成度が低く、現状のまま保証なしで提供されています。ベータ機能にはオフィシャル GA 機能の SLA が適用されません。",
"xpack.triggersActionsUI.sections.alertAdd.cancelButtonLabel": "キャンセル",
"xpack.triggersActionsUI.sections.alertAdd.conditionPrompt": "条件を定義してください",
"xpack.triggersActionsUI.sections.alertAdd.errorLoadingAlertVisualizationTitle": "アラートビジュアライゼーションを読み込めません",
"xpack.triggersActionsUI.sections.alertAdd.flyoutTitle": "アラートの作成",
"xpack.triggersActionsUI.sections.alertAdd.geoThreshold.closePopoverLabel": "閉じる",
"xpack.triggersActionsUI.sections.alertAdd.loadingAlertVisualizationDescription": "アラートビジュアライゼーションを読み込み中...",
"xpack.triggersActionsUI.sections.alertAdd.operationName": "作成",
"xpack.triggersActionsUI.sections.alertAdd.previewAlertVisualizationDescription": "プレビューを生成するための式を完成します。",
"xpack.triggersActionsUI.sections.alertAdd.saveButtonLabel": "保存",
"xpack.triggersActionsUI.sections.alertAdd.saveErrorNotificationText": "アラートを作成できません。",
"xpack.triggersActionsUI.sections.alertAdd.saveSuccessNotificationText": "「{alertName}」 を保存しました",
"xpack.triggersActionsUI.sections.alertAdd.selectIndex": "インデックスを選択してください",
Expand Down
2 changes: 0 additions & 2 deletions x-pack/plugins/translations/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -20292,15 +20292,13 @@
"xpack.triggersActionsUI.sections.addModalConnectorForm.saveButtonLabel": "保存",
"xpack.triggersActionsUI.sections.addModalConnectorForm.updateSuccessNotificationText": "已创建“{connectorName}”",
"xpack.triggersActionsUI.sections.alertAdd.betaBadgeTooltipContent": "{pluginName} 为公测版,可能会进行更改。设计和代码相对于正式发行版功能还不够成熟,将按原样提供,且不提供任何保证。公测版功能不受正式发行版功能支持 SLA 的约束。",
"xpack.triggersActionsUI.sections.alertAdd.cancelButtonLabel": "取消",
"xpack.triggersActionsUI.sections.alertAdd.conditionPrompt": "定义条件",
"xpack.triggersActionsUI.sections.alertAdd.errorLoadingAlertVisualizationTitle": "无法加载告警可视化",
"xpack.triggersActionsUI.sections.alertAdd.flyoutTitle": "创建告警",
"xpack.triggersActionsUI.sections.alertAdd.geoThreshold.closePopoverLabel": "关闭",
"xpack.triggersActionsUI.sections.alertAdd.loadingAlertVisualizationDescription": "正在加载告警可视化……",
"xpack.triggersActionsUI.sections.alertAdd.operationName": "创建",
"xpack.triggersActionsUI.sections.alertAdd.previewAlertVisualizationDescription": "完成表达式以生成预览。",
"xpack.triggersActionsUI.sections.alertAdd.saveButtonLabel": "保存",
"xpack.triggersActionsUI.sections.alertAdd.saveErrorNotificationText": "无法创建告警。",
"xpack.triggersActionsUI.sections.alertAdd.saveSuccessNotificationText": "已保存“{alertName}”",
"xpack.triggersActionsUI.sections.alertAdd.selectIndex": "选择索引",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { HealthCheck } from './health_check';

import { act } from 'react-dom/test-utils';
import { httpServiceMock } from '../../../../../../src/core/public/mocks';
import { HealthContextProvider } from '../context/health_context';

const docLinks = { ELASTIC_WEBSITE_URL: 'elastic.co/', DOC_LINK_VERSION: 'current' };

Expand All @@ -20,9 +21,11 @@ describe('health check', () => {
http.get.mockImplementationOnce(() => new Promise(() => {}));

const { queryByText, container } = render(
<HealthCheck http={http} docLinks={docLinks}>
<p>{'shouldnt render'}</p>
</HealthCheck>
<HealthContextProvider>
<HealthCheck http={http} docLinks={docLinks} waitForCheck={true}>
<p>{'shouldnt render'}</p>
</HealthCheck>
</HealthContextProvider>
);
await act(async () => {
// wait for useEffect to run
Expand All @@ -32,13 +35,33 @@ describe('health check', () => {
expect(queryByText('shouldnt render')).not.toBeInTheDocument();
});

it('renders children immediately if waitForCheck is false', async () => {
http.get.mockImplementationOnce(() => new Promise(() => {}));

const { queryByText, container } = render(
<HealthContextProvider>
<HealthCheck http={http} docLinks={docLinks} waitForCheck={false}>
<p>{'should render'}</p>
</HealthCheck>
</HealthContextProvider>
);
await act(async () => {
// wait for useEffect to run
});

expect(container.getElementsByClassName('euiLoadingSpinner').length).toBe(0);
expect(queryByText('should render')).toBeInTheDocument();
});

it('renders children if keys are enabled', async () => {
http.get.mockResolvedValue({ isSufficientlySecure: true, hasPermanentEncryptionKey: true });

const { queryByText } = render(
<HealthCheck http={http} docLinks={docLinks}>
<p>{'should render'}</p>
</HealthCheck>
<HealthContextProvider>
<HealthCheck http={http} docLinks={docLinks} waitForCheck={true}>
<p>{'should render'}</p>
</HealthCheck>
</HealthContextProvider>
);
await act(async () => {
// wait for useEffect to run
Expand All @@ -53,9 +76,11 @@ describe('health check', () => {
}));

const { queryAllByText } = render(
<HealthCheck http={http} docLinks={docLinks}>
<p>{'should render'}</p>
</HealthCheck>
<HealthContextProvider>
<HealthCheck http={http} docLinks={docLinks} waitForCheck={true}>
<p>{'should render'}</p>
</HealthCheck>
</HealthContextProvider>
);
await act(async () => {
// wait for useEffect to run
Expand All @@ -81,9 +106,11 @@ describe('health check', () => {
}));

const { queryByText, queryByRole } = render(
<HealthCheck http={http} docLinks={docLinks}>
<p>{'should render'}</p>
</HealthCheck>
<HealthContextProvider>
<HealthCheck http={http} docLinks={docLinks} waitForCheck={true}>
<p>{'should render'}</p>
</HealthCheck>
</HealthContextProvider>
);
await act(async () => {
// wait for useEffect to run
Expand All @@ -108,9 +135,11 @@ describe('health check', () => {
}));

const { queryByText } = render(
<HealthCheck http={http} docLinks={docLinks}>
<p>{'should render'}</p>
</HealthCheck>
<HealthContextProvider>
<HealthCheck http={http} docLinks={docLinks} waitForCheck={true}>
<p>{'should render'}</p>
</HealthCheck>
</HealthContextProvider>
);
await act(async () => {
// wait for useEffect to run
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,39 @@ import { EuiEmptyPrompt, EuiCode } from '@elastic/eui';
import { AlertingFrameworkHealth } from '../../types';
import { health } from '../lib/alert_api';
import './health_check.scss';
import { useHealthContext } from '../context/health_context';

interface Props {
docLinks: Pick<DocLinksStart, 'ELASTIC_WEBSITE_URL' | 'DOC_LINK_VERSION'>;
http: HttpSetup;
inFlyout?: boolean;
waitForCheck: boolean;
}

export const HealthCheck: React.FunctionComponent<Props> = ({
docLinks,
http,
children,
waitForCheck,
inFlyout = false,
}) => {
const { setLoadingHealthCheck } = useHealthContext();
const [alertingHealth, setAlertingHealth] = React.useState<Option<AlertingFrameworkHealth>>(none);

React.useEffect(() => {
(async function () {
setLoadingHealthCheck(true);
setAlertingHealth(some(await health({ http })));
setLoadingHealthCheck(false);
})();
}, [http]);
}, [http, setLoadingHealthCheck]);

const className = inFlyout ? 'alertingFlyoutHealthCheck' : 'alertingHealthCheck';

return pipe(
alertingHealth,
fold(
() => <EuiLoadingSpinner size="m" />,
() => (waitForCheck ? <EuiLoadingSpinner size="m" /> : <Fragment>{children}</Fragment>),
(healthCheck) => {
return healthCheck?.isSufficientlySecure && healthCheck?.hasPermanentEncryptionKey ? (
<Fragment>{children}</Fragment>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import React, { createContext, useCallback, useContext, useMemo, useState } from 'react';

export interface HealthContextValue {
loadingHealthCheck: boolean;
setLoadingHealthCheck: (loading: boolean) => void;
}

const defaultHealthContext: HealthContextValue = {
loadingHealthCheck: false,
setLoadingHealthCheck: (loading: boolean) => {
throw new Error(
'setLoadingHealthCheck was not initialized, set it when you invoke the context'
);
},
};

const HealthContext = createContext<HealthContextValue>(defaultHealthContext);

export const HealthContextProvider = ({ children }: { children: React.ReactNode }) => {
const [loading, setLoading] = useState<boolean>(false);

const setLoadingHealthCheck = useCallback((isLoading: boolean) => {
setLoading(isLoading);
}, []);

const value = useMemo(() => {
return { loadingHealthCheck: loading, setLoadingHealthCheck };
}, [loading, setLoadingHealthCheck]);

return <HealthContext.Provider value={value}>{children}</HealthContext.Provider>;
};

export const useHealthContext = () => {
const ctx = useContext(HealthContext);
if (!ctx) {
throw new Error('HealthContext has not been set.');
}
return ctx;
};
17 changes: 11 additions & 6 deletions x-pack/plugins/triggers_actions_ui/public/application/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { ActionsConnectorsList } from './sections/actions_connectors_list/compon
import { AlertsList } from './sections/alerts_list/components/alerts_list';
import { PLUGIN } from './constants/plugin';
import { HealthCheck } from './components/health_check';
import { HealthContextProvider } from './context/health_context';

interface MatchParams {
section: Section;
Expand Down Expand Up @@ -139,19 +140,23 @@ export const TriggersActionsUIHome: React.FunctionComponent<RouteComponentProps<
exact
path={routeToConnectors}
component={() => (
<HealthCheck docLinks={docLinks} http={http}>
<ActionsConnectorsList />
</HealthCheck>
<HealthContextProvider>
<HealthCheck docLinks={docLinks} http={http} waitForCheck={true}>
<ActionsConnectorsList />
</HealthCheck>
</HealthContextProvider>
)}
/>
)}
<Route
exact
path={routeToAlerts}
component={() => (
<HealthCheck docLinks={docLinks} http={http}>
<AlertsList />
</HealthCheck>
<HealthContextProvider>
<HealthCheck docLinks={docLinks} http={http} inFlyout={true} waitForCheck={true}>
<AlertsList />
</HealthCheck>
</HealthContextProvider>
)}
/>
</Switch>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ import {
EuiTitle,
EuiFlyoutHeader,
EuiFlyout,
EuiFlyoutFooter,
EuiFlexGroup,
EuiFlexItem,
EuiButtonEmpty,
EuiButton,
EuiFlyoutBody,
EuiPortal,
EuiBetaBadge,
Expand All @@ -29,6 +24,8 @@ import { HealthCheck } from '../../components/health_check';
import { PLUGIN } from '../../constants/plugin';
import { ConfirmAlertSave } from './confirm_alert_save';
import { hasShowActionsCapability } from '../../lib/capabilities';
import AlertAddFooter from './alert_add_footer';
import { HealthContextProvider } from '../../context/health_context';

interface AlertAddProps {
consumer: string;
Expand Down Expand Up @@ -183,54 +180,37 @@ export const AlertAdd = ({
</h3>
</EuiTitle>
</EuiFlyoutHeader>
<HealthCheck docLinks={docLinks} http={http} inFlyout={true}>
<EuiFlyoutBody>
<AlertForm
alert={alert}
dispatch={dispatch}
errors={errors}
canChangeTrigger={canChangeTrigger}
operation={i18n.translate('xpack.triggersActionsUI.sections.alertAdd.operationName', {
defaultMessage: 'create',
})}
<HealthContextProvider>
<HealthCheck docLinks={docLinks} http={http} inFlyout={true} waitForCheck={false}>
<EuiFlyoutBody>
<AlertForm
alert={alert}
dispatch={dispatch}
errors={errors}
canChangeTrigger={canChangeTrigger}
operation={i18n.translate(
'xpack.triggersActionsUI.sections.alertAdd.operationName',
{
defaultMessage: 'create',
}
)}
/>
</EuiFlyoutBody>
<AlertAddFooter
isSaving={isSaving}
hasErrors={hasErrors || hasActionErrors}
onSave={async () => {
setIsSaving(true);
if (shouldConfirmSave) {
setIsConfirmAlertSaveModalOpen(true);
} else {
await saveAlertAndCloseFlyout();
}
}}
onCancel={closeFlyout}
/>
</EuiFlyoutBody>
<EuiFlyoutFooter>
<EuiFlexGroup justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiButtonEmpty data-test-subj="cancelSaveAlertButton" onClick={closeFlyout}>
{i18n.translate('xpack.triggersActionsUI.sections.alertAdd.cancelButtonLabel', {
defaultMessage: 'Cancel',
})}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
fill
color="secondary"
data-test-subj="saveAlertButton"
type="submit"
iconType="check"
isDisabled={hasErrors || hasActionErrors}
isLoading={isSaving}
onClick={async () => {
setIsSaving(true);
if (shouldConfirmSave) {
setIsConfirmAlertSaveModalOpen(true);
} else {
await saveAlertAndCloseFlyout();
}
}}
>
<FormattedMessage
id="xpack.triggersActionsUI.sections.alertAdd.saveButtonLabel"
defaultMessage="Save"
/>
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlyoutFooter>
</HealthCheck>
</HealthCheck>
</HealthContextProvider>
{isConfirmAlertSaveModalOpen && (
<ConfirmAlertSave
onConfirm={async () => {
Expand Down
Loading