-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[RAM] Add alert summary API (#146709)
## Summary Resolve: #141487 ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
- Loading branch information
Showing
14 changed files
with
614 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
116 changes: 116 additions & 0 deletions
116
x-pack/plugins/rule_registry/server/routes/get_alert_summary.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
/* | ||
* 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 { BASE_RAC_ALERTS_API_PATH } from '../../common/constants'; | ||
import { getAlertSummaryRoute } from './get_alert_summary'; | ||
import { requestContextMock } from './__mocks__/request_context'; | ||
import { requestMock, serverMock } from './__mocks__/server'; | ||
|
||
describe('getAlertSummaryRoute', () => { | ||
let server: ReturnType<typeof serverMock.create>; | ||
let { clients, context } = requestContextMock.createTools(); | ||
|
||
beforeEach(async () => { | ||
server = serverMock.create(); | ||
({ clients, context } = requestContextMock.createTools()); | ||
|
||
clients.rac.getAlertSummary.mockResolvedValue({ | ||
activeAlertCount: 0, | ||
recoveredAlertCount: 0, | ||
activeAlerts: [], | ||
recoveredAlerts: [], | ||
}); | ||
|
||
getAlertSummaryRoute(server.router); | ||
}); | ||
|
||
describe('request validation', () => { | ||
test('rejects invalid query params', async () => { | ||
await expect( | ||
server.inject( | ||
requestMock.create({ | ||
method: 'post', | ||
path: `${BASE_RAC_ALERTS_API_PATH}/_alert_summary`, | ||
body: { gte: 4, lte: 3, featureIds: ['logs'] }, | ||
}), | ||
context | ||
) | ||
).rejects.toThrowErrorMatchingInlineSnapshot( | ||
`"Request was rejected with message: 'Invalid value \\"4\\" supplied to \\"gte\\",Invalid value \\"3\\" supplied to \\"lte\\"'"` | ||
); | ||
}); | ||
|
||
test('validate gte/lte format', async () => { | ||
const resp = await server.inject( | ||
requestMock.create({ | ||
method: 'post', | ||
path: `${BASE_RAC_ALERTS_API_PATH}/_alert_summary`, | ||
body: { | ||
gte: '2020-12-16T15:00:00.000Z', | ||
lte: '2020-12-16', | ||
featureIds: ['logs'], | ||
}, | ||
}), | ||
context | ||
); | ||
expect(resp.status).toEqual(400); | ||
expect(resp.body).toMatchInlineSnapshot(` | ||
Object { | ||
"attributes": Object { | ||
"success": false, | ||
}, | ||
"message": "gte and/or lte are not following the UTC format", | ||
} | ||
`); | ||
}); | ||
|
||
test('validate fixed_interval ', async () => { | ||
const resp = await server.inject( | ||
requestMock.create({ | ||
method: 'post', | ||
path: `${BASE_RAC_ALERTS_API_PATH}/_alert_summary`, | ||
body: { | ||
gte: '2020-12-16T15:00:00.000Z', | ||
lte: '2020-12-16T16:00:00.000Z', | ||
featureIds: ['logs'], | ||
fixed_interval: 'xx', | ||
}, | ||
}), | ||
context | ||
); | ||
expect(resp.status).toEqual(400); | ||
expect(resp.body).toMatchInlineSnapshot(` | ||
Object { | ||
"attributes": Object { | ||
"success": false, | ||
}, | ||
"message": "fixed_interval is not following the expected format 1m, 1h, 1d, 1w", | ||
} | ||
`); | ||
}); | ||
|
||
test('rejects unknown query params', async () => { | ||
await expect( | ||
server.inject( | ||
requestMock.create({ | ||
method: 'post', | ||
path: `${BASE_RAC_ALERTS_API_PATH}/_alert_summary`, | ||
body: { | ||
gte: '2020-12-16T15:00:00.000Z', | ||
lte: '2020-12-16T16:00:00.000Z', | ||
featureIds: ['logs'], | ||
boop: 'unknown', | ||
}, | ||
}), | ||
context | ||
) | ||
).rejects.toThrowErrorMatchingInlineSnapshot( | ||
`"Request was rejected with message: 'invalid keys \\"boop\\"'"` | ||
); | ||
}); | ||
}); | ||
}); |
97 changes: 97 additions & 0 deletions
97
x-pack/plugins/rule_registry/server/routes/get_alert_summary.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
/* | ||
* 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 Boom from '@hapi/boom'; | ||
import { IRouter } from '@kbn/core/server'; | ||
import * as t from 'io-ts'; | ||
import { transformError } from '@kbn/securitysolution-es-utils'; | ||
import moment from 'moment'; | ||
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; | ||
|
||
import { RacRequestHandlerContext } from '../types'; | ||
import { BASE_RAC_ALERTS_API_PATH } from '../../common/constants'; | ||
import { buildRouteValidation } from './utils/route_validation'; | ||
|
||
export const getAlertSummaryRoute = (router: IRouter<RacRequestHandlerContext>) => { | ||
router.post( | ||
{ | ||
path: `${BASE_RAC_ALERTS_API_PATH}/_alert_summary`, | ||
validate: { | ||
body: buildRouteValidation( | ||
t.intersection([ | ||
t.exact( | ||
t.type({ | ||
gte: t.string, | ||
lte: t.string, | ||
featureIds: t.array(t.string), | ||
}) | ||
), | ||
t.exact( | ||
t.partial({ | ||
fixed_interval: t.string, | ||
filter: t.array(t.object), | ||
}) | ||
), | ||
]) | ||
), | ||
}, | ||
options: { | ||
tags: ['access:rac'], | ||
}, | ||
}, | ||
async (context, request, response) => { | ||
try { | ||
const racContext = await context.rac; | ||
const alertsClient = await racContext.getAlertsClient(); | ||
const { gte, lte, featureIds, filter, fixed_interval: fixedInterval } = request.body; | ||
if ( | ||
!( | ||
moment(gte, 'YYYY-MM-DDTHH:mm:ss.SSSZ', true).isValid() && | ||
moment(lte, 'YYYY-MM-DDTHH:mm:ss.SSSZ', true).isValid() | ||
) | ||
) { | ||
throw Boom.badRequest('gte and/or lte are not following the UTC format'); | ||
} | ||
|
||
if (fixedInterval && fixedInterval?.match(/^\d{1,2}['m','h','d','w']$/) == null) { | ||
throw Boom.badRequest( | ||
'fixed_interval is not following the expected format 1m, 1h, 1d, 1w' | ||
); | ||
} | ||
|
||
const aggs = await alertsClient.getAlertSummary({ | ||
gte, | ||
lte, | ||
featureIds, | ||
filter: filter as estypes.QueryDslQueryContainer[], | ||
fixedInterval, | ||
}); | ||
return response.ok({ | ||
body: aggs, | ||
}); | ||
} catch (exc) { | ||
const err = transformError(exc); | ||
const contentType = { | ||
'content-type': 'application/json', | ||
}; | ||
const defaultedHeaders = { | ||
...contentType, | ||
}; | ||
return response.customError({ | ||
headers: defaultedHeaders, | ||
statusCode: err.statusCode, | ||
body: { | ||
message: err.message, | ||
attributes: { | ||
success: false, | ||
}, | ||
}, | ||
}); | ||
} | ||
} | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.