Skip to content

Commit

Permalink
[SIEM] Do not update state component when they did unmount (#45847)
Browse files Browse the repository at this point in the history
* do not update state component when they did unmount

* Adds signals to the fetches

* review I
  • Loading branch information
XavierM authored Sep 17, 2019
1 parent 1e122cf commit 677f662
Show file tree
Hide file tree
Showing 13 changed files with 370 additions and 290 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,49 +53,59 @@ export const EmbeddedMap = React.memo<EmbeddedMapProps>(
const [loadingKibanaIndexPatterns, kibanaIndexPatterns] = useIndexPatterns();
const [siemDefaultIndices] = useKibanaUiSetting(DEFAULT_INDEX_KEY);

const setupEmbeddable = async () => {
// Configure Embeddables API
try {
setupEmbeddablesAPI(applyFilterQueryFromKueryExpression);
} catch (e) {
displayErrorToast(i18n.ERROR_CONFIGURING_EMBEDDABLES_API, e.message, dispatchToaster);
setIsLoading(false);
setIsError(true);
return false;
}

// Ensure at least one `siem:defaultIndex` index pattern exists before trying to import
const matchingIndexPatterns = kibanaIndexPatterns.filter(ip =>
siemDefaultIndices.includes(ip.attributes.title)
);
if (matchingIndexPatterns.length === 0) {
setIsLoading(false);
setIsIndexError(true);
return;
}

// Create & set Embeddable
try {
const embeddableObject = await createEmbeddable(
getIndexPatternTitleIdMapping(matchingIndexPatterns),
queryExpression,
startDate,
endDate,
setQuery
// Initial Load useEffect
useEffect(() => {
let isSubscribed = true;
async function setupEmbeddable() {
// Configure Embeddables API
try {
setupEmbeddablesAPI(applyFilterQueryFromKueryExpression);
} catch (e) {
displayErrorToast(i18n.ERROR_CONFIGURING_EMBEDDABLES_API, e.message, dispatchToaster);
setIsLoading(false);
setIsError(true);
return false;
}

// Ensure at least one `siem:defaultIndex` index pattern exists before trying to import
const matchingIndexPatterns = kibanaIndexPatterns.filter(ip =>
siemDefaultIndices.includes(ip.attributes.title)
);
setEmbeddable(embeddableObject);
} catch (e) {
displayErrorToast(i18n.ERROR_CREATING_EMBEDDABLE, e.message, dispatchToaster);
setIsError(true);
if (matchingIndexPatterns.length === 0 && isSubscribed) {
setIsLoading(false);
setIsIndexError(true);
return;
}

// Create & set Embeddable
try {
const embeddableObject = await createEmbeddable(
getIndexPatternTitleIdMapping(matchingIndexPatterns),
queryExpression,
startDate,
endDate,
setQuery
);
if (isSubscribed) {
setEmbeddable(embeddableObject);
}
} catch (e) {
if (isSubscribed) {
displayErrorToast(i18n.ERROR_CREATING_EMBEDDABLE, e.message, dispatchToaster);
setIsError(true);
}
}
if (isSubscribed) {
setIsLoading(false);
}
}
setIsLoading(false);
};

// Initial Load useEffect
useEffect(() => {
if (!loadingKibanaIndexPatterns) {
setupEmbeddable();
}
return () => {
isSubscribed = false;
};
}, [loadingKibanaIndexPatterns, kibanaIndexPatterns]);

// queryExpression updated useEffect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,95 +4,83 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { cloneDeep } from 'lodash/fp';
import * as React from 'react';
import { MockedProvider } from 'react-apollo/test-utils';
import { render } from 'react-testing-library';

import { getEmptyValue } from '../empty_value';
import { LastEventIndexKey } from '../../graphql/types';
import { mockLastEventTimeQuery } from '../../containers/events/last_event_time/mock';
import { wait } from '../../lib/helpers';

import { useLastEventTimeQuery } from '../../containers/events/last_event_time';
import { TestProviders } from '../../mock';
import '../../mock/ui_settings';

import { LastEventTime } from '.';
import { mount } from 'enzyme';

describe('Last Event Time Stat', () => {
// this is just a little hack to silence a warning that we'll get until react
// fixes this: https://github.com/facebook/react/pull/14853
// For us that mean we need to upgrade to 16.9.0
// and we will be able to do that when we are in master
// eslint-disable-next-line no-console
const originalError = console.error;

beforeAll(() => {
// eslint-disable-next-line no-console
console.error = (...args: string[]) => {
if (/Warning.*not wrapped in act/.test(args[0])) {
return;
}
originalError.call(console, ...args);
};
});
const mockUseLastEventTimeQuery: jest.Mock = useLastEventTimeQuery as jest.Mock;
jest.mock('../../containers/events/last_event_time', () => ({
useLastEventTimeQuery: jest.fn(),
}));

afterAll(() => {
// eslint-disable-next-line no-console
console.error = originalError;
describe('Last Event Time Stat', () => {
beforeEach(() => {
mockUseLastEventTimeQuery.mockReset();
});

test('Loading', async () => {
const { container } = render(
mockUseLastEventTimeQuery.mockImplementation(() => ({
loading: true,
lastSeen: null,
errorMessage: null,
}));
const wrapper = mount(
<TestProviders>
<MockedProvider mocks={mockLastEventTimeQuery} addTypename={false}>
<LastEventTime indexKey={LastEventIndexKey.hosts} />
</MockedProvider>
<LastEventTime indexKey={LastEventIndexKey.hosts} />
</TestProviders>
);
expect(container.innerHTML).toBe(
expect(wrapper.html()).toBe(
'<span class="euiLoadingSpinner euiLoadingSpinner--medium"></span>'
);
});
test('Last seen', async () => {
const { container } = render(
mockUseLastEventTimeQuery.mockImplementation(() => ({
loading: false,
lastSeen: mockLastEventTimeQuery[0].result.data!.source.LastEventTime.lastSeen,
errorMessage: mockLastEventTimeQuery[0].result.data!.source.LastEventTime.errorMessage,
}));
const wrapper = mount(
<TestProviders>
<MockedProvider mocks={mockLastEventTimeQuery} addTypename={false}>
<LastEventTime indexKey={LastEventIndexKey.hosts} />
</MockedProvider>
<LastEventTime indexKey={LastEventIndexKey.hosts} />
</TestProviders>
);
await wait();

expect(container.innerHTML).toBe(
'<span class="euiToolTipAnchor">Last event: 12 days ago</span>'
);
expect(wrapper.html()).toBe('<span class="euiToolTipAnchor">Last event: 12 days ago</span>');
});
test('Bad date time string', async () => {
const badDateTime = cloneDeep(mockLastEventTimeQuery);
badDateTime[0].result.data!.source.LastEventTime.lastSeen = 'something-invalid';
const { container } = render(
mockUseLastEventTimeQuery.mockImplementation(() => ({
loading: false,
lastSeen: 'something-invalid',
errorMessage: mockLastEventTimeQuery[0].result.data!.source.LastEventTime.errorMessage,
}));
const wrapper = mount(
<TestProviders>
<MockedProvider mocks={badDateTime} addTypename={false}>
<LastEventTime indexKey={LastEventIndexKey.hosts} />
</MockedProvider>
<LastEventTime indexKey={LastEventIndexKey.hosts} />
</TestProviders>
);
await wait();

expect(container.innerHTML).toBe('something-invalid');
expect(wrapper.html()).toBe('something-invalid');
});
test('Null time string', async () => {
const nullDateTime = cloneDeep(mockLastEventTimeQuery);
nullDateTime[0].result.data!.source.LastEventTime.lastSeen = null;
const { container } = render(
mockUseLastEventTimeQuery.mockImplementation(() => ({
loading: false,
lastSeen: null,
errorMessage: mockLastEventTimeQuery[0].result.data!.source.LastEventTime.errorMessage,
}));
const wrapper = mount(
<TestProviders>
<MockedProvider mocks={nullDateTime} addTypename={false}>
<LastEventTime indexKey={LastEventIndexKey.hosts} />
</MockedProvider>
<LastEventTime indexKey={LastEventIndexKey.hosts} />
</TestProviders>
);
await wait();

expect(container.innerHTML).toContain(getEmptyValue());
expect(wrapper.html()).toContain(getEmptyValue());
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,63 +6,56 @@

import { EuiIcon, EuiLoadingSpinner, EuiToolTip } from '@elastic/eui';
import { FormattedMessage, FormattedRelative } from '@kbn/i18n/react';
import React from 'react';
import { ApolloConsumer } from 'react-apollo';
import { pure } from 'recompose';
import React, { memo } from 'react';

import { LastEventIndexKey } from '../../graphql/types';
import { useLastEventTimeQuery } from '../../containers/events/last_event_time';
import { getEmptyTagValue } from '../empty_value';
interface LastEventTimeProps {

export interface LastEventTimeProps {
hostName?: string;
indexKey: LastEventIndexKey;
ip?: string;
}
export const LastEventTime = pure<LastEventTimeProps>(({ hostName, indexKey, ip }) => {
export const LastEventTime = memo<LastEventTimeProps>(({ hostName, indexKey, ip }) => {
const { loading, lastSeen, errorMessage } = useLastEventTimeQuery(
indexKey,
{ hostName, ip },
'default'
);

if (errorMessage != null) {
return (
<EuiToolTip
position="top"
content={errorMessage}
data-test-subj="last_event_time_error"
aria-label="last_event_time_error"
id={`last_event_time_error-${indexKey}`}
>
<EuiIcon aria-describedby={`last_event_time_error-${indexKey}`} type="alert" />
</EuiToolTip>
);
}
return (
<ApolloConsumer>
{client => {
const { loading, lastSeen, errorMessage } = useLastEventTimeQuery(
indexKey,
{ hostName, ip },
'default',
client
);
if (errorMessage != null) {
return (
<EuiToolTip
position="top"
content={errorMessage}
data-test-subj="last_event_time_error"
aria-label="last_event_time_error"
id={`last_event_time_error-${indexKey}`}
>
<EuiIcon aria-describedby={`last_event_time_error-${indexKey}`} type="alert" />
<>
{loading && <EuiLoadingSpinner size="m" />}
{!loading && lastSeen != null && new Date(lastSeen).toString() === 'Invalid Date'
? lastSeen
: !loading &&
lastSeen != null && (
<EuiToolTip data-test-subj="last_event_time" position="bottom" content={lastSeen}>
<FormattedMessage
id="xpack.siem.headerPage.pageSubtitle"
defaultMessage="Last event: {beat}"
values={{
beat: <FormattedRelative value={new Date(lastSeen)} />,
}}
/>
</EuiToolTip>
);
}
return (
<>
{loading && <EuiLoadingSpinner size="m" />}
{!loading && lastSeen != null && new Date(lastSeen).toString() === 'Invalid Date'
? lastSeen
: !loading &&
lastSeen != null && (
<EuiToolTip data-test-subj="last_event_time" position="bottom" content={lastSeen}>
<FormattedMessage
id="xpack.siem.headerPage.pageSubtitle"
defaultMessage="Last event: {beat}"
values={{
beat: <FormattedRelative value={new Date(lastSeen)} />,
}}
/>
</EuiToolTip>
)}
{!loading && lastSeen == null && getEmptyTagValue()}
</>
);
}}
</ApolloConsumer>
)}
{!loading && lastSeen == null && getEmptyTagValue()}
</>
);
});

Expand Down
Loading

0 comments on commit 677f662

Please sign in to comment.