Skip to content

Commit

Permalink
[Security Solution][Data Quality Dashboard] fix pattern state reset o…
Browse files Browse the repository at this point in the history
…n ilm phase filter change (elastic#198549)

addresses elastic#196523

- Fixes ilm phase change propagation on patterns.
- Adds missing tests for useResultsRollup functionality

## UI changes

### Before

https://github.com/user-attachments/assets/78a1d809-6a9a-4bfc-88a9-079f829a2017

### After

https://github.com/user-attachments/assets/f689fcc9-e1c6-4ccf-a7ca-8f13e9507ba4
  • Loading branch information
kapral18 authored Nov 4, 2024
1 parent 0a1ec8f commit ddf55ea
Show file tree
Hide file tree
Showing 8 changed files with 986 additions and 71 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { renderHook } from '@testing-library/react-hooks';
import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks';

import { getHistoricalResultStub } from '../../../../stub/get_historical_result_stub';
import { useStoredPatternResults } from '.';

describe('useStoredPatternResults', () => {
const httpFetch = jest.fn();
const mockToasts = notificationServiceMock.createStartContract().toasts;

beforeEach(() => {
jest.clearAllMocks();
});

describe('when patterns are empty', () => {
it('should return an empty array and not call getStorageResults', () => {
const { result } = renderHook(() => useStoredPatternResults([], mockToasts, httpFetch));

expect(result.current).toEqual([]);
expect(httpFetch).not.toHaveBeenCalled();
});
});

describe('when patterns are provided', () => {
it('should fetch and return stored pattern results correctly', async () => {
const patterns = ['pattern1-*', 'pattern2-*'];

httpFetch.mockImplementation((path: string) => {
if (path === '/internal/ecs_data_quality_dashboard/results_latest/pattern1-*') {
return Promise.resolve([getHistoricalResultStub('pattern1-index1')]);
}

if (path === '/internal/ecs_data_quality_dashboard/results_latest/pattern2-*') {
return Promise.resolve([getHistoricalResultStub('pattern2-index1')]);
}

return Promise.reject(new Error('Invalid path'));
});

const { result, waitFor } = renderHook(() =>
useStoredPatternResults(patterns, mockToasts, httpFetch)
);

await waitFor(() => result.current.length > 0);

expect(httpFetch).toHaveBeenCalledTimes(2);

expect(httpFetch).toHaveBeenCalledWith(
'/internal/ecs_data_quality_dashboard/results_latest/pattern1-*',
{
method: 'GET',
signal: expect.any(AbortSignal),
version: '1',
}
);
expect(httpFetch).toHaveBeenCalledWith(
'/internal/ecs_data_quality_dashboard/results_latest/pattern2-*',
{
method: 'GET',
signal: expect.any(AbortSignal),
version: '1',
}
);

expect(result.current).toEqual([
{
pattern: 'pattern1-*',
results: {
'pattern1-index1': {
docsCount: expect.any(Number),
error: null,
ilmPhase: expect.any(String),
incompatible: expect.any(Number),
indexName: 'pattern1-index1',
pattern: 'pattern1-*',
markdownComments: expect.any(Array),
sameFamily: expect.any(Number),
checkedAt: expect.any(Number),
},
},
},
{
pattern: 'pattern2-*',
results: {
'pattern2-index1': {
docsCount: expect.any(Number),
error: null,
ilmPhase: expect.any(String),
incompatible: expect.any(Number),
indexName: 'pattern2-index1',
pattern: 'pattern2-*',
markdownComments: expect.any(Array),
sameFamily: expect.any(Number),
checkedAt: expect.any(Number),
},
},
},
]);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { useEffect, useState } from 'react';
import { IToasts } from '@kbn/core-notifications-browser';
import { HttpHandler } from '@kbn/core-http-browser';
import { isEmpty } from 'lodash/fp';

import { DataQualityCheckResult } from '../../../../types';
import { formatResultFromStorage, getStorageResults } from '../../utils/storage';

export const useStoredPatternResults = (
patterns: string[],
toasts: IToasts,
httpFetch: HttpHandler
) => {
const [storedPatternResults, setStoredPatternResults] = useState<
Array<{ pattern: string; results: Record<string, DataQualityCheckResult> }>
>([]);

useEffect(() => {
if (isEmpty(patterns)) {
return;
}

const abortController = new AbortController();
const fetchStoredPatternResults = async () => {
const requests = patterns.map((pattern) =>
getStorageResults({ pattern, httpFetch, abortController, toasts }).then((results = []) => ({
pattern,
results: Object.fromEntries(
results.map((storageResult) => [
storageResult.indexName,
formatResultFromStorage({ storageResult, pattern }),
])
),
}))
);

const patternResults = await Promise.all(requests);
if (patternResults?.length) {
setStoredPatternResults(patternResults);
}
};

fetchStoredPatternResults();
}, [httpFetch, patterns, toasts]);

return storedPatternResults;
};
Loading

0 comments on commit ddf55ea

Please sign in to comment.