@abyss-project/probe 2.0.1 → 3.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.
Files changed (58) hide show
  1. package/dist/api/index.d.ts +2 -1
  2. package/dist/api/index.js +2 -1
  3. package/dist/api/index.js.map +1 -1
  4. package/dist/api/probe-delivery-subscription.api.d.ts +11 -0
  5. package/dist/api/probe-delivery-subscription.api.js +42 -0
  6. package/dist/api/probe-delivery-subscription.api.js.map +1 -0
  7. package/dist/api/probe-delivery-subscription.application.api.d.ts +11 -0
  8. package/dist/api/probe-delivery-subscription.application.api.js +38 -0
  9. package/dist/api/probe-delivery-subscription.application.api.js.map +1 -0
  10. package/dist/api/probe-webhook.application.api.d.ts +1 -1
  11. package/dist/api/probe-webhook.application.api.js.map +1 -1
  12. package/dist/index.d.ts +29 -7
  13. package/dist/index.js +29 -7
  14. package/dist/index.js.map +1 -1
  15. package/dist/types/enum/api-error.enum.d.ts +2 -1
  16. package/dist/types/enum/api-error.enum.js +1 -0
  17. package/dist/types/enum/api-error.enum.js.map +1 -1
  18. package/dist/types/enum/probe-scan.enum.js.map +1 -1
  19. package/dist/types/interface/api/requests/index.d.ts +1 -0
  20. package/dist/types/interface/api/requests/index.js +1 -0
  21. package/dist/types/interface/api/requests/index.js.map +1 -1
  22. package/dist/types/interface/api/requests/probe-delivery-subscription.request.d.ts +23 -0
  23. package/dist/types/interface/api/requests/probe-delivery-subscription.request.js +3 -0
  24. package/dist/types/interface/api/requests/probe-delivery-subscription.request.js.map +1 -0
  25. package/dist/types/interface/api/requests/probe-webhook.request.d.ts +1 -4
  26. package/dist/types/interface/api/responses/index.d.ts +1 -0
  27. package/dist/types/interface/api/responses/index.js +1 -0
  28. package/dist/types/interface/api/responses/index.js.map +1 -1
  29. package/dist/types/interface/api/responses/probe-delivery-subscription.response.d.ts +10 -0
  30. package/dist/types/interface/api/responses/probe-delivery-subscription.response.js +3 -0
  31. package/dist/types/interface/api/responses/probe-delivery-subscription.response.js.map +1 -0
  32. package/dist/types/tests/probe.dto.test.js +22 -0
  33. package/dist/types/tests/probe.dto.test.js.map +1 -1
  34. package/dist/utils/probe-webhook-signature.utils.js.map +1 -1
  35. package/package.json +10 -1
  36. package/src/api/index.ts +7 -6
  37. package/src/api/probe-delivery-subscription.api.ts +183 -0
  38. package/src/api/probe-delivery-subscription.application.api.ts +186 -0
  39. package/src/api/probe-webhook.application.api.ts +128 -141
  40. package/src/index.ts +262 -205
  41. package/src/types/enum/api-error.enum.ts +35 -33
  42. package/src/types/enum/probe-scan.enum.ts +45 -44
  43. package/src/types/interface/api/requests/index.ts +4 -3
  44. package/src/types/interface/api/requests/probe-delivery-subscription.request.ts +42 -0
  45. package/src/types/interface/api/requests/probe-webhook.request.ts +31 -33
  46. package/src/types/interface/api/responses/index.ts +4 -3
  47. package/src/types/interface/api/responses/probe-delivery-subscription.response.ts +27 -0
  48. package/src/types/interface/models/probe-webhook-delivery.dto.ts +70 -73
  49. package/src/types/tests/probe.dto.test.ts +192 -156
  50. package/src/utils/probe-webhook-signature.utils.ts +57 -56
  51. package/dist/api/probe-webhook.api.d.ts +0 -6
  52. package/dist/api/probe-webhook.api.js +0 -41
  53. package/dist/api/probe-webhook.api.js.map +0 -1
  54. package/dist/api/probe-webhook.api.test.d.ts +0 -1
  55. package/dist/api/probe-webhook.api.test.js +0 -43
  56. package/dist/api/probe-webhook.api.test.js.map +0 -1
  57. package/src/api/probe-webhook.api.test.ts +0 -17
  58. package/src/api/probe-webhook.api.ts +0 -119
