@sentry/junior-sentry 0.264.0 → 0.266.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/README.md +4 -2
- package/dist/{chunk-BIDX42KS.js → chunk-EHSVXFHR.js} +182 -81
- package/dist/events/issue.d.ts +13 -1
- package/dist/events/metric-alert.d.ts +14 -1
- package/dist/events/scope.d.ts +17 -0
- package/dist/index.js +15 -9
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -3
- package/dist/webhooks/events.d.ts +1 -2
- package/dist/webhooks/handler.d.ts +0 -1
- package/dist/webhooks/secret.d.ts +0 -2
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -43,15 +43,17 @@ Use a **Sentry internal integration** (not a public app install flow):
|
|
|
43
43
|
|
|
44
44
|
1. Subscribe to the **issue** webhook resource.
|
|
45
45
|
2. Point it at `https://<junior-host>/api/webhooks/sentry`.
|
|
46
|
-
3. Set the
|
|
46
|
+
3. Set the integration client secret as `SENTRY_WEBHOOK_SECRET`, then redeploy.
|
|
47
47
|
|
|
48
48
|
For metric alerts, also enable **Alert Rule Action** on the integration and add it as a `sentry_app` trigger action on each alert. Sentry sends metric alerts only to alert actions.
|
|
49
49
|
|
|
50
|
-
Junior verifies `Sentry-Hook-Signature`,
|
|
50
|
+
Junior verifies `Sentry-Hook-Signature`, reads the org slug from the signed payload, and publishes:
|
|
51
51
|
|
|
52
52
|
- `issue.created` for `org/project#issueId` and `org/project`.
|
|
53
53
|
- `metric_alert.critical`, `metric_alert.warning`, and `metric_alert.resolved` for `metric-alert:org/alertRuleId` and for `org/project` of each project on the alert rule.
|
|
54
54
|
|
|
55
|
+
Each event has trusted data from Sentry. Issue events have `issueId`, `level`, `priority`, `issueCategory`, `issueType`, and `url` when Sentry sends them. Metric alert events have `alertRuleId`, `incidentId`, and `url`. On `org/project`, watches and event automations can filter issue events on `level`, `priority`, and `issueCategory`, and metric alert events on `alertRuleId`. The issue title, culprit, and alert description stay untrusted text.
|
|
56
|
+
|
|
55
57
|
Create watches/event automations before the webhook arrives; unmatched deliveries are not replayed.
|
|
56
58
|
|
|
57
59
|
Full guide: https://junior.sentry.dev/extend/sentry-plugin/
|
|
@@ -1,5 +1,60 @@
|
|
|
1
|
+
// src/events/issue.ts
|
|
2
|
+
import {
|
|
3
|
+
eventMatchFieldsSchema as eventMatchFieldsSchema2
|
|
4
|
+
} from "@sentry/junior-plugin-api";
|
|
5
|
+
|
|
6
|
+
// src/events/scope.ts
|
|
7
|
+
import {
|
|
8
|
+
eventMatchFieldsSchema
|
|
9
|
+
} from "@sentry/junior-plugin-api";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
var sentrySlugDataSchema = z.string().min(1).toLowerCase();
|
|
12
|
+
function sentryProjectsMatchFields(events) {
|
|
13
|
+
return eventMatchFieldsSchema.parse({
|
|
14
|
+
projects: {
|
|
15
|
+
kind: "string",
|
|
16
|
+
list: true,
|
|
17
|
+
description: "lowercase Sentry project slugs, such as junior; an issue has one, a metric alert has the projects of its rule",
|
|
18
|
+
events
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
1
23
|
// src/events/issue.ts
|
|
2
24
|
var SENTRY_ISSUE_EVENTS = ["issue.created"];
|
|
25
|
+
var SENTRY_ISSUE_LEVELS = [
|
|
26
|
+
"fatal",
|
|
27
|
+
"error",
|
|
28
|
+
"warning",
|
|
29
|
+
"info",
|
|
30
|
+
"debug"
|
|
31
|
+
];
|
|
32
|
+
var SENTRY_ISSUE_PRIORITIES = ["high", "medium", "low"];
|
|
33
|
+
var SENTRY_ISSUE_MATCH_FIELDS = eventMatchFieldsSchema2.parse({
|
|
34
|
+
...sentryProjectsMatchFields(SENTRY_ISSUE_EVENTS),
|
|
35
|
+
issueId: {
|
|
36
|
+
kind: "string",
|
|
37
|
+
description: "numeric Sentry issue id as text, such as 7513266773",
|
|
38
|
+
events: [...SENTRY_ISSUE_EVENTS]
|
|
39
|
+
},
|
|
40
|
+
level: {
|
|
41
|
+
kind: "string",
|
|
42
|
+
description: "issue level: fatal, error, warning, info, or debug",
|
|
43
|
+
enum: [...SENTRY_ISSUE_LEVELS],
|
|
44
|
+
events: [...SENTRY_ISSUE_EVENTS]
|
|
45
|
+
},
|
|
46
|
+
priority: {
|
|
47
|
+
kind: "string",
|
|
48
|
+
description: "issue priority: high, medium, or low",
|
|
49
|
+
enum: [...SENTRY_ISSUE_PRIORITIES],
|
|
50
|
+
events: [...SENTRY_ISSUE_EVENTS]
|
|
51
|
+
},
|
|
52
|
+
issueCategory: {
|
|
53
|
+
kind: "string",
|
|
54
|
+
description: "kind of problem, such as error, performance, cron, or feedback",
|
|
55
|
+
events: [...SENTRY_ISSUE_EVENTS]
|
|
56
|
+
}
|
|
57
|
+
});
|
|
3
58
|
function sentryIssueResource(input) {
|
|
4
59
|
return {
|
|
5
60
|
identifier: `${input.org}/${input.project}#${input.issueId}`,
|
|
@@ -16,6 +71,14 @@ function sentryProjectResource(input) {
|
|
|
16
71
|
}
|
|
17
72
|
|
|
18
73
|
// src/events/metric-alert.ts
|
|
74
|
+
import {
|
|
75
|
+
eventMatchFieldsSchema as eventMatchFieldsSchema3
|
|
76
|
+
} from "@sentry/junior-plugin-api";
|
|
77
|
+
var SENTRY_METRIC_ALERT_ACTIONS = [
|
|
78
|
+
"critical",
|
|
79
|
+
"warning",
|
|
80
|
+
"resolved"
|
|
81
|
+
];
|
|
19
82
|
var SENTRY_METRIC_ALERT_EVENTS = [
|
|
20
83
|
"metric_alert.critical",
|
|
21
84
|
"metric_alert.warning",
|
|
@@ -25,6 +88,14 @@ var SENTRY_METRIC_ALERT_SUGGESTED_EVENTS = [
|
|
|
25
88
|
"metric_alert.critical",
|
|
26
89
|
"metric_alert.warning"
|
|
27
90
|
];
|
|
91
|
+
var SENTRY_METRIC_ALERT_MATCH_FIELDS = eventMatchFieldsSchema3.parse({
|
|
92
|
+
...sentryProjectsMatchFields(SENTRY_METRIC_ALERT_EVENTS),
|
|
93
|
+
alertRuleId: {
|
|
94
|
+
kind: "string",
|
|
95
|
+
description: "id of the Sentry metric alert rule, such as 7",
|
|
96
|
+
events: [...SENTRY_METRIC_ALERT_EVENTS]
|
|
97
|
+
}
|
|
98
|
+
});
|
|
28
99
|
var SENTRY_METRIC_ALERT_IDENTIFIER = {
|
|
29
100
|
format: "metric-alert:org/alertRuleId",
|
|
30
101
|
pattern: String.raw`^metric-alert:[^\s/:#]+\/\d+$`
|
|
@@ -38,52 +109,55 @@ function sentryMetricAlertResource(input) {
|
|
|
38
109
|
}
|
|
39
110
|
|
|
40
111
|
// src/webhooks/events.ts
|
|
41
|
-
import {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
112
|
+
import {
|
|
113
|
+
eventDataSchema
|
|
114
|
+
} from "@sentry/junior-plugin-api";
|
|
115
|
+
import { z as z2 } from "zod";
|
|
116
|
+
var issueLevelSchema = z2.enum(SENTRY_ISSUE_LEVELS).optional().catch(void 0);
|
|
117
|
+
var issuePrioritySchema = z2.enum(SENTRY_ISSUE_PRIORITIES).optional().catch(void 0);
|
|
118
|
+
var issueWebhookSchema = z2.object({
|
|
119
|
+
action: z2.literal("created"),
|
|
120
|
+
data: z2.object({
|
|
121
|
+
issue: z2.object({
|
|
122
|
+
culprit: z2.string().optional(),
|
|
123
|
+
firstSeen: z2.string().optional(),
|
|
124
|
+
id: z2.string().min(1),
|
|
125
|
+
issueCategory: z2.string().optional(),
|
|
126
|
+
issueType: z2.string().optional(),
|
|
127
|
+
level: issueLevelSchema,
|
|
128
|
+
priority: issuePrioritySchema,
|
|
129
|
+
project: z2.object({ slug: z2.string().min(1) }).passthrough(),
|
|
130
|
+
title: z2.string().optional(),
|
|
131
|
+
url: z2.string().url(),
|
|
132
|
+
web_url: z2.string().url().optional()
|
|
59
133
|
}).passthrough()
|
|
60
134
|
}),
|
|
61
|
-
installation:
|
|
135
|
+
installation: z2.object({ uuid: z2.string().min(1) }).passthrough()
|
|
62
136
|
}).passthrough();
|
|
63
|
-
var metricAlertWebhookSchema =
|
|
64
|
-
action:
|
|
65
|
-
data:
|
|
66
|
-
description_text:
|
|
67
|
-
description_title:
|
|
68
|
-
metric_alert:
|
|
69
|
-
alert_rule:
|
|
70
|
-
id:
|
|
71
|
-
projects:
|
|
137
|
+
var metricAlertWebhookSchema = z2.object({
|
|
138
|
+
action: z2.enum(SENTRY_METRIC_ALERT_ACTIONS),
|
|
139
|
+
data: z2.object({
|
|
140
|
+
description_text: z2.string().optional(),
|
|
141
|
+
description_title: z2.string().optional(),
|
|
142
|
+
metric_alert: z2.object({
|
|
143
|
+
alert_rule: z2.object({
|
|
144
|
+
id: z2.coerce.string().min(1),
|
|
145
|
+
projects: z2.array(z2.string().min(1)).optional()
|
|
72
146
|
}).passthrough(),
|
|
73
|
-
id:
|
|
147
|
+
id: z2.coerce.string().min(1)
|
|
74
148
|
}).passthrough(),
|
|
75
|
-
web_url:
|
|
149
|
+
web_url: z2.string().url()
|
|
76
150
|
}),
|
|
77
|
-
installation:
|
|
151
|
+
installation: z2.object({ uuid: z2.string().min(1) }).passthrough()
|
|
78
152
|
}).passthrough();
|
|
79
|
-
function organizationSlug(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
153
|
+
function organizationSlug(url) {
|
|
154
|
+
const parsed = z2.url().safeParse(url);
|
|
155
|
+
if (!parsed.success) return void 0;
|
|
156
|
+
const { hostname, pathname } = new URL(parsed.data);
|
|
157
|
+
const segments = pathname.split("/").filter(Boolean);
|
|
158
|
+
const index = segments.indexOf("organizations");
|
|
159
|
+
const slug = index >= 0 ? segments[index + 1] : hostname.split(".").length >= 3 ? hostname.split(".")[0] : void 0;
|
|
160
|
+
return slug?.toLowerCase() || void 0;
|
|
87
161
|
}
|
|
88
162
|
function providerTime(value) {
|
|
89
163
|
if (!value) return void 0;
|
|
@@ -97,29 +171,60 @@ function providerTime(value) {
|
|
|
97
171
|
function issueText(issue) {
|
|
98
172
|
const parts = [
|
|
99
173
|
issue.title ? `Title: ${issue.title}` : void 0,
|
|
100
|
-
issue.culprit ? `Culprit: ${issue.culprit}` : void 0
|
|
101
|
-
issue.level ? `Level: ${issue.level}` : void 0,
|
|
102
|
-
issue.priority ? `Priority: ${issue.priority}` : void 0,
|
|
103
|
-
issue.issueCategory ? `Category: ${issue.issueCategory}` : void 0,
|
|
104
|
-
issue.issueType ? `Type: ${issue.issueType}` : void 0,
|
|
105
|
-
issue.status ? `Status: ${issue.status}` : void 0,
|
|
106
|
-
issue.substatus ? `Substatus: ${issue.substatus}` : void 0,
|
|
107
|
-
issue.web_url ? `URL: ${issue.web_url}` : void 0
|
|
174
|
+
issue.culprit ? `Culprit: ${issue.culprit}` : void 0
|
|
108
175
|
].filter((part) => part !== void 0);
|
|
109
176
|
return parts.length > 0 ? parts.join("\n") : void 0;
|
|
110
177
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
178
|
+
var issueEventDataSchema = z2.object({
|
|
179
|
+
projects: z2.array(sentrySlugDataSchema).length(1),
|
|
180
|
+
issueId: z2.string().min(1),
|
|
181
|
+
level: z2.enum(SENTRY_ISSUE_LEVELS).optional(),
|
|
182
|
+
priority: z2.enum(SENTRY_ISSUE_PRIORITIES).optional(),
|
|
183
|
+
issueCategory: z2.string().min(1).optional(),
|
|
184
|
+
issueType: z2.string().min(1).optional(),
|
|
185
|
+
url: z2.string().url().optional()
|
|
186
|
+
}).strict();
|
|
187
|
+
function issueData(issue) {
|
|
188
|
+
return issueEventDataSchema.parse(
|
|
189
|
+
definedValues({
|
|
190
|
+
projects: [issue.project.slug],
|
|
191
|
+
issueId: issue.id,
|
|
192
|
+
level: issue.level,
|
|
193
|
+
priority: issue.priority,
|
|
194
|
+
issueCategory: issue.issueCategory || void 0,
|
|
195
|
+
issueType: issue.issueType || void 0,
|
|
196
|
+
url: issue.web_url
|
|
197
|
+
})
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
var metricAlertEventDataSchema = z2.object({
|
|
201
|
+
projects: z2.array(sentrySlugDataSchema).min(1).optional(),
|
|
202
|
+
alertRuleId: z2.string().min(1),
|
|
203
|
+
incidentId: z2.string().min(1),
|
|
204
|
+
url: z2.string().url()
|
|
205
|
+
}).strict();
|
|
206
|
+
function definedValues(data) {
|
|
207
|
+
return Object.fromEntries(
|
|
208
|
+
Object.entries(data).filter(([, value]) => value !== void 0)
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
function metricAlertData(input, projects) {
|
|
212
|
+
const data = metricAlertEventDataSchema.parse(
|
|
213
|
+
definedValues({
|
|
214
|
+
...input,
|
|
215
|
+
projects: projects.length > 0 ? projects : void 0
|
|
216
|
+
})
|
|
217
|
+
);
|
|
218
|
+
if (eventDataSchema.safeParse(data).success) return data;
|
|
219
|
+
const { projects: _projects, ...rest } = data;
|
|
220
|
+
return rest;
|
|
116
221
|
}
|
|
117
222
|
function normalizeIssueEvents(input) {
|
|
118
223
|
const parsed = issueWebhookSchema.safeParse(input.body);
|
|
119
|
-
if (!parsed.success
|
|
224
|
+
if (!parsed.success) return [];
|
|
120
225
|
const issue = parsed.data.data.issue;
|
|
121
|
-
const org = organizationSlug(issue.url)
|
|
122
|
-
if (!org
|
|
226
|
+
const org = organizationSlug(issue.url);
|
|
227
|
+
if (!org) return [];
|
|
123
228
|
const issueResource = sentryIssueResource({
|
|
124
229
|
issueId: issue.id,
|
|
125
230
|
org,
|
|
@@ -137,8 +242,10 @@ function normalizeIssueEvents(input) {
|
|
|
137
242
|
// idempotency across repeated deliveries of the same lifecycle event.
|
|
138
243
|
eventKey: `sentry:${issueResource.identifier}:${eventType}`,
|
|
139
244
|
eventType,
|
|
245
|
+
objectType: "task",
|
|
140
246
|
occurredAtMs,
|
|
141
247
|
trustedSummary: `${issueResource.label} was created.`,
|
|
248
|
+
data: issueData(issue),
|
|
142
249
|
...untrustedText ? { untrustedText } : void 0
|
|
143
250
|
};
|
|
144
251
|
return [
|
|
@@ -150,30 +257,33 @@ function normalizeMetricAlertEvents(input) {
|
|
|
150
257
|
const parsed = metricAlertWebhookSchema.safeParse(input.body);
|
|
151
258
|
if (!parsed.success) return [];
|
|
152
259
|
const eventType = `metric_alert.${parsed.data.action}`;
|
|
153
|
-
if (!SENTRY_METRIC_ALERT_EVENTS.includes(eventType)) {
|
|
154
|
-
return [];
|
|
155
|
-
}
|
|
156
260
|
const { data } = parsed.data;
|
|
157
|
-
const org =
|
|
158
|
-
if (!org
|
|
261
|
+
const org = organizationSlug(data.web_url);
|
|
262
|
+
if (!org) return [];
|
|
159
263
|
const alertRuleId = data.metric_alert.alert_rule.id;
|
|
160
264
|
const incidentId = data.metric_alert.id;
|
|
161
265
|
const resource = sentryMetricAlertResource({ alertRuleId, org });
|
|
162
|
-
const untrustedText = [
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
266
|
+
const untrustedText = [data.description_title, data.description_text].filter((part) => Boolean(part)).join("\n");
|
|
267
|
+
const projects = [
|
|
268
|
+
...new Set(
|
|
269
|
+
(data.metric_alert.alert_rule.projects ?? []).map(
|
|
270
|
+
(project) => project.toLowerCase()
|
|
271
|
+
)
|
|
272
|
+
)
|
|
273
|
+
];
|
|
167
274
|
const event = {
|
|
168
275
|
// One event per incident state. Sentry retries get new Request-IDs.
|
|
169
276
|
eventKey: `sentry:${resource.identifier}:${incidentId}:${eventType}`,
|
|
170
277
|
eventType,
|
|
278
|
+
objectType: "item",
|
|
171
279
|
occurredAtMs: providerTime(input.hookTimestamp) ?? Date.now(),
|
|
172
280
|
trustedSummary: `${resource.label} is ${parsed.data.action}.`,
|
|
173
|
-
data:
|
|
174
|
-
|
|
281
|
+
data: metricAlertData(
|
|
282
|
+
{ alertRuleId, incidentId, url: data.web_url },
|
|
283
|
+
projects
|
|
284
|
+
),
|
|
285
|
+
...untrustedText ? { untrustedText } : void 0
|
|
175
286
|
};
|
|
176
|
-
const projects = [...new Set(data.metric_alert.alert_rule.projects ?? [])];
|
|
177
287
|
return [
|
|
178
288
|
{ ...event, identifier: resource.identifier },
|
|
179
289
|
...projects.map((project) => ({
|
|
@@ -231,17 +341,10 @@ function createSentryWebhookRoute(args) {
|
|
|
231
341
|
status: 400
|
|
232
342
|
});
|
|
233
343
|
}
|
|
234
|
-
const webhookOrg = args.webhookOrg();
|
|
235
|
-
if (!webhookOrg) {
|
|
236
|
-
return new Response("Sentry webhook organization is not configured", {
|
|
237
|
-
status: 503
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
344
|
const events = normalizeSentryEvents({
|
|
241
345
|
body,
|
|
242
346
|
hookResource,
|
|
243
|
-
hookTimestamp: request.headers.get("sentry-hook-timestamp")?.trim() || void 0
|
|
244
|
-
webhookOrg
|
|
347
|
+
hookTimestamp: request.headers.get("sentry-hook-timestamp")?.trim() || void 0
|
|
245
348
|
});
|
|
246
349
|
for (const event of events) {
|
|
247
350
|
await args.events.publish(event);
|
|
@@ -257,19 +360,17 @@ function createSentryWebhookRoute(args) {
|
|
|
257
360
|
function sentryWebhookSecret() {
|
|
258
361
|
return process.env.SENTRY_WEBHOOK_SECRET?.trim() || void 0;
|
|
259
362
|
}
|
|
260
|
-
function sentryWebhookOrg() {
|
|
261
|
-
return process.env.SENTRY_WEBHOOK_ORG?.trim().toLowerCase() || void 0;
|
|
262
|
-
}
|
|
263
363
|
|
|
264
364
|
export {
|
|
265
365
|
SENTRY_ISSUE_EVENTS,
|
|
366
|
+
SENTRY_ISSUE_MATCH_FIELDS,
|
|
266
367
|
sentryIssueResource,
|
|
267
368
|
sentryProjectResource,
|
|
268
369
|
SENTRY_METRIC_ALERT_EVENTS,
|
|
269
370
|
SENTRY_METRIC_ALERT_SUGGESTED_EVENTS,
|
|
371
|
+
SENTRY_METRIC_ALERT_MATCH_FIELDS,
|
|
270
372
|
SENTRY_METRIC_ALERT_IDENTIFIER,
|
|
271
373
|
normalizeSentryEvents,
|
|
272
374
|
createSentryWebhookRoute,
|
|
273
|
-
sentryWebhookSecret
|
|
274
|
-
sentryWebhookOrg
|
|
375
|
+
sentryWebhookSecret
|
|
275
376
|
};
|
package/dist/events/issue.d.ts
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type SubscribableResource } from "@sentry/junior-plugin-api";
|
|
2
2
|
export declare const SENTRY_ISSUE_EVENTS: readonly ["issue.created"];
|
|
3
|
+
/** Issue levels that Sentry sets, from most to least severe. */
|
|
4
|
+
export declare const SENTRY_ISSUE_LEVELS: readonly ["fatal", "error", "warning", "info", "debug"];
|
|
5
|
+
/** Issue priorities that Sentry sets. */
|
|
6
|
+
export declare const SENTRY_ISSUE_PRIORITIES: readonly ["high", "medium", "low"];
|
|
7
|
+
/** Trusted values that Sentry issue events set, when the webhook has them. */
|
|
8
|
+
export declare const SENTRY_ISSUE_MATCH_FIELDS: Record<string, {
|
|
9
|
+
kind: "boolean" | "number" | "string";
|
|
10
|
+
description: string;
|
|
11
|
+
enum?: string[] | undefined;
|
|
12
|
+
list?: true | undefined;
|
|
13
|
+
events?: string[] | undefined;
|
|
14
|
+
}>;
|
|
3
15
|
/** Build the stable Sentry issue identity shared by tools and webhooks. */
|
|
4
16
|
export declare function sentryIssueResource(input: {
|
|
5
17
|
issueId: string;
|
|
@@ -1,7 +1,20 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type SubscribableResource } from "@sentry/junior-plugin-api";
|
|
2
|
+
/** The `action` values of Sentry metric alert webhooks. */
|
|
3
|
+
export declare const SENTRY_METRIC_ALERT_ACTIONS: readonly ["critical", "warning", "resolved"];
|
|
2
4
|
/** Metric alert states, named after Sentry's `metric_alert.<action>` webhooks. */
|
|
3
5
|
export declare const SENTRY_METRIC_ALERT_EVENTS: readonly ["metric_alert.critical", "metric_alert.warning", "metric_alert.resolved"];
|
|
4
6
|
export declare const SENTRY_METRIC_ALERT_SUGGESTED_EVENTS: string[];
|
|
7
|
+
/**
|
|
8
|
+
* Trusted values that Sentry metric alert events set. `projects` is missing
|
|
9
|
+
* when the rule lists no projects or too many to fit in event data.
|
|
10
|
+
*/
|
|
11
|
+
export declare const SENTRY_METRIC_ALERT_MATCH_FIELDS: Record<string, {
|
|
12
|
+
kind: "boolean" | "number" | "string";
|
|
13
|
+
description: string;
|
|
14
|
+
enum?: string[] | undefined;
|
|
15
|
+
list?: true | undefined;
|
|
16
|
+
events?: string[] | undefined;
|
|
17
|
+
}>;
|
|
5
18
|
/** The identifier shape that `sentryMetricAlertResource` builds. */
|
|
6
19
|
export declare const SENTRY_METRIC_ALERT_IDENTIFIER: {
|
|
7
20
|
format: string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type EventMatchFields } from "@sentry/junior-plugin-api";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* A project slug in event data. It is lowercase, the same form as
|
|
5
|
+
* identifiers, because the plugin lowercases every identifier.
|
|
6
|
+
*/
|
|
7
|
+
export declare const sentrySlugDataSchema: z.ZodString;
|
|
8
|
+
/**
|
|
9
|
+
* Filter on the projects of a Sentry event. `projects` is a list on every
|
|
10
|
+
* event, so one filter fits an issue, which has one project, and a metric
|
|
11
|
+
* alert, whose rule can cover several projects.
|
|
12
|
+
*
|
|
13
|
+
* There is no `org` filter. One Sentry integration belongs to one org, and
|
|
14
|
+
* its webhook secret scopes every delivery to that org. So `org` is the same
|
|
15
|
+
* on every event of an install, and a filter on it cannot narrow anything.
|
|
16
|
+
*/
|
|
17
|
+
export declare function sentryProjectsMatchFields(events: readonly string[]): EventMatchFields;
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SENTRY_ISSUE_EVENTS,
|
|
3
|
+
SENTRY_ISSUE_MATCH_FIELDS,
|
|
3
4
|
SENTRY_METRIC_ALERT_EVENTS,
|
|
4
5
|
SENTRY_METRIC_ALERT_IDENTIFIER,
|
|
6
|
+
SENTRY_METRIC_ALERT_MATCH_FIELDS,
|
|
5
7
|
SENTRY_METRIC_ALERT_SUGGESTED_EVENTS,
|
|
6
8
|
createSentryWebhookRoute,
|
|
7
|
-
sentryWebhookOrg,
|
|
8
9
|
sentryWebhookSecret
|
|
9
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-EHSVXFHR.js";
|
|
10
11
|
|
|
11
12
|
// src/index.ts
|
|
12
13
|
import {
|
|
13
|
-
defineJuniorPlugin
|
|
14
|
+
defineJuniorPlugin,
|
|
15
|
+
mergeEventMatchFields
|
|
14
16
|
} from "@sentry/junior-plugin-api";
|
|
15
17
|
|
|
16
18
|
// src/org-read.ts
|
|
@@ -102,7 +104,8 @@ function sentryPlugin() {
|
|
|
102
104
|
{
|
|
103
105
|
type: "issue",
|
|
104
106
|
supportedEvents: [...SENTRY_ISSUE_EVENTS],
|
|
105
|
-
suggestedEvents: [...SENTRY_ISSUE_EVENTS]
|
|
107
|
+
suggestedEvents: [...SENTRY_ISSUE_EVENTS],
|
|
108
|
+
matchFields: SENTRY_ISSUE_MATCH_FIELDS
|
|
106
109
|
},
|
|
107
110
|
{
|
|
108
111
|
type: "project",
|
|
@@ -113,16 +116,21 @@ function sentryPlugin() {
|
|
|
113
116
|
suggestedEvents: [
|
|
114
117
|
...SENTRY_ISSUE_EVENTS,
|
|
115
118
|
...SENTRY_METRIC_ALERT_SUGGESTED_EVENTS
|
|
116
|
-
]
|
|
119
|
+
],
|
|
120
|
+
matchFields: mergeEventMatchFields(
|
|
121
|
+
SENTRY_ISSUE_MATCH_FIELDS,
|
|
122
|
+
SENTRY_METRIC_ALERT_MATCH_FIELDS
|
|
123
|
+
)
|
|
117
124
|
},
|
|
118
125
|
{
|
|
119
126
|
type: "metric_alert",
|
|
120
127
|
identifier: SENTRY_METRIC_ALERT_IDENTIFIER,
|
|
121
128
|
supportedEvents: [...SENTRY_METRIC_ALERT_EVENTS],
|
|
122
|
-
suggestedEvents: SENTRY_METRIC_ALERT_SUGGESTED_EVENTS
|
|
129
|
+
suggestedEvents: SENTRY_METRIC_ALERT_SUGGESTED_EVENTS,
|
|
130
|
+
matchFields: SENTRY_METRIC_ALERT_MATCH_FIELDS
|
|
123
131
|
}
|
|
124
132
|
],
|
|
125
|
-
isEnabled: () => Boolean(sentryWebhookSecret())
|
|
133
|
+
isEnabled: () => Boolean(sentryWebhookSecret()),
|
|
126
134
|
normalizeIdentifier: (identifier) => identifier.toLowerCase()
|
|
127
135
|
},
|
|
128
136
|
manifest: {
|
|
@@ -142,7 +150,6 @@ function sentryPlugin() {
|
|
|
142
150
|
SENTRY_CLIENT_ID: {},
|
|
143
151
|
SENTRY_CLIENT_SECRET: {},
|
|
144
152
|
SENTRY_READ_TOKEN: {},
|
|
145
|
-
SENTRY_WEBHOOK_ORG: {},
|
|
146
153
|
SENTRY_WEBHOOK_SECRET: {}
|
|
147
154
|
},
|
|
148
155
|
name: "sentry",
|
|
@@ -169,7 +176,6 @@ function sentryPlugin() {
|
|
|
169
176
|
return [
|
|
170
177
|
createSentryWebhookRoute({
|
|
171
178
|
events: ctx.events,
|
|
172
|
-
webhookOrg: sentryWebhookOrg,
|
|
173
179
|
webhookSecret: sentryWebhookSecret
|
|
174
180
|
})
|
|
175
181
|
];
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { sentryIssueResource, sentryProjectResource } from "./events/issue.js";
|
|
2
2
|
export { createSentryWebhookRoute } from "./webhooks/handler.js";
|
|
3
3
|
export { normalizeSentryEvents } from "./webhooks/events.js";
|
|
4
|
-
export {
|
|
4
|
+
export { sentryWebhookSecret } from "./webhooks/secret.js";
|
package/dist/testing.js
CHANGED
|
@@ -3,14 +3,12 @@ import {
|
|
|
3
3
|
normalizeSentryEvents,
|
|
4
4
|
sentryIssueResource,
|
|
5
5
|
sentryProjectResource,
|
|
6
|
-
sentryWebhookOrg,
|
|
7
6
|
sentryWebhookSecret
|
|
8
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-EHSVXFHR.js";
|
|
9
8
|
export {
|
|
10
9
|
createSentryWebhookRoute,
|
|
11
10
|
normalizeSentryEvents,
|
|
12
11
|
sentryIssueResource,
|
|
13
12
|
sentryProjectResource,
|
|
14
|
-
sentryWebhookOrg,
|
|
15
13
|
sentryWebhookSecret
|
|
16
14
|
};
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type EventInput } from "@sentry/junior-plugin-api";
|
|
2
2
|
interface WebhookInput {
|
|
3
3
|
body: unknown;
|
|
4
4
|
hookResource: string;
|
|
5
5
|
hookTimestamp?: string;
|
|
6
|
-
webhookOrg: string;
|
|
7
6
|
}
|
|
8
7
|
/** Normalize one verified Sentry webhook into subscribable Sentry events. */
|
|
9
8
|
export declare function normalizeSentryEvents(input: WebhookInput): EventInput[];
|
|
@@ -2,6 +2,5 @@ import type { PluginRoute, EventPublisher } from "@sentry/junior-plugin-api";
|
|
|
2
2
|
/** Create the public, signed Sentry event webhook route. */
|
|
3
3
|
export declare function createSentryWebhookRoute(args: {
|
|
4
4
|
events: EventPublisher;
|
|
5
|
-
webhookOrg(): string | undefined;
|
|
6
5
|
webhookSecret(): string | undefined;
|
|
7
6
|
}): PluginRoute;
|
|
@@ -1,4 +1,2 @@
|
|
|
1
1
|
/** Read the internal-integration client secret used for issue webhook ingress. */
|
|
2
2
|
export declare function sentryWebhookSecret(): string | undefined;
|
|
3
|
-
/** Read the Sentry organization allowed to publish issue webhooks. */
|
|
4
|
-
export declare function sentryWebhookOrg(): string | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior-sentry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.266.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
],
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"zod": "^4.5.4",
|
|
30
|
-
"@sentry/junior-plugin-api": "0.
|
|
30
|
+
"@sentry/junior-plugin-api": "0.266.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@oxlint/plugins": "1.79.0",
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
"oxlint": "^1.79.0",
|
|
36
36
|
"tsup": "^8.5.1",
|
|
37
37
|
"typescript": "^7.0.2",
|
|
38
|
-
"vitest": "^4.1.12"
|
|
38
|
+
"vitest": "^4.1.12",
|
|
39
|
+
"@sentry/junior-testing": "0.0.0"
|
|
39
40
|
},
|
|
40
41
|
"scripts": {
|
|
41
42
|
"build": "tsup && tsc -p tsconfig.build.json --emitDeclarationOnly",
|