Skip to content

Commit

Permalink
use health check in alert add/edit
Browse files Browse the repository at this point in the history
  • Loading branch information
gmmorris committed Apr 7, 2020
1 parent 61ee568 commit 62ad819
Show file tree
Hide file tree
Showing 6 changed files with 366 additions and 267 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* 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 from 'react';
import { render } from '@testing-library/react';

import { AlertActionHealthCheck } from './alert_action_health_check';

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

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

const http = httpServiceMock.createStartContract();

describe('alert creation health check', () => {
test('renders spinner while health is loading', async () => {
http.get.mockImplementationOnce(() => new Promise(() => {}));

const { queryByText, container } = render(
<AlertActionHealthCheck action="creation" http={http} docLinks={docLinks}>
<p>{'shouldnt render'}</p>
</AlertActionHealthCheck>
);
await act(async () => {
// wait for useEffect to run
});

expect(container.getElementsByClassName('euiLoadingSpinner').length).toBe(1);
expect(queryByText('shouldnt render')).not.toBeInTheDocument();
});

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

const { queryByText } = render(
<AlertActionHealthCheck action="creation" http={http} docLinks={docLinks}>
<p>{'should render'}</p>
</AlertActionHealthCheck>
);
await act(async () => {
// wait for useEffect to run
});
expect(queryByText('should render')).toBeInTheDocument();
});

test('renders warning if keys are disabled', async () => {
http.get.mockImplementationOnce(async () => ({
isSufficientlySecure: false,
hasPermanentEncryptionKey: true,
}));

const { queryAllByText, queryByTitle } = render(
<AlertActionHealthCheck action="creation" http={http} docLinks={docLinks}>
<p>{'should render'}</p>
</AlertActionHealthCheck>
);
await act(async () => {
// wait for useEffect to run
});

const [description, action] = queryAllByText(/TLS/i);
expect(description.textContent).toMatchInlineSnapshot(
`"Alert creation requires TLS between Elasticsearch and Kibana."`
);

expect(action.textContent).toMatchInlineSnapshot(`"enable TLS"`);

const actionLink = queryByTitle(action.textContent!);
expect(actionLink!.getAttribute('href')).toMatchInlineSnapshot(
`"elastic.co/guide/en/kibana/current/configuring-tls.html"`
);
});

test('renders warning if encryption key is ephemeral', async () => {
http.get.mockImplementationOnce(async () => ({
isSufficientlySecure: true,
hasPermanentEncryptionKey: false,
}));

const { queryAllByText, queryByTitle } = render(
<AlertActionHealthCheck action="creation" http={http} docLinks={docLinks}>
<p>{'should render'}</p>
</AlertActionHealthCheck>
);
await act(async () => {
// wait for useEffect to run
});

const [description, action] = queryAllByText(/Encryption/i);

expect(description.textContent).toMatchInlineSnapshot(
`"Alert creation requires a permanent Encryption Key."`
);

expect(action.textContent).toMatchInlineSnapshot(`"Configure an Encryption Key"`);

const actionLink = queryByTitle(action.textContent!);
expect(actionLink!.getAttribute('href')).toMatchInlineSnapshot(
`"elastic.co/guide/en/kibana/current/alert-action-settings-kb.html#general-alert-action-settings"`
);
});