@@ -0,0 +1,183 @@
1
+ // ──────────────────────────────────────────────
2
+ // AbyssProbe delivery-subscription API for a personal or organization caller: the endpoints that
3
+ // receive the verdict of the caller's scans (`PROBE_SCAN_COMPLETED`). The subscriptions live in
4
+ // Flow; AbyssProbe checks the caller's access to the scope (self, or a member of the organization
5
+ // named by `organizationId`) and calls Flow for it. Mirrors
6
+ // apps/api/src/api/controllers/probe-delivery-subscription.controller.ts.
7
+ //
8
+ // See probe-delivery-subscription.application.api.ts for the application counterpart.
9
+ // ──────────────────────────────────────────────
10
+
11
+ import { AbyssProbeCore } from '..';
12
+ import type {
13
+ ICreateProbeDeliverySubscriptionBody,
14
+ ICreateProbeDeliverySubscriptionResponse,
15
+ IDeleteProbeDeliverySubscriptionResponse,
16
+ IGetProbeDeliveryResponse,
17
+ IGetProbeDeliverySubscriptionResponse,
18
+ IPaginateProbeDeliveryQuery,
19
+ IPaginateProbeDeliveryResponse,
20
+ IPaginateProbeDeliverySubscriptionQuery,
21
+ IPaginateProbeDeliverySubscriptionResponse,
22
+ IProbeDeliverySubscriptionScopeQuery,
23
+ IReplayProbeDeliveryResponse,
24
+ IResetSecretProbeDeliverySubscriptionResponse,
25
+ IUpdateProbeDeliverySubscriptionBody,
26
+ IUpdateProbeDeliverySubscriptionResponse,
27
+ } from '../types';
28
+
29
+ const DEFAULT_API_KEY_HEADER = 'abyss-api-key';
30
+
31
+ function authHeader(apiKey?: string): Record<string, string> {
32
+ return apiKey ? { [DEFAULT_API_KEY_HEADER]: apiKey } : {};
33
+ }
34
+
35
+ /** Renames `organizationId` to the wire's `organization_id` (see probe-scan.api.ts). */
36
+ function serializeProbeQuery<T extends object>(query: T): Record<string, unknown> {
37
+ const out: Record<string, unknown> = {};
38
+ for (const [key, value] of Object.entries(query as Record<string, unknown>)) {
39
+ if (value === undefined || value === null) continue;
40
+ out[key === 'organizationId' ? 'organization_id' : key] = value;
41
+ }
42
+ return out;
43
+ }
44
+
45
+ const SUBSCRIPTIONS_PATH = 'delivery-subscription';
46
+
47
+ /** `GET /delivery-subscription` — the subscriptions of the scope. */
48
+ export const paginateProbeDeliverySubscription = async (
49
+ query: IPaginateProbeDeliverySubscriptionQuery = {},
50
+ apiKey?: string,
51
+ ): Promise<IPaginateProbeDeliverySubscriptionResponse> =>
52
+ (
53
+ await AbyssProbeCore.axios.get<IPaginateProbeDeliverySubscriptionResponse>(SUBSCRIPTIONS_PATH, {
54
+ params: serializeProbeQuery(query),
55
+ headers: authHeader(apiKey),
56
+ })
57
+ ).data;
58
+
59
+ /** `GET /delivery-subscription/:deliverySubscriptionId` — never carries the secret. */
60
+ export const getProbeDeliverySubscription = async (
61
+ deliverySubscriptionId: string,
62
+ scope: IProbeDeliverySubscriptionScopeQuery = {},
63
+ apiKey?: string,
64
+ ): Promise<IGetProbeDeliverySubscriptionResponse> =>
65
+ (
66
+ await AbyssProbeCore.axios.get<IGetProbeDeliverySubscriptionResponse>(
67
+ `${SUBSCRIPTIONS_PATH}/${deliverySubscriptionId}`,
68
+ { params: serializeProbeQuery(scope), headers: authHeader(apiKey) },
69
+ )
70
+ ).data;
71
+
72
+ /**
73
+ * `POST /delivery-subscription` — HTTP 201. The response's `secret` (signing secret of the
74
+ * deliveries) is returned ONLY here and by {@link resetSecretProbeDeliverySubscription}.
75
+ */
76
+ export const createProbeDeliverySubscription = async (
77
+ body: ICreateProbeDeliverySubscriptionBody,
78
+ scope: IProbeDeliverySubscriptionScopeQuery = {},
79
+ apiKey?: string,
80
+ ): Promise<ICreateProbeDeliverySubscriptionResponse> =>
81
+ (
82
+ await AbyssProbeCore.axios.post<ICreateProbeDeliverySubscriptionResponse>(
83
+ SUBSCRIPTIONS_PATH,
84
+ body,
85
+ { params: serializeProbeQuery(scope), headers: authHeader(apiKey) },
86
+ )
87
+ ).data;
88
+
89
+ /** `PUT /delivery-subscription/:deliverySubscriptionId` — at least one field. */
90
+ export const updateProbeDeliverySubscription = async (
91
+ deliverySubscriptionId: string,
92
+ body: IUpdateProbeDeliverySubscriptionBody,
93
+ scope: IProbeDeliverySubscriptionScopeQuery = {},
94
+ apiKey?: string,
95
+ ): Promise<IUpdateProbeDeliverySubscriptionResponse> =>
96
+ (
97
+ await AbyssProbeCore.axios.put<IUpdateProbeDeliverySubscriptionResponse>(
98
+ `${SUBSCRIPTIONS_PATH}/${deliverySubscriptionId}`,
99
+ body,
100
+ { params: serializeProbeQuery(scope), headers: authHeader(apiKey) },
101
+ )
102
+ ).data;
103
+
104
+ /** `DELETE /delivery-subscription/:deliverySubscriptionId`. */
105
+ export const deleteProbeDeliverySubscription = async (
106
+ deliverySubscriptionId: string,
107
+ scope: IProbeDeliverySubscriptionScopeQuery = {},
108
+ apiKey?: string,
109
+ ): Promise<IDeleteProbeDeliverySubscriptionResponse> =>
110
+ (
111
+ await AbyssProbeCore.axios.delete<IDeleteProbeDeliverySubscriptionResponse>(
112
+ `${SUBSCRIPTIONS_PATH}/${deliverySubscriptionId}`,
113
+ { params: serializeProbeQuery(scope), headers: authHeader(apiKey) },
114
+ )
115
+ ).data;
116
+
117
+ /** `POST /delivery-subscription/:deliverySubscriptionId/reset-secret` — the new secret, once. */
118
+ export const resetSecretProbeDeliverySubscription = async (
119
+ deliverySubscriptionId: string,
120
+ scope: IProbeDeliverySubscriptionScopeQuery = {},
121
+ apiKey?: string,
122
+ ): Promise<IResetSecretProbeDeliverySubscriptionResponse> =>
123
+ (
124
+ await AbyssProbeCore.axios.post<IResetSecretProbeDeliverySubscriptionResponse>(
125
+ `${SUBSCRIPTIONS_PATH}/${deliverySubscriptionId}/reset-secret`,
126
+ undefined,
127
+ { params: serializeProbeQuery(scope), headers: authHeader(apiKey) },
128
+ )
129
+ ).data;
130
+
131
+ /** `GET /delivery-subscription/delivery` — the deliveries of every subscription of the scope. */
132
+ export const paginateProbeScopeDelivery = async (
133
+ query: IPaginateProbeDeliveryQuery = {},
134
+ apiKey?: string,
135
+ ): Promise<IPaginateProbeDeliveryResponse> =>
136
+ (
137
+ await AbyssProbeCore.axios.get<IPaginateProbeDeliveryResponse>(
138
+ `${SUBSCRIPTIONS_PATH}/delivery`,
139
+ { params: serializeProbeQuery(query), headers: authHeader(apiKey) },
140
+ )
141
+ ).data;
142
+
143
+ /** `GET /delivery-subscription/:deliverySubscriptionId/delivery`. */
144
+ export const paginateProbeDelivery = async (
145
+ deliverySubscriptionId: string,
146
+ query: IPaginateProbeDeliveryQuery = {},
147
+ apiKey?: string,
148
+ ): Promise<IPaginateProbeDeliveryResponse> =>
149
+ (
150
+ await AbyssProbeCore.axios.get<IPaginateProbeDeliveryResponse>(
151
+ `${SUBSCRIPTIONS_PATH}/${deliverySubscriptionId}/delivery`,
152
+ { params: serializeProbeQuery(query), headers: authHeader(apiKey) },
153
+ )
154
+ ).data;
155
+
156
+ /** `GET /delivery-subscription/:deliverySubscriptionId/delivery/:deliveryId`. */
157
+ export const getProbeDelivery = async (
158
+ deliverySubscriptionId: string,
159
+ deliveryId: string,
160
+ scope: IProbeDeliverySubscriptionScopeQuery = {},
161
+ apiKey?: string,
162
+ ): Promise<IGetProbeDeliveryResponse> =>
163
+ (
164
+ await AbyssProbeCore.axios.get<IGetProbeDeliveryResponse>(
165
+ `${SUBSCRIPTIONS_PATH}/${deliverySubscriptionId}/delivery/${deliveryId}`,
166
+ { params: serializeProbeQuery(scope), headers: authHeader(apiKey) },
167
+ )
168
+ ).data;
169
+
170
+ /** `POST .../delivery/:deliveryId/replay` — only a delivered or failed one (409 otherwise). */
171
+ export const replayProbeDelivery = async (
172
+ deliverySubscriptionId: string,
173
+ deliveryId: string,
174
+ scope: IProbeDeliverySubscriptionScopeQuery = {},
175
+ apiKey?: string,
176
+ ): Promise<IReplayProbeDeliveryResponse> =>
177
+ (
178
+ await AbyssProbeCore.axios.post<IReplayProbeDeliveryResponse>(
179
+ `${SUBSCRIPTIONS_PATH}/${deliverySubscriptionId}/delivery/${deliveryId}/replay`,
180
+ undefined,
181
+ { params: serializeProbeQuery(scope), headers: authHeader(apiKey) },
182
+ )
183
+ ).data;
@@ -0,0 +1,186 @@
1
+ // ──────────────────────────────────────────────
2
+ // AbyssProbe delivery-subscription API for an application caller (project API key, or a project
3
+ // member's session): the endpoints that receive the verdict of the application's scans. The
4
+ // application counterpart of probe-delivery-subscription.api.ts, with `FULL_ACCESS` on the
5
+ // application as the access rule and no scope argument (the scope is the `:applicationId` URL
6
+ // segment). Mirrors
7
+ // apps/api/src/api/controllers/probe-delivery-subscription-application.controller.ts.
8
+ //
9
+ // `authHeaderApplication` sends `API_KEY_APPLICATION_HEADER` instead of the personal header.
10
+ // ──────────────────────────────────────────────
11
+
12
+ import { AbyssProbeCore, API_KEY_APPLICATION_HEADER } from '..';
13
+ import type {
14
+ ICreateProbeDeliverySubscriptionBody,
15
+ ICreateProbeDeliverySubscriptionResponse,
16
+ IDeleteProbeDeliverySubscriptionResponse,
17
+ IGetProbeDeliveryResponse,
18
+ IGetProbeDeliverySubscriptionResponse,
19
+ IPaginateProbeDeliveryQuery,
20
+ IPaginateProbeDeliveryResponse,
21
+ IPaginateProbeDeliverySubscriptionQuery,
22
+ IPaginateProbeDeliverySubscriptionResponse,
23
+ IReplayProbeDeliveryResponse,
24
+ IResetSecretProbeDeliverySubscriptionResponse,
25
+ IUpdateProbeDeliverySubscriptionBody,
26
+ IUpdateProbeDeliverySubscriptionResponse,
27
+ } from '../types';
28
+
29
+ function authHeaderApplication(applicationApiKey?: string): Record<string, string> {
30
+ return applicationApiKey ? { [API_KEY_APPLICATION_HEADER]: applicationApiKey } : {};
31
+ }
32
+
33
+ /** Drops undefined/null: no `organizationId` ever travels on an application call. */
34
+ function serializeProbeQuery<T extends object>(query: T): Record<string, unknown> {
35
+ const out: Record<string, unknown> = {};
36
+ for (const [key, value] of Object.entries(query as Record<string, unknown>)) {
37
+ if (value === undefined || value === null) continue;
38
+ out[key] = value;
39
+ }
40
+ return out;
41
+ }
42
+
43
+ const subscriptionsPath = (applicationId: string): string =>
44
+ `application/${applicationId}/delivery-subscription`;
45
+
46
+ /** `GET /application/:applicationId/delivery-subscription`. */
47
+ export const paginateProbeDeliverySubscriptionApplication = async (
48
+ applicationId: string,
49
+ query: Omit<IPaginateProbeDeliverySubscriptionQuery, 'organizationId'> = {},
50
+ applicationApiKey?: string,
51
+ ): Promise<IPaginateProbeDeliverySubscriptionResponse> =>
52
+ (
53
+ await AbyssProbeCore.axios.get<IPaginateProbeDeliverySubscriptionResponse>(
54
+ subscriptionsPath(applicationId),
55
+ { params: serializeProbeQuery(query), headers: authHeaderApplication(applicationApiKey) },
56
+ )
57
+ ).data;
58
+
59
+ /** `GET /application/:applicationId/delivery-subscription/:deliverySubscriptionId` — no secret. */
60
+ export const getProbeDeliverySubscriptionApplication = async (
61
+ applicationId: string,
62
+ deliverySubscriptionId: string,
63
+ applicationApiKey?: string,
64
+ ): Promise<IGetProbeDeliverySubscriptionResponse> =>
65
+ (
66
+ await AbyssProbeCore.axios.get<IGetProbeDeliverySubscriptionResponse>(
67
+ `${subscriptionsPath(applicationId)}/${deliverySubscriptionId}`,
68
+ { headers: authHeaderApplication(applicationApiKey) },
69
+ )
70
+ ).data;
71
+
72
+ /**
73
+ * `POST /application/:applicationId/delivery-subscription` — HTTP 201, the `secret` once. With a
74
+ * project member's session the subscription is theirs; with an API key it is recorded under the
75
+ * project's creator.
76
+ */
77
+ export const createProbeDeliverySubscriptionApplication = async (
78
+ applicationId: string,
79
+ body: ICreateProbeDeliverySubscriptionBody,
80
+ applicationApiKey?: string,
81
+ ): Promise<ICreateProbeDeliverySubscriptionResponse> =>
82
+ (
83
+ await AbyssProbeCore.axios.post<ICreateProbeDeliverySubscriptionResponse>(
84
+ subscriptionsPath(applicationId),
85
+ body,
86
+ { headers: authHeaderApplication(applicationApiKey) },
87
+ )
88
+ ).data;
89
+
90
+ /** `PUT /application/:applicationId/delivery-subscription/:deliverySubscriptionId`. */
91
+ export const updateProbeDeliverySubscriptionApplication = async (
92
+ applicationId: string,
93
+ deliverySubscriptionId: string,
94
+ body: IUpdateProbeDeliverySubscriptionBody,
95
+ applicationApiKey?: string,
96
+ ): Promise<IUpdateProbeDeliverySubscriptionResponse> =>
97
+ (
98
+ await AbyssProbeCore.axios.put<IUpdateProbeDeliverySubscriptionResponse>(
99
+ `${subscriptionsPath(applicationId)}/${deliverySubscriptionId}`,
100
+ body,
101
+ { headers: authHeaderApplication(applicationApiKey) },
102
+ )
103
+ ).data;
104
+
105
+ /** `DELETE /application/:applicationId/delivery-subscription/:deliverySubscriptionId`. */
106
+ export const deleteProbeDeliverySubscriptionApplication = async (
107
+ applicationId: string,
108
+ deliverySubscriptionId: string,
109
+ applicationApiKey?: string,
110
+ ): Promise<IDeleteProbeDeliverySubscriptionResponse> =>
111
+ (
112
+ await AbyssProbeCore.axios.delete<IDeleteProbeDeliverySubscriptionResponse>(
113
+ `${subscriptionsPath(applicationId)}/${deliverySubscriptionId}`,
114
+ { headers: authHeaderApplication(applicationApiKey) },
115
+ )
116
+ ).data;
117
+
118
+ /** `POST .../delivery-subscription/:deliverySubscriptionId/reset-secret` — the new secret, once. */
119
+ export const resetSecretProbeDeliverySubscriptionApplication = async (
120
+ applicationId: string,
121
+ deliverySubscriptionId: string,
122
+ applicationApiKey?: string,
123
+ ): Promise<IResetSecretProbeDeliverySubscriptionResponse> =>
124
+ (
125
+ await AbyssProbeCore.axios.post<IResetSecretProbeDeliverySubscriptionResponse>(
126
+ `${subscriptionsPath(applicationId)}/${deliverySubscriptionId}/reset-secret`,
127
+ undefined,
128
+ { headers: authHeaderApplication(applicationApiKey) },
129
+ )
130
+ ).data;
131
+
132
+ /** `GET /application/:applicationId/delivery-subscription/delivery` — every subscription of the application. */
133
+ export const paginateProbeScopeDeliveryApplication = async (
134
+ applicationId: string,
135
+ query: Omit<IPaginateProbeDeliveryQuery, 'organizationId'> = {},
136
+ applicationApiKey?: string,
137
+ ): Promise<IPaginateProbeDeliveryResponse> =>
138
+ (
139
+ await AbyssProbeCore.axios.get<IPaginateProbeDeliveryResponse>(
140
+ `${subscriptionsPath(applicationId)}/delivery`,
141
+ { params: serializeProbeQuery(query), headers: authHeaderApplication(applicationApiKey) },
142
+ )
143
+ ).data;
144
+
145
+ /** `GET .../delivery-subscription/:deliverySubscriptionId/delivery`. */
146
+ export const paginateProbeDeliveryApplication = async (
147
+ applicationId: string,
148
+ deliverySubscriptionId: string,
149
+ query: Omit<IPaginateProbeDeliveryQuery, 'organizationId'> = {},
150
+ applicationApiKey?: string,
151
+ ): Promise<IPaginateProbeDeliveryResponse> =>
152
+ (
153
+ await AbyssProbeCore.axios.get<IPaginateProbeDeliveryResponse>(
154
+ `${subscriptionsPath(applicationId)}/${deliverySubscriptionId}/delivery`,
155
+ { params: serializeProbeQuery(query), headers: authHeaderApplication(applicationApiKey) },
156
+ )
157
+ ).data;
158
+
159
+ /** `GET .../delivery-subscription/:deliverySubscriptionId/delivery/:deliveryId`. */
160
+ export const getProbeDeliveryApplication = async (
161
+ applicationId: string,
162
+ deliverySubscriptionId: string,
163
+ deliveryId: string,
164
+ applicationApiKey?: string,
165
+ ): Promise<IGetProbeDeliveryResponse> =>
166
+ (
167
+ await AbyssProbeCore.axios.get<IGetProbeDeliveryResponse>(
168
+ `${subscriptionsPath(applicationId)}/${deliverySubscriptionId}/delivery/${deliveryId}`,
169
+ { headers: authHeaderApplication(applicationApiKey) },
170
+ )
171
+ ).data;
172
+
173
+ /** `POST .../delivery/:deliveryId/replay` — only a delivered or failed one (409 otherwise). */
174
+ export const replayProbeDeliveryApplication = async (
175
+ applicationId: string,
176
+ deliverySubscriptionId: string,
177
+ deliveryId: string,
178
+ applicationApiKey?: string,
179
+ ): Promise<IReplayProbeDeliveryResponse> =>
180
+ (
181
+ await AbyssProbeCore.axios.post<IReplayProbeDeliveryResponse>(
182
+ `${subscriptionsPath(applicationId)}/${deliverySubscriptionId}/delivery/${deliveryId}/replay`,
183
+ undefined,
184
+ { headers: authHeaderApplication(applicationApiKey) },
185
+ )
186
+ ).data;
@@ -1,141 +1,128 @@
1
- // ──────────────────────────────────────────────
2
- // AbyssProbe APPLICATION webhook subscription API — CRUD over registered
3
- // `probe_webhooks` owned by a calling application (create / list / delete /
4
- // update, + paginated delivery history — same surface as the personal/org
5
- // client, see probe-webhook.api.ts's header comment). Mirrors
6
- // apps/api/src/api/controllers/probe-webhook-application.controller.ts
7
- // EXACTLY — the application counterpart of probe-webhook.api.ts, for
8
- // inter-service callers (abyss-storage, external integrations)
9
- // authenticating with a project API key instead of a personal/org session.
10
- //
11
- // Reuses every request/response TYPE verbatim (`ICreateProbeWebhookBody`,
12
- // `ICreateProbeWebhookResponse`, `IListProbeWebhookResponse`,
13
- // `IDeleteProbeWebhookResponse`, `IUpdateProbeWebhookBody`,
14
- // `IUpdateProbeWebhookResponse`, `IListProbeWebhookDeliveriesQuery`,
15
- // `IListProbeWebhookDeliveriesResponse`) — never redefined here. No scope
16
- // argument/query on any of these calls (unlike the personal/org client's
17
- // `IProbeWebhookScopeQuery`) — an application caller's scope is entirely its
18
- // `:applicationId` URL segment, resolved server-side by
19
- // ProbeApplicationMiddleware; there is nothing to select between.
20
- //
21
- // `authHeaderApplication` is kept LOCAL and unexported, same convention as
22
- // the personal/org client's own `authHeader` — it sends
23
- // `API_KEY_APPLICATION_HEADER` (`abyss-api-key-application`, imported from
24
- // this package's root) instead of the personal `abyss-api-key` header.
25
- // ──────────────────────────────────────────────
26
-
27
- import { AbyssProbeCore, API_KEY_APPLICATION_HEADER } from '..';
28
- import type {
29
- ICreateProbeWebhookBody,
30
- IUpdateProbeWebhookBody,
31
- IListProbeWebhookDeliveriesQuery,
32
- ICreateProbeWebhookResponse,
33
- IListProbeWebhookResponse,
34
- IDeleteProbeWebhookResponse,
35
- IUpdateProbeWebhookResponse,
36
- IListProbeWebhookDeliveriesResponse,
37
- } from '../types';
38
-
39
- function authHeaderApplication(applicationApiKey?: string): Record<string, string> {
40
- return applicationApiKey ? { [API_KEY_APPLICATION_HEADER]: applicationApiKey } : {};
41
- }
42
-
43
- /** Renames nothing (no `organizationId` ever travels on an application call) — drops undefined/null and ISO-stringifies `Date`s, mirroring probe-scan.application.api.ts's own local helper. */
44
- function serializeProbeQuery<T extends object>(query: T): Record<string, unknown> {
45
- const out: Record<string, unknown> = {};
46
- for (const [key, value] of Object.entries(query as Record<string, unknown>)) {
47
- if (value === undefined || value === null) continue;
48
- out[key] = value instanceof Date ? value.toISOString() : value;
49
- }
50
- return out;
51
- }
52
-
53
- /**
54
- * `POST /application/:applicationId/webhook` — register a callbackUrl owned
55
- * by the calling application. The response's `secret` field (HMAC signing
56
- * secret) is returned ONLY here — store it immediately, it cannot be
57
- * re-revealed later.
58
- */
59
- export const createProbeWebhookApplication = async (
60
- applicationId: string,
61
- body: ICreateProbeWebhookBody,
62
- applicationApiKey?: string,
63
- ): Promise<ICreateProbeWebhookResponse> =>
64
- (
65
- await AbyssProbeCore.axios.post<ICreateProbeWebhookResponse>(
66
- `application/${applicationId}/webhook`,
67
- body,
68
- { headers: authHeaderApplication(applicationApiKey) },
69
- )
70
- ).data;
71
-
72
- /**
73
- * `GET /application/:applicationId/webhook` — flat list of the calling
74
- * application's own webhooks (not paginated — bounded by
75
- * WEBHOOK_MAX_PER_SCOPE server-side).
76
- */
77
- export const listProbeWebhookApplication = async (
78
- applicationId: string,
79
- applicationApiKey?: string,
80
- ): Promise<IListProbeWebhookResponse> =>
81
- (
82
- await AbyssProbeCore.axios.get<IListProbeWebhookResponse>(
83
- `application/${applicationId}/webhook`,
84
- { headers: authHeaderApplication(applicationApiKey) },
85
- )
86
- ).data;
87
-
88
- /**
89
- * `DELETE /application/:applicationId/webhook/:webhookId` — cross-tenant
90
- * guarded server-side by a direct `applicationId` comparison (404, never
91
- * 403).
92
- */
93
- export const deleteProbeWebhookApplication = async (
94
- applicationId: string,
95
- webhookId: string,
96
- applicationApiKey?: string,
97
- ): Promise<IDeleteProbeWebhookResponse> =>
98
- (
99
- await AbyssProbeCore.axios.delete<IDeleteProbeWebhookResponse>(
100
- `application/${applicationId}/webhook/${webhookId}`,
101
- { headers: authHeaderApplication(applicationApiKey) },
102
- )
103
- ).data;
104
-
105
- /**
106
- * `PATCH /application/:applicationId/webhook/:webhookId` (WS5-5) — edit
107
- * `callbackUrl` and/or `active` on a webhook owned by the calling
108
- * application. Cross-tenant guarded server-side (404, never 403). The
109
- * response includes `secret` — re-displayable now, see {@link IProbeWebhook}.
110
- */
111
- export const updateProbeWebhookApplication = async (
112
- applicationId: string,
113
- webhookId: string,
114
- body: IUpdateProbeWebhookBody,
115
- applicationApiKey?: string,
116
- ): Promise<IUpdateProbeWebhookResponse> =>
117
- (
118
- await AbyssProbeCore.axios.patch<IUpdateProbeWebhookResponse>(
119
- `application/${applicationId}/webhook/${webhookId}`,
120
- body,
121
- { headers: authHeaderApplication(applicationApiKey) },
122
- )
123
- ).data;
124
-
125
- /**
126
- * `GET /application/:applicationId/webhook/:webhookId/deliveries` (WS5-6) —
127
- * paginated delivery-attempt history for one webhook owned by the calling
128
- * application. Cross-tenant guarded server-side (404, never 403).
129
- */
130
- export const listProbeWebhookDeliveriesApplication = async (
131
- applicationId: string,
132
- webhookId: string,
133
- query: Omit<IListProbeWebhookDeliveriesQuery, 'organizationId'> = {},
134
- applicationApiKey?: string,
135
- ): Promise<IListProbeWebhookDeliveriesResponse> =>
136
- (
137
- await AbyssProbeCore.axios.get<IListProbeWebhookDeliveriesResponse>(
138
- `application/${applicationId}/webhook/${webhookId}/deliveries`,
139
- { params: serializeProbeQuery(query), headers: authHeaderApplication(applicationApiKey) },
140
- )
141
- ).data;
1
+ // ──────────────────────────────────────────────
2
+ // INTERNAL (P5 #792): AbyssProbe application webhooks, CRUD over the `probe_webhooks` rows owned
3
+ // by a calling application (create / list / delete / update, + paginated delivery history).
4
+ // They serve AbyssStorage's antivirus subscription until its client subscriptions move to Flow
5
+ // (L2). A client's own subscriptions to the verdict of its scans are the Flow delivery
6
+ // subscriptions (probe-delivery-subscription.application.api.ts). Mirrors
7
+ // apps/api/src/api/controllers/probe-webhook-application.controller.ts EXACTLY.
8
+ //
9
+ // The scope is entirely the `:applicationId` URL segment, resolved server-side by
10
+ // ProbeApplicationMiddleware; there is nothing to select between. `authHeaderApplication` sends
11
+ // `API_KEY_APPLICATION_HEADER` (`abyss-api-key-application`, imported from this package's root).
12
+ // ──────────────────────────────────────────────
13
+
14
+ import { AbyssProbeCore, API_KEY_APPLICATION_HEADER } from '..';
15
+ import type {
16
+ ICreateProbeWebhookBody,
17
+ IUpdateProbeWebhookBody,
18
+ IListProbeWebhookDeliveriesQuery,
19
+ ICreateProbeWebhookResponse,
20
+ IListProbeWebhookResponse,
21
+ IDeleteProbeWebhookResponse,
22
+ IUpdateProbeWebhookResponse,
23
+ IListProbeWebhookDeliveriesResponse,
24
+ } from '../types';
25
+
26
+ function authHeaderApplication(applicationApiKey?: string): Record<string, string> {
27
+ return applicationApiKey ? { [API_KEY_APPLICATION_HEADER]: applicationApiKey } : {};
28
+ }
29
+
30
+ /** Renames nothing (no `organizationId` ever travels on an application call) — drops undefined/null and ISO-stringifies `Date`s, mirroring probe-scan.application.api.ts's own local helper. */
31
+ function serializeProbeQuery<T extends object>(query: T): Record<string, unknown> {
32
+ const out: Record<string, unknown> = {};
33
+ for (const [key, value] of Object.entries(query as Record<string, unknown>)) {
34
+ if (value === undefined || value === null) continue;
35
+ out[key] = value instanceof Date ? value.toISOString() : value;
36
+ }
37
+ return out;
38
+ }
39
+
40
+ /**
41
+ * `POST /application/:applicationId/webhook` — register a callbackUrl owned
42
+ * by the calling application. The response's `secret` field (HMAC signing
43
+ * secret) is returned ONLY here — store it immediately, it cannot be
44
+ * re-revealed later.
45
+ */
46
+ export const createProbeWebhookApplication = async (
47
+ applicationId: string,
48
+ body: ICreateProbeWebhookBody,
49
+ applicationApiKey?: string,
50
+ ): Promise<ICreateProbeWebhookResponse> =>
51
+ (
52
+ await AbyssProbeCore.axios.post<ICreateProbeWebhookResponse>(
53
+ `application/${applicationId}/webhook`,
54
+ body,
55
+ { headers: authHeaderApplication(applicationApiKey) },
56
+ )
57
+ ).data;
58
+
59
+ /**
60
+ * `GET /application/:applicationId/webhook` — flat list of the calling
61
+ * application's own webhooks (not paginated — bounded by
62
+ * WEBHOOK_MAX_PER_SCOPE server-side).
63
+ */
64
+ export const listProbeWebhookApplication = async (
65
+ applicationId: string,
66
+ applicationApiKey?: string,
67
+ ): Promise<IListProbeWebhookResponse> =>
68
+ (
69
+ await AbyssProbeCore.axios.get<IListProbeWebhookResponse>(
70
+ `application/${applicationId}/webhook`,
71
+ { headers: authHeaderApplication(applicationApiKey) },
72
+ )
73
+ ).data;
74
+
75
+ /**
76
+ * `DELETE /application/:applicationId/webhook/:webhookId` — cross-tenant
77
+ * guarded server-side by a direct `applicationId` comparison (404, never
78
+ * 403).
79
+ */
80
+ export const deleteProbeWebhookApplication = async (
81
+ applicationId: string,
82
+ webhookId: string,
83
+ applicationApiKey?: string,
84
+ ): Promise<IDeleteProbeWebhookResponse> =>
85
+ (
86
+ await AbyssProbeCore.axios.delete<IDeleteProbeWebhookResponse>(
87
+ `application/${applicationId}/webhook/${webhookId}`,
88
+ { headers: authHeaderApplication(applicationApiKey) },
89
+ )
90
+ ).data;
91
+
92
+ /**
93
+ * `PATCH /application/:applicationId/webhook/:webhookId` (WS5-5) — edit
94
+ * `callbackUrl` and/or `active` on a webhook owned by the calling
95
+ * application. Cross-tenant guarded server-side (404, never 403). The
96
+ * response includes `secret` — re-displayable now, see {@link IProbeWebhook}.
97
+ */
98
+ export const updateProbeWebhookApplication = async (
99
+ applicationId: string,
100
+ webhookId: string,
101
+ body: IUpdateProbeWebhookBody,
102
+ applicationApiKey?: string,
103
+ ): Promise<IUpdateProbeWebhookResponse> =>
104
+ (
105
+ await AbyssProbeCore.axios.patch<IUpdateProbeWebhookResponse>(
106
+ `application/${applicationId}/webhook/${webhookId}`,
107
+ body,
108
+ { headers: authHeaderApplication(applicationApiKey) },
109
+ )
110
+ ).data;
111
+
112
+ /**
113
+ * `GET /application/:applicationId/webhook/:webhookId/deliveries` (WS5-6) —
114
+ * paginated delivery-attempt history for one webhook owned by the calling
115
+ * application. Cross-tenant guarded server-side (404, never 403).
116
+ */
117
+ export const listProbeWebhookDeliveriesApplication = async (
118
+ applicationId: string,
119
+ webhookId: string,
120
+ query: IListProbeWebhookDeliveriesQuery = {},
121
+ applicationApiKey?: string,
122
+ ): Promise<IListProbeWebhookDeliveriesResponse> =>
123
+ (
124
+ await AbyssProbeCore.axios.get<IListProbeWebhookDeliveriesResponse>(
125
+ `application/${applicationId}/webhook/${webhookId}/deliveries`,
126
+ { params: serializeProbeQuery(query), headers: authHeaderApplication(applicationApiKey) },
127
+ )
128
+ ).data;