Skip to content

Commit d3838a1

Browse files
committed
feat: API for Tags
1 parent cbdbe72 commit d3838a1

File tree

9 files changed

+121
-10
lines changed

9 files changed

+121
-10
lines changed

.changeset/six-lemons-compete.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@hyperdx/api': patch
3+
---
4+
5+
Add tags to Dashboards and LogViews

packages/api/src/models/dashboard.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export interface IDashboard {
7878
query: string;
7979
team: ObjectId;
8080
charts: Chart[];
81+
tags: string[];
8182
}
8283

8384
const DashboardSchema = new Schema<IDashboard>(
@@ -89,6 +90,10 @@ const DashboardSchema = new Schema<IDashboard>(
8990
query: String,
9091
team: { type: mongoose.Schema.Types.ObjectId, ref: 'Team' },
9192
charts: { type: mongoose.Schema.Types.Mixed, required: true },
93+
tags: {
94+
type: [String],
95+
default: [],
96+
},
9297
},
9398
{
9499
timestamps: true,

packages/api/src/models/logView.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export interface ILogView {
88
name: string;
99
query: string;
1010
team: ObjectId;
11+
tags: string[];
1112
}
1213

1314
const LogViewSchema = new Schema<ILogView>(
@@ -22,6 +23,10 @@ const LogViewSchema = new Schema<ILogView>(
2223
},
2324
team: { type: mongoose.Schema.Types.ObjectId, ref: 'Team' },
2425
creator: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
26+
tags: {
27+
type: [String],
28+
default: [],
29+
},
2530
},
2631
{
2732
timestamps: true,

packages/api/src/routers/api/__tests__/team.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,38 @@ Object {
4545
}
4646
`);
4747
});
48+
49+
it('GET /team/tags - no tags', async () => {
50+
const { agent } = await getLoggedInAgent(server);
51+
52+
const resp = await agent.get('/team/tags').expect(200);
53+
54+
expect(resp.body.data).toMatchInlineSnapshot(`Array []`);
55+
});
56+
57+
it('GET /team/tags', async () => {
58+
const { agent } = await getLoggedInAgent(server);
59+
await agent
60+
.post('/dashboards')
61+
.send({
62+
name: 'Test',
63+
charts: [],
64+
query: '',
65+
tags: ['test'],
66+
})
67+
.expect(200);
68+
await agent
69+
.post('/log-views')
70+
.send({
71+
name: 'Test',
72+
query: '',
73+
tags: ['test2'],
74+
})
75+
.expect(200);
76+
const resp = await agent.get('/team/tags').expect(200);
77+
expect(resp.body.data).toMatchInlineSnapshot(`Array [
78+
"test",
79+
"test2",
80+
]`);
81+
});
4882
});

packages/api/src/routers/api/dashboards.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ router.post(
9797
name: z.string(),
9898
charts: z.array(zChart),
9999
query: z.string(),
100+
tags: z.array(z.string()).optional(),
100101
}),
101102
}),
102103
async (req, res, next) => {
@@ -106,12 +107,13 @@ router.post(
106107
return res.sendStatus(403);
107108
}
108109

109-
const { name, charts, query } = req.body ?? {};
110+
const { name, charts, query, tags } = req.body ?? {};
110111
// Create new dashboard from name and charts
111112
const newDashboard = await new Dashboard({
112113
name,
113114
charts,
114115
query,
116+
tags,
115117
team: teamId,
116118
}).save();
117119

@@ -131,6 +133,7 @@ router.put(
131133
name: z.string(),
132134
charts: z.array(zChart),
133135
query: z.string(),
136+
tags: z.array(z.string()).optional(),
134137
}),
135138
}),
136139
async (req, res, next) => {
@@ -144,7 +147,7 @@ router.put(
144147
return res.sendStatus(400);
145148
}
146149

147-
const { name, charts, query } = req.body ?? {};
150+
const { name, charts, query, tags } = req.body ?? {};
148151
// Update dashboard from name and charts
149152
const oldDashboard = await Dashboard.findById(dashboardId);
150153
const updatedDashboard = await Dashboard.findByIdAndUpdate(
@@ -153,6 +156,7 @@ router.put(
153156
name,
154157
charts,
155158
query,
159+
tags,
156160
},
157161
{ new: true },
158162
);

packages/api/src/routers/api/logViews.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ router.post('/', async (req, res, next) => {
99
try {
1010
const teamId = req.user?.team;
1111
const userId = req.user?._id;
12-
const { query, name } = req.body;
12+
const { query, name, tags } = req.body;
1313
if (teamId == null) {
1414
return res.sendStatus(403);
1515
}
@@ -18,6 +18,7 @@ router.post('/', async (req, res, next) => {
1818
}
1919
const logView = await new LogView({
2020
name,
21+
tags,
2122
query: `${query}`,
2223
team: teamId,
2324
creator: userId,
@@ -64,7 +65,7 @@ router.patch('/:id', async (req, res, next) => {
6465
try {
6566
const teamId = req.user?.team;
6667
const { id: logViewId } = req.params;
67-
const { query } = req.body;
68+
const { query, tags } = req.body;
6869
if (teamId == null) {
6970
return res.sendStatus(403);
7071
}
@@ -76,6 +77,7 @@ router.patch('/:id', async (req, res, next) => {
7677
logViewId,
7778
{
7879
query,
80+
tags,
7981
},
8082
{ new: true },
8183
);

packages/api/src/routers/api/team.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@ import crypto from 'crypto';
22
import express from 'express';
33
import isemail from 'isemail';
44
import pick from 'lodash/pick';
5+
import ms from 'ms';
56
import { serializeError } from 'serialize-error';
67

78
import * as config from '@/config';
89
import { getTeam, rotateTeamApiKey } from '@/controllers/team';
910
import { findUserByEmail, findUsersByTeam } from '@/controllers/user';
11+
import Dashboard from '@/models/dashboard';
12+
import LogView from '@/models/logView';
1013
import TeamInvite from '@/models/teamInvite';
1114
import logger from '@/utils/logger';
15+
import { SimpleCache } from '@/utils/redis';
1216

1317
const router = express.Router();
1418

@@ -144,4 +148,43 @@ router.patch('/apiKey', async (req, res, next) => {
144148
}
145149
});
146150

151+
router.get('/tags', async (req, res, next) => {
152+
try {
153+
const teamId = req.user?.team;
154+
if (teamId == null) {
155+
throw new Error(`User ${req.user?._id} not associated with a team`);
156+
}
157+
const simpleCache = new SimpleCache(
158+
`tags:${teamId}`,
159+
ms('10m'),
160+
async () => {
161+
const _tags: string[] = [];
162+
const dashboards = await Dashboard.find(
163+
{ team: teamId },
164+
{ tags: 1 },
165+
).lean();
166+
dashboards?.forEach(d => {
167+
if (d?.tags?.length) {
168+
_tags?.push(...d.tags);
169+
}
170+
});
171+
const logViews = await LogView.find(
172+
{ team: teamId },
173+
{ tags: 1 },
174+
).lean();
175+
logViews?.forEach(lv => {
176+
if (lv?.tags?.length) {
177+
_tags?.push(...lv.tags);
178+
}
179+
});
180+
return _tags;
181+
},
182+
);
183+
const tags = await simpleCache.get();
184+
return res.json({ data: tags });
185+
} catch (e) {
186+
next(e);
187+
}
188+
});
189+
147190
export default router;

packages/app/src/api.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -572,23 +572,29 @@ const api = {
572572
return useMutation<
573573
any,
574574
HTTPError,
575-
{ name: string; query: string; charts: any[] }
576-
>(async ({ name, charts, query }) =>
575+
{ name: string; query: string; charts: any[]; tags?: string[] }
576+
>(async ({ name, charts, query, tags }) =>
577577
server(`dashboards`, {
578578
method: 'POST',
579-
json: { name, charts, query },
579+
json: { name, charts, query, tags },
580580
}).json(),
581581
);
582582
},
583583
useUpdateDashboard() {
584584
return useMutation<
585585
any,
586586
HTTPError,
587-
{ id: string; name: string; query: string; charts: any[] }
588-
>(async ({ id, name, charts, query }) =>
587+
{
588+
id: string;
589+
name: string;
590+
query: string;
591+
charts: any[];
592+
tags?: string[];
593+
}
594+
>(async ({ id, name, charts, query, tags }) =>
589595
server(`dashboards/${id}`, {
590596
method: 'PUT',
591-
json: { name, charts, query },
597+
json: { name, charts, query, tags },
592598
}).json(),
593599
);
594600
},
@@ -651,6 +657,11 @@ const api = {
651657
retry: 1,
652658
});
653659
},
660+
useTags() {
661+
return useQuery<{ data: string[] }, HTTPError>(`team/tags`, () =>
662+
server(`team/tags`).json<{ data: string[] }>(),
663+
);
664+
},
654665
useSaveWebhook() {
655666
return useMutation<
656667
any,

packages/app/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export type LogView = {
4141
name: string;
4242
query: string;
4343
alerts?: Alert[];
44+
tags: string[];
4445
};
4546

4647
export type Dashboard = {
@@ -51,6 +52,7 @@ export type Dashboard = {
5152
charts: Chart[];
5253
alerts?: Alert[];
5354
query?: string;
55+
tags: string[];
5456
};
5557

5658
export type AlertType = 'presence' | 'absence';

0 commit comments

Comments
 (0)