test('renders warning if encryption key is ephemeral and keys are disabled', async () => {
http.get.mockImplementationOnce(async () => ({
isSufficientlySecure: false,
hasPermanentEncryptionKey: false,
}));

const { queryAllByText, queryByTitle } = render(
<AlertActionHealthCheck action="creation" http={http} docLinks={docLinks}>
<p>{'should render'}</p>
</AlertActionHealthCheck>
);
await act(async () => {
// wait for useEffect to run
});

const [description, action] = queryAllByText(/TLS/i);

expect(description.textContent).toMatchInlineSnapshot(
`"Alert creation requires TLS between Elasticsearch and Kibana, and a permanent Encryption Key."`
);

expect(action.textContent).toMatchInlineSnapshot(
`"enable TLS and configure an Encryption Key"`
);

const actionLink = queryByTitle(action.textContent!);
expect(actionLink!.getAttribute('href')).toMatchInlineSnapshot(
`"elastic.co/guide/en/kibana/current/alerting-getting-started.html#alerting-setup-prerequisites"`
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* 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, { Fragment } from 'react';
import { Option, none, some, fold } from 'fp-ts/lib/Option';
import { pipe } from 'fp-ts/lib/pipeable';

import { EuiCallOut, EuiButton, EuiLoadingSpinner } from '@elastic/eui';
import { i18n } from '@kbn/i18n';

import { DocLinksStart, HttpSetup } from 'kibana/public';
import { AlertingFrameworkHealth } from '../../types';
import { health } from '../lib/alert_api';

interface Props {
docLinks: Pick<DocLinksStart, 'ELASTIC_WEBSITE_URL' | 'DOC_LINK_VERSION'>;
action: string;
http: HttpSetup;
}

export const AlertActionHealthCheck: React.FunctionComponent<Props> = ({
docLinks,
http,
action,
children,
}) => {
const [alertingHealth, setAlertingHealth] = React.useState<Option<AlertingFrameworkHealth>>(none);

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

return pipe(
alertingHealth,
fold(
() => <EuiLoadingSpinner size="m" />,
healthCheck => {
const [title, actionDescription, actionUrl] = getTextByHealthIssue(healthCheck, {
action,
docLinks,
});
return healthCheck?.isSufficientlySecure && healthCheck?.hasPermanentEncryptionKey ? (
<Fragment>{children}</Fragment>
) : (
<EuiCallOut title={title} color="warning" iconType="iInCircle">
<EuiButton title={actionDescription} color="warning" href={actionUrl}>
{actionDescription}
</EuiButton>
</EuiCallOut>
);
}
)
);
};

function getTextByHealthIssue(
healthCheck: AlertingFrameworkHealth,
{ docLinks, action }: Pick<Props, 'action' | 'docLinks'>
) {
const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks;
return !healthCheck.isSufficientlySecure && !healthCheck.hasPermanentEncryptionKey
? [
i18n.translate(
'xpack.triggersActionsUI.components.alertActionHealthCheck.tlsAndEncryptionError',
{
defaultMessage:
'Alert {action} requires TLS between Elasticsearch and Kibana, and a permanent Encryption Key.',
values: {
action,
},
}
),
i18n.translate(
'xpack.triggersActionsUI.components.alertActionHealthCheck.tlsAndEncryptionErrorAction',
{
defaultMessage: 'enable TLS and configure an Encryption Key',
}
),
`${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/alerting-getting-started.html#alerting-setup-prerequisites`,
]
: !healthCheck.hasPermanentEncryptionKey
? [
i18n.translate(
'xpack.triggersActionsUI.components.alertActionHealthCheck.encryptionError',
{
defaultMessage: 'Alert {action} requires a permanent Encryption Key.',
values: {
action,
},
}
),
i18n.translate(
'xpack.triggersActionsUI.components.alertActionHealthCheck.encryptionErrorAction',
{
defaultMessage: 'Configure an Encryption Key',
}
),
`${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/alert-action-settings-kb.html#general-alert-action-settings`,
]
: [
i18n.translate('xpack.triggersActionsUI.components.alertActionHealthCheck.tlsError', {
defaultMessage: 'Alert {action} requires TLS between Elasticsearch and Kibana.',
values: {
action,
},
}),
i18n.translate('xpack.triggersActionsUI.components.alertActionHealthCheck.tlsErrorAction', {
defaultMessage: 'enable TLS',
}),
`${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/configuring-tls.html`,
];
}

This file was deleted.

Loading

0 comments on commit 62ad819

Please sign in to comment.