@checkstack/incident-backend 0.4.25 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/package.json +2 -2
- package/src/notifications.test.ts +48 -2
- package/src/notifications.ts +8 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @checkstack/incident-backend
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 298bf42: ### Notification System Optimizations
|
|
8
|
+
|
|
9
|
+
**System context in notifications**: All notification senders (healthcheck, incident, maintenance, dependency) now include the affected system name in the notification title and body. Users can immediately identify which system is affected without clicking through to the detail page.
|
|
10
|
+
|
|
11
|
+
**Upstream notification deduplication**: When an upstream dependency goes down affecting multiple downstream systems, the dependency notification sidecar now sends **one personalized notification per user** instead of one notification per affected system. Each user's notification lists only the systems they are subscribed to, with a link to the upstream root cause system. This prevents notification floods for users subscribed to groups containing many dependent systems.
|
|
12
|
+
|
|
13
|
+
**New catalog endpoint**: Added `getSystemGroupIds` S2S RPC endpoint on the catalog to resolve which catalog groups contain a given system, used by the dependency plugin for efficient subscriber resolution during batched notification dispatch.
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [298bf42]
|
|
18
|
+
- @checkstack/catalog-common@1.5.0
|
|
19
|
+
- @checkstack/catalog-backend@0.6.0
|
|
20
|
+
|
|
3
21
|
## 0.4.25
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@checkstack/incident-backend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"checkstack": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"@checkstack/backend-api": "0.12.0",
|
|
17
17
|
"@checkstack/incident-common": "0.4.7",
|
|
18
18
|
"@checkstack/catalog-common": "1.4.1",
|
|
19
|
-
"@checkstack/catalog-backend": "0.5.
|
|
19
|
+
"@checkstack/catalog-backend": "0.5.4",
|
|
20
20
|
"@checkstack/auth-common": "0.6.2",
|
|
21
21
|
"@checkstack/command-backend": "0.1.19",
|
|
22
22
|
"@checkstack/signal-common": "0.1.9",
|
|
@@ -115,7 +115,7 @@ describe("notifyAffectedSystems", () => {
|
|
|
115
115
|
|
|
116
116
|
expect(mockCatalogClient.notifySystemSubscribers).toHaveBeenCalledWith(
|
|
117
117
|
expect.objectContaining({
|
|
118
|
-
title: "Incident reported",
|
|
118
|
+
title: "Incident reported: sys-1",
|
|
119
119
|
body: expect.stringContaining("reported"),
|
|
120
120
|
}),
|
|
121
121
|
);
|
|
@@ -134,7 +134,7 @@ describe("notifyAffectedSystems", () => {
|
|
|
134
134
|
|
|
135
135
|
expect(mockCatalogClient.notifySystemSubscribers).toHaveBeenCalledWith(
|
|
136
136
|
expect.objectContaining({
|
|
137
|
-
title: "Incident reopened",
|
|
137
|
+
title: "Incident reopened: sys-1",
|
|
138
138
|
body: expect.stringContaining("reopened"),
|
|
139
139
|
}),
|
|
140
140
|
);
|
|
@@ -180,4 +180,50 @@ describe("notifyAffectedSystems", () => {
|
|
|
180
180
|
expect(mockLogger.warn).toHaveBeenCalled();
|
|
181
181
|
});
|
|
182
182
|
});
|
|
183
|
+
|
|
184
|
+
describe("system name inclusion", () => {
|
|
185
|
+
it("should include system name in title and body when systemNames map is provided", async () => {
|
|
186
|
+
const systemNames = new Map([
|
|
187
|
+
["sys-1", "Production Database"],
|
|
188
|
+
]);
|
|
189
|
+
|
|
190
|
+
await notifyAffectedSystems({
|
|
191
|
+
catalogClient: mockCatalogClient as never,
|
|
192
|
+
logger: mockLogger as never,
|
|
193
|
+
incidentId: "inc-1",
|
|
194
|
+
incidentTitle: "DB Outage",
|
|
195
|
+
systemIds: ["sys-1"],
|
|
196
|
+
systemNames,
|
|
197
|
+
action: "created",
|
|
198
|
+
severity: "critical",
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
expect(mockCatalogClient.notifySystemSubscribers).toHaveBeenCalledWith(
|
|
202
|
+
expect.objectContaining({
|
|
203
|
+
title: "Incident reported: Production Database",
|
|
204
|
+
body: expect.stringContaining("**Production Database**"),
|
|
205
|
+
}),
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("should fall back to systemId when systemNames map is not provided", async () => {
|
|
210
|
+
await notifyAffectedSystems({
|
|
211
|
+
catalogClient: mockCatalogClient as never,
|
|
212
|
+
logger: mockLogger as never,
|
|
213
|
+
incidentId: "inc-1",
|
|
214
|
+
incidentTitle: "Test Incident",
|
|
215
|
+
systemIds: ["sys-1"],
|
|
216
|
+
action: "resolved",
|
|
217
|
+
severity: "minor",
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
expect(mockCatalogClient.notifySystemSubscribers).toHaveBeenCalledWith(
|
|
221
|
+
expect.objectContaining({
|
|
222
|
+
title: "Incident resolved: sys-1",
|
|
223
|
+
body: expect.stringContaining("**sys-1**"),
|
|
224
|
+
}),
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
183
228
|
});
|
|
229
|
+
|
package/src/notifications.ts
CHANGED
|
@@ -39,6 +39,7 @@ export async function notifyAffectedSystems(props: {
|
|
|
39
39
|
incidentId: string;
|
|
40
40
|
incidentTitle: string;
|
|
41
41
|
systemIds: string[];
|
|
42
|
+
systemNames?: Map<string, string>;
|
|
42
43
|
action: "created" | "updated" | "resolved" | "reopened";
|
|
43
44
|
severity: string;
|
|
44
45
|
}): Promise<void> {
|
|
@@ -48,6 +49,7 @@ export async function notifyAffectedSystems(props: {
|
|
|
48
49
|
incidentId,
|
|
49
50
|
incidentTitle,
|
|
50
51
|
systemIds,
|
|
52
|
+
systemNames,
|
|
51
53
|
action,
|
|
52
54
|
severity,
|
|
53
55
|
} = props;
|
|
@@ -69,11 +71,14 @@ export async function notifyAffectedSystems(props: {
|
|
|
69
71
|
const uniqueSystemIds = [...new Set(systemIds)];
|
|
70
72
|
|
|
71
73
|
for (const systemId of uniqueSystemIds) {
|
|
74
|
+
// Resolve system name from provided map, or fall back to systemId
|
|
75
|
+
const systemName = systemNames?.get(systemId) ?? systemId;
|
|
76
|
+
|
|
72
77
|
try {
|
|
73
78
|
await catalogClient.notifySystemSubscribers({
|
|
74
79
|
systemId,
|
|
75
|
-
title: `Incident ${actionText}`,
|
|
76
|
-
body: `Incident **"${incidentTitle}"** has been ${actionText}
|
|
80
|
+
title: `Incident ${actionText}: ${systemName}`,
|
|
81
|
+
body: `Incident **"${incidentTitle}"** has been ${actionText} affecting **${systemName}**.`,
|
|
77
82
|
importance,
|
|
78
83
|
action: { label: "View Incident", url: incidentDetailPath },
|
|
79
84
|
includeGroupSubscribers: true,
|
|
@@ -87,3 +92,4 @@ export async function notifyAffectedSystems(props: {
|
|
|
87
92
|
}
|
|
88
93
|
}
|
|
89
94
|
}
|
|
95
|
+
|