From f924b455f638c918fbb3d6d5c71bed6e9f083097 Mon Sep 17 00:00:00 2001 From: Kaituo Li Date: Fri, 7 Jun 2024 15:55:10 -0700 Subject: [PATCH 1/3] Update Frontend for Custom Result Index Query and Fix Issues This PR finalizes the frontend changes related to PR #1225. The custom result index query now uses an index pattern instead of a single index. Additionally, this PR addresses an issue where missing custom result indices would appear because the original code checked for the existence of an index name, but now we use it as a prefix. We have updated the logic to perform a prefix search instead of checking for index name equality. This PR also resolves issue #765 by downgrading the version of jest-canvas-mock. Testing Done: * Added unit tests. * Verified that the custom result index missing callout is not shown. * Confirmed that the frontend can still display old and new results after a rollover. Signed-off-by: Kaituo Li --- package.json | 2 +- .../containers/DetectorDetail.tsx | 12 +- .../__tests__/CustomIndexErrorMsg.test.tsx | 34 ++++ public/pages/DetectorDetail/utils/helpers.tsx | 25 ++- .../reducers/__tests__/anomalyResults.test.ts | 161 +++++++++++++++++- .../__tests__/liveAnomalyResults.test.ts | 136 +++++++++++++++ public/redux/reducers/anomalyResults.ts | 12 ++ public/redux/reducers/liveAnomalyResults.ts | 6 + 8 files changed, 381 insertions(+), 7 deletions(-) create mode 100644 public/redux/reducers/__tests__/liveAnomalyResults.test.ts diff --git a/package.json b/package.json index d09c1a22..d20e006a 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "babel-polyfill": "^6.26.0", "eslint-plugin-no-unsanitized": "^3.0.2", "eslint-plugin-prefer-object-spread": "^1.2.1", - "jest-canvas-mock": "^2.5.2", + "jest-canvas-mock": "^2.5.1", "lint-staged": "^9.2.0", "moment": "^2.24.0", "redux-mock-store": "^1.5.4", diff --git a/public/pages/DetectorDetail/containers/DetectorDetail.tsx b/public/pages/DetectorDetail/containers/DetectorDetail.tsx index 19635e85..16dfe2aa 100644 --- a/public/pages/DetectorDetail/containers/DetectorDetail.tsx +++ b/public/pages/DetectorDetail/containers/DetectorDetail.tsx @@ -62,8 +62,8 @@ import { prettifyErrorMessage, } from '../../../../server/utils/helpers'; import { DETECTOR_STATE } from '../../../../server/utils/constants'; -import { CatIndex } from '../../../../server/models/types'; -import { containsIndex } from '../utils/helpers'; +import { CatIndex, IndexAlias } from '../../../../server/models/types'; +import { containsIndex, containsAlias } from '../utils/helpers'; import { DataSourceViewConfig } from '../../../../../../src/plugins/data_source_management/public'; import { getDataSourceManagementPlugin, @@ -136,6 +136,9 @@ export const DetectorDetail = (props: DetectorDetailProps) => { const isCatIndicesRequesting = useSelector( (state: AppState) => state.opensearch.requesting ) as boolean; + const visibleAliases = useSelector( + (state: AppState) => state.opensearch.aliases + ) as IndexAlias[]; /* Determine if the result index is missing based on several conditions: @@ -146,6 +149,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { To be safe, we'd rather not show the error message and consider the result index not missing. - If the result index is not found in the visible indices, then it is missing. */ + const resultIndexOrAlias = get(detector, 'resultIndex', '') const isResultIndexMissing = isLoadingDetector ? false : isEmpty(get(detector, 'resultIndex', '')) @@ -154,11 +158,11 @@ export const DetectorDetail = (props: DetectorDetailProps) => { ? false : isEmpty(visibleIndices) ? false - : !containsIndex(get(detector, 'resultIndex', ''), visibleIndices); + : !containsIndex(resultIndexOrAlias, visibleIndices) && !containsAlias(resultIndexOrAlias, visibleAliases); // debug message: prints visibleIndices if isResultIndexMissing is true if (isResultIndexMissing) { - console.log(`isResultIndexMissing is true, visibleIndices: ${visibleIndices}, detector result index: ${get(detector, 'resultIndex', '')}`); + console.log(`isResultIndexMissing is true, visibleIndices: ${visibleIndices}, visibleAliases: ${visibleAliases}, detector result index: ${resultIndexOrAlias}`); } // String to set in the modal if the realtime detector and/or historical analysis diff --git a/public/pages/DetectorDetail/containers/__tests__/CustomIndexErrorMsg.test.tsx b/public/pages/DetectorDetail/containers/__tests__/CustomIndexErrorMsg.test.tsx index 3848fa2c..0c569c31 100644 --- a/public/pages/DetectorDetail/containers/__tests__/CustomIndexErrorMsg.test.tsx +++ b/public/pages/DetectorDetail/containers/__tests__/CustomIndexErrorMsg.test.tsx @@ -272,4 +272,38 @@ describe('detector detail', () => { // Assert that the element is not in the document expect(element).toBeNull(); }); + + test('the result index prefix is found in the visible indices', () => { + const detector = getRandomDetector(true, resultIndex); + + // Set up the mock implementation for useFetchDetectorInfo + (useFetchDetectorInfo as jest.Mock).mockImplementation(() => ({ + detector: detector, + hasError: false, + isLoadingDetector: false, + errorMessage: undefined, + })); + + const initialState = { + opensearch: { + indices: [ + { health: 'green', index: '.kibana_-962704462_v992471_1' }, + { health: 'green', index: resultIndex + '-history-2024.06.05-1' }, + ], + requesting: false, + }, + ad: { + detectors: {}, + }, + alerting: { + monitors: {}, + }, + }; + + renderWithRouter(detectorId, initialState); + const element = screen.queryByTestId('missingResultIndexCallOut'); + + // Assert that the element is not in the document + expect(element).toBeNull(); + }); }); diff --git a/public/pages/DetectorDetail/utils/helpers.tsx b/public/pages/DetectorDetail/utils/helpers.tsx index bb57e319..8eb0e7a2 100644 --- a/public/pages/DetectorDetail/utils/helpers.tsx +++ b/public/pages/DetectorDetail/utils/helpers.tsx @@ -17,7 +17,7 @@ import { DETECTOR_STATE } from '../../../../server/utils/constants'; import { Detector } from '../../../models/interfaces'; import { EuiHealth } from '@elastic/eui'; import moment from 'moment'; -import { CatIndex } from '../../../../server/models/types'; +import { CatIndex, IndexAlias } from '../../../../server/models/types'; export const getInitFailureMessageAndActionItem = (error: string): object => { const failureDetails = Object.values(DETECTOR_INIT_FAILURES); @@ -142,6 +142,17 @@ export const getDetectorStateDetails = ( ); }; +/** + * Checks if any of the given indices contain the specified index. + * + * This function iterates through an array of `CatIndex` objects and checks if the `index` property of any + * `CatIndex` object equals to the specified `index` string. It returns `true` if such an `index` is found, + * otherwise it returns `false`. + * + * @param index - The string to check against the `index` properties of the `CatIndex` objects. + * @param indices - An array of `CatIndex` objects to search through. + * @returns A boolean value indicating whether any `CatIndex` object's `index` property equals to the specified prefix. + */ export const containsIndex = (index: string, indices: CatIndex[]) => { let containsIndex = false; if (!isEmpty(indices)) { @@ -153,3 +164,15 @@ export const containsIndex = (index: string, indices: CatIndex[]) => { } return containsIndex; }; + +export const containsAlias = (alias: string, aliases: IndexAlias[]) => { + let containsAlias = false; + if (!isEmpty(aliases)) { + aliases.forEach((catAlias: IndexAlias) => { + if (get(catAlias, 'alias', '') == alias) { + containsAlias = true; + } + }); + } + return containsAlias; +}; diff --git a/public/redux/reducers/__tests__/anomalyResults.test.ts b/public/redux/reducers/__tests__/anomalyResults.test.ts index e668e2d1..ff6646df 100644 --- a/public/redux/reducers/__tests__/anomalyResults.test.ts +++ b/public/redux/reducers/__tests__/anomalyResults.test.ts @@ -18,7 +18,10 @@ import { mockedStore } from '../../utils/testUtils'; import reducer, { getDetectorResults, initialDetectorsState, + searchResults, } from '../anomalyResults'; +import { ALL_CUSTOM_AD_RESULT_INDICES } from '../../../pages/utils/constants' +import { getAnomalySummaryQuery } from '../../../pages/utils/anomalyResultUtils' jest.mock('../../../services', () => ({ ...jest.requireActual('../../../services'), @@ -78,7 +81,7 @@ describe('anomaly results reducer actions', () => { expect(httpMockedClient.get).toHaveBeenCalledWith( `..${ AD_NODE_API.DETECTOR - }/${tempDetectorId}/results/${false}/${resultIndex}/true`, + }/${tempDetectorId}/results/${false}/${resultIndex}*/true`, { query: queryParams } ); }); @@ -117,5 +120,161 @@ describe('anomaly results reducer actions', () => { ); } }); + test('result index pattern will not result in appended wildcard star', async () => { + const response = { + totalAnomalies: 1, + results: [{ anomalyGrade: 0, confidence: 1, starTime: 1, endTime: 2 }], + }; + httpMockedClient.get = jest + .fn() + .mockResolvedValue({ ok: true, response }); + const tempDetectorId = '123'; + let queryParams: DetectorResultsQueryParams = { + from: 0, + size: 20, + sortDirection: SORT_DIRECTION.ASC, + sortField: 'startTime', + }; + await store.dispatch( + getDetectorResults( + tempDetectorId, + '', + queryParams, + false, + ALL_CUSTOM_AD_RESULT_INDICES, + true + ) + ); + const actions = store.getActions(); + + expect(actions[0].type).toBe('ad/DETECTOR_RESULTS_REQUEST'); + expect(reducer(initialDetectorsState, actions[0])).toEqual({ + ...initialDetectorsState, + requesting: true, + }); + expect(actions[1].type).toBe('ad/DETECTOR_RESULTS_SUCCESS'); + expect(reducer(initialDetectorsState, actions[1])).toEqual({ + ...initialDetectorsState, + requesting: false, + total: response.totalAnomalies, + anomalies: response.results, + featureData: undefined, + }); + expect(httpMockedClient.get).toHaveBeenCalledWith( + `..${ + AD_NODE_API.DETECTOR + }/${tempDetectorId}/results/${false}/${ALL_CUSTOM_AD_RESULT_INDICES}/true`, + { query: queryParams } + ); + }); + }); + test('searchResults should append wildcard star at the end of custom result index', async () => { + const response = { + aggregations: { + top_entities: { + doc_count: 0, + top_entity_aggs: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [] + } + } + } + }; + + httpMockedClient.post = jest + .fn() + .mockResolvedValue({ ok: true, response }); + const tempDetectorId = '123'; + const resultIndex = 'opensearch-ad-plugin-result-test'; + const requestBody = getAnomalySummaryQuery(1717529636479, 1717529736479, tempDetectorId, undefined, false, undefined, undefined) + await store.dispatch( + searchResults( + requestBody, + resultIndex, + '', + true + ) + ); + const actions = store.getActions(); + + expect(actions[0].type).toBe('ad/SEARCH_ANOMALY_RESULTS_REQUEST'); + expect(reducer(initialDetectorsState, actions[0])).toEqual({ + ...initialDetectorsState, + requesting: true, + }); + expect(actions[1].type).toBe('ad/SEARCH_ANOMALY_RESULTS_SUCCESS'); + expect(reducer(initialDetectorsState, actions[1])).toEqual({ + ...initialDetectorsState, + requesting: false, + }); + expect(httpMockedClient.post).toHaveBeenCalledWith( + `..${ + AD_NODE_API.DETECTOR + }/results/_search/${resultIndex}*/true`, + { body: JSON.stringify(requestBody) } + ); + }); + test('searchResults should not append wildcard star at the end of custom result index', async () => { + const response = { + took: 1, + timed_out: false, + _shards: { + total: 2, + successful: 2, + skipped: 0, + failed: 0 + }, + hits: { + total: { + value: 0, + relation: "eq" + }, + max_score: null, + hits: [] + }, + aggregations: { + top_entities: { + doc_count: 0, + top_entity_aggs: { + doc_count_error_upper_bound: 0, + sum_other_doc_count: 0, + buckets: [] + } + } + } + }; + + httpMockedClient.post = jest + .fn() + .mockResolvedValue({ ok: true, response }); + const tempDetectorId = '123'; + const requestBody = getAnomalySummaryQuery(1717529636479, 1717529736479, tempDetectorId, undefined, false, undefined, undefined) + await store.dispatch( + searchResults( + requestBody, + ALL_CUSTOM_AD_RESULT_INDICES, + '', + true + ) + ); + const actions = store.getActions(); + + expect(actions[0].type).toBe('ad/SEARCH_ANOMALY_RESULTS_REQUEST'); + expect(reducer(initialDetectorsState, actions[0])).toEqual({ + ...initialDetectorsState, + requesting: true, + }); + expect(actions[1].type).toBe('ad/SEARCH_ANOMALY_RESULTS_SUCCESS'); + expect(reducer(initialDetectorsState, actions[1])).toEqual({ + ...initialDetectorsState, + requesting: false, + }); + expect(httpMockedClient.post).toHaveBeenCalledWith( + `..${ + AD_NODE_API.DETECTOR + }/results/_search/${ALL_CUSTOM_AD_RESULT_INDICES}/true`, + { body: JSON.stringify(requestBody) } + ); }); }); diff --git a/public/redux/reducers/__tests__/liveAnomalyResults.test.ts b/public/redux/reducers/__tests__/liveAnomalyResults.test.ts new file mode 100644 index 00000000..a6f359b7 --- /dev/null +++ b/public/redux/reducers/__tests__/liveAnomalyResults.test.ts @@ -0,0 +1,136 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +import { MockStore } from 'redux-mock-store'; +import { DetectorResultsQueryParams } from '../../../../server/models/types'; +import { SORT_DIRECTION } from '../../../../server/utils/constants'; +import httpMockedClient from '../../../../test/mocks/httpClientMock'; +import { AD_NODE_API } from '../../../../utils/constants'; +import { mockedStore } from '../../utils/testUtils'; +import { ALL_CUSTOM_AD_RESULT_INDICES } from '../../../pages/utils/constants'; +import reducer, { + getDetectorLiveResults, + initialDetectorLiveResults, +} from '../liveAnomalyResults'; + +jest.mock('../../../services', () => ({ + ...jest.requireActual('../../../services'), + + getDataSourceEnabled: () => ({ + enabled: false, + }), +})); + +describe('live anomaly results reducer actions', () => { + let store: MockStore; + beforeEach(() => { + store = mockedStore(); + }); + describe('getDetectorLiveResults', () => { + test('getDetectorLiveResults should append wildcard star at the end of custom result index', async () => { + const response = { + totalAnomalies: 1, + results: [{ anomalyGrade: 0, confidence: 1, starTime: 1, endTime: 2 }], + }; + + httpMockedClient.get = jest + .fn() + .mockResolvedValue({ ok: true, response }); + const tempDetectorId = '123'; + const resultIndex = 'opensearch-ad-plugin-result-test'; + let queryParams: DetectorResultsQueryParams = { + from: 0, + size: 20, + sortDirection: SORT_DIRECTION.ASC, + sortField: 'startTime', + }; + await store.dispatch( + getDetectorLiveResults( + tempDetectorId, + '', + queryParams, + false, + resultIndex, + true + ) + ); + const actions = store.getActions(); + + expect(actions[0].type).toBe('ad/DETECTOR_LIVE_RESULTS_REQUEST'); + expect(reducer(initialDetectorLiveResults, actions[0])).toEqual({ + ...initialDetectorLiveResults, + requesting: true, + }); + expect(actions[1].type).toBe('ad/DETECTOR_LIVE_RESULTS_SUCCESS'); + expect(reducer(initialDetectorLiveResults, actions[1])).toEqual({ + ...initialDetectorLiveResults, + requesting: false, + totalLiveAnomalies: response.totalAnomalies, + liveAnomalies: response.results, + errorMessage: '', + }); + expect(httpMockedClient.get).toHaveBeenCalledWith( + `..${ + AD_NODE_API.DETECTOR + }/${tempDetectorId}/results/${false}/${resultIndex}*/true`, + { query: queryParams } + ); + }); + test('getDetectorLiveResults should not append wildcard star at the end of custom result index', async () => { + const response = { + totalAnomalies: 1, + results: [{ anomalyGrade: 0, confidence: 1, starTime: 1, endTime: 2 }], + }; + + httpMockedClient.get = jest + .fn() + .mockResolvedValue({ ok: true, response }); + const tempDetectorId = '123'; + let queryParams: DetectorResultsQueryParams = { + from: 0, + size: 20, + sortDirection: SORT_DIRECTION.ASC, + sortField: 'startTime', + }; + await store.dispatch( + getDetectorLiveResults( + tempDetectorId, + '', + queryParams, + false, + ALL_CUSTOM_AD_RESULT_INDICES, + true + ) + ); + const actions = store.getActions(); + + expect(actions[0].type).toBe('ad/DETECTOR_LIVE_RESULTS_REQUEST'); + expect(reducer(initialDetectorLiveResults, actions[0])).toEqual({ + ...initialDetectorLiveResults, + requesting: true, + }); + expect(actions[1].type).toBe('ad/DETECTOR_LIVE_RESULTS_SUCCESS'); + expect(reducer(initialDetectorLiveResults, actions[1])).toEqual({ + ...initialDetectorLiveResults, + requesting: false, + totalLiveAnomalies: response.totalAnomalies, + liveAnomalies: response.results, + errorMessage: '', + }); + expect(httpMockedClient.get).toHaveBeenCalledWith( + `..${ + AD_NODE_API.DETECTOR + }/${tempDetectorId}/results/${false}/${ALL_CUSTOM_AD_RESULT_INDICES}/true`, + { query: queryParams } + ); + }); + }); +}); diff --git a/public/redux/reducers/anomalyResults.ts b/public/redux/reducers/anomalyResults.ts index bf643a3c..0643db64 100644 --- a/public/redux/reducers/anomalyResults.ts +++ b/public/redux/reducers/anomalyResults.ts @@ -103,6 +103,12 @@ export const getDetectorResults = ( let url = `..${AD_NODE_API.DETECTOR}/${id}/results/${isHistorical}`; if (resultIndex) { + // search for custom index pattern instead of specific index/alias + // as a custom index will be rolled over and we don't want to lose + // history + if (!resultIndex.endsWith('*')) { + resultIndex += '*'; + } url += `/${resultIndex}/${onlyQueryCustomResultIndex}`; } @@ -125,6 +131,12 @@ export const searchResults = ( let baseUrl = `..${AD_NODE_API.DETECTOR}/results/_search`; if (resultIndex) { + // search for custom index pattern instead of specific index/alias + // as a custom index will be rolled over and we don't want to lose + // history + if (!resultIndex.endsWith('*')) { + resultIndex += '*'; + } baseUrl += `/${resultIndex}/${onlyQueryCustomResultIndex}`; } diff --git a/public/redux/reducers/liveAnomalyResults.ts b/public/redux/reducers/liveAnomalyResults.ts index aa06efef..482fc286 100644 --- a/public/redux/reducers/liveAnomalyResults.ts +++ b/public/redux/reducers/liveAnomalyResults.ts @@ -66,6 +66,12 @@ export const getDetectorLiveResults = ( let url = `..${AD_NODE_API.DETECTOR}/${detectorId}/results/${isHistorical}`; if (resultIndex) { + // search for custom index pattern instead of specific index/alias + // as a custom index will be rolled over and we don't want to lose + // history + if (!resultIndex.endsWith('*')) { + resultIndex += '*'; + } url += `/${resultIndex}/${onlyQueryCustomResultIndex}`; } From 4cf545fb27b6c733791b040247cd58ba3ccd486b Mon Sep 17 00:00:00 2001 From: Kaituo Li Date: Sat, 8 Jun 2024 16:08:29 -0700 Subject: [PATCH 2/3] change to check alias Signed-off-by: Kaituo Li --- .../CustomResultIndex/CustomResultIndex.tsx | 2 +- .../containers/DetectorDetail.tsx | 16 +++--- .../__tests__/CustomIndexErrorMsg.test.tsx | 51 +++++++++++++++++-- .../DetectorDefinitionFields.tsx | 10 ++-- .../ReviewAndCreate.test.tsx.snap | 12 ++--- 5 files changed, 70 insertions(+), 21 deletions(-) diff --git a/public/pages/DefineDetector/components/CustomResultIndex/CustomResultIndex.tsx b/public/pages/DefineDetector/components/CustomResultIndex/CustomResultIndex.tsx index 55e53297..ccbc08e5 100644 --- a/public/pages/DefineDetector/components/CustomResultIndex/CustomResultIndex.tsx +++ b/public/pages/DefineDetector/components/CustomResultIndex/CustomResultIndex.tsx @@ -89,7 +89,7 @@ function CustomResultIndex(props: CustomResultIndexProps) { { - If the detector is still loading, the result index is not missing. - If the result index retrieved from the detector is empty, it is not missing. - If cat indices are being requested, the result index is not missing. - - If visible indices are empty, it is likely there is an issue retrieving existing indices. + - If visible indices/aliaes are empty, it is likely there is an issue retrieving existing indices. To be safe, we'd rather not show the error message and consider the result index not missing. - If the result index is not found in the visible indices, then it is missing. */ @@ -156,13 +156,14 @@ export const DetectorDetail = (props: DetectorDetailProps) => { ? false : isCatIndicesRequesting ? false - : isEmpty(visibleIndices) + : isEmpty(visibleIndices) || isEmpty(visibleAliases) ? false : !containsIndex(resultIndexOrAlias, visibleIndices) && !containsAlias(resultIndexOrAlias, visibleAliases); // debug message: prints visibleIndices if isResultIndexMissing is true if (isResultIndexMissing) { - console.log(`isResultIndexMissing is true, visibleIndices: ${visibleIndices}, visibleAliases: ${visibleAliases}, detector result index: ${resultIndexOrAlias}`); + // The JSON.stringify method converts a JavaScript object or value to a JSON string. The optional null parameter is for the replacer function (not used here), and 2 specifies the indentation level for pretty-printing the JSON. + console.log(`isResultIndexMissing is true, visibleIndices: ${JSON.stringify(visibleIndices, null, 2)}, visibleAliases: ${JSON.stringify(visibleAliases, null, 2)}, detector result index: ${resultIndexOrAlias}`); } // String to set in the modal if the realtime detector and/or historical analysis @@ -201,12 +202,13 @@ export const DetectorDetail = (props: DetectorDetailProps) => { scroll(0, 0); }, []); - // Getting all visible indices. Will re-fetch if changes to the detector (e.g., + // Getting all visible indices & aliases. Will re-fetch if changes to the detector (e.g., // detector starts, result index recreated or user switches tabs to re-fetch detector) useEffect(() => { - const getInitialIndices = async () => { + const getInitialIndicesAliases = async () => { try { await dispatch(getIndices('', dataSourceId)); + await dispatch(getAliases('', dataSourceId)); } catch (error) { console.error(error); core.notifications.toasts.addDanger('Error getting all indices'); @@ -214,7 +216,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { }; // only need to check if indices exist after detector finishes loading if (!isLoadingDetector) { - getInitialIndices(); + getInitialIndicesAliases(); } }, [detector]); diff --git a/public/pages/DetectorDetail/containers/__tests__/CustomIndexErrorMsg.test.tsx b/public/pages/DetectorDetail/containers/__tests__/CustomIndexErrorMsg.test.tsx index 0c569c31..93abd62f 100644 --- a/public/pages/DetectorDetail/containers/__tests__/CustomIndexErrorMsg.test.tsx +++ b/public/pages/DetectorDetail/containers/__tests__/CustomIndexErrorMsg.test.tsx @@ -209,7 +209,7 @@ describe('detector detail', () => { expect(element).toBeNull(); }); - test('the result index is not found in the visible indices', () => { + test('the result index is not found in the visible indices but alias is empty', () => { const detectorInfo = { detector: getRandomDetector(true, resultIndex), hasError: false, @@ -236,7 +236,7 @@ describe('detector detail', () => { const element = screen.queryByTestId('missingResultIndexCallOut'); // Assert that the element is in the document - expect(element).not.toBeNull(); + expect(element).toBeNull(); }); test('the result index is found in the visible indices', () => { @@ -273,8 +273,9 @@ describe('detector detail', () => { expect(element).toBeNull(); }); - test('the result index prefix is found in the visible indices', () => { + test('the result index prefix is found in the visible aliaes', () => { const detector = getRandomDetector(true, resultIndex); + const resultIndexFull = resultIndex + '-history-2024.06.05-1'; // Set up the mock implementation for useFetchDetectorInfo (useFetchDetectorInfo as jest.Mock).mockImplementation(() => ({ @@ -288,7 +289,12 @@ describe('detector detail', () => { opensearch: { indices: [ { health: 'green', index: '.kibana_-962704462_v992471_1' }, - { health: 'green', index: resultIndex + '-history-2024.06.05-1' }, + { health: 'green', index: resultIndexFull}, + ], + aliases : [ + {index: '.opendistro-anomaly-results-history-2024.06.08-1', alias: '.opendistro-anomaly-results'}, + {index: resultIndexFull, alias: resultIndex}, + {index: '.kibana_1', alias: '.kibana'}, ], requesting: false, }, @@ -306,4 +312,41 @@ describe('detector detail', () => { // Assert that the element is not in the document expect(element).toBeNull(); }); + + test('the result index prefix is not found in both visible aliaes and indices', () => { + const detector = getRandomDetector(true, resultIndex); + + // Set up the mock implementation for useFetchDetectorInfo + (useFetchDetectorInfo as jest.Mock).mockImplementation(() => ({ + detector: detector, + hasError: false, + isLoadingDetector: false, + errorMessage: undefined, + })); + + const initialState = { + opensearch: { + indices: [ + { health: 'green', index: '.kibana_-962704462_v992471_1' }, + ], + aliases : [ + {index: '.opendistro-anomaly-results-history-2024.06.08-1', alias: '.opendistro-anomaly-results'}, + {index: '.kibana_1', alias: '.kibana'}, + ], + requesting: false, + }, + ad: { + detectors: {}, + }, + alerting: { + monitors: {}, + }, + }; + + renderWithRouter(detectorId, initialState); + const element = screen.queryByTestId('missingResultIndexCallOut'); + + // Assert that the element is not in the document + expect(element).not.toBeNull(); + }); }); diff --git a/public/pages/ReviewAndCreate/components/DetectorDefinitionFields/DetectorDefinitionFields.tsx b/public/pages/ReviewAndCreate/components/DetectorDefinitionFields/DetectorDefinitionFields.tsx index ccdc50e5..e67f33a0 100644 --- a/public/pages/ReviewAndCreate/components/DetectorDefinitionFields/DetectorDefinitionFields.tsx +++ b/public/pages/ReviewAndCreate/components/DetectorDefinitionFields/DetectorDefinitionFields.tsx @@ -131,6 +131,10 @@ export const DetectorDefinitionFields = ( } }; + const minAge = get(props, 'detector.resultIndexMinAge', '-'); + const minSize = get(props, 'detector.resultIndexMinSize', '-'); + const ttl = get(props, 'detector.resultIndexTtl', '-'); + return ( diff --git a/public/pages/ReviewAndCreate/containers/__tests__/__snapshots__/ReviewAndCreate.test.tsx.snap b/public/pages/ReviewAndCreate/containers/__tests__/__snapshots__/ReviewAndCreate.test.tsx.snap index f257fde9..b723fc9b 100644 --- a/public/pages/ReviewAndCreate/containers/__tests__/__snapshots__/ReviewAndCreate.test.tsx.snap +++ b/public/pages/ReviewAndCreate/containers/__tests__/__snapshots__/ReviewAndCreate.test.tsx.snap @@ -424,7 +424,7 @@ exports[` spec renders the component, validation loading 1`]

- - Days + -

@@ -458,7 +458,7 @@ exports[` spec renders the component, validation loading 1`]

- - MB + -

@@ -492,7 +492,7 @@ exports[` spec renders the component, validation loading 1`]

- - Days + -

@@ -1518,7 +1518,7 @@ exports[`issue in detector validation issues in feature query 1`] = `

- - Days + -

@@ -1552,7 +1552,7 @@ exports[`issue in detector validation issues in feature query 1`] = `

- - MB + -

@@ -1586,7 +1586,7 @@ exports[`issue in detector validation issues in feature query 1`] = `

- - Days + -

From 89e33bbb70a77b68cf27b26601d5d61708a8f3fe Mon Sep 17 00:00:00 2001 From: Kaituo Li Date: Mon, 10 Jun 2024 08:54:48 -0700 Subject: [PATCH 3/3] fix warning msg Signed-off-by: Kaituo Li --- public/pages/DetectorDetail/containers/DetectorDetail.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/pages/DetectorDetail/containers/DetectorDetail.tsx b/public/pages/DetectorDetail/containers/DetectorDetail.tsx index 2b5db8a7..0275ca62 100644 --- a/public/pages/DetectorDetail/containers/DetectorDetail.tsx +++ b/public/pages/DetectorDetail/containers/DetectorDetail.tsx @@ -211,7 +211,7 @@ export const DetectorDetail = (props: DetectorDetailProps) => { await dispatch(getAliases('', dataSourceId)); } catch (error) { console.error(error); - core.notifications.toasts.addDanger('Error getting all indices'); + core.notifications.toasts.addDanger('Error getting all indices or aliases'); } }; // only need to check if indices exist after detector finishes loading