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/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) {
- - Days + -
@@ -458,7 +458,7 @@ exports[`- - MB + -
@@ -492,7 +492,7 @@ exports[`- - 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 + -
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}`; }