fix: GET alerts endpoint (#220)

This commit is contained in:
Warren 2024-01-10 17:08:58 -08:00 committed by GitHub
parent e2c02985fc
commit 76d7d73b29
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 82 additions and 18 deletions

View file

@ -0,0 +1,6 @@
---
'@hyperdx/api': patch
'@hyperdx/app': patch
---
fix: GET alerts endpoint

View file

@ -3,6 +3,7 @@ import ms from 'ms';
import * as clickhouse from '@/clickhouse';
import { SQLSerializer } from '@/clickhouse/searchQueryParser';
import type { ObjectId } from '@/models';
import Alert, {
AlertChannel,
AlertInterval,
@ -76,11 +77,11 @@ export const validateGroupByProperty = async ({
const makeAlert = (alert: AlertInput) => {
return {
source: alert.source,
channel: alert.channel,
interval: alert.interval,
type: alert.type,
source: alert.source,
threshold: alert.threshold,
type: alert.type,
// Log alerts
logView: alert.logViewId,
groupBy: alert.groupBy,
@ -92,12 +93,20 @@ const makeAlert = (alert: AlertInput) => {
};
};
export const createAlert = async (alertInput: AlertInput) => {
return new Alert(makeAlert(alertInput)).save();
export const createAlert = async (teamId: ObjectId, alertInput: AlertInput) => {
return new Alert({
...makeAlert(alertInput),
team: teamId,
}).save();
};
// create an update alert function based off of the above create alert function
export const updateAlert = async (id: string, alertInput: AlertInput) => {
export const updateAlert = async (
id: string,
teamId: ObjectId,
alertInput: AlertInput,
) => {
// TODO: find by id and teamId
// should consider clearing AlertHistory when updating an alert?
return Alert.findByIdAndUpdate(id, makeAlert(alertInput), {
returnDocument: 'after',

View file

@ -34,11 +34,12 @@ export interface IAlert {
channel: AlertChannel;
cron: string;
interval: AlertInterval;
source?: AlertSource;
state: AlertState;
team: ObjectId;
threshold: number;
timezone: string;
type: AlertType;
source?: AlertSource;
// Log alerts
groupBy?: string;
@ -85,6 +86,10 @@ const AlertSchema = new Schema<IAlert>(
required: false,
default: 'LOG',
},
team: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Team',
},
// Log alerts
logView: {

View file

@ -11,8 +11,8 @@ import {
import { getTeam } from '@/controllers/team';
import Alert from '@/models/alert';
import AlertHistory from '@/models/alertHistory';
import { IDashboard } from '@/models/dashboard';
import { ILogView } from '@/models/logView';
import Dashboard, { IDashboard } from '@/models/dashboard';
import LogView, { ILogView } from '@/models/logView';
const router = express.Router();
@ -82,7 +82,27 @@ router.get('/', async (req, res, next) => {
if (teamId == null) {
return res.sendStatus(403);
}
const alerts = await Alert.find({ team: teamId }).populate<{
// TODO: to use team field in the alert model
const [dashboards, logViews] = await Promise.all([
Dashboard.find({ team: teamId }, { _id: 1 }),
LogView.find({ team: teamId }, { _id: 1 }),
]);
const alerts = await Alert.find({
$or: [
{
logView: {
$in: logViews.map(logView => logView._id),
},
},
{
dashboardId: {
$in: dashboards.map(dashboard => dashboard._id),
},
},
],
}).populate<{
logView: ILogView;
dashboardId: IDashboard;
}>(['logView', 'dashboardId']);
@ -92,7 +112,6 @@ router.get('/', async (req, res, next) => {
const history = await AlertHistory.find(
{
alert: alert._id,
team: teamId,
},
{
__v: 0,
@ -105,16 +124,30 @@ router.get('/', async (req, res, next) => {
return {
history,
dashboard: alert.dashboardId,
channel: _.pick(alert.channel, ['type']),
...(alert.dashboardId && {
dashboard: {
charts: alert.dashboardId.charts
.filter(chart => chart.id === alert.chartId)
.map(chart => _.pick(chart, ['id', 'name'])),
..._.pick(alert.dashboardId, ['_id', 'name', 'updatedAt']),
},
}),
...(alert.logView && {
logView: _.pick(alert.logView, [
'_id',
'createdAt',
'name',
'updatedAt',
]),
}),
..._.pick(alert, [
'_id',
'channel',
'interval',
'threshold',
'state',
'type',
'source',
'logView',
'chartId',
'createdAt',
'updatedAt',
@ -135,10 +168,14 @@ router.post(
validateRequest({ body: zAlert }),
validateGroupBy,
async (req, res, next) => {
const teamId = req.user?.team;
if (teamId == null) {
return res.sendStatus(403);
}
try {
const alertInput = req.body;
return res.json({
data: await createAlert(alertInput),
data: await createAlert(teamId, alertInput),
});
} catch (e) {
next(e);
@ -152,10 +189,14 @@ router.put(
validateGroupBy,
async (req, res, next) => {
try {
const teamId = req.user?.team;
if (teamId == null) {
return res.sendStatus(403);
}
const { id } = req.params;
const alertInput = req.body;
res.json({
data: await updateAlert(id, alertInput),
data: await updateAlert(id, teamId, alertInput),
});
} catch (e) {
next(e);
@ -173,6 +214,7 @@ router.delete('/:id', async (req, res, next) => {
if (!alertId) {
return res.sendStatus(400);
}
// FIXME: should add teamId to the find query
await Alert.findByIdAndDelete(alertId);
res.sendStatus(200);
} catch (e) {

View file

@ -71,6 +71,7 @@ router.patch('/:id', async (req, res, next) => {
if (!logViewId || !query) {
return res.sendStatus(400);
}
// TODO: query teamId
const logView = await LogView.findByIdAndUpdate(
logViewId,
{
@ -96,6 +97,7 @@ router.delete('/:id', async (req, res, next) => {
if (!logViewId) {
return res.sendStatus(400);
}
// TODO: query teamId
// delete all alerts
await Alert.deleteMany({ logView: logViewId });
await LogView.findByIdAndDelete(logViewId);

View file

@ -179,7 +179,7 @@ describe('checkAlerts', () => {
url: 'https://hooks.slack.com/services/123',
name: 'My Webhook',
}).save();
const alert = await createAlert({
const alert = await createAlert(team._id, {
source: 'LOG',
channel: {
type: 'webhook',
@ -353,7 +353,7 @@ describe('checkAlerts', () => {
},
],
}).save();
const alert = await createAlert({
const alert = await createAlert(team._id, {
source: 'CHART',
channel: {
type: 'webhook',
@ -586,7 +586,7 @@ describe('checkAlerts', () => {
},
],
}).save();
const alert = await createAlert({
const alert = await createAlert(team._id, {
source: 'CHART',
channel: {
type: 'webhook',