@sentry/junior-sentry 0.242.0 → 0.243.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 +9 -2
- package/dist/{chunk-UITYE7V6.js → chunk-6VG4NPNH.js} +86 -2
- package/dist/events/metric-alert.d.ts +14 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +11 -2
- package/dist/testing.js +1 -1
- package/dist/webhooks/events.d.ts +5 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ Verified CLI surface (check live `sentry --help` before blocking):
|
|
|
36
36
|
|
|
37
37
|
Set `SENTRY_READ_TOKEN` to a read-only internal integration token. Junior then uses it for Sentry API reads (`GET`, `HEAD`, `OPTIONS`), including Explore queries, scheduled tasks, and event automations. Writes and `/api/0/users/` requests still use the requesting user's OAuth token.
|
|
38
38
|
|
|
39
|
-
## Issue webhooks
|
|
39
|
+
## Issue and metric alert webhooks
|
|
40
40
|
|
|
41
41
|
Use a **Sentry internal integration** (not a public app install flow):
|
|
42
42
|
|
|
@@ -44,6 +44,13 @@ Use a **Sentry internal integration** (not a public app install flow):
|
|
|
44
44
|
2. Point it at `https://<junior-host>/api/webhooks/sentry`.
|
|
45
45
|
3. Set the org slug as `SENTRY_WEBHOOK_ORG` and the integration client secret as `SENTRY_WEBHOOK_SECRET`, then redeploy.
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
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.
|
|
48
|
+
|
|
49
|
+
Junior verifies `Sentry-Hook-Signature`, rejects payloads outside `SENTRY_WEBHOOK_ORG`, and publishes:
|
|
50
|
+
|
|
51
|
+
- `issue.created` for `org/project#issueId` and `org/project`.
|
|
52
|
+
- `metric_alert.critical`, `metric_alert.warning`, and `metric_alert.resolved` for `metric-alert:org/alertRuleId`.
|
|
53
|
+
|
|
54
|
+
Create watches/event automations before the webhook arrives; unmatched deliveries are not replayed.
|
|
48
55
|
|
|
49
56
|
Full guide: https://junior.sentry.dev/extend/sentry-plugin/
|
|
@@ -15,6 +15,28 @@ function sentryProjectResource(input) {
|
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
// src/events/metric-alert.ts
|
|
19
|
+
var SENTRY_METRIC_ALERT_EVENTS = [
|
|
20
|
+
"metric_alert.critical",
|
|
21
|
+
"metric_alert.warning",
|
|
22
|
+
"metric_alert.resolved"
|
|
23
|
+
];
|
|
24
|
+
var SENTRY_METRIC_ALERT_SUGGESTED_EVENTS = [
|
|
25
|
+
"metric_alert.critical",
|
|
26
|
+
"metric_alert.warning"
|
|
27
|
+
];
|
|
28
|
+
var SENTRY_METRIC_ALERT_IDENTIFIER = {
|
|
29
|
+
format: "metric-alert:org/alertRuleId",
|
|
30
|
+
pattern: /^metric-alert:[^\s/:#]+\/\d+$/
|
|
31
|
+
};
|
|
32
|
+
function sentryMetricAlertResource(input) {
|
|
33
|
+
return {
|
|
34
|
+
identifier: `metric-alert:${input.org}/${input.alertRuleId}`,
|
|
35
|
+
label: `Sentry metric alert ${input.org}/${input.alertRuleId}`,
|
|
36
|
+
namespace: "sentry"
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
18
40
|
// src/webhooks/events.ts
|
|
19
41
|
import { z } from "zod";
|
|
20
42
|
var issueWebhookSchema = z.object({
|
|
@@ -38,6 +60,19 @@ var issueWebhookSchema = z.object({
|
|
|
38
60
|
}),
|
|
39
61
|
installation: z.object({ uuid: z.string().min(1) }).passthrough()
|
|
40
62
|
}).passthrough();
|
|
63
|
+
var metricAlertWebhookSchema = z.object({
|
|
64
|
+
action: z.string(),
|
|
65
|
+
data: z.object({
|
|
66
|
+
description_text: z.string().optional(),
|
|
67
|
+
description_title: z.string().optional(),
|
|
68
|
+
metric_alert: z.object({
|
|
69
|
+
alert_rule: z.object({ id: z.coerce.string().min(1) }).passthrough(),
|
|
70
|
+
id: z.coerce.string().min(1)
|
|
71
|
+
}).passthrough(),
|
|
72
|
+
web_url: z.string().url()
|
|
73
|
+
}),
|
|
74
|
+
installation: z.object({ uuid: z.string().min(1) }).passthrough()
|
|
75
|
+
}).passthrough();
|
|
41
76
|
function organizationSlug(issueApiUrl) {
|
|
42
77
|
try {
|
|
43
78
|
const segments = new URL(issueApiUrl).pathname.split("/").filter(Boolean);
|
|
@@ -70,8 +105,13 @@ function issueText(issue) {
|
|
|
70
105
|
].filter((part) => part !== void 0);
|
|
71
106
|
return parts.length > 0 ? parts.join("\n") : void 0;
|
|
72
107
|
}
|
|
73
|
-
function
|
|
74
|
-
|
|
108
|
+
function webOrganizationSlug(webUrl) {
|
|
109
|
+
const fromPath = organizationSlug(webUrl);
|
|
110
|
+
if (fromPath) return fromPath;
|
|
111
|
+
const labels = new URL(webUrl).hostname.split(".");
|
|
112
|
+
return labels.length >= 3 ? labels[0] : void 0;
|
|
113
|
+
}
|
|
114
|
+
function normalizeIssueEvents(input) {
|
|
75
115
|
const parsed = issueWebhookSchema.safeParse(input.body);
|
|
76
116
|
if (!parsed.success || parsed.data.action !== "created") return [];
|
|
77
117
|
const issue = parsed.data.data.issue;
|
|
@@ -103,6 +143,47 @@ function normalizeSentryEvents(input) {
|
|
|
103
143
|
{ ...event, identifier: projectResource.identifier }
|
|
104
144
|
];
|
|
105
145
|
}
|
|
146
|
+
function normalizeMetricAlertEvents(input) {
|
|
147
|
+
const parsed = metricAlertWebhookSchema.safeParse(input.body);
|
|
148
|
+
if (!parsed.success) return [];
|
|
149
|
+
const eventType = `metric_alert.${parsed.data.action}`;
|
|
150
|
+
if (!SENTRY_METRIC_ALERT_EVENTS.includes(eventType)) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
const { data } = parsed.data;
|
|
154
|
+
const org = webOrganizationSlug(data.web_url)?.toLowerCase();
|
|
155
|
+
if (!org || org !== input.webhookOrg.toLowerCase()) return [];
|
|
156
|
+
const alertRuleId = data.metric_alert.alert_rule.id;
|
|
157
|
+
const incidentId = data.metric_alert.id;
|
|
158
|
+
const resource = sentryMetricAlertResource({ alertRuleId, org });
|
|
159
|
+
const untrustedText = [
|
|
160
|
+
data.description_title,
|
|
161
|
+
data.description_text,
|
|
162
|
+
`URL: ${data.web_url}`
|
|
163
|
+
].filter((part) => Boolean(part)).join("\n");
|
|
164
|
+
return [
|
|
165
|
+
{
|
|
166
|
+
// One event per incident state. Sentry retries get new Request-IDs.
|
|
167
|
+
eventKey: `sentry:${resource.identifier}:${incidentId}:${eventType}`,
|
|
168
|
+
eventType,
|
|
169
|
+
identifier: resource.identifier,
|
|
170
|
+
occurredAtMs: providerTime(input.hookTimestamp) ?? Date.now(),
|
|
171
|
+
trustedSummary: `${resource.label} is ${parsed.data.action}.`,
|
|
172
|
+
data: { alertRuleId, incidentId, url: data.web_url },
|
|
173
|
+
untrustedText
|
|
174
|
+
}
|
|
175
|
+
];
|
|
176
|
+
}
|
|
177
|
+
function normalizeSentryEvents(input) {
|
|
178
|
+
switch (input.hookResource) {
|
|
179
|
+
case "issue":
|
|
180
|
+
return normalizeIssueEvents(input);
|
|
181
|
+
case "metric_alert":
|
|
182
|
+
return normalizeMetricAlertEvents(input);
|
|
183
|
+
default:
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
}
|
|
106
187
|
|
|
107
188
|
// src/webhooks/handler.ts
|
|
108
189
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
@@ -176,6 +257,9 @@ export {
|
|
|
176
257
|
SENTRY_ISSUE_EVENTS,
|
|
177
258
|
sentryIssueResource,
|
|
178
259
|
sentryProjectResource,
|
|
260
|
+
SENTRY_METRIC_ALERT_EVENTS,
|
|
261
|
+
SENTRY_METRIC_ALERT_SUGGESTED_EVENTS,
|
|
262
|
+
SENTRY_METRIC_ALERT_IDENTIFIER,
|
|
179
263
|
normalizeSentryEvents,
|
|
180
264
|
createSentryWebhookRoute,
|
|
181
265
|
sentryWebhookSecret,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SubscribableResource } from "@sentry/junior-plugin-api";
|
|
2
|
+
/** Metric alert states, named after Sentry's `metric_alert.<action>` webhooks. */
|
|
3
|
+
export declare const SENTRY_METRIC_ALERT_EVENTS: readonly ["metric_alert.critical", "metric_alert.warning", "metric_alert.resolved"];
|
|
4
|
+
export declare const SENTRY_METRIC_ALERT_SUGGESTED_EVENTS: string[];
|
|
5
|
+
/** The identifier shape that `sentryMetricAlertResource` builds. */
|
|
6
|
+
export declare const SENTRY_METRIC_ALERT_IDENTIFIER: {
|
|
7
|
+
format: string;
|
|
8
|
+
pattern: RegExp;
|
|
9
|
+
};
|
|
10
|
+
/** Build the stable Sentry metric alert identity shared by tools and webhooks. */
|
|
11
|
+
export declare function sentryMetricAlertResource(input: {
|
|
12
|
+
alertRuleId: string;
|
|
13
|
+
org: string;
|
|
14
|
+
}): Pick<SubscribableResource, "identifier" | "label" | "namespace">;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Sentry plugin runtime boundary.
|
|
3
3
|
*
|
|
4
4
|
* This package owns per-user Sentry OAuth, org read access, CLI setup,
|
|
5
|
-
* internal-integration issue webhook normalization, and
|
|
6
|
-
* identities. Junior core owns watches and event automations.
|
|
5
|
+
* internal-integration issue and metric alert webhook normalization, and
|
|
6
|
+
* Sentry resource identities. Junior core owns watches and event automations.
|
|
7
7
|
*/
|
|
8
8
|
import { type PluginRegistration } from "@sentry/junior-plugin-api";
|
|
9
9
|
/** Register Sentry runtime metadata and signed event ingress. */
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SENTRY_ISSUE_EVENTS,
|
|
3
|
+
SENTRY_METRIC_ALERT_EVENTS,
|
|
4
|
+
SENTRY_METRIC_ALERT_IDENTIFIER,
|
|
5
|
+
SENTRY_METRIC_ALERT_SUGGESTED_EVENTS,
|
|
3
6
|
createSentryWebhookRoute,
|
|
4
7
|
sentryWebhookOrg,
|
|
5
8
|
sentryWebhookSecret
|
|
6
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-6VG4NPNH.js";
|
|
7
10
|
|
|
8
11
|
// src/index.ts
|
|
9
12
|
import {
|
|
@@ -79,6 +82,12 @@ function sentryPlugin() {
|
|
|
79
82
|
type: "project",
|
|
80
83
|
supportedEvents: [...SENTRY_ISSUE_EVENTS],
|
|
81
84
|
suggestedEvents: [...SENTRY_ISSUE_EVENTS]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
type: "metric_alert",
|
|
88
|
+
identifier: SENTRY_METRIC_ALERT_IDENTIFIER,
|
|
89
|
+
supportedEvents: [...SENTRY_METRIC_ALERT_EVENTS],
|
|
90
|
+
suggestedEvents: SENTRY_METRIC_ALERT_SUGGESTED_EVENTS
|
|
82
91
|
}
|
|
83
92
|
],
|
|
84
93
|
isEnabled: () => Boolean(sentryWebhookSecret()) && Boolean(sentryWebhookOrg()),
|
|
@@ -95,7 +104,7 @@ function sentryPlugin() {
|
|
|
95
104
|
domains: SENTRY_API_DOMAINS,
|
|
96
105
|
type: "oauth-bearer"
|
|
97
106
|
},
|
|
98
|
-
description: "Investigate Sentry telemetry, manage alerting, and receive issue webhooks",
|
|
107
|
+
description: "Investigate Sentry telemetry, manage alerting, and receive issue and metric alert webhooks",
|
|
99
108
|
displayName: "Sentry",
|
|
100
109
|
envVars: {
|
|
101
110
|
SENTRY_CLIENT_ID: {},
|
package/dist/testing.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { EventInput } from "@sentry/junior-plugin-api";
|
|
2
|
-
|
|
3
|
-
export declare function normalizeSentryEvents(input: {
|
|
2
|
+
interface WebhookInput {
|
|
4
3
|
body: unknown;
|
|
5
4
|
hookResource: string;
|
|
6
5
|
hookTimestamp?: string;
|
|
7
6
|
webhookOrg: string;
|
|
8
|
-
}
|
|
7
|
+
}
|
|
8
|
+
/** Normalize one verified Sentry webhook into subscribable Sentry events. */
|
|
9
|
+
export declare function normalizeSentryEvents(input: WebhookInput): EventInput[];
|
|
10
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior-sentry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.243.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.243.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@oxlint/plugins": "1.79.0",
|