Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

add start/stop AD job api #12

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions public/models/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ export type Detector = {
windowDelay: { period: Schedule };
detectionInterval: { period: Schedule };
uiMetadata: UiMetaData;
enabled?: boolean;
enabledTime?: Date;
disabledTime?: Date;
};

export type DetectorListItem = {
Expand Down
65 changes: 65 additions & 0 deletions public/redux/reducers/ad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ const GET_DETECTOR_LIST = 'ad/GET_DETECTOR_LIST';
const UPDATE_DETECTOR = 'ad/UPDATE_DETECTOR';
const SEARCH_DETECTOR = 'ad/SEARCH_DETECTOR';
const DELETE_DETECTOR = 'ad/DELETE_DETECTOR';
const START_DETECTOR = 'ad/START_DETECTOR';
const STOP_DETECTOR = 'ad/STOP_DETECTOR';

export interface Detectors {
requesting: boolean;
Expand Down Expand Up @@ -92,6 +94,51 @@ const reducer = handleActions<Detectors>(
errorMessage: action.error.data.error,
}),
},
[START_DETECTOR]: {
REQUEST: (state: Detectors): Detectors => {
const newState = { ...state, requesting: true, errorMessage: '' };
return newState;
},
SUCCESS: (state: Detectors, action: APIResponseAction): Detectors => ({
...state,
requesting: false,
detectors: {
...state.detectors,
[action.detectorId]: {
...[action.detectorId],
enabled: true,
},
},
}),
FAILURE: (state: Detectors, action: APIErrorAction): Detectors => ({
...state,
requesting: false,
errorMessage: action.error,
}),
},

[STOP_DETECTOR]: {
REQUEST: (state: Detectors): Detectors => {
const newState = { ...state, requesting: true, errorMessage: '' };
return newState;
},
SUCCESS: (state: Detectors, action: APIResponseAction): Detectors => ({
...state,
requesting: false,
detectors: {
...state.detectors,
[action.detectorId]: {
...[action.detectorId],
enabled: false,
},
},
}),
FAILURE: (state: Detectors, action: APIErrorAction): Detectors => ({
...state,
requesting: false,
errorMessage: action.error,
}),
},
[SEARCH_DETECTOR]: {
REQUEST: (state: Detectors): Detectors => ({
...state,
Expand Down Expand Up @@ -233,4 +280,22 @@ export const deleteDetector = (detectorId: string): APIAction => ({
detectorId,
});

export const startDetector = (detectorId: string): APIAction => ({
type: START_DETECTOR,
request: (client: IHttpService) =>
client.post(`..${AD_NODE_API.DETECTOR}/${detectorId}/start`, {
detectorId: detectorId,
}),
detectorId,
});

export const stopDetector = (detectorId: string): APIAction => ({
type: STOP_DETECTOR,
request: (client: IHttpService) =>
client.post(`..${AD_NODE_API.DETECTOR}/${detectorId}/stop`, {
detectorId: detectorId,
}),
detectorId,
});

export default reducer;
28 changes: 27 additions & 1 deletion server/cluster/ad/adPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export default function adPlugin(Client: any, config: any, components: any) {
});
ad.getDetector = ca({
url: {
fmt: `${API.DETECTOR_BASE}/<%=detectorId%>`,
fmt: `${API.DETECTOR_BASE}/<%=detectorId%>?job=true`,
req: {
detectorId: {
type: 'string',
Expand All @@ -100,4 +100,30 @@ export default function adPlugin(Client: any, config: any, components: any) {
needBody: true,
method: 'POST',
});

ad.startDetector = ca({
url: {
fmt: `${API.DETECTOR_BASE}/<%=detectorId%>/_start`,
req: {
detectorId: {
type: 'string',
required: true,
},
},
},
method: 'POST',
});

ad.stopDetector = ca({
url: {
fmt: `${API.DETECTOR_BASE}/<%=detectorId%>/_stop`,
req: {
detectorId: {
type: 'string',
required: true,
},
},
},
method: 'POST',
});
}
3 changes: 3 additions & 0 deletions server/models/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ export type Detector = {
windowDelay?: { period: Schedule };
detectionInterval?: { period: Schedule };
uiMetadata?: { [key: string]: any };
enabled: boolean;
enabledTime?: Date;
disabledTime?: Date;
};

export type GetDetectorsQueryParams = {
Expand Down
51 changes: 47 additions & 4 deletions server/routes/ad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ export default function (apiRouter: Router) {
apiRouter.post('/detectors/{detectorId}/preview', previewDetector);
apiRouter.get('/detectors/{detectorId}/results', getAnomalyResults);
apiRouter.delete('/detectors/{detectorId}', deleteDetector);
apiRouter.post('/detectors/{detectorId}/start', startDetector);
apiRouter.post('/detectors/{detectorId}/stop', stopDetector);
}

const deleteDetector = async (
Expand Down Expand Up @@ -165,6 +167,7 @@ const getDetector = async (
id: response._id,
primaryTerm: response._primary_term,
seqNo: response._seq_no,
adJob: { ...response.anomaly_detector_job },
};
return {
ok: true,
Expand All @@ -176,6 +179,46 @@ const getDetector = async (
}
};

const startDetector = async (
req: Request,
h: ResponseToolkit,
callWithRequest: CallClusterWithRequest
): Promise<ServerResponse<AnomalyResults>> => {
try {
const { detectorId } = req.params;
const response = await callWithRequest(req, 'ad.startDetector', {
detectorId,
});
return {
ok: true,
response: response,
};
} catch (err) {
console.log('Anomaly detector - strartDetector', err);
return { ok: false, error: err.body || err.message };
}
};

const stopDetector = async (
req: Request,
h: ResponseToolkit,
callWithRequest: CallClusterWithRequest
): Promise<ServerResponse<AnomalyResults>> => {
try {
const { detectorId } = req.params;
const response = await callWithRequest(req, 'ad.stopDetector', {
detectorId,
});
return {
ok: true,
response: response,
};
} catch (err) {
console.log('Anomaly detector - stopDetector', err);
return { ok: false, error: err.body || err.message };
}
};

const searchDetector = async (
req: Request,
h: ResponseToolkit,
Expand Down Expand Up @@ -366,8 +409,8 @@ const getAnomalyResults = async (
const sortQueryMap = {
anomalyGrade: { anomaly_grade: sortDirection },
confidence: { confidence: sortDirection },
startTime: { start_time: sortDirection },
endTime: { end_time: sortDirection },
startTime: { data_start_time: sortDirection },
endTime: { data_end_time: sortDirection },
} as { [key: string]: object };
let sort = {};
const sortQuery = sortQueryMap[sortField];
Expand Down Expand Up @@ -396,8 +439,8 @@ const getAnomalyResults = async (
// Get all detectors from search detector API
const detectorResults: AnomalyResult[] = get(response, 'hits.hits', []).map(
(result: any) => ({
startTime: result._source.start_time,
endTime: result._source.end_time,
startTime: result._source.data_start_time,
endTime: result._source.data_end_time,
confidence: result._source.confidence != null && result._source.confidence > 0 ? Number.parseFloat(result._source.confidence).toFixed(3) : 0,
anomalyGrade: result._source.anomaly_grade != null && result._source.anomaly_grade > 0 ? Number.parseFloat(result._source.anomaly_grade).toFixed(3) : 0
})
Expand Down
16 changes: 8 additions & 8 deletions server/routes/utils/__tests__/adHelpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,10 @@ describe('adHelpers', () => {
aggs: {
total_anomalies_in_24hr: {
filter: {
range: { start_time: { gte: 'now-24h', lte: 'now' } },
range: { data_start_time: { gte: 'now-24h', lte: 'now' } },
},
},
latest_anomaly_time: { max: { field: 'start_time' } },
latest_anomaly_time: { max: { field: 'data_start_time' } },
},
},
},
Expand Down Expand Up @@ -261,10 +261,10 @@ describe('adHelpers', () => {
aggs: {
total_anomalies_in_24hr: {
filter: {
range: { start_time: { gte: 'now-24h', lte: 'now' } },
range: { data_start_time: { gte: 'now-24h', lte: 'now' } },
},
},
latest_anomaly_time: { max: { field: 'start_time' } },
latest_anomaly_time: { max: { field: 'data_start_time' } },
},
},
},
Expand Down Expand Up @@ -301,10 +301,10 @@ describe('adHelpers', () => {
aggs: {
total_anomalies_in_24hr: {
filter: {
range: { start_time: { gte: 'now-24h', lte: 'now' } },
range: { data_start_time: { gte: 'now-24h', lte: 'now' } },
},
},
latest_anomaly_time: { max: { field: 'start_time' } },
latest_anomaly_time: { max: { field: 'data_start_time' } },
},
},
},
Expand Down Expand Up @@ -341,10 +341,10 @@ describe('adHelpers', () => {
aggs: {
total_anomalies_in_24hr: {
filter: {
range: { start_time: { gte: 'now-24h', lte: 'now' } },
range: { data_start_time: { gte: 'now-24h', lte: 'now' } },
},
},
latest_anomaly_time: { max: { field: 'start_time' } },
latest_anomaly_time: { max: { field: 'data_start_time' } },
},
},
},
Expand Down
25 changes: 18 additions & 7 deletions server/routes/utils/adHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const convertDetectorKeysToCamelCase = (response: object) => {
'ui_metadata',
'feature_query',
'feature_attributes',
'adJob',
]),
toCamel
),
Expand All @@ -56,6 +57,13 @@ export const convertDetectorKeysToCamelCase = (response: object) => {
})
),
uiMetadata: get(response, 'ui_metadata', {}),
enabled: get(response, 'adJob.enabled', false),
enabledTime: get(response, 'adJob.enabled_time')
? new Date(get(response, 'adJob.enabled_time'))
: undefined,
disabledTime: get(response, 'adJob.disabled_time')
? new Date(get(response, 'adJob.disabled_time'))
: undefined,
};
};

Expand Down Expand Up @@ -97,9 +105,9 @@ export const getResultAggregationQuery = (
},
aggs: {
total_anomalies_in_24hr: {
filter: { range: { start_time: { gte: 'now-24h', lte: 'now' } } },
filter: { range: { data_start_time: { gte: 'now-24h', lte: 'now' } } },
},
latest_anomaly_time: { max: { field: 'start_time' } },
latest_anomaly_time: { max: { field: 'data_start_time' } },
},
},
},
Expand All @@ -118,8 +126,9 @@ export const anomalyResultMapper = (anomalyResults: any[]): AnomalyResults => {
resultData.featureData[feature.featureId] = [];
});
anomalyResults.forEach(({ featureData, ...rest }) => {
const { dataStartTime, dataEndTime, ...others } = rest;
resultData.anomalies.push({
...rest,
...others,
anomalyGrade:
rest.anomalyGrade != null && rest.anomalyGrade > 0
? Number.parseFloat(rest.anomalyGrade).toFixed(3)
Expand All @@ -128,15 +137,17 @@ export const anomalyResultMapper = (anomalyResults: any[]): AnomalyResults => {
rest.anomalyGrade != null && rest.anomalyGrade > 0
? Number.parseFloat(rest.confidence).toFixed(3)
: 0,
startTime: rest.dataStartTime,
endTime: rest.dataEndTime,
plotTime:
rest.startTime + Math.floor((rest.endTime - rest.startTime) / 2),
rest.dataStartTime + Math.floor((rest.dataEndTime - rest.dataStartTime) / 2),
});
featureData.forEach((feature: any) => {
resultData.featureData[feature.featureId].push({
startTime: rest.startTime,
endTime: rest.endTime,
startTime: rest.dataStartTime,
endTime: rest.dataEndTime,
plotTime:
rest.startTime + Math.floor((rest.endTime - rest.startTime) / 2),
rest.dataStartTime + Math.floor((rest.dataEndTime - rest.dataStartTime) / 2),
data: feature.data,
});
});
Expand Down