@shipfox/api-integration-sentry-dto 2.0.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/.turbo/turbo-build.log +2 -0
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +99 -0
- package/LICENSE +21 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/index.d.ts +3 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +4 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/sentry.d.ts +37 -0
- package/dist/schemas/sentry.d.ts.map +1 -0
- package/dist/schemas/sentry.js +18 -0
- package/dist/schemas/sentry.js.map +1 -0
- package/dist/schemas/webhooks.d.ts +61 -0
- package/dist/schemas/webhooks.d.ts.map +1 -0
- package/dist/schemas/webhooks.js +64 -0
- package/dist/schemas/webhooks.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -0
- package/package.json +56 -0
- package/src/index.ts +1 -0
- package/src/schemas/index.ts +2 -0
- package/src/schemas/sentry.test.ts +63 -0
- package/src/schemas/sentry.ts +24 -0
- package/src/schemas/webhooks.test.ts +148 -0
- package/src/schemas/webhooks.ts +60 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +3 -0
- package/tsconfig.test.json +9 -0
- package/vitest.config.ts +3 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
$ shipfox-tsc-emit
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
$ shipfox-tsc-check
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# @shipfox/api-integration-sentry-dto
|
|
2
|
+
|
|
3
|
+
## 2.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1b0d344: Publishes the complete API runtime closure with packed-consumer-safe internal imports and records its exact package set in application releases.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [1b0d344]
|
|
12
|
+
- @shipfox/api-integration-core-dto@2.0.0
|
|
13
|
+
|
|
14
|
+
## 0.1.0
|
|
15
|
+
|
|
16
|
+
### Minor Changes
|
|
17
|
+
|
|
18
|
+
- d245be8: [api/integrations] Make the signed Sentry installation webhook authoritative.
|
|
19
|
+
- `@shipfox/api-integration-sentry-dto`: reshape `sentryInstallationWebhookSchema`
|
|
20
|
+
to read `data.installation.{uuid, organization.slug, status, code}` plus an
|
|
21
|
+
optional top-level `actor`. Only consumed fields are validated and the raw
|
|
22
|
+
`code` is never logged.
|
|
23
|
+
- `@shipfox/api-integration-sentry`: the signed `installation.created` webhook now
|
|
24
|
+
exchanges the single-use code and persists a verified-but-unclaimed installation
|
|
25
|
+
(`connection_id IS NULL`, `code_hash = sha256(code)`). The browser flow narrows
|
|
26
|
+
to a claim that binds a verified install to a workspace under unified claim auth
|
|
27
|
+
(exchange-success, same-code hash match, or a retryable
|
|
28
|
+
`verification-in-progress` while a concurrent webhook is mid-exchange), with a
|
|
29
|
+
proof-mismatch 403 closing the bare-uuid IDOR. The exchange/verify run outside
|
|
30
|
+
the DB transaction; a short transaction wraps persist + delivery record. Adds
|
|
31
|
+
`connection_id` nullable + `code_hash` to the installations table and a daily TTL
|
|
32
|
+
cron that tombstones never-claimed installs.
|
|
33
|
+
- `@shipfox/api-integration-core`: inject the Sentry client into the webhook
|
|
34
|
+
context, resolve a null `connection_id` to "no connection" for pre-claim issue
|
|
35
|
+
deliveries, and register the unclaimed-installation cleanup cron when Sentry is
|
|
36
|
+
enabled.
|
|
37
|
+
- `@shipfox/client-integrations`: treat the retryable `verification-in-progress`
|
|
38
|
+
response as a backoff-eligible failure on the connect callback.
|
|
39
|
+
|
|
40
|
+
- 5b8ed32: Add the Sentry install/connect flow so a workspace member can link a Sentry
|
|
41
|
+
installation to a Shipfox workspace, writing the connection + installation rows
|
|
42
|
+
that the webhook receiver looks up.
|
|
43
|
+
- `@shipfox/api-integration-sentry`: add a stateless `SentryApiClient`
|
|
44
|
+
(authorization-code exchange, org-slug derivation, optional verify-install),
|
|
45
|
+
`handleSentryConnect`, and two authenticated routes —
|
|
46
|
+
`POST /integrations/sentry/install` (returns the external-install URL) and
|
|
47
|
+
`POST /integrations/sentry/connect` (links the installation). Sentry has no
|
|
48
|
+
`state` param, so the workspace is taken from the request body and authorized
|
|
49
|
+
against the live session; the org slug is derived from Sentry, never trusted
|
|
50
|
+
from the body. The verify-install side effect runs after the row is persisted,
|
|
51
|
+
and no Sentry token is stored. Provider HTTP errors map to a typed
|
|
52
|
+
`SentryIntegrationProviderError` and never carry the token, code, or client
|
|
53
|
+
secret.
|
|
54
|
+
- `@shipfox/api-integration-sentry-dto`: add the install/connect request and
|
|
55
|
+
response schemas.
|
|
56
|
+
- `@shipfox/api-integration-core`: wire the `getExistingSentryConnection` and
|
|
57
|
+
`connectSentryInstallation` closures into the Sentry provider (internal
|
|
58
|
+
wiring, no public API change).
|
|
59
|
+
|
|
60
|
+
A concurrent same-install connect race (shared with GitHub) is tracked
|
|
61
|
+
separately in ENG-409.
|
|
62
|
+
|
|
63
|
+
- d6d4862: Add a Sentry integration provider that ingests issue webhooks.
|
|
64
|
+
- New `@shipfox/api-integration-sentry` + `-sentry-dto` packages: a webhook
|
|
65
|
+
receiver that verifies the HMAC-SHA256 signature (keyed with the app client
|
|
66
|
+
secret), dedups on `Request-ID`, normalizes the issue payload, and publishes
|
|
67
|
+
`integrations.event.received` with `source: 'sentry'` and `event:
|
|
68
|
+
issue.<action>`. A raw `ignored` action is normalized to `archived`. Malformed,
|
|
69
|
+
bad-JSON, unknown-action, and unknown-resource deliveries are recorded-and-dropped
|
|
70
|
+
with a 204 (deliberate deviation from GitHub's 400 to avoid Sentry disabling the
|
|
71
|
+
webhook).
|
|
72
|
+
- `@shipfox/api-integration-core-dto`: add the `SentryIssuePayload` contract.
|
|
73
|
+
- `@shipfox/api-integration-core`: register the Sentry provider behind
|
|
74
|
+
`INTEGRATIONS_ENABLE_SENTRY_PROVIDER`, add the
|
|
75
|
+
`updateIntegrationConnectionLifecycleStatus` helper, and pin stable
|
|
76
|
+
migration-tracking table names per provider database.
|
|
77
|
+
- `@shipfox/node-fastify`: add the shared `rawBodyPlugin` and `WEBHOOK_BODY_LIMIT`
|
|
78
|
+
exports for webhook receivers.
|
|
79
|
+
- `@shipfox/node-module`: add an optional `migrationsTableName` to `ModuleDatabase`
|
|
80
|
+
so conditionally-composed databases get a position-independent migration table.
|
|
81
|
+
- `@shipfox/api-integration-github`: consume the shared `rawBodyPlugin` instead of
|
|
82
|
+
a local copy (internal refactor, no behavior change).
|
|
83
|
+
|
|
84
|
+
Deploy note: environments with GitHub enabled must rename the existing
|
|
85
|
+
`__drizzle_migrations_integrations_1` table to
|
|
86
|
+
`__drizzle_migrations_integrations_github` as part of this release, or GitHub
|
|
87
|
+
migrations re-run against existing tables.
|
|
88
|
+
|
|
89
|
+
### Patch Changes
|
|
90
|
+
|
|
91
|
+
- Updated dependencies [115655e]
|
|
92
|
+
- Updated dependencies [ce062a9]
|
|
93
|
+
- Updated dependencies [f3614ae]
|
|
94
|
+
- Updated dependencies [f8f339a]
|
|
95
|
+
- Updated dependencies [b8e49ff]
|
|
96
|
+
- Updated dependencies [d6d4862]
|
|
97
|
+
- Updated dependencies [01be723]
|
|
98
|
+
- Updated dependencies [2933c33]
|
|
99
|
+
- @shipfox/api-integration-core-dto@0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shipfox
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './schemas/index.js';\n"],"names":[],"mappings":"AAAA,cAAc,qBAAqB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schemas/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/schemas/index.ts"],"sourcesContent":["export * from './sentry.js';\nexport * from './webhooks.js';\n"],"names":[],"mappings":"AAAA,cAAc,cAAc;AAC5B,cAAc,gBAAgB"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const createSentryInstallBodySchema: z.ZodObject<{
|
|
3
|
+
workspace_id: z.ZodString;
|
|
4
|
+
}, z.core.$strip>;
|
|
5
|
+
export type CreateSentryInstallBodyDto = z.infer<typeof createSentryInstallBodySchema>;
|
|
6
|
+
export declare const createSentryInstallResponseSchema: z.ZodObject<{
|
|
7
|
+
install_url: z.ZodString;
|
|
8
|
+
}, z.core.$strip>;
|
|
9
|
+
export type CreateSentryInstallResponseDto = z.infer<typeof createSentryInstallResponseSchema>;
|
|
10
|
+
export declare const sentryConnectBodySchema: z.ZodObject<{
|
|
11
|
+
workspace_id: z.ZodString;
|
|
12
|
+
code: z.ZodString;
|
|
13
|
+
installation_id: z.ZodString;
|
|
14
|
+
}, z.core.$strip>;
|
|
15
|
+
export type SentryConnectBodyDto = z.infer<typeof sentryConnectBodySchema>;
|
|
16
|
+
export declare const sentryConnectResponseSchema: z.ZodObject<{
|
|
17
|
+
id: z.ZodString;
|
|
18
|
+
workspace_id: z.ZodString;
|
|
19
|
+
provider: z.ZodString;
|
|
20
|
+
external_account_id: z.ZodString;
|
|
21
|
+
slug: z.ZodString;
|
|
22
|
+
display_name: z.ZodString;
|
|
23
|
+
lifecycle_status: z.ZodEnum<{
|
|
24
|
+
error: "error";
|
|
25
|
+
active: "active";
|
|
26
|
+
disabled: "disabled";
|
|
27
|
+
}>;
|
|
28
|
+
capabilities: z.ZodArray<z.ZodEnum<{
|
|
29
|
+
source_control: "source_control";
|
|
30
|
+
agent_tools: "agent_tools";
|
|
31
|
+
}>>;
|
|
32
|
+
external_url: z.ZodOptional<z.ZodString>;
|
|
33
|
+
created_at: z.ZodString;
|
|
34
|
+
updated_at: z.ZodString;
|
|
35
|
+
}, z.core.$strip>;
|
|
36
|
+
export type SentryConnectResponseDto = z.infer<typeof sentryConnectResponseSchema>;
|
|
37
|
+
//# sourceMappingURL=sentry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sentry.d.ts","sourceRoot":"","sources":["../../src/schemas/sentry.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,eAAO,MAAM,6BAA6B;;iBAExC,CAAC;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEvF,eAAO,MAAM,iCAAiC;;iBAE5C,CAAC;AACH,MAAM,MAAM,8BAA8B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAI/F,eAAO,MAAM,uBAAuB;;;;iBAIlC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE3E,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;iBAAiC,CAAC;AAC1E,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { integrationConnectionDtoSchema } from '@shipfox/api-integration-core-dto';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export const createSentryInstallBodySchema = z.object({
|
|
4
|
+
workspace_id: z.string().uuid()
|
|
5
|
+
});
|
|
6
|
+
export const createSentryInstallResponseSchema = z.object({
|
|
7
|
+
install_url: z.string().url()
|
|
8
|
+
});
|
|
9
|
+
// org_slug is deliberately absent: it is derived from Sentry after the exchange,
|
|
10
|
+
// so a forged slug in the body cannot influence the stored connection.
|
|
11
|
+
export const sentryConnectBodySchema = z.object({
|
|
12
|
+
workspace_id: z.string().uuid(),
|
|
13
|
+
code: z.string().min(1),
|
|
14
|
+
installation_id: z.string().min(1)
|
|
15
|
+
});
|
|
16
|
+
export const sentryConnectResponseSchema = integrationConnectionDtoSchema;
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=sentry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/schemas/sentry.ts"],"sourcesContent":["import {integrationConnectionDtoSchema} from '@shipfox/api-integration-core-dto';\nimport {z} from 'zod';\n\nexport const createSentryInstallBodySchema = z.object({\n workspace_id: z.string().uuid(),\n});\nexport type CreateSentryInstallBodyDto = z.infer<typeof createSentryInstallBodySchema>;\n\nexport const createSentryInstallResponseSchema = z.object({\n install_url: z.string().url(),\n});\nexport type CreateSentryInstallResponseDto = z.infer<typeof createSentryInstallResponseSchema>;\n\n// org_slug is deliberately absent: it is derived from Sentry after the exchange,\n// so a forged slug in the body cannot influence the stored connection.\nexport const sentryConnectBodySchema = z.object({\n workspace_id: z.string().uuid(),\n code: z.string().min(1),\n installation_id: z.string().min(1),\n});\nexport type SentryConnectBodyDto = z.infer<typeof sentryConnectBodySchema>;\n\nexport const sentryConnectResponseSchema = integrationConnectionDtoSchema;\nexport type SentryConnectResponseDto = z.infer<typeof sentryConnectResponseSchema>;\n"],"names":["integrationConnectionDtoSchema","z","createSentryInstallBodySchema","object","workspace_id","string","uuid","createSentryInstallResponseSchema","install_url","url","sentryConnectBodySchema","code","min","installation_id","sentryConnectResponseSchema"],"mappings":"AAAA,SAAQA,8BAA8B,QAAO,oCAAoC;AACjF,SAAQC,CAAC,QAAO,MAAM;AAEtB,OAAO,MAAMC,gCAAgCD,EAAEE,MAAM,CAAC;IACpDC,cAAcH,EAAEI,MAAM,GAAGC,IAAI;AAC/B,GAAG;AAGH,OAAO,MAAMC,oCAAoCN,EAAEE,MAAM,CAAC;IACxDK,aAAaP,EAAEI,MAAM,GAAGI,GAAG;AAC7B,GAAG;AAGH,iFAAiF;AACjF,uEAAuE;AACvE,OAAO,MAAMC,0BAA0BT,EAAEE,MAAM,CAAC;IAC9CC,cAAcH,EAAEI,MAAM,GAAGC,IAAI;IAC7BK,MAAMV,EAAEI,MAAM,GAAGO,GAAG,CAAC;IACrBC,iBAAiBZ,EAAEI,MAAM,GAAGO,GAAG,CAAC;AAClC,GAAG;AAGH,OAAO,MAAME,8BAA8Bd,+BAA+B"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const sentryIssueActionSchema: z.ZodEnum<{
|
|
3
|
+
created: "created";
|
|
4
|
+
resolved: "resolved";
|
|
5
|
+
assigned: "assigned";
|
|
6
|
+
archived: "archived";
|
|
7
|
+
unresolved: "unresolved";
|
|
8
|
+
}>;
|
|
9
|
+
export type SentryIssueAction = z.infer<typeof sentryIssueActionSchema>;
|
|
10
|
+
export declare const sentryIssueWebhookSchema: z.ZodObject<{
|
|
11
|
+
action: z.ZodEnum<{
|
|
12
|
+
created: "created";
|
|
13
|
+
resolved: "resolved";
|
|
14
|
+
assigned: "assigned";
|
|
15
|
+
archived: "archived";
|
|
16
|
+
unresolved: "unresolved";
|
|
17
|
+
}>;
|
|
18
|
+
installation: z.ZodObject<{
|
|
19
|
+
uuid: z.ZodString;
|
|
20
|
+
}, z.core.$strip>;
|
|
21
|
+
data: z.ZodObject<{
|
|
22
|
+
issue: z.ZodObject<{
|
|
23
|
+
id: z.ZodCoercedString<unknown>;
|
|
24
|
+
shortId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
25
|
+
title: z.ZodOptional<z.ZodString>;
|
|
26
|
+
culprit: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
27
|
+
level: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
28
|
+
status: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
29
|
+
platform: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
30
|
+
web_url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
31
|
+
url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
32
|
+
project_url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
33
|
+
firstSeen: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
34
|
+
lastSeen: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
35
|
+
}, z.core.$strip>;
|
|
36
|
+
}, z.core.$strip>;
|
|
37
|
+
}, z.core.$strip>;
|
|
38
|
+
export type SentryIssueWebhookDto = z.infer<typeof sentryIssueWebhookSchema>;
|
|
39
|
+
export declare const sentryInstallationWebhookSchema: z.ZodObject<{
|
|
40
|
+
action: z.ZodEnum<{
|
|
41
|
+
created: "created";
|
|
42
|
+
deleted: "deleted";
|
|
43
|
+
}>;
|
|
44
|
+
actor: z.ZodOptional<z.ZodObject<{
|
|
45
|
+
type: z.ZodOptional<z.ZodString>;
|
|
46
|
+
id: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
|
|
47
|
+
name: z.ZodOptional<z.ZodString>;
|
|
48
|
+
}, z.core.$strip>>;
|
|
49
|
+
data: z.ZodObject<{
|
|
50
|
+
installation: z.ZodObject<{
|
|
51
|
+
uuid: z.ZodString;
|
|
52
|
+
status: z.ZodOptional<z.ZodString>;
|
|
53
|
+
code: z.ZodOptional<z.ZodString>;
|
|
54
|
+
organization: z.ZodOptional<z.ZodObject<{
|
|
55
|
+
slug: z.ZodString;
|
|
56
|
+
}, z.core.$strip>>;
|
|
57
|
+
}, z.core.$strip>;
|
|
58
|
+
}, z.core.$strip>;
|
|
59
|
+
}, z.core.$strip>;
|
|
60
|
+
export type SentryInstallationWebhookDto = z.infer<typeof sentryInstallationWebhookSchema>;
|
|
61
|
+
//# sourceMappingURL=webhooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/schemas/webhooks.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAKtB,eAAO,MAAM,uBAAuB;;;;;;EAA+B,CAAC;AACpE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAIxE,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmBnC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAQ7E,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;iBAmB1C,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { SENTRY_ISSUE_ACTIONS } from '@shipfox/api-integration-core-dto';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
// Built from the SENTRY_ISSUE_ACTIONS tuple in core-dto so the accepted webhook
|
|
4
|
+
// actions stay in lockstep with the published SentryIssuePayload contract. Sentry
|
|
5
|
+
// may add actions without notice; unknown actions are acknowledged and dropped.
|
|
6
|
+
export const sentryIssueActionSchema = z.enum(SENTRY_ISSUE_ACTIONS);
|
|
7
|
+
// Sentry sends large webhook envelopes; validating only consumed fields avoids
|
|
8
|
+
// coupling ingestion to unrelated provider payload changes.
|
|
9
|
+
export const sentryIssueWebhookSchema = z.object({
|
|
10
|
+
action: sentryIssueActionSchema,
|
|
11
|
+
installation: z.object({
|
|
12
|
+
uuid: z.string().min(1)
|
|
13
|
+
}),
|
|
14
|
+
data: z.object({
|
|
15
|
+
issue: z.object({
|
|
16
|
+
id: z.coerce.string().min(1),
|
|
17
|
+
shortId: z.string().nullable().optional(),
|
|
18
|
+
title: z.string().optional(),
|
|
19
|
+
culprit: z.string().nullable().optional(),
|
|
20
|
+
level: z.string().nullable().optional(),
|
|
21
|
+
status: z.string().nullable().optional(),
|
|
22
|
+
platform: z.string().nullable().optional(),
|
|
23
|
+
web_url: z.string().nullable().optional(),
|
|
24
|
+
url: z.string().nullable().optional(),
|
|
25
|
+
project_url: z.string().nullable().optional(),
|
|
26
|
+
firstSeen: z.string().nullable().optional(),
|
|
27
|
+
lastSeen: z.string().nullable().optional()
|
|
28
|
+
})
|
|
29
|
+
})
|
|
30
|
+
});
|
|
31
|
+
// Sentry delivers the installation lifecycle under `data.installation` (issue
|
|
32
|
+
// webhooks instead carry a top-level `installation`). The signed payload carries
|
|
33
|
+
// the same material the browser redirect delivers unauthenticated — the install
|
|
34
|
+
// uuid, the org slug, and the single-use authorization `code`. Only consumed
|
|
35
|
+
// fields are validated; `status`/`actor` are tolerated-but-optional. The raw
|
|
36
|
+
// `code` is security-sensitive and must never be logged.
|
|
37
|
+
export const sentryInstallationWebhookSchema = z.object({
|
|
38
|
+
action: z.enum([
|
|
39
|
+
'created',
|
|
40
|
+
'deleted'
|
|
41
|
+
]),
|
|
42
|
+
// Identifies who performed the install in Sentry. Tolerated for forward-compat
|
|
43
|
+
// but not consumed; never trusted.
|
|
44
|
+
actor: z.object({
|
|
45
|
+
type: z.string().optional(),
|
|
46
|
+
id: z.union([
|
|
47
|
+
z.string(),
|
|
48
|
+
z.number()
|
|
49
|
+
]).optional(),
|
|
50
|
+
name: z.string().optional()
|
|
51
|
+
}).optional(),
|
|
52
|
+
data: z.object({
|
|
53
|
+
installation: z.object({
|
|
54
|
+
uuid: z.string().min(1),
|
|
55
|
+
status: z.string().optional(),
|
|
56
|
+
code: z.string().min(1).optional(),
|
|
57
|
+
organization: z.object({
|
|
58
|
+
slug: z.string().min(1)
|
|
59
|
+
}).optional()
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/schemas/webhooks.ts"],"sourcesContent":["import {SENTRY_ISSUE_ACTIONS} from '@shipfox/api-integration-core-dto';\nimport {z} from 'zod';\n\n// Built from the SENTRY_ISSUE_ACTIONS tuple in core-dto so the accepted webhook\n// actions stay in lockstep with the published SentryIssuePayload contract. Sentry\n// may add actions without notice; unknown actions are acknowledged and dropped.\nexport const sentryIssueActionSchema = z.enum(SENTRY_ISSUE_ACTIONS);\nexport type SentryIssueAction = z.infer<typeof sentryIssueActionSchema>;\n\n// Sentry sends large webhook envelopes; validating only consumed fields avoids\n// coupling ingestion to unrelated provider payload changes.\nexport const sentryIssueWebhookSchema = z.object({\n action: sentryIssueActionSchema,\n installation: z.object({uuid: z.string().min(1)}),\n data: z.object({\n issue: z.object({\n id: z.coerce.string().min(1),\n shortId: z.string().nullable().optional(),\n title: z.string().optional(),\n culprit: z.string().nullable().optional(),\n level: z.string().nullable().optional(),\n status: z.string().nullable().optional(),\n platform: z.string().nullable().optional(),\n web_url: z.string().nullable().optional(),\n url: z.string().nullable().optional(),\n project_url: z.string().nullable().optional(),\n firstSeen: z.string().nullable().optional(),\n lastSeen: z.string().nullable().optional(),\n }),\n }),\n});\nexport type SentryIssueWebhookDto = z.infer<typeof sentryIssueWebhookSchema>;\n\n// Sentry delivers the installation lifecycle under `data.installation` (issue\n// webhooks instead carry a top-level `installation`). The signed payload carries\n// the same material the browser redirect delivers unauthenticated — the install\n// uuid, the org slug, and the single-use authorization `code`. Only consumed\n// fields are validated; `status`/`actor` are tolerated-but-optional. The raw\n// `code` is security-sensitive and must never be logged.\nexport const sentryInstallationWebhookSchema = z.object({\n action: z.enum(['created', 'deleted']),\n // Identifies who performed the install in Sentry. Tolerated for forward-compat\n // but not consumed; never trusted.\n actor: z\n .object({\n type: z.string().optional(),\n id: z.union([z.string(), z.number()]).optional(),\n name: z.string().optional(),\n })\n .optional(),\n data: z.object({\n installation: z.object({\n uuid: z.string().min(1),\n status: z.string().optional(),\n code: z.string().min(1).optional(),\n organization: z.object({slug: z.string().min(1)}).optional(),\n }),\n }),\n});\nexport type SentryInstallationWebhookDto = z.infer<typeof sentryInstallationWebhookSchema>;\n"],"names":["SENTRY_ISSUE_ACTIONS","z","sentryIssueActionSchema","enum","sentryIssueWebhookSchema","object","action","installation","uuid","string","min","data","issue","id","coerce","shortId","nullable","optional","title","culprit","level","status","platform","web_url","url","project_url","firstSeen","lastSeen","sentryInstallationWebhookSchema","actor","type","union","number","name","code","organization","slug"],"mappings":"AAAA,SAAQA,oBAAoB,QAAO,oCAAoC;AACvE,SAAQC,CAAC,QAAO,MAAM;AAEtB,gFAAgF;AAChF,kFAAkF;AAClF,gFAAgF;AAChF,OAAO,MAAMC,0BAA0BD,EAAEE,IAAI,CAACH,sBAAsB;AAGpE,+EAA+E;AAC/E,4DAA4D;AAC5D,OAAO,MAAMI,2BAA2BH,EAAEI,MAAM,CAAC;IAC/CC,QAAQJ;IACRK,cAAcN,EAAEI,MAAM,CAAC;QAACG,MAAMP,EAAEQ,MAAM,GAAGC,GAAG,CAAC;IAAE;IAC/CC,MAAMV,EAAEI,MAAM,CAAC;QACbO,OAAOX,EAAEI,MAAM,CAAC;YACdQ,IAAIZ,EAAEa,MAAM,CAACL,MAAM,GAAGC,GAAG,CAAC;YAC1BK,SAASd,EAAEQ,MAAM,GAAGO,QAAQ,GAAGC,QAAQ;YACvCC,OAAOjB,EAAEQ,MAAM,GAAGQ,QAAQ;YAC1BE,SAASlB,EAAEQ,MAAM,GAAGO,QAAQ,GAAGC,QAAQ;YACvCG,OAAOnB,EAAEQ,MAAM,GAAGO,QAAQ,GAAGC,QAAQ;YACrCI,QAAQpB,EAAEQ,MAAM,GAAGO,QAAQ,GAAGC,QAAQ;YACtCK,UAAUrB,EAAEQ,MAAM,GAAGO,QAAQ,GAAGC,QAAQ;YACxCM,SAAStB,EAAEQ,MAAM,GAAGO,QAAQ,GAAGC,QAAQ;YACvCO,KAAKvB,EAAEQ,MAAM,GAAGO,QAAQ,GAAGC,QAAQ;YACnCQ,aAAaxB,EAAEQ,MAAM,GAAGO,QAAQ,GAAGC,QAAQ;YAC3CS,WAAWzB,EAAEQ,MAAM,GAAGO,QAAQ,GAAGC,QAAQ;YACzCU,UAAU1B,EAAEQ,MAAM,GAAGO,QAAQ,GAAGC,QAAQ;QAC1C;IACF;AACF,GAAG;AAGH,8EAA8E;AAC9E,iFAAiF;AACjF,gFAAgF;AAChF,6EAA6E;AAC7E,6EAA6E;AAC7E,yDAAyD;AACzD,OAAO,MAAMW,kCAAkC3B,EAAEI,MAAM,CAAC;IACtDC,QAAQL,EAAEE,IAAI,CAAC;QAAC;QAAW;KAAU;IACrC,+EAA+E;IAC/E,mCAAmC;IACnC0B,OAAO5B,EACJI,MAAM,CAAC;QACNyB,MAAM7B,EAAEQ,MAAM,GAAGQ,QAAQ;QACzBJ,IAAIZ,EAAE8B,KAAK,CAAC;YAAC9B,EAAEQ,MAAM;YAAIR,EAAE+B,MAAM;SAAG,EAAEf,QAAQ;QAC9CgB,MAAMhC,EAAEQ,MAAM,GAAGQ,QAAQ;IAC3B,GACCA,QAAQ;IACXN,MAAMV,EAAEI,MAAM,CAAC;QACbE,cAAcN,EAAEI,MAAM,CAAC;YACrBG,MAAMP,EAAEQ,MAAM,GAAGC,GAAG,CAAC;YACrBW,QAAQpB,EAAEQ,MAAM,GAAGQ,QAAQ;YAC3BiB,MAAMjC,EAAEQ,MAAM,GAAGC,GAAG,CAAC,GAAGO,QAAQ;YAChCkB,cAAclC,EAAEI,MAAM,CAAC;gBAAC+B,MAAMnC,EAAEQ,MAAM,GAAGC,GAAG,CAAC;YAAE,GAAGO,QAAQ;QAC5D;IACF;AACF,GAAG"}
|