Skip to content

Commit af40fa6

Browse files
jackiehanyanggithub-actions[bot]
authored andcommitted
merge feature/mds branch into main (#739)
* Support MDS on List, Detail, Dashboard, Overview pages (#722) * update snapshots Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add snpshot Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add data source client Signed-off-by: Jackie Han <jkhanjob@gmail.com> * test * neo List Detectors page Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add opensearch_dashboards.json file Signed-off-by: Jackie Han <jkhanjob@gmail.com> * neo List Detectors page Signed-off-by: Jackie Han <jkhanjob@gmail.com> * test Signed-off-by: Jackie Han <jkhanjob@gmail.com> * Support MDS on DetectorDetails page Signed-off-by: Jackie Han <jkhanjob@gmail.com> * change 4/7 Signed-off-by: Jackie Han <jkhanjob@gmail.com> * version change Signed-off-by: Jackie Han <jkhanjob@gmail.com> * list detector list change Signed-off-by: Jackie Han <jkhanjob@gmail.com> * Support MDS on List, Detail, Dashboard, Overview pages Signed-off-by: Jackie Han <jkhanjob@gmail.com> * change version back to 3.0 Signed-off-by: Jackie Han <jkhanjob@gmail.com> * revert version change Signed-off-by: Jackie Han <jkhanjob@gmail.com> * make dataSourceId optional in DetectorListItem type Signed-off-by: Jackie Han <jkhanjob@gmail.com> * update imports Signed-off-by: Jackie Han <jkhanjob@gmail.com> * remove used function Signed-off-by: Jackie Han <jkhanjob@gmail.com> * cleanup Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add getter and setter for dataSource plugin Signed-off-by: Jackie Han <jkhanjob@gmail.com> * read dataSourceId from the url instead of passing props Signed-off-by: Jackie Han <jkhanjob@gmail.com> * addressing comments and run prettier Signed-off-by: Jackie Han <jkhanjob@gmail.com> * addressing comments Signed-off-by: Jackie Han <jkhanjob@gmail.com> * make getDataSourceManagementPlugin() optional Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add comment Signed-off-by: Jackie Han <jkhanjob@gmail.com> * make dataSourceId type safe Signed-off-by: Jackie Han <jkhanjob@gmail.com> --------- Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add MDS support for detector creation&Update flow (#728) * update snapshots Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add snpshot Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add data source client Signed-off-by: Jackie Han <jkhanjob@gmail.com> * test * neo List Detectors page Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add opensearch_dashboards.json file Signed-off-by: Jackie Han <jkhanjob@gmail.com> * neo List Detectors page Signed-off-by: Jackie Han <jkhanjob@gmail.com> * test Signed-off-by: Jackie Han <jkhanjob@gmail.com> * Support MDS on DetectorDetails page Signed-off-by: Jackie Han <jkhanjob@gmail.com> * change 4/7 Signed-off-by: Jackie Han <jkhanjob@gmail.com> * version change Signed-off-by: Jackie Han <jkhanjob@gmail.com> * list detector list change Signed-off-by: Jackie Han <jkhanjob@gmail.com> * Support MDS on List, Detail, Dashboard, Overview pages Signed-off-by: Jackie Han <jkhanjob@gmail.com> * change version back to 3.0 Signed-off-by: Jackie Han <jkhanjob@gmail.com> * revert version change Signed-off-by: Jackie Han <jkhanjob@gmail.com> * make dataSourceId optional in DetectorListItem type Signed-off-by: Jackie Han <jkhanjob@gmail.com> * update imports Signed-off-by: Jackie Han <jkhanjob@gmail.com> * remove used function Signed-off-by: Jackie Han <jkhanjob@gmail.com> * cleanup Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add getter and setter for dataSource plugin Signed-off-by: Jackie Han <jkhanjob@gmail.com> * read dataSourceId from the url instead of passing props Signed-off-by: Jackie Han <jkhanjob@gmail.com> * addressing comments Signed-off-by: Jackie Han <jkhanjob@gmail.com> * addressing comments and run prettier Signed-off-by: Jackie Han <jkhanjob@gmail.com> * addressing comments in neo1 branch Signed-off-by: Jackie Han <jkhanjob@gmail.com> * addressing comments Signed-off-by: Jackie Han <jkhanjob@gmail.com> * make getDataSourceManagementPlugin() optional Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add comment Signed-off-by: Jackie Han <jkhanjob@gmail.com> * make dataSourceId type safe Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add MDS support on creation page Signed-off-by: Jackie Han <jkhanjob@gmail.com> * updated methods to get data source query param from url Signed-off-by: Jackie Han <jkhanjob@gmail.com> * using helper to construct url with dataSourceId Signed-off-by: Jackie Han <jkhanjob@gmail.com> * add update page, and update urls on detector detail page Signed-off-by: Jackie Han <jkhanjob@gmail.com> * update update call router Signed-off-by: Jackie Han <jkhanjob@gmail.com> * update reducer and router Signed-off-by: Jackie Han <jkhanjob@gmail.com> * clean up Signed-off-by: Jackie Han <jkhanjob@gmail.com> * addressing comments Signed-off-by: Jackie Han <jkhanjob@gmail.com> * update enums Signed-off-by: Jackie Han <jkhanjob@gmail.com> --------- Signed-off-by: Jackie Han <jkhanjob@gmail.com> * update actionOption attribution for all data soure components (#732) Signed-off-by: Jackie Han <jkhanjob@gmail.com> * Add MDS UTs and bug fixes (#734) * working on ut Signed-off-by: Jackie Han <jkhanjob@gmail.com> * update ut Signed-off-by: Jackie Han <jkhanjob@gmail.com> * update UTs and add bug fixes Signed-off-by: Jackie Han <jkhanjob@gmail.com> --------- Signed-off-by: Jackie Han <jkhanjob@gmail.com> * bug fixes (#737) Signed-off-by: Jackie Han <jkhanjob@gmail.com> --------- Signed-off-by: Jackie Han <jkhanjob@gmail.com> (cherry picked from commit 48acb93)
1 parent 982b432 commit af40fa6

File tree

79 files changed

+2571
-852
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+2571
-852
lines changed

opensearch_dashboards.json

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"configPath": [
66
"anomaly_detection_dashboards"
77
],
8+
"optionalPlugins": ["dataSource","dataSourceManagement"],
89
"requiredPlugins": [
910
"opensearchDashboardsUtils",
1011
"expressions",

package.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@
2525
"devDependencies": {
2626
"@testing-library/user-event": "^12.1.6",
2727
"@types/react-plotly.js": "^2.6.0",
28-
"@types/redux-mock-store": "^1.0.1",
28+
"@types/redux-mock-store": "^1.0.6",
2929
"babel-polyfill": "^6.26.0",
3030
"eslint-plugin-no-unsanitized": "^3.0.2",
3131
"eslint-plugin-prefer-object-spread": "^1.2.1",
3232
"lint-staged": "^9.2.0",
3333
"moment": "^2.24.0",
34-
"redux-mock-store": "^1.5.3",
34+
"redux-mock-store": "^1.5.4",
3535
"start-server-and-test": "^1.11.7"
3636
},
3737
"dependencies": {
@@ -56,4 +56,4 @@
5656
"browserify-sign": "^4.2.2",
5757
"axios": "^1.6.1"
5858
}
59-
}
59+
}

public/anomaly_detection_app.tsx

+5-1
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,17 @@ export function renderApp(coreStart: CoreStart, params: AppMountParameters) {
2929
} else {
3030
require('@elastic/charts/dist/theme_only_light.css');
3131
}
32+
3233
ReactDOM.render(
3334
<Provider store={store}>
3435
<Router>
3536
<Route
3637
render={(props) => (
3738
<CoreServicesContext.Provider value={coreStart}>
38-
<Main {...props} />
39+
<Main
40+
setHeaderActionMenu={params.setHeaderActionMenu}
41+
{...props}
42+
/>
3943
</CoreServicesContext.Provider>
4044
)}
4145
/>

public/components/CreateDetectorButtons/CreateDetectorButtons.tsx

+12-2
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,24 @@
1212
import { EuiFlexGroup, EuiFlexItem, EuiButton } from '@elastic/eui';
1313
import React from 'react';
1414
import { APP_PATH, PLUGIN_NAME } from '../../utils/constants';
15+
import { useLocation } from 'react-router-dom';
16+
import { constructHrefWithDataSourceId, getDataSourceFromURL } from '../../pages/utils/helpers';
1517

1618
export const CreateDetectorButtons = () => {
19+
const location = useLocation();
20+
const MDSQueryParams = getDataSourceFromURL(location);
21+
const dataSourceId = MDSQueryParams.dataSourceId;
22+
23+
const createDetectorUrl = `${PLUGIN_NAME}#` + constructHrefWithDataSourceId(`${APP_PATH.CREATE_DETECTOR}`, dataSourceId, false);
24+
25+
const sampleDetectorUrl = `${PLUGIN_NAME}#` + constructHrefWithDataSourceId(`${APP_PATH.OVERVIEW}`, dataSourceId, false);
26+
1727
return (
1828
<EuiFlexGroup direction="row" gutterSize="m" justifyContent="center">
1929
<EuiFlexItem grow={false}>
2030
<EuiButton
2131
style={{ width: '200px' }}
22-
href={`${PLUGIN_NAME}#${APP_PATH.OVERVIEW}`}
32+
href={sampleDetectorUrl}
2333
data-test-subj="sampleDetectorButton"
2434
>
2535
Try a sample detector
@@ -29,7 +39,7 @@ export const CreateDetectorButtons = () => {
2939
<EuiButton
3040
style={{ width: '200px' }}
3141
fill
32-
href={`${PLUGIN_NAME}#${APP_PATH.CREATE_DETECTOR}`}
42+
href={createDetectorUrl}
3343
data-test-subj="createDetectorButton"
3444
>
3545
Create detector

public/models/interfaces.ts

+6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { DATA_TYPES } from '../utils/constants';
1414
import { DETECTOR_STATE } from '../../server/utils/constants';
1515
import { Duration } from 'moment';
1616
import moment from 'moment';
17+
import { MDSQueryParams } from '../../server/models/types';
1718

1819
export type FieldInfo = {
1920
label: string;
@@ -326,3 +327,8 @@ export interface ValidationSettingResponse {
326327
message: string;
327328
validationType: string;
328329
}
330+
331+
export interface MDSStates {
332+
queryParams: MDSQueryParams;
333+
selectedDataSourceId: string | undefined;
334+
}

public/pages/AnomalyCharts/containers/AnomalyDetailsChart.tsx

+19-5
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,19 @@ import {
8181
} from '../utils/constants';
8282
import { HeatmapCell } from './AnomalyHeatmapChart';
8383
import { ANOMALY_AGG, MIN_END_TIME, MAX_END_TIME } from '../../utils/constants';
84-
import { MAX_HISTORICAL_AGG_RESULTS } from '../../../utils/constants';
84+
import {
85+
DATA_SOURCE_ID,
86+
MAX_HISTORICAL_AGG_RESULTS,
87+
} from '../../../utils/constants';
8588
import { searchResults } from '../../../redux/reducers/anomalyResults';
8689
import {
8790
DAY_IN_MILLI_SECS,
8891
WEEK_IN_MILLI_SECS,
8992
DETECTOR_STATE,
9093
} from '../../../../server/utils/constants';
9194
import { ENTITY_COLORS } from '../../DetectorResults/utils/constants';
95+
import { useLocation } from 'react-router-dom';
96+
import { getDataSourceFromURL } from '../../../pages/utils/helpers';
9297

9398
interface AnomalyDetailsChartProps {
9499
onDateRangeChange(
@@ -118,6 +123,9 @@ interface AnomalyDetailsChartProps {
118123
export const AnomalyDetailsChart = React.memo(
119124
(props: AnomalyDetailsChartProps) => {
120125
const dispatch = useDispatch();
126+
const location = useLocation();
127+
const MDSQueryParams = getDataSourceFromURL(location);
128+
const dataSourceId = MDSQueryParams.dataSourceId;
121129
const [showAlertsFlyout, setShowAlertsFlyout] = useState<boolean>(false);
122130
const [alertAnnotations, setAlertAnnotations] = useState<any[]>([]);
123131
const [isLoadingAlerts, setIsLoadingAlerts] = useState<boolean>(false);
@@ -174,7 +182,9 @@ export const AnomalyDetailsChart = React.memo(
174182
zoomRange.endDate,
175183
taskId
176184
);
177-
dispatch(searchResults(anomalyDataRangeQuery, resultIndex, true))
185+
dispatch(
186+
searchResults(anomalyDataRangeQuery, resultIndex, dataSourceId, true)
187+
)
178188
.then((response: any) => {
179189
// Only retrieve buckets that are in the anomaly results range. This is so
180190
// we don't show aggregate results for where there is no data at all
@@ -193,7 +203,9 @@ export const AnomalyDetailsChart = React.memo(
193203
taskId,
194204
selectedAggId
195205
);
196-
dispatch(searchResults(historicalAggQuery, resultIndex, true))
206+
dispatch(
207+
searchResults(historicalAggQuery, resultIndex, dataSourceId, true)
208+
)
197209
.then((response: any) => {
198210
const aggregatedAnomalies = parseHistoricalAggregatedAnomalies(
199211
response,
@@ -229,7 +241,9 @@ export const AnomalyDetailsChart = React.memo(
229241
zoomRange.endDate,
230242
taskId
231243
);
232-
dispatch(searchResults(anomalyDataRangeQuery, resultIndex, true))
244+
dispatch(
245+
searchResults(anomalyDataRangeQuery, resultIndex, dataSourceId, true)
246+
)
233247
.then((response: any) => {
234248
const dataStartDate = get(
235249
response,
@@ -318,7 +332,7 @@ export const AnomalyDetailsChart = React.memo(
318332
try {
319333
setIsLoadingAlerts(true);
320334
const result = await dispatch(
321-
searchAlerts(monitorId, startDateTime, endDateTime)
335+
searchAlerts(monitorId, startDateTime, endDateTime, dataSourceId)
322336
);
323337
setIsLoadingAlerts(false);
324338
setTotalAlerts(get(result, 'response.totalAlerts'));

public/pages/AnomalyCharts/containers/__tests__/AnomaliesChart.test.tsx

+18-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import {
2222
FAKE_ANOMALIES_RESULT,
2323
FAKE_DATE_RANGE,
2424
} from '../../../../pages/utils/__tests__/constants';
25+
import { Router } from 'react-router-dom';
26+
import { createMemoryHistory } from 'history';
2527

2628
const DEFAULT_PROPS = {
2729
onDateRangeChange: jest.fn(),
@@ -47,17 +49,32 @@ const DEFAULT_PROPS = {
4749
entityAnomalySummaries: [],
4850
} as AnomaliesChartProps;
4951

52+
const history = createMemoryHistory();
53+
5054
const renderDataFilter = (chartProps: AnomaliesChartProps) => ({
5155
...render(
5256
<Provider store={mockedStore()}>
5357
<CoreServicesContext.Provider value={coreServicesMock}>
54-
<AnomaliesChart {...chartProps} />
58+
<Router history={history}>
59+
<AnomaliesChart {...chartProps} />
60+
</Router>
5561
</CoreServicesContext.Provider>
5662
</Provider>
5763
),
5864
});
5965

6066
describe('<AnomaliesChart /> spec', () => {
67+
beforeAll(() => {
68+
Object.defineProperty(window, 'location', {
69+
value: {
70+
href: 'http://test.com',
71+
pathname: '/',
72+
search: '',
73+
hash: '',
74+
},
75+
writable: true
76+
});
77+
});
6178
test('renders the component for sample / preview', () => {
6279
console.error = jest.fn();
6380
const { getByText, getAllByText } = renderDataFilter({

public/pages/AnomalyCharts/containers/__tests__/AnomalyDetailsChart.test.tsx

+41-15
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ import {
2222
} from '../../../../pages/utils/__tests__/constants';
2323
import { INITIAL_ANOMALY_SUMMARY } from '../../utils/constants';
2424
import { getRandomDetector } from '../../../../redux/reducers/__tests__/utils';
25+
import { createMemoryHistory } from 'history';
26+
import { Router } from 'react-router-dom';
27+
28+
jest.mock('../../../../services', () => ({
29+
...jest.requireActual('../../../../services'),
30+
31+
getDataSourceEnabled: () => ({
32+
enabled: false
33+
})
34+
}));
35+
36+
const history = createMemoryHistory();
2537

2638
const renderAnomalyOccurenceChart = (
2739
isNotSample: boolean,
@@ -30,27 +42,41 @@ const renderAnomalyOccurenceChart = (
3042
...render(
3143
<Provider store={mockedStore()}>
3244
<CoreServicesContext.Provider value={coreServicesMock}>
33-
<AnomalyDetailsChart
34-
onDateRangeChange={jest.fn()}
35-
onZoomRangeChange={jest.fn()}
36-
bucketizedAnomalies={false}
37-
anomalySummary={INITIAL_ANOMALY_SUMMARY}
38-
anomalies={FAKE_ANOMALY_DATA}
39-
detector={getRandomDetector()}
40-
dateRange={FAKE_DATE_RANGE}
41-
isLoading={false}
42-
showAlerts={isNotSample}
43-
isNotSample={isNotSample}
44-
isHCDetector={isHCDetector}
45-
anomalyGradeSeriesName={'testAnomalyGradeSeriesName'}
46-
confidenceSeriesName={'testConfidenceSeriesName'}
47-
/>
45+
<Router history={history}>
46+
<AnomalyDetailsChart
47+
onDateRangeChange={jest.fn()}
48+
onZoomRangeChange={jest.fn()}
49+
bucketizedAnomalies={false}
50+
anomalySummary={INITIAL_ANOMALY_SUMMARY}
51+
anomalies={FAKE_ANOMALY_DATA}
52+
detector={getRandomDetector()}
53+
dateRange={FAKE_DATE_RANGE}
54+
isLoading={false}
55+
showAlerts={isNotSample}
56+
isNotSample={isNotSample}
57+
isHCDetector={isHCDetector}
58+
anomalyGradeSeriesName={'testAnomalyGradeSeriesName'}
59+
confidenceSeriesName={'testConfidenceSeriesName'}
60+
/>
61+
</Router>
4862
</CoreServicesContext.Provider>
4963
</Provider>
5064
),
5165
});
5266

5367
describe('<AnomalyDetailsChart /> spec', () => {
68+
beforeAll(() => {
69+
Object.defineProperty(window, 'location', {
70+
value: {
71+
href: 'http://test.com',
72+
pathname: '/',
73+
search: '',
74+
hash: '',
75+
},
76+
writable: true
77+
});
78+
});
79+
5480
test('renders the component in case of Sample Anomaly', () => {
5581
console.error = jest.fn();
5682
const { getByText } = renderAnomalyOccurenceChart(false, false);

public/pages/AnomalyCharts/containers/__tests__/AnomalyOccurrenceChart.test.tsx

+41-16
Original file line numberDiff line numberDiff line change
@@ -16,42 +16,67 @@ import { mockedStore } from '../../../../redux/utils/testUtils';
1616
import { CoreServicesContext } from '../../../../components/CoreServices/CoreServices';
1717
import { coreServicesMock } from '../../../../../test/mocks';
1818
import { AnomalyOccurrenceChart } from '../AnomalyOccurrenceChart';
19+
import { createMemoryHistory } from 'history';
20+
import { Router } from 'react-router-dom';
1921
import {
2022
FAKE_ANOMALY_DATA,
2123
FAKE_DATE_RANGE,
2224
} from '../../../../pages/utils/__tests__/constants';
2325
import { INITIAL_ANOMALY_SUMMARY } from '../../utils/constants';
2426
import { getRandomDetector } from '../../../../redux/reducers/__tests__/utils';
2527

28+
jest.mock('../../../../services', () => ({
29+
...jest.requireActual('../../../../services'),
30+
31+
getDataSourceEnabled: () => ({
32+
enabled: false
33+
})
34+
}));
35+
36+
const history = createMemoryHistory();
37+
2638
const renderAnomalyOccurenceChart = (
2739
isNotSample: boolean,
2840
isHCDetector: boolean
2941
) => ({
3042
...render(
3143
<Provider store={mockedStore()}>
3244
<CoreServicesContext.Provider value={coreServicesMock}>
33-
<AnomalyOccurrenceChart
34-
onDateRangeChange={jest.fn()}
35-
onZoomRangeChange={jest.fn()}
36-
title="test"
37-
bucketizedAnomalies={false}
38-
anomalySummary={INITIAL_ANOMALY_SUMMARY}
39-
anomalies={FAKE_ANOMALY_DATA}
40-
detector={getRandomDetector()}
41-
dateRange={FAKE_DATE_RANGE}
42-
isLoading={false}
43-
showAlerts={isNotSample}
44-
isNotSample={isNotSample}
45-
isHCDetector={isHCDetector}
46-
anomalyGradeSeriesName={'testAnomalyGradeSeriesName'}
47-
confidenceSeriesName={'testConfidenceSeriesName'}
48-
/>
45+
<Router history={history}>
46+
<AnomalyOccurrenceChart
47+
onDateRangeChange={jest.fn()}
48+
onZoomRangeChange={jest.fn()}
49+
title="test"
50+
bucketizedAnomalies={false}
51+
anomalySummary={INITIAL_ANOMALY_SUMMARY}
52+
anomalies={FAKE_ANOMALY_DATA}
53+
detector={getRandomDetector()}
54+
dateRange={FAKE_DATE_RANGE}
55+
isLoading={false}
56+
showAlerts={isNotSample}
57+
isNotSample={isNotSample}
58+
isHCDetector={isHCDetector}
59+
anomalyGradeSeriesName={'testAnomalyGradeSeriesName'}
60+
confidenceSeriesName={'testConfidenceSeriesName'}
61+
/>
62+
</Router>
4963
</CoreServicesContext.Provider>
5064
</Provider>
5165
),
5266
});
5367

5468
describe('<AnomalyOccurrenceChart /> spec', () => {
69+
beforeAll(() => {
70+
Object.defineProperty(window, 'location', {
71+
value: {
72+
href: 'http://test.com',
73+
pathname: '/',
74+
search: '',
75+
hash: '',
76+
},
77+
writable: true
78+
});
79+
});
5580
test('renders the component in case of Sample Anomaly', () => {
5681
console.error = jest.fn();
5782
const { getByText } = renderAnomalyOccurenceChart(false, false);

0 commit comments

Comments
 (0)