@checkstack/incident-backend 0.3.1 → 0.4.1

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 CHANGED
@@ -1,5 +1,56 @@
1
1
  # @checkstack/incident-backend
2
2
 
3
+ ## 0.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [f676e11]
8
+ - Updated dependencies [48c2080]
9
+ - @checkstack/common@0.6.2
10
+ - @checkstack/backend-api@0.6.0
11
+ - @checkstack/auth-common@0.5.5
12
+ - @checkstack/catalog-backend@0.2.11
13
+ - @checkstack/catalog-common@1.2.7
14
+ - @checkstack/command-backend@0.1.9
15
+ - @checkstack/incident-common@0.4.3
16
+ - @checkstack/integration-backend@0.1.9
17
+ - @checkstack/integration-common@0.2.5
18
+ - @checkstack/signal-common@0.1.6
19
+
20
+ ## 0.4.0
21
+
22
+ ### Minor Changes
23
+
24
+ - c208a5b: ### @checkstack/incident-backend
25
+
26
+ Added notifications for incident status changes via the "Add Update" functionality:
27
+
28
+ - Notifications are now sent when an incident is reopened (status changed from resolved)
29
+ - Notifications are now sent when an incident status changes to any new value
30
+ - Notifications are now sent when an incident is resolved via addUpdate
31
+ - Extracted `notifyAffectedSystems` into a reusable module with proper importance logic:
32
+ - Resolved incidents always use "info" importance (good news)
33
+ - Reopened/created/updated incidents derive importance from severity
34
+
35
+ ### @checkstack/maintenance-backend
36
+
37
+ Fixed missing notification in `closeMaintenance` handler - the "Close" button now sends a "completed" notification to subscribers.
38
+
39
+ ### Patch Changes
40
+
41
+ - 9551fd7: Fix creator display in incident and maintenance status updates
42
+
43
+ - Show the creator's profile name instead of UUID in status updates
44
+ - For maintenances, now properly displays the creator name (was missing)
45
+ - For incidents, replaces UUID with human-readable profile name
46
+ - System-generated updates (automatic maintenance transitions) show no creator
47
+
48
+ - Updated dependencies [e5079e1]
49
+ - Updated dependencies [9551fd7]
50
+ - @checkstack/catalog-common@1.2.6
51
+ - @checkstack/incident-common@0.4.2
52
+ - @checkstack/catalog-backend@0.2.10
53
+
3
54
  ## 0.3.1
4
55
 
5
56
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/incident-backend",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "scripts": {
@@ -10,23 +10,24 @@
10
10
  "lint:code": "eslint . --max-warnings 0"
11
11
  },
12
12
  "dependencies": {
13
- "@checkstack/backend-api": "0.5.1",
14
- "@checkstack/incident-common": "0.4.0",
15
- "@checkstack/catalog-common": "1.2.4",
16
- "@checkstack/catalog-backend": "0.2.8",
17
- "@checkstack/command-backend": "0.1.7",
18
- "@checkstack/signal-common": "0.1.4",
19
- "@checkstack/integration-backend": "0.1.7",
20
- "@checkstack/integration-common": "0.2.3",
21
- "@checkstack/common": "0.6.0",
13
+ "@checkstack/backend-api": "0.5.2",
14
+ "@checkstack/incident-common": "0.4.2",
15
+ "@checkstack/catalog-common": "1.2.6",
16
+ "@checkstack/catalog-backend": "0.2.10",
17
+ "@checkstack/auth-common": "0.5.4",
18
+ "@checkstack/command-backend": "0.1.8",
19
+ "@checkstack/signal-common": "0.1.5",
20
+ "@checkstack/integration-backend": "0.1.8",
21
+ "@checkstack/integration-common": "0.2.4",
22
+ "@checkstack/common": "0.6.1",
22
23
  "drizzle-orm": "^0.45.1",
23
24
  "zod": "^4.2.1"
24
25
  },
25
26
  "devDependencies": {
26
- "@checkstack/drizzle-helper": "0.0.2",
27
- "@checkstack/scripts": "0.1.0",
28
- "@checkstack/test-utils-backend": "0.1.7",
29
- "@checkstack/tsconfig": "0.0.2",
27
+ "@checkstack/drizzle-helper": "0.0.3",
28
+ "@checkstack/scripts": "0.1.1",
29
+ "@checkstack/test-utils-backend": "0.1.8",
30
+ "@checkstack/tsconfig": "0.0.3",
30
31
  "@orpc/server": "^1.13.2",
31
32
  "@types/bun": "^1.0.0",
32
33
  "drizzle-kit": "^0.31.8",
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@ import { integrationEventExtensionPoint } from "@checkstack/integration-backend"
13
13
  import { IncidentService } from "./service";
14
14
  import { createRouter } from "./router";
15
15
  import { CatalogApi } from "@checkstack/catalog-common";
16
+ import { AuthApi } from "@checkstack/auth-common";
16
17
  import { catalogHooks } from "@checkstack/catalog-backend";
17
18
  import { registerSearchProvider } from "@checkstack/command-backend";
18
19
  import { resolveRoute } from "@checkstack/common";
@@ -61,7 +62,7 @@ export default createBackendPlugin({
61
62
 
62
63
  // Register hooks as integration events
63
64
  const integrationEvents = env.getExtensionPoint(
64
- integrationEventExtensionPoint
65
+ integrationEventExtensionPoint,
65
66
  );
66
67
 
67
68
  integrationEvents.registerEvent(
@@ -72,7 +73,7 @@ export default createBackendPlugin({
72
73
  category: "Incidents",
73
74
  payloadSchema: incidentCreatedPayloadSchema,
74
75
  },
75
- pluginMetadata
76
+ pluginMetadata,
76
77
  );
77
78
 
78
79
  integrationEvents.registerEvent(
@@ -84,7 +85,7 @@ export default createBackendPlugin({
84
85
  category: "Incidents",
85
86
  payloadSchema: incidentUpdatedPayloadSchema,
86
87
  },
87
- pluginMetadata
88
+ pluginMetadata,
88
89
  );
89
90
 
90
91
  integrationEvents.registerEvent(
@@ -95,7 +96,7 @@ export default createBackendPlugin({
95
96
  category: "Incidents",
96
97
  payloadSchema: incidentResolvedPayloadSchema,
97
98
  },
98
- pluginMetadata
99
+ pluginMetadata,
99
100
  );
100
101
 
101
102
  env.registerInit({
@@ -110,15 +111,17 @@ export default createBackendPlugin({
110
111
  logger.debug("🔧 Initializing Incident Backend...");
111
112
 
112
113
  const catalogClient = rpcClient.forPlugin(CatalogApi);
114
+ const authClient = rpcClient.forPlugin(AuthApi);
113
115
 
114
116
  const service = new IncidentService(
115
- database as SafeDatabase<typeof schema>
117
+ database as SafeDatabase<typeof schema>,
116
118
  );
117
119
  const router = createRouter(
118
120
  service,
119
121
  signalService,
120
122
  catalogClient,
121
- logger
123
+ authClient,
124
+ logger,
122
125
  );
123
126
  rpc.registerRouter(router, incidentContract);
124
127
 
@@ -159,11 +162,11 @@ export default createBackendPlugin({
159
162
  catalogHooks.systemDeleted,
160
163
  async (payload) => {
161
164
  logger.debug(
162
- `Cleaning up incident associations for deleted system: ${payload.systemId}`
165
+ `Cleaning up incident associations for deleted system: ${payload.systemId}`,
163
166
  );
164
167
  await service.removeSystemAssociations(payload.systemId);
165
168
  },
166
- { mode: "work-queue", workerGroup: "incident-system-cleanup" }
169
+ { mode: "work-queue", workerGroup: "incident-system-cleanup" },
167
170
  );
168
171
 
169
172
  logger.debug("✅ Incident Backend afterPluginsReady complete.");
@@ -0,0 +1,183 @@
1
+ import { describe, it, expect, mock, beforeEach } from "bun:test";
2
+ import { notifyAffectedSystems } from "./notifications";
3
+
4
+ // Mock catalog client
5
+ function createMockCatalogClient() {
6
+ return {
7
+ notifySystemSubscribers: mock(() => Promise.resolve()),
8
+ };
9
+ }
10
+
11
+ // Mock logger
12
+ function createMockLogger() {
13
+ return {
14
+ warn: mock(() => {}),
15
+ error: mock(() => {}),
16
+ info: mock(() => {}),
17
+ debug: mock(() => {}),
18
+ };
19
+ }
20
+
21
+ describe("notifyAffectedSystems", () => {
22
+ let mockCatalogClient: ReturnType<typeof createMockCatalogClient>;
23
+ let mockLogger: ReturnType<typeof createMockLogger>;
24
+
25
+ beforeEach(() => {
26
+ mockCatalogClient = createMockCatalogClient();
27
+ mockLogger = createMockLogger();
28
+ });
29
+
30
+ describe("importance logic", () => {
31
+ it("should use 'info' importance for resolved action regardless of severity", async () => {
32
+ await notifyAffectedSystems({
33
+ catalogClient: mockCatalogClient as never,
34
+ logger: mockLogger as never,
35
+ incidentId: "inc-1",
36
+ incidentTitle: "Test Incident",
37
+ systemIds: ["sys-1"],
38
+ action: "resolved",
39
+ severity: "critical", // Even critical severity should be info when resolved
40
+ });
41
+
42
+ expect(mockCatalogClient.notifySystemSubscribers).toHaveBeenCalledWith(
43
+ expect.objectContaining({
44
+ importance: "info",
45
+ }),
46
+ );
47
+ });
48
+
49
+ it("should use 'critical' importance for reopened action with critical severity", async () => {
50
+ await notifyAffectedSystems({
51
+ catalogClient: mockCatalogClient as never,
52
+ logger: mockLogger as never,
53
+ incidentId: "inc-1",
54
+ incidentTitle: "Test Incident",
55
+ systemIds: ["sys-1"],
56
+ action: "reopened",
57
+ severity: "critical",
58
+ });
59
+
60
+ expect(mockCatalogClient.notifySystemSubscribers).toHaveBeenCalledWith(
61
+ expect.objectContaining({
62
+ importance: "critical",
63
+ }),
64
+ );
65
+ });
66
+
67
+ it("should use 'warning' importance for created action with major severity", async () => {
68
+ await notifyAffectedSystems({
69
+ catalogClient: mockCatalogClient as never,
70
+ logger: mockLogger as never,
71
+ incidentId: "inc-1",
72
+ incidentTitle: "Test Incident",
73
+ systemIds: ["sys-1"],
74
+ action: "created",
75
+ severity: "major",
76
+ });
77
+
78
+ expect(mockCatalogClient.notifySystemSubscribers).toHaveBeenCalledWith(
79
+ expect.objectContaining({
80
+ importance: "warning",
81
+ }),
82
+ );
83
+ });
84
+
85
+ it("should use 'info' importance for updated action with minor severity", async () => {
86
+ await notifyAffectedSystems({
87
+ catalogClient: mockCatalogClient as never,
88
+ logger: mockLogger as never,
89
+ incidentId: "inc-1",
90
+ incidentTitle: "Test Incident",
91
+ systemIds: ["sys-1"],
92
+ action: "updated",
93
+ severity: "minor",
94
+ });
95
+
96
+ expect(mockCatalogClient.notifySystemSubscribers).toHaveBeenCalledWith(
97
+ expect.objectContaining({
98
+ importance: "info",
99
+ }),
100
+ );
101
+ });
102
+ });
103
+
104
+ describe("action text", () => {
105
+ it("should use 'reported' for created action", async () => {
106
+ await notifyAffectedSystems({
107
+ catalogClient: mockCatalogClient as never,
108
+ logger: mockLogger as never,
109
+ incidentId: "inc-1",
110
+ incidentTitle: "Test Incident",
111
+ systemIds: ["sys-1"],
112
+ action: "created",
113
+ severity: "minor",
114
+ });
115
+
116
+ expect(mockCatalogClient.notifySystemSubscribers).toHaveBeenCalledWith(
117
+ expect.objectContaining({
118
+ title: "Incident reported",
119
+ body: expect.stringContaining("reported"),
120
+ }),
121
+ );
122
+ });
123
+
124
+ it("should use 'reopened' for reopened action", async () => {
125
+ await notifyAffectedSystems({
126
+ catalogClient: mockCatalogClient as never,
127
+ logger: mockLogger as never,
128
+ incidentId: "inc-1",
129
+ incidentTitle: "Test Incident",
130
+ systemIds: ["sys-1"],
131
+ action: "reopened",
132
+ severity: "minor",
133
+ });
134
+
135
+ expect(mockCatalogClient.notifySystemSubscribers).toHaveBeenCalledWith(
136
+ expect.objectContaining({
137
+ title: "Incident reopened",
138
+ body: expect.stringContaining("reopened"),
139
+ }),
140
+ );
141
+ });
142
+ });
143
+
144
+ describe("system deduplication", () => {
145
+ it("should deduplicate system IDs", async () => {
146
+ await notifyAffectedSystems({
147
+ catalogClient: mockCatalogClient as never,
148
+ logger: mockLogger as never,
149
+ incidentId: "inc-1",
150
+ incidentTitle: "Test Incident",
151
+ systemIds: ["sys-1", "sys-1", "sys-2", "sys-2", "sys-1"],
152
+ action: "created",
153
+ severity: "minor",
154
+ });
155
+
156
+ // Should only be called twice (for sys-1 and sys-2)
157
+ expect(mockCatalogClient.notifySystemSubscribers).toHaveBeenCalledTimes(
158
+ 2,
159
+ );
160
+ });
161
+ });
162
+
163
+ describe("error handling", () => {
164
+ it("should log warning but not throw when notification fails", async () => {
165
+ mockCatalogClient.notifySystemSubscribers.mockRejectedValue(
166
+ new Error("Network error"),
167
+ );
168
+
169
+ // Should not throw
170
+ await notifyAffectedSystems({
171
+ catalogClient: mockCatalogClient as never,
172
+ logger: mockLogger as never,
173
+ incidentId: "inc-1",
174
+ incidentTitle: "Test Incident",
175
+ systemIds: ["sys-1"],
176
+ action: "created",
177
+ severity: "minor",
178
+ });
179
+
180
+ expect(mockLogger.warn).toHaveBeenCalled();
181
+ });
182
+ });
183
+ });
@@ -0,0 +1,89 @@
1
+ import { CatalogApi } from "@checkstack/catalog-common";
2
+ import type { Logger } from "@checkstack/backend-api";
3
+ import type { InferClient } from "@checkstack/common";
4
+ import { resolveRoute } from "@checkstack/common";
5
+ import { incidentRoutes } from "@checkstack/incident-common";
6
+
7
+ /**
8
+ * Determines notification importance based on action and severity.
9
+ * Resolved actions are always "info" (good news).
10
+ * Other actions derive importance from severity.
11
+ */
12
+ function getImportance(
13
+ action: "created" | "updated" | "resolved" | "reopened",
14
+ severity: string,
15
+ ): "info" | "warning" | "critical" {
16
+ // Resolved is always good news
17
+ if (action === "resolved") {
18
+ return "info";
19
+ }
20
+
21
+ // For other actions, derive from severity
22
+ if (severity === "critical") {
23
+ return "critical";
24
+ }
25
+ if (severity === "major") {
26
+ return "warning";
27
+ }
28
+ return "info";
29
+ }
30
+
31
+ /**
32
+ * Helper to notify subscribers of affected systems about an incident event.
33
+ * Each system triggers a separate notification call, but within each call
34
+ * the subscribers are deduplicated (system + its groups).
35
+ */
36
+ export async function notifyAffectedSystems(props: {
37
+ catalogClient: InferClient<typeof CatalogApi>;
38
+ logger: Logger;
39
+ incidentId: string;
40
+ incidentTitle: string;
41
+ systemIds: string[];
42
+ action: "created" | "updated" | "resolved" | "reopened";
43
+ severity: string;
44
+ }): Promise<void> {
45
+ const {
46
+ catalogClient,
47
+ logger,
48
+ incidentId,
49
+ incidentTitle,
50
+ systemIds,
51
+ action,
52
+ severity,
53
+ } = props;
54
+
55
+ const actionText = {
56
+ created: "reported",
57
+ updated: "updated",
58
+ resolved: "resolved",
59
+ reopened: "reopened",
60
+ }[action];
61
+
62
+ const importance = getImportance(action, severity);
63
+
64
+ const incidentDetailPath = resolveRoute(incidentRoutes.routes.detail, {
65
+ incidentId,
66
+ });
67
+
68
+ // Deduplicate: collect unique system IDs
69
+ const uniqueSystemIds = [...new Set(systemIds)];
70
+
71
+ for (const systemId of uniqueSystemIds) {
72
+ try {
73
+ await catalogClient.notifySystemSubscribers({
74
+ systemId,
75
+ title: `Incident ${actionText}`,
76
+ body: `Incident **"${incidentTitle}"** has been ${actionText} for a system you're subscribed to.`,
77
+ importance,
78
+ action: { label: "View Incident", url: incidentDetailPath },
79
+ includeGroupSubscribers: true,
80
+ });
81
+ } catch (error) {
82
+ // Log but don't fail the operation - notifications are best-effort
83
+ logger.warn(
84
+ `Failed to notify subscribers for system ${systemId}:`,
85
+ error,
86
+ );
87
+ }
88
+ }
89
+ }
package/src/router.ts CHANGED
@@ -2,7 +2,6 @@ import { implement, ORPCError } from "@orpc/server";
2
2
  import {
3
3
  incidentContract,
4
4
  INCIDENT_UPDATED,
5
- incidentRoutes,
6
5
  } from "@checkstack/incident-common";
7
6
  import {
8
7
  autoAuthMiddleware,
@@ -12,75 +11,57 @@ import {
12
11
  import type { SignalService } from "@checkstack/signal-common";
13
12
  import type { IncidentService } from "./service";
14
13
  import { CatalogApi } from "@checkstack/catalog-common";
14
+ import { AuthApi } from "@checkstack/auth-common";
15
15
  import type { InferClient } from "@checkstack/common";
16
- import { resolveRoute } from "@checkstack/common";
17
16
  import { incidentHooks } from "./hooks";
17
+ import { notifyAffectedSystems } from "./notifications";
18
+ import type { IncidentUpdate } from "@checkstack/incident-common";
18
19
 
19
20
  export function createRouter(
20
21
  service: IncidentService,
21
22
  signalService: SignalService,
22
23
  catalogClient: InferClient<typeof CatalogApi>,
24
+ authClient: InferClient<typeof AuthApi>,
23
25
  logger: Logger,
24
26
  ) {
27
+ /**
28
+ * Resolve user IDs to profile names for a list of updates.
29
+ * Falls back to "Unknown User" if the user cannot be found.
30
+ */
31
+ async function resolveUserNames(
32
+ updates: IncidentUpdate[],
33
+ ): Promise<IncidentUpdate[]> {
34
+ const userIds = [
35
+ ...new Set(updates.map((u) => u.createdBy).filter(Boolean)),
36
+ ];
37
+ if (userIds.length === 0) return updates;
38
+
39
+ const userMap = new Map<string, string>();
40
+ await Promise.all(
41
+ userIds.map(async (userId) => {
42
+ try {
43
+ const user = await authClient.getUserById({ userId: userId! });
44
+ if (user?.name) {
45
+ userMap.set(userId!, user.name);
46
+ }
47
+ } catch {
48
+ // User not found, skip
49
+ }
50
+ }),
51
+ );
52
+
53
+ return updates.map((update) => ({
54
+ ...update,
55
+ createdByName: update.createdBy
56
+ ? (userMap.get(update.createdBy) ?? undefined)
57
+ : undefined,
58
+ }));
59
+ }
60
+
25
61
  const os = implement(incidentContract)
26
62
  .$context<RpcContext>()
27
63
  .use(autoAuthMiddleware);
28
64
 
29
- /**
30
- * Helper to notify subscribers of affected systems about an incident event.
31
- * Each system triggers a separate notification call, but within each call
32
- * the subscribers are deduplicated (system + its groups).
33
- */
34
- const notifyAffectedSystems = async (props: {
35
- incidentId: string;
36
- incidentTitle: string;
37
- systemIds: string[];
38
- action: "created" | "updated" | "resolved";
39
- severity: string;
40
- }) => {
41
- const { incidentId, incidentTitle, systemIds, action, severity } = props;
42
-
43
- const actionText =
44
- action === "created"
45
- ? "reported"
46
- : action === "resolved"
47
- ? "resolved"
48
- : "updated";
49
-
50
- const importance =
51
- severity === "critical"
52
- ? "critical"
53
- : severity === "major"
54
- ? "warning"
55
- : "info";
56
-
57
- const incidentDetailPath = resolveRoute(incidentRoutes.routes.detail, {
58
- incidentId,
59
- });
60
-
61
- // Deduplicate: collect unique system IDs
62
- const uniqueSystemIds = [...new Set(systemIds)];
63
-
64
- for (const systemId of uniqueSystemIds) {
65
- try {
66
- await catalogClient.notifySystemSubscribers({
67
- systemId,
68
- title: `Incident ${actionText}`,
69
- body: `Incident **"${incidentTitle}"** has been ${actionText} for a system you're subscribed to.`,
70
- importance: importance as "info" | "warning" | "critical",
71
- action: { label: "View Incident", url: incidentDetailPath },
72
- includeGroupSubscribers: true,
73
- });
74
- } catch (error) {
75
- // Log but don't fail the operation - notifications are best-effort
76
- logger.warn(
77
- `Failed to notify subscribers for system ${systemId}:`,
78
- error,
79
- );
80
- }
81
- }
82
- };
83
-
84
65
  return os.router({
85
66
  listIncidents: os.listIncidents.handler(async ({ input }) => {
86
67
  return { incidents: await service.listIncidents(input ?? {}) };
@@ -88,8 +69,13 @@ export function createRouter(
88
69
 
89
70
  getIncident: os.getIncident.handler(async ({ input }) => {
90
71
  const result = await service.getIncident(input.id);
91
- // eslint-disable-next-line unicorn/no-null -- oRPC contract requires null for missing values
92
- return result ?? null;
72
+ if (!result) {
73
+ // eslint-disable-next-line unicorn/no-null -- oRPC contract requires null for missing values
74
+ return null;
75
+ }
76
+ // Resolve user names for updates
77
+ const updatesWithNames = await resolveUserNames(result.updates);
78
+ return { ...result, updates: updatesWithNames };
93
79
  }),
94
80
 
95
81
  getIncidentsForSystem: os.getIncidentsForSystem.handler(
@@ -141,6 +127,8 @@ export function createRouter(
141
127
 
142
128
  // Send notifications to system subscribers
143
129
  await notifyAffectedSystems({
130
+ catalogClient,
131
+ logger,
144
132
  incidentId: result.id,
145
133
  incidentTitle: result.title,
146
134
  systemIds: result.systemIds,
@@ -176,6 +164,8 @@ export function createRouter(
176
164
 
177
165
  // Send notifications to system subscribers
178
166
  await notifyAffectedSystems({
167
+ catalogClient,
168
+ logger,
179
169
  incidentId: result.id,
180
170
  incidentTitle: result.title,
181
171
  systemIds: result.systemIds,
@@ -189,6 +179,13 @@ export function createRouter(
189
179
  addUpdate: os.addUpdate.handler(async ({ input, context }) => {
190
180
  const userId =
191
181
  context.user && "id" in context.user ? context.user.id : undefined;
182
+
183
+ // Get previous status before update for reopening detection
184
+ const previousIncident = input.statusChange
185
+ ? await service.getIncident(input.incidentId)
186
+ : undefined;
187
+ const previousStatus = previousIncident?.status;
188
+
192
189
  const result = await service.addUpdate(input, userId);
193
190
 
194
191
  // Get incident to broadcast with correct systemIds
@@ -221,6 +218,30 @@ export function createRouter(
221
218
  resolvedAt: new Date().toISOString(),
222
219
  });
223
220
  }
221
+
222
+ // Send notifications when status changes
223
+ if (input.statusChange && previousStatus !== input.statusChange) {
224
+ // Determine notification action based on status transition
225
+ let notificationAction: "resolved" | "reopened" | "updated";
226
+ if (input.statusChange === "resolved") {
227
+ notificationAction = "resolved";
228
+ } else if (previousStatus === "resolved") {
229
+ // Reopening: was resolved, now not resolved
230
+ notificationAction = "reopened";
231
+ } else {
232
+ notificationAction = "updated";
233
+ }
234
+
235
+ await notifyAffectedSystems({
236
+ catalogClient,
237
+ logger,
238
+ incidentId: input.incidentId,
239
+ incidentTitle: incident.title,
240
+ systemIds: incident.systemIds,
241
+ action: notificationAction,
242
+ severity: incident.severity,
243
+ });
244
+ }
224
245
  }
225
246
 
226
247
  return result;
@@ -256,6 +277,8 @@ export function createRouter(
256
277
 
257
278
  // Send notifications to system subscribers
258
279
  await notifyAffectedSystems({
280
+ catalogClient,
281
+ logger,
259
282
  incidentId: result.id,
260
283
  incidentTitle: result.title,
261
284
  systemIds: result.systemIds,