@shipfox/api-integration-jira 12.2.0 → 12.3.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 +8 -7
- package/.turbo/turbo-type$colon$emit.log +0 -1
- package/CHANGELOG.md +11 -0
- package/README.md +5 -1
- package/dist/api/client.d.ts +10 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +55 -11
- package/dist/api/client.js.map +1 -1
- package/dist/core/disconnect.d.ts +12 -0
- package/dist/core/disconnect.d.ts.map +1 -1
- package/dist/core/disconnect.js +45 -1
- package/dist/core/disconnect.js.map +1 -1
- package/dist/core/install.d.ts +1 -0
- package/dist/core/install.d.ts.map +1 -1
- package/dist/core/install.js +2 -1
- package/dist/core/install.js.map +1 -1
- package/dist/core/tokens.d.ts +2 -0
- package/dist/core/tokens.d.ts.map +1 -1
- package/dist/core/tokens.js +2 -2
- package/dist/core/tokens.js.map +1 -1
- package/dist/core/webhook-registration.d.ts +1 -0
- package/dist/core/webhook-registration.d.ts.map +1 -1
- package/dist/core/webhook-registration.js +3 -1
- package/dist/core/webhook-registration.js.map +1 -1
- package/dist/core/webhook-url.d.ts +3 -0
- package/dist/core/webhook-url.d.ts.map +1 -0
- package/dist/core/webhook-url.js +8 -0
- package/dist/core/webhook-url.js.map +1 -0
- package/dist/db/installations.d.ts +13 -0
- package/dist/db/installations.d.ts.map +1 -1
- package/dist/db/installations.js +18 -1
- package/dist/db/installations.js.map +1 -1
- package/dist/index.d.ts +17 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -7
- package/dist/index.js.map +1 -1
- package/dist/presentation/routes/webhooks.d.ts +2 -1
- package/dist/presentation/routes/webhooks.d.ts.map +1 -1
- package/dist/presentation/routes/webhooks.js +2 -1
- package/dist/presentation/routes/webhooks.js.map +1 -1
- package/dist/temporal/activities/index.d.ts +8 -1
- package/dist/temporal/activities/index.d.ts.map +1 -1
- package/dist/temporal/activities/index.js +8 -1
- package/dist/temporal/activities/index.js.map +1 -1
- package/dist/temporal/activities/renew-jira-webhooks.d.ts +26 -0
- package/dist/temporal/activities/renew-jira-webhooks.d.ts.map +1 -0
- package/dist/temporal/activities/renew-jira-webhooks.js +96 -0
- package/dist/temporal/activities/renew-jira-webhooks.js.map +1 -0
- package/dist/temporal/constants.d.ts +2 -0
- package/dist/temporal/constants.d.ts.map +1 -1
- package/dist/temporal/constants.js +2 -0
- package/dist/temporal/constants.js.map +1 -1
- package/dist/temporal/worker.d.ts +3 -0
- package/dist/temporal/worker.d.ts.map +1 -1
- package/dist/temporal/worker.js +5 -0
- package/dist/temporal/worker.js.map +1 -1
- package/dist/temporal/workflows/index.bundle.js +38 -2
- package/dist/temporal/workflows/index.d.ts +1 -0
- package/dist/temporal/workflows/index.d.ts.map +1 -1
- package/dist/temporal/workflows/index.js +1 -0
- package/dist/temporal/workflows/index.js.map +1 -1
- package/dist/temporal/workflows/renew-jira-webhooks-cron.d.ts +2 -0
- package/dist/temporal/workflows/renew-jira-webhooks-cron.d.ts.map +1 -0
- package/dist/temporal/workflows/renew-jira-webhooks-cron.js +18 -0
- package/dist/temporal/workflows/renew-jira-webhooks-cron.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/api/client.test.ts +81 -2
- package/src/api/client.ts +68 -7
- package/src/core/disconnect.test.ts +107 -1
- package/src/core/disconnect.ts +64 -1
- package/src/core/install.test.ts +23 -0
- package/src/core/install.ts +5 -2
- package/src/core/tokens-refresh.test.ts +39 -0
- package/src/core/tokens.ts +4 -2
- package/src/core/webhook-registration.test.ts +32 -0
- package/src/core/webhook-registration.ts +4 -1
- package/src/core/webhook-url.ts +9 -0
- package/src/db/installations.test.ts +82 -0
- package/src/db/installations.ts +54 -1
- package/src/index.test.ts +21 -1
- package/src/index.ts +21 -9
- package/src/presentation/routes/webhooks.ts +3 -1
- package/src/temporal/activities/index.ts +20 -3
- package/src/temporal/activities/renew-jira-webhooks.test.ts +215 -0
- package/src/temporal/activities/renew-jira-webhooks.ts +144 -0
- package/src/temporal/constants.ts +2 -0
- package/src/temporal/worker.ts +11 -0
- package/src/temporal/workflows/index.ts +1 -0
- package/src/temporal/workflows/renew-jira-webhooks-cron.ts +17 -0
- package/tsconfig.build.tsbuildinfo +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
$ shipfox-swc && shipfox-temporal-bundle dist/temporal/workflows/index.js
|
|
2
|
-
Successfully compiled:
|
|
3
|
-
2026-08-
|
|
2
|
+
Successfully compiled: 35 files with swc (436.44ms)
|
|
3
|
+
2026-08-07T08:46:34.151Z [INFO] asset workflow-bundle-0005de700a374d33c72a.js 3.17 MiB [emitted] [immutable] (name: main)
|
|
4
4
|
orphan modules 33.5 KiB [orphan] 21 modules
|
|
5
5
|
runtime modules 1.13 KiB 5 modules
|
|
6
6
|
modules by path ../../../../node_modules/.pnpm/ 970 KiB 196 modules
|
|
7
|
-
modules by path ./dist/temporal/workflows/
|
|
7
|
+
modules by path ./dist/temporal/workflows/ 2.23 KiB
|
|
8
8
|
./dist/temporal/workflows/index-autogenerated-entrypoint.cjs 941 bytes [built] [code generated]
|
|
9
|
-
./dist/temporal/workflows/index.js
|
|
9
|
+
./dist/temporal/workflows/index.js 143 bytes [built] [code generated]
|
|
10
10
|
./dist/temporal/workflows/refresh-jira-tokens-cron.js 666 bytes [built] [code generated]
|
|
11
|
+
./dist/temporal/workflows/renew-jira-webhooks-cron.js 537 bytes [built] [code generated]
|
|
11
12
|
optional modules 30 bytes [optional]
|
|
12
13
|
__temporal_custom_payload_converter (ignored) 15 bytes [optional] [built] [code generated]
|
|
13
14
|
__temporal_custom_failure_converter (ignored) 15 bytes [optional] [built] [code generated]
|
|
14
15
|
../../../shared/node/temporal/dist/workflow-error-interceptor.js 1.21 KiB [built] [code generated]
|
|
15
|
-
webpack 5.107.2 compiled successfully in
|
|
16
|
-
2026-08-
|
|
17
|
-
/home/runner/work/shipfox/shipfox/libs/api/integration/jira/dist/temporal/workflows/index.js:
|
|
16
|
+
webpack 5.107.2 compiled successfully in 7008 ms
|
|
17
|
+
2026-08-07T08:46:34.170Z [INFO] Workflow bundle created { size: '3.17MB' }
|
|
18
|
+
/home/runner/work/shipfox/shipfox/libs/api/integration/jira/dist/temporal/workflows/index.js: 3327328 bytes
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @shipfox/api-integration-jira
|
|
2
2
|
|
|
3
|
+
## 12.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- e0110fe: Adds Jira webhook renewal and disconnect deregistration support.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [e0110fe]
|
|
12
|
+
- @shipfox/api-integration-spi@1.1.0
|
|
13
|
+
|
|
3
14
|
## 12.2.0
|
|
4
15
|
|
|
5
16
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Jira OAuth, site links, token storage, webhooks, and token upkeep for Shipfox.
|
|
|
6
6
|
|
|
7
7
|
- **`createJiraIntegrationProvider`** builds Jira OAuth and webhook routes.
|
|
8
8
|
- **`createJiraTokenStore`** stores Jira access and refresh tokens.
|
|
9
|
-
- **`createJiraMaintenanceWorker`** runs
|
|
9
|
+
- **`createJiraMaintenanceWorker`** runs token refresh and webhook renewal workflows.
|
|
10
10
|
- **Installation functions** store Jira sites, webhooks, and token dates.
|
|
11
11
|
|
|
12
12
|
## Token upkeep
|
|
@@ -17,6 +17,10 @@ It checks active sites in small batches. It refreshes a token after 76 idle days
|
|
|
17
17
|
|
|
18
18
|
A rejected refresh token or timeout marks the link as `error`. The user must connect Jira again. A new OAuth flow clears this state.
|
|
19
19
|
|
|
20
|
+
The worker renews dynamic webhooks seven days before expiry. If Jira no longer recognizes the stored webhook ids, it registers a replacement.
|
|
21
|
+
|
|
22
|
+
Disconnecting a link or deleting its connection deregisters the stored webhooks at Jira. Deregistration is best effort and never blocks the removal.
|
|
23
|
+
|
|
20
24
|
## Installation and setup
|
|
21
25
|
|
|
22
26
|
```sh
|
package/dist/api/client.d.ts
CHANGED
|
@@ -55,6 +55,16 @@ export interface JiraApiClient {
|
|
|
55
55
|
cloudId: string;
|
|
56
56
|
url: string;
|
|
57
57
|
}): Promise<JiraDynamicWebhookRegistration>;
|
|
58
|
+
refreshDynamicWebhooks(input: {
|
|
59
|
+
accessToken: string;
|
|
60
|
+
cloudId: string;
|
|
61
|
+
webhookIds: number[];
|
|
62
|
+
}): Promise<Date | undefined>;
|
|
63
|
+
deleteDynamicWebhooks(input: {
|
|
64
|
+
accessToken: string;
|
|
65
|
+
cloudId: string;
|
|
66
|
+
webhookIds: number[];
|
|
67
|
+
}): Promise<void>;
|
|
58
68
|
deleteDynamicWebhook(input: {
|
|
59
69
|
accessToken: string;
|
|
60
70
|
cloudId: string;
|
package/dist/api/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AASA,eAAO,MAAM,2BAA2B,sIAAwB,CAAC;AACjE,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAE3C,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,SAAS,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,uBAAuB,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;AAE7D,MAAM,MAAM,uBAAuB,GAC/B,MAAM,GACN,MAAM,GACN,OAAO,GACP,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAC5B,SAAS,CAAC;AAEd,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,uBAAuB,CAAC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,GAAG,SAAS,CAAC;IAC5D,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;CACtE;AAED,MAAM,WAAW,aAAa;IAC5B,yBAAyB,CAAC,KAAK,EAAE;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC7E,kBAAkB,CAAC,KAAK,EAAE;QAAC,YAAY,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC9E,sBAAsB,CAAC,KAAK,EAAE;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAAC;IACxF,SAAS,CAAC,KAAK,EAAE;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAChF,sBAAsB,CAAC,KAAK,EAAE;QAC5B,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,GAAG,EAAE,MAAM,CAAC;KACb,GAAG,OAAO,CAAC,8BAA8B,CAAC,CAAC;IAC5C,oBAAoB,CAAC,KAAK,EAAE;QAC1B,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnB;AA0BD,wBAAgB,mBAAmB,IAAI,aAAa,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
|
package/dist/api/client.js
CHANGED
|
@@ -13,6 +13,17 @@ const JIRA_REFRESH_TOKEN_TERMINAL_ERROR_CODES = new Set([
|
|
|
13
13
|
'unauthorized_client'
|
|
14
14
|
]);
|
|
15
15
|
export function createJiraApiClient() {
|
|
16
|
+
const deleteDynamicWebhooks = async (input)=>{
|
|
17
|
+
await mapJiraError('delete-dynamic-webhooks', ()=>ky.delete(`${config.JIRA_API_BASE_URL}/ex/jira/${input.cloudId}/rest/api/3/webhook`, {
|
|
18
|
+
headers: {
|
|
19
|
+
authorization: `Bearer ${input.accessToken}`
|
|
20
|
+
},
|
|
21
|
+
json: {
|
|
22
|
+
webhookIds: input.webhookIds
|
|
23
|
+
},
|
|
24
|
+
timeout: JIRA_API_TIMEOUT_MS
|
|
25
|
+
}));
|
|
26
|
+
};
|
|
16
27
|
return {
|
|
17
28
|
async exchangeAuthorizationCode (input) {
|
|
18
29
|
const body = await mapJiraError('exchange-authorization-code', ()=>ky.post(`${config.JIRA_AUTH_BASE_URL}/oauth/token`, {
|
|
@@ -83,18 +94,35 @@ export function createJiraApiClient() {
|
|
|
83
94
|
}).json());
|
|
84
95
|
return parseDynamicWebhookRegistration(body);
|
|
85
96
|
},
|
|
97
|
+
async refreshDynamicWebhooks (input) {
|
|
98
|
+
const response = await mapJiraError('refresh-dynamic-webhooks', async ()=>{
|
|
99
|
+
try {
|
|
100
|
+
return await ky.put(`${config.JIRA_API_BASE_URL}/ex/jira/${input.cloudId}/rest/api/3/webhook/refresh`, {
|
|
101
|
+
headers: {
|
|
102
|
+
authorization: `Bearer ${input.accessToken}`
|
|
103
|
+
},
|
|
104
|
+
json: {
|
|
105
|
+
webhookIds: input.webhookIds
|
|
106
|
+
},
|
|
107
|
+
timeout: JIRA_API_TIMEOUT_MS
|
|
108
|
+
});
|
|
109
|
+
} catch (error) {
|
|
110
|
+
if (error instanceof HTTPError && (error.response.status === 400 || error.response.status === 404)) return undefined;
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
if (!response) return undefined;
|
|
115
|
+
return parseDynamicWebhookExpiration(await readJiraResponseBody(response));
|
|
116
|
+
},
|
|
117
|
+
deleteDynamicWebhooks,
|
|
86
118
|
async deleteDynamicWebhook (input) {
|
|
87
|
-
await
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
]
|
|
95
|
-
},
|
|
96
|
-
timeout: JIRA_API_TIMEOUT_MS
|
|
97
|
-
}));
|
|
119
|
+
await deleteDynamicWebhooks({
|
|
120
|
+
accessToken: input.accessToken,
|
|
121
|
+
cloudId: input.cloudId,
|
|
122
|
+
webhookIds: [
|
|
123
|
+
input.webhookId
|
|
124
|
+
]
|
|
125
|
+
});
|
|
98
126
|
}
|
|
99
127
|
};
|
|
100
128
|
}
|
|
@@ -187,6 +215,22 @@ function parseDynamicWebhookRegistration(body) {
|
|
|
187
215
|
webhookId: createdWebhookId
|
|
188
216
|
};
|
|
189
217
|
}
|
|
218
|
+
function parseDynamicWebhookExpiration(body) {
|
|
219
|
+
if (body === undefined || body === null) return undefined;
|
|
220
|
+
if (typeof body !== 'object' || Array.isArray(body)) {
|
|
221
|
+
throw malformed('Jira webhook refresh response was malformed');
|
|
222
|
+
}
|
|
223
|
+
const expirationDate = body.expirationDate;
|
|
224
|
+
if (expirationDate === undefined) return undefined;
|
|
225
|
+
if (typeof expirationDate !== 'string') {
|
|
226
|
+
throw malformed('Jira webhook refresh response included a malformed expiration date');
|
|
227
|
+
}
|
|
228
|
+
const parsed = new Date(expirationDate);
|
|
229
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
230
|
+
throw malformed('Jira webhook refresh response included a malformed expiration date');
|
|
231
|
+
}
|
|
232
|
+
return parsed;
|
|
233
|
+
}
|
|
190
234
|
function parseAuthorization(body) {
|
|
191
235
|
if (typeof body.access_token !== 'string' || body.access_token.length === 0) {
|
|
192
236
|
throw malformed('Jira authorization response did not include an access token');
|
package/dist/api/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/api/client.ts"],"sourcesContent":["import {jiraWebhookEventNames} from '@shipfox/api-integration-jira-dto';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport ky, {HTTPError, TimeoutError} from 'ky';\nimport {config} from '#config.js';\nimport {JiraIntegrationProviderError} from '#core/errors.js';\n\nconst JIRA_API_TIMEOUT_MS = 10_000;\nconst SCOPE_SEPARATOR_RE = /[,\\s]+/;\nconst TRAILING_SLASHES_RE = /\\/+$/;\nexport const JIRA_DYNAMIC_WEBHOOK_EVENTS = jiraWebhookEventNames;\nexport const JIRA_DYNAMIC_WEBHOOK_JQL = '';\n\nexport interface JiraAuthorization {\n accessToken: string;\n refreshToken?: string | undefined;\n expiresAt?: Date | undefined;\n scopes: string[];\n}\n\nexport interface JiraAccessibleResource {\n cloudId: string;\n name: string;\n url: string;\n scopes: string[];\n}\n\nexport interface JiraIdentity {\n accountId: string;\n}\n\nexport interface JiraDynamicWebhookRegistration {\n webhookId: number;\n}\n\nexport type JiraAgentToolHttpMethod = 'GET' | 'POST' | 'PUT';\n\nexport type JiraAgentToolQueryValue =\n | string\n | number\n | boolean\n | readonly (string | number)[]\n | undefined;\n\nexport interface JiraAgentToolRequest {\n accessToken: string;\n cloudId: string;\n method: JiraAgentToolHttpMethod;\n path: string;\n query?: Record<string, JiraAgentToolQueryValue> | undefined;\n body?: unknown;\n operation?: string | undefined;\n}\n\nexport interface JiraAgentToolResponse {\n status: number;\n body: unknown;\n}\n\nexport interface JiraAgentToolsClient {\n request(input: JiraAgentToolRequest): Promise<JiraAgentToolResponse>;\n}\n\nexport interface JiraApiClient {\n exchangeAuthorizationCode(input: {code: string}): Promise<JiraAuthorization>;\n refreshAccessToken(input: {refreshToken: string}): Promise<JiraAuthorization>;\n getAccessibleResources(input: {accessToken: string}): Promise<JiraAccessibleResource[]>;\n getMyself(input: {accessToken: string; cloudId: string}): Promise<JiraIdentity>;\n registerDynamicWebhook(input: {\n accessToken: string;\n cloudId: string;\n url: string;\n }): Promise<JiraDynamicWebhookRegistration>;\n deleteDynamicWebhook(input: {\n accessToken: string;\n cloudId: string;\n webhookId: number;\n }): Promise<void>;\n}\n\ninterface JiraTokenResponse {\n access_token?: unknown;\n refresh_token?: unknown;\n expires_in?: unknown;\n scope?: unknown;\n}\n\ninterface JiraResourceResponse {\n id?: unknown;\n name?: unknown;\n url?: unknown;\n scopes?: unknown;\n}\n\ninterface JiraMyselfResponse {\n accountId?: unknown;\n}\n\ninterface JiraDynamicWebhookRegistrationResponse {\n webhookRegistrationResult?: unknown;\n}\n\nconst JIRA_REFRESH_TOKEN_TERMINAL_ERROR_CODES = new Set(['invalid_grant', 'unauthorized_client']);\n\nexport function createJiraApiClient(): JiraApiClient {\n return {\n async exchangeAuthorizationCode(input) {\n const body = await mapJiraError('exchange-authorization-code', () =>\n ky\n .post(`${config.JIRA_AUTH_BASE_URL}/oauth/token`, {\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: config.JIRA_OAUTH_CLIENT_ID,\n client_secret: config.JIRA_OAUTH_CLIENT_SECRET,\n code: input.code,\n redirect_uri: config.JIRA_OAUTH_REDIRECT_URL,\n }),\n timeout: JIRA_API_TIMEOUT_MS,\n })\n .json<JiraTokenResponse>(),\n );\n return parseAuthorization(body);\n },\n\n async refreshAccessToken(input) {\n const body = await mapJiraError('refresh-access-token', () =>\n ky\n .post(`${config.JIRA_AUTH_BASE_URL}/oauth/token`, {\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: config.JIRA_OAUTH_CLIENT_ID,\n client_secret: config.JIRA_OAUTH_CLIENT_SECRET,\n refresh_token: input.refreshToken,\n }),\n timeout: JIRA_API_TIMEOUT_MS,\n })\n .json<JiraTokenResponse>(),\n );\n return parseAuthorization(body);\n },\n\n async getAccessibleResources(input) {\n const body = await mapJiraError('get-accessible-resources', () =>\n ky\n .get(`${config.JIRA_API_BASE_URL}/oauth/token/accessible-resources`, {\n headers: {authorization: `Bearer ${input.accessToken}`},\n timeout: JIRA_API_TIMEOUT_MS,\n })\n .json<unknown>(),\n );\n if (!Array.isArray(body)) {\n throw malformed('Jira accessible-resources response was not an array');\n }\n return body.map(parseAccessibleResource);\n },\n\n async getMyself(input) {\n const body = await mapJiraError('get-myself', () =>\n ky\n .get(`${config.JIRA_API_BASE_URL}/ex/jira/${input.cloudId}/rest/api/3/myself`, {\n headers: {authorization: `Bearer ${input.accessToken}`},\n timeout: JIRA_API_TIMEOUT_MS,\n })\n .json<JiraMyselfResponse>(),\n );\n if (typeof body.accountId !== 'string' || body.accountId.length === 0) {\n throw malformed('Jira identity response did not include an accountId');\n }\n return {accountId: body.accountId};\n },\n\n async registerDynamicWebhook(input) {\n const body = await mapJiraError('register-dynamic-webhook', () =>\n ky\n .post(`${config.JIRA_API_BASE_URL}/ex/jira/${input.cloudId}/rest/api/3/webhook`, {\n headers: {authorization: `Bearer ${input.accessToken}`},\n json: {\n url: input.url,\n webhooks: [\n {\n events: JIRA_DYNAMIC_WEBHOOK_EVENTS,\n jqlFilter: JIRA_DYNAMIC_WEBHOOK_JQL,\n },\n ],\n },\n timeout: JIRA_API_TIMEOUT_MS,\n })\n .json<JiraDynamicWebhookRegistrationResponse>(),\n );\n return parseDynamicWebhookRegistration(body);\n },\n\n async deleteDynamicWebhook(input) {\n await mapJiraError('delete-dynamic-webhook', () =>\n ky.delete(`${config.JIRA_API_BASE_URL}/ex/jira/${input.cloudId}/rest/api/3/webhook`, {\n headers: {authorization: `Bearer ${input.accessToken}`},\n json: {webhookIds: [input.webhookId]},\n timeout: JIRA_API_TIMEOUT_MS,\n }),\n );\n },\n };\n}\n\nexport function createJiraAgentToolsClient(): JiraAgentToolsClient {\n return {request: requestJiraRest};\n}\n\nasync function requestJiraRest(input: JiraAgentToolRequest): Promise<JiraAgentToolResponse> {\n return await mapJiraError(input.operation ?? 'agent-tool', async () => {\n try {\n const response = await ky(jiraRestUrl(input.cloudId, input.path), {\n method: input.method,\n headers: {authorization: `Bearer ${input.accessToken}`},\n ...(input.query === undefined ? {} : {searchParams: jiraQueryParams(input.query)}),\n ...(input.body === undefined ? {} : {json: input.body}),\n timeout: JIRA_API_TIMEOUT_MS,\n });\n return {status: response.status, body: await readJiraResponseBody(response)};\n } catch (error) {\n if (\n error instanceof HTTPError &&\n (error.response.status === 400 || error.response.status === 404)\n ) {\n return {\n status: error.response.status,\n body: await readJiraResponseBody(error.response),\n };\n }\n throw error;\n }\n });\n}\n\nfunction jiraRestUrl(cloudId: string, path: string): string {\n const baseUrl = config.JIRA_API_BASE_URL.replace(TRAILING_SLASHES_RE, '');\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n return `${baseUrl}/ex/jira/${encodeURIComponent(cloudId)}/rest/api/3${normalizedPath}`;\n}\n\nfunction jiraQueryParams(\n query: Record<string, JiraAgentToolQueryValue>,\n): URLSearchParams | undefined {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const item of value) params.append(key, String(item));\n continue;\n }\n params.set(key, String(value));\n }\n return params.size > 0 ? params : undefined;\n}\n\nasync function readJiraResponseBody(response: Response): Promise<unknown> {\n const text = await response.text();\n if (text.length === 0) return undefined;\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction parseDynamicWebhookRegistration(\n body: JiraDynamicWebhookRegistrationResponse,\n): JiraDynamicWebhookRegistration {\n if (\n !body ||\n typeof body !== 'object' ||\n !Array.isArray(body.webhookRegistrationResult) ||\n body.webhookRegistrationResult.length !== 1\n ) {\n throw malformed('Jira webhook registration response did not contain exactly one result');\n }\n\n const result = body.webhookRegistrationResult[0];\n if (!result || typeof result !== 'object') {\n throw malformed('Jira webhook registration result was malformed');\n }\n const {createdWebhookId, errors} = result as {\n createdWebhookId?: unknown;\n errors?: unknown;\n };\n if (errors !== undefined && !Array.isArray(errors)) {\n throw malformed('Jira webhook registration returned errors');\n }\n if (Array.isArray(errors) && errors.length > 0) {\n logger().warn(\n {\n operation: 'register-dynamic-webhook',\n providerErrors: errors\n .filter((error): error is string => typeof error === 'string')\n .slice(0, 5)\n .map((error) => error.slice(0, 500)),\n providerErrorCount: errors.length,\n },\n 'Jira dynamic webhook registration rejected',\n );\n throw malformed('Jira webhook registration returned errors');\n }\n if (\n typeof createdWebhookId !== 'number' ||\n !Number.isSafeInteger(createdWebhookId) ||\n createdWebhookId <= 0\n ) {\n throw malformed('Jira webhook registration did not return a created webhook id');\n }\n return {webhookId: createdWebhookId};\n}\n\nfunction parseAuthorization(body: JiraTokenResponse): JiraAuthorization {\n if (typeof body.access_token !== 'string' || body.access_token.length === 0) {\n throw malformed('Jira authorization response did not include an access token');\n }\n return {\n accessToken: body.access_token,\n refreshToken: typeof body.refresh_token === 'string' ? body.refresh_token : undefined,\n expiresAt: parseExpiresAt(body.expires_in),\n scopes: parseScopes(body.scope),\n };\n}\n\nfunction parseAccessibleResource(value: unknown): JiraAccessibleResource {\n if (!value || typeof value !== 'object') throw malformed('Jira site response was malformed');\n const {id, name, url, scopes} = value as JiraResourceResponse;\n if (\n typeof id !== 'string' ||\n id.length === 0 ||\n typeof name !== 'string' ||\n name.length === 0 ||\n typeof url !== 'string' ||\n url.length === 0 ||\n !Array.isArray(scopes) ||\n !scopes.every((scope) => typeof scope === 'string')\n ) {\n throw malformed('Jira site response did not include a valid cloud id, name, URL, and scopes');\n }\n return {cloudId: id, name, url, scopes};\n}\n\nfunction parseScopes(scope: unknown): string[] {\n if (typeof scope === 'string')\n return scope\n .split(SCOPE_SEPARATOR_RE)\n .map((value) => value.trim())\n .filter(Boolean);\n if (Array.isArray(scope) && scope.every((value) => typeof value === 'string')) return scope;\n if (scope === undefined) return [];\n throw malformed('Jira authorization response included malformed scopes');\n}\n\nfunction parseExpiresAt(expiresIn: unknown): Date | undefined {\n if (expiresIn === undefined) return undefined;\n if (typeof expiresIn !== 'number' || !Number.isFinite(expiresIn) || expiresIn <= 0) {\n throw malformed('Jira authorization response included a malformed expiry');\n }\n return new Date(Date.now() + expiresIn * 1000);\n}\n\nexport async function mapJiraError<T>(operation: string, request: () => Promise<T>): Promise<T> {\n try {\n return await request();\n } catch (error) {\n if (error instanceof JiraIntegrationProviderError) throw error;\n if (error instanceof HTTPError) {\n const {status, statusText, headers} = error.response;\n logger().warn({operation, status, statusText}, 'Jira API request rejected');\n if (status === 429) {\n throw new JiraIntegrationProviderError(\n 'rate-limited',\n 'Jira request was rate limited',\n retryAfterSeconds(headers),\n );\n }\n if (status >= 500)\n throw new JiraIntegrationProviderError('provider-unavailable', 'Jira request failed');\n if (status === 401 || status === 403) {\n throw new JiraIntegrationProviderError('access-denied', 'Jira request was rejected');\n }\n if (status === 400 && operation === 'refresh-access-token') {\n const errorCode = readJiraOAuthErrorCode(error.data);\n if (errorCode && JIRA_REFRESH_TOKEN_TERMINAL_ERROR_CODES.has(errorCode)) {\n throw new JiraIntegrationProviderError(\n 'access-denied',\n 'Jira refresh token was rejected; reconnect is required',\n );\n }\n }\n throw malformed('Jira request was rejected');\n }\n if (error instanceof TimeoutError) {\n logger().warn({operation}, 'Jira API request timed out');\n throw new JiraIntegrationProviderError('timeout', 'Jira request timed out');\n }\n logger().warn(\n {operation, errName: error instanceof Error ? error.name : typeof error},\n 'Jira API request failed',\n );\n throw new JiraIntegrationProviderError('provider-unavailable', 'Jira request failed');\n }\n}\n\nfunction malformed(message: string): JiraIntegrationProviderError {\n return new JiraIntegrationProviderError('malformed-provider-response', message);\n}\n\nfunction readJiraOAuthErrorCode(data: unknown): string | undefined {\n if (!data || typeof data !== 'object') return undefined;\n const errorCode = (data as {error?: unknown}).error;\n return typeof errorCode === 'string' ? errorCode : undefined;\n}\n\nfunction retryAfterSeconds(headers: Headers): number | undefined {\n const value = headers.get('retry-after');\n if (!value) return undefined;\n const parsed = Number.parseInt(value, 10);\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n"],"names":["jiraWebhookEventNames","logger","ky","HTTPError","TimeoutError","config","JiraIntegrationProviderError","JIRA_API_TIMEOUT_MS","SCOPE_SEPARATOR_RE","TRAILING_SLASHES_RE","JIRA_DYNAMIC_WEBHOOK_EVENTS","JIRA_DYNAMIC_WEBHOOK_JQL","JIRA_REFRESH_TOKEN_TERMINAL_ERROR_CODES","Set","createJiraApiClient","exchangeAuthorizationCode","input","body","mapJiraError","post","JIRA_AUTH_BASE_URL","URLSearchParams","grant_type","client_id","JIRA_OAUTH_CLIENT_ID","client_secret","JIRA_OAUTH_CLIENT_SECRET","code","redirect_uri","JIRA_OAUTH_REDIRECT_URL","timeout","json","parseAuthorization","refreshAccessToken","refresh_token","refreshToken","getAccessibleResources","get","JIRA_API_BASE_URL","headers","authorization","accessToken","Array","isArray","malformed","map","parseAccessibleResource","getMyself","cloudId","accountId","length","registerDynamicWebhook","url","webhooks","events","jqlFilter","parseDynamicWebhookRegistration","deleteDynamicWebhook","delete","webhookIds","webhookId","createJiraAgentToolsClient","request","requestJiraRest","operation","response","jiraRestUrl","path","method","query","undefined","searchParams","jiraQueryParams","status","readJiraResponseBody","error","baseUrl","replace","normalizedPath","startsWith","encodeURIComponent","params","key","value","Object","entries","item","append","String","set","size","text","JSON","parse","webhookRegistrationResult","result","createdWebhookId","errors","warn","providerErrors","filter","slice","providerErrorCount","Number","isSafeInteger","access_token","expiresAt","parseExpiresAt","expires_in","scopes","parseScopes","scope","id","name","every","split","trim","Boolean","expiresIn","isFinite","Date","now","statusText","retryAfterSeconds","errorCode","readJiraOAuthErrorCode","data","has","errName","Error","message","parsed","parseInt","isNaN"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/api/client.ts"],"sourcesContent":["import {jiraWebhookEventNames} from '@shipfox/api-integration-jira-dto';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport ky, {HTTPError, TimeoutError} from 'ky';\nimport {config} from '#config.js';\nimport {JiraIntegrationProviderError} from '#core/errors.js';\n\nconst JIRA_API_TIMEOUT_MS = 10_000;\nconst SCOPE_SEPARATOR_RE = /[,\\s]+/;\nconst TRAILING_SLASHES_RE = /\\/+$/;\nexport const JIRA_DYNAMIC_WEBHOOK_EVENTS = jiraWebhookEventNames;\nexport const JIRA_DYNAMIC_WEBHOOK_JQL = '';\n\nexport interface JiraAuthorization {\n accessToken: string;\n refreshToken?: string | undefined;\n expiresAt?: Date | undefined;\n scopes: string[];\n}\n\nexport interface JiraAccessibleResource {\n cloudId: string;\n name: string;\n url: string;\n scopes: string[];\n}\n\nexport interface JiraIdentity {\n accountId: string;\n}\n\nexport interface JiraDynamicWebhookRegistration {\n webhookId: number;\n}\n\nexport type JiraAgentToolHttpMethod = 'GET' | 'POST' | 'PUT';\n\nexport type JiraAgentToolQueryValue =\n | string\n | number\n | boolean\n | readonly (string | number)[]\n | undefined;\n\nexport interface JiraAgentToolRequest {\n accessToken: string;\n cloudId: string;\n method: JiraAgentToolHttpMethod;\n path: string;\n query?: Record<string, JiraAgentToolQueryValue> | undefined;\n body?: unknown;\n operation?: string | undefined;\n}\n\nexport interface JiraAgentToolResponse {\n status: number;\n body: unknown;\n}\n\nexport interface JiraAgentToolsClient {\n request(input: JiraAgentToolRequest): Promise<JiraAgentToolResponse>;\n}\n\nexport interface JiraApiClient {\n exchangeAuthorizationCode(input: {code: string}): Promise<JiraAuthorization>;\n refreshAccessToken(input: {refreshToken: string}): Promise<JiraAuthorization>;\n getAccessibleResources(input: {accessToken: string}): Promise<JiraAccessibleResource[]>;\n getMyself(input: {accessToken: string; cloudId: string}): Promise<JiraIdentity>;\n registerDynamicWebhook(input: {\n accessToken: string;\n cloudId: string;\n url: string;\n }): Promise<JiraDynamicWebhookRegistration>;\n refreshDynamicWebhooks(input: {\n accessToken: string;\n cloudId: string;\n webhookIds: number[];\n }): Promise<Date | undefined>;\n deleteDynamicWebhooks(input: {\n accessToken: string;\n cloudId: string;\n webhookIds: number[];\n }): Promise<void>;\n deleteDynamicWebhook(input: {\n accessToken: string;\n cloudId: string;\n webhookId: number;\n }): Promise<void>;\n}\n\ninterface JiraTokenResponse {\n access_token?: unknown;\n refresh_token?: unknown;\n expires_in?: unknown;\n scope?: unknown;\n}\n\ninterface JiraResourceResponse {\n id?: unknown;\n name?: unknown;\n url?: unknown;\n scopes?: unknown;\n}\n\ninterface JiraMyselfResponse {\n accountId?: unknown;\n}\n\ninterface JiraDynamicWebhookRegistrationResponse {\n webhookRegistrationResult?: unknown;\n}\n\nconst JIRA_REFRESH_TOKEN_TERMINAL_ERROR_CODES = new Set(['invalid_grant', 'unauthorized_client']);\n\nexport function createJiraApiClient(): JiraApiClient {\n const deleteDynamicWebhooks: JiraApiClient['deleteDynamicWebhooks'] = async (input) => {\n await mapJiraError('delete-dynamic-webhooks', () =>\n ky.delete(`${config.JIRA_API_BASE_URL}/ex/jira/${input.cloudId}/rest/api/3/webhook`, {\n headers: {authorization: `Bearer ${input.accessToken}`},\n json: {webhookIds: input.webhookIds},\n timeout: JIRA_API_TIMEOUT_MS,\n }),\n );\n };\n\n return {\n async exchangeAuthorizationCode(input) {\n const body = await mapJiraError('exchange-authorization-code', () =>\n ky\n .post(`${config.JIRA_AUTH_BASE_URL}/oauth/token`, {\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: config.JIRA_OAUTH_CLIENT_ID,\n client_secret: config.JIRA_OAUTH_CLIENT_SECRET,\n code: input.code,\n redirect_uri: config.JIRA_OAUTH_REDIRECT_URL,\n }),\n timeout: JIRA_API_TIMEOUT_MS,\n })\n .json<JiraTokenResponse>(),\n );\n return parseAuthorization(body);\n },\n\n async refreshAccessToken(input) {\n const body = await mapJiraError('refresh-access-token', () =>\n ky\n .post(`${config.JIRA_AUTH_BASE_URL}/oauth/token`, {\n body: new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: config.JIRA_OAUTH_CLIENT_ID,\n client_secret: config.JIRA_OAUTH_CLIENT_SECRET,\n refresh_token: input.refreshToken,\n }),\n timeout: JIRA_API_TIMEOUT_MS,\n })\n .json<JiraTokenResponse>(),\n );\n return parseAuthorization(body);\n },\n\n async getAccessibleResources(input) {\n const body = await mapJiraError('get-accessible-resources', () =>\n ky\n .get(`${config.JIRA_API_BASE_URL}/oauth/token/accessible-resources`, {\n headers: {authorization: `Bearer ${input.accessToken}`},\n timeout: JIRA_API_TIMEOUT_MS,\n })\n .json<unknown>(),\n );\n if (!Array.isArray(body)) {\n throw malformed('Jira accessible-resources response was not an array');\n }\n return body.map(parseAccessibleResource);\n },\n\n async getMyself(input) {\n const body = await mapJiraError('get-myself', () =>\n ky\n .get(`${config.JIRA_API_BASE_URL}/ex/jira/${input.cloudId}/rest/api/3/myself`, {\n headers: {authorization: `Bearer ${input.accessToken}`},\n timeout: JIRA_API_TIMEOUT_MS,\n })\n .json<JiraMyselfResponse>(),\n );\n if (typeof body.accountId !== 'string' || body.accountId.length === 0) {\n throw malformed('Jira identity response did not include an accountId');\n }\n return {accountId: body.accountId};\n },\n\n async registerDynamicWebhook(input) {\n const body = await mapJiraError('register-dynamic-webhook', () =>\n ky\n .post(`${config.JIRA_API_BASE_URL}/ex/jira/${input.cloudId}/rest/api/3/webhook`, {\n headers: {authorization: `Bearer ${input.accessToken}`},\n json: {\n url: input.url,\n webhooks: [\n {\n events: JIRA_DYNAMIC_WEBHOOK_EVENTS,\n jqlFilter: JIRA_DYNAMIC_WEBHOOK_JQL,\n },\n ],\n },\n timeout: JIRA_API_TIMEOUT_MS,\n })\n .json<JiraDynamicWebhookRegistrationResponse>(),\n );\n return parseDynamicWebhookRegistration(body);\n },\n\n async refreshDynamicWebhooks(input) {\n const response = await mapJiraError('refresh-dynamic-webhooks', async () => {\n try {\n return await ky.put(\n `${config.JIRA_API_BASE_URL}/ex/jira/${input.cloudId}/rest/api/3/webhook/refresh`,\n {\n headers: {authorization: `Bearer ${input.accessToken}`},\n json: {webhookIds: input.webhookIds},\n timeout: JIRA_API_TIMEOUT_MS,\n },\n );\n } catch (error) {\n if (\n error instanceof HTTPError &&\n (error.response.status === 400 || error.response.status === 404)\n )\n return undefined;\n throw error;\n }\n });\n if (!response) return undefined;\n return parseDynamicWebhookExpiration(await readJiraResponseBody(response));\n },\n\n deleteDynamicWebhooks,\n\n async deleteDynamicWebhook(input) {\n await deleteDynamicWebhooks({\n accessToken: input.accessToken,\n cloudId: input.cloudId,\n webhookIds: [input.webhookId],\n });\n },\n };\n}\n\nexport function createJiraAgentToolsClient(): JiraAgentToolsClient {\n return {request: requestJiraRest};\n}\n\nasync function requestJiraRest(input: JiraAgentToolRequest): Promise<JiraAgentToolResponse> {\n return await mapJiraError(input.operation ?? 'agent-tool', async () => {\n try {\n const response = await ky(jiraRestUrl(input.cloudId, input.path), {\n method: input.method,\n headers: {authorization: `Bearer ${input.accessToken}`},\n ...(input.query === undefined ? {} : {searchParams: jiraQueryParams(input.query)}),\n ...(input.body === undefined ? {} : {json: input.body}),\n timeout: JIRA_API_TIMEOUT_MS,\n });\n return {status: response.status, body: await readJiraResponseBody(response)};\n } catch (error) {\n if (\n error instanceof HTTPError &&\n (error.response.status === 400 || error.response.status === 404)\n ) {\n return {\n status: error.response.status,\n body: await readJiraResponseBody(error.response),\n };\n }\n throw error;\n }\n });\n}\n\nfunction jiraRestUrl(cloudId: string, path: string): string {\n const baseUrl = config.JIRA_API_BASE_URL.replace(TRAILING_SLASHES_RE, '');\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n return `${baseUrl}/ex/jira/${encodeURIComponent(cloudId)}/rest/api/3${normalizedPath}`;\n}\n\nfunction jiraQueryParams(\n query: Record<string, JiraAgentToolQueryValue>,\n): URLSearchParams | undefined {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n for (const item of value) params.append(key, String(item));\n continue;\n }\n params.set(key, String(value));\n }\n return params.size > 0 ? params : undefined;\n}\n\nasync function readJiraResponseBody(response: Response): Promise<unknown> {\n const text = await response.text();\n if (text.length === 0) return undefined;\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction parseDynamicWebhookRegistration(\n body: JiraDynamicWebhookRegistrationResponse,\n): JiraDynamicWebhookRegistration {\n if (\n !body ||\n typeof body !== 'object' ||\n !Array.isArray(body.webhookRegistrationResult) ||\n body.webhookRegistrationResult.length !== 1\n ) {\n throw malformed('Jira webhook registration response did not contain exactly one result');\n }\n\n const result = body.webhookRegistrationResult[0];\n if (!result || typeof result !== 'object') {\n throw malformed('Jira webhook registration result was malformed');\n }\n const {createdWebhookId, errors} = result as {\n createdWebhookId?: unknown;\n errors?: unknown;\n };\n if (errors !== undefined && !Array.isArray(errors)) {\n throw malformed('Jira webhook registration returned errors');\n }\n if (Array.isArray(errors) && errors.length > 0) {\n logger().warn(\n {\n operation: 'register-dynamic-webhook',\n providerErrors: errors\n .filter((error): error is string => typeof error === 'string')\n .slice(0, 5)\n .map((error) => error.slice(0, 500)),\n providerErrorCount: errors.length,\n },\n 'Jira dynamic webhook registration rejected',\n );\n throw malformed('Jira webhook registration returned errors');\n }\n if (\n typeof createdWebhookId !== 'number' ||\n !Number.isSafeInteger(createdWebhookId) ||\n createdWebhookId <= 0\n ) {\n throw malformed('Jira webhook registration did not return a created webhook id');\n }\n return {webhookId: createdWebhookId};\n}\n\nfunction parseDynamicWebhookExpiration(body: unknown): Date | undefined {\n if (body === undefined || body === null) return undefined;\n if (typeof body !== 'object' || Array.isArray(body)) {\n throw malformed('Jira webhook refresh response was malformed');\n }\n const expirationDate = (body as {expirationDate?: unknown}).expirationDate;\n if (expirationDate === undefined) return undefined;\n if (typeof expirationDate !== 'string') {\n throw malformed('Jira webhook refresh response included a malformed expiration date');\n }\n const parsed = new Date(expirationDate);\n if (Number.isNaN(parsed.getTime())) {\n throw malformed('Jira webhook refresh response included a malformed expiration date');\n }\n return parsed;\n}\n\nfunction parseAuthorization(body: JiraTokenResponse): JiraAuthorization {\n if (typeof body.access_token !== 'string' || body.access_token.length === 0) {\n throw malformed('Jira authorization response did not include an access token');\n }\n return {\n accessToken: body.access_token,\n refreshToken: typeof body.refresh_token === 'string' ? body.refresh_token : undefined,\n expiresAt: parseExpiresAt(body.expires_in),\n scopes: parseScopes(body.scope),\n };\n}\n\nfunction parseAccessibleResource(value: unknown): JiraAccessibleResource {\n if (!value || typeof value !== 'object') throw malformed('Jira site response was malformed');\n const {id, name, url, scopes} = value as JiraResourceResponse;\n if (\n typeof id !== 'string' ||\n id.length === 0 ||\n typeof name !== 'string' ||\n name.length === 0 ||\n typeof url !== 'string' ||\n url.length === 0 ||\n !Array.isArray(scopes) ||\n !scopes.every((scope) => typeof scope === 'string')\n ) {\n throw malformed('Jira site response did not include a valid cloud id, name, URL, and scopes');\n }\n return {cloudId: id, name, url, scopes};\n}\n\nfunction parseScopes(scope: unknown): string[] {\n if (typeof scope === 'string')\n return scope\n .split(SCOPE_SEPARATOR_RE)\n .map((value) => value.trim())\n .filter(Boolean);\n if (Array.isArray(scope) && scope.every((value) => typeof value === 'string')) return scope;\n if (scope === undefined) return [];\n throw malformed('Jira authorization response included malformed scopes');\n}\n\nfunction parseExpiresAt(expiresIn: unknown): Date | undefined {\n if (expiresIn === undefined) return undefined;\n if (typeof expiresIn !== 'number' || !Number.isFinite(expiresIn) || expiresIn <= 0) {\n throw malformed('Jira authorization response included a malformed expiry');\n }\n return new Date(Date.now() + expiresIn * 1000);\n}\n\nexport async function mapJiraError<T>(operation: string, request: () => Promise<T>): Promise<T> {\n try {\n return await request();\n } catch (error) {\n if (error instanceof JiraIntegrationProviderError) throw error;\n if (error instanceof HTTPError) {\n const {status, statusText, headers} = error.response;\n logger().warn({operation, status, statusText}, 'Jira API request rejected');\n if (status === 429) {\n throw new JiraIntegrationProviderError(\n 'rate-limited',\n 'Jira request was rate limited',\n retryAfterSeconds(headers),\n );\n }\n if (status >= 500)\n throw new JiraIntegrationProviderError('provider-unavailable', 'Jira request failed');\n if (status === 401 || status === 403) {\n throw new JiraIntegrationProviderError('access-denied', 'Jira request was rejected');\n }\n if (status === 400 && operation === 'refresh-access-token') {\n const errorCode = readJiraOAuthErrorCode(error.data);\n if (errorCode && JIRA_REFRESH_TOKEN_TERMINAL_ERROR_CODES.has(errorCode)) {\n throw new JiraIntegrationProviderError(\n 'access-denied',\n 'Jira refresh token was rejected; reconnect is required',\n );\n }\n }\n throw malformed('Jira request was rejected');\n }\n if (error instanceof TimeoutError) {\n logger().warn({operation}, 'Jira API request timed out');\n throw new JiraIntegrationProviderError('timeout', 'Jira request timed out');\n }\n logger().warn(\n {operation, errName: error instanceof Error ? error.name : typeof error},\n 'Jira API request failed',\n );\n throw new JiraIntegrationProviderError('provider-unavailable', 'Jira request failed');\n }\n}\n\nfunction malformed(message: string): JiraIntegrationProviderError {\n return new JiraIntegrationProviderError('malformed-provider-response', message);\n}\n\nfunction readJiraOAuthErrorCode(data: unknown): string | undefined {\n if (!data || typeof data !== 'object') return undefined;\n const errorCode = (data as {error?: unknown}).error;\n return typeof errorCode === 'string' ? errorCode : undefined;\n}\n\nfunction retryAfterSeconds(headers: Headers): number | undefined {\n const value = headers.get('retry-after');\n if (!value) return undefined;\n const parsed = Number.parseInt(value, 10);\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n"],"names":["jiraWebhookEventNames","logger","ky","HTTPError","TimeoutError","config","JiraIntegrationProviderError","JIRA_API_TIMEOUT_MS","SCOPE_SEPARATOR_RE","TRAILING_SLASHES_RE","JIRA_DYNAMIC_WEBHOOK_EVENTS","JIRA_DYNAMIC_WEBHOOK_JQL","JIRA_REFRESH_TOKEN_TERMINAL_ERROR_CODES","Set","createJiraApiClient","deleteDynamicWebhooks","input","mapJiraError","delete","JIRA_API_BASE_URL","cloudId","headers","authorization","accessToken","json","webhookIds","timeout","exchangeAuthorizationCode","body","post","JIRA_AUTH_BASE_URL","URLSearchParams","grant_type","client_id","JIRA_OAUTH_CLIENT_ID","client_secret","JIRA_OAUTH_CLIENT_SECRET","code","redirect_uri","JIRA_OAUTH_REDIRECT_URL","parseAuthorization","refreshAccessToken","refresh_token","refreshToken","getAccessibleResources","get","Array","isArray","malformed","map","parseAccessibleResource","getMyself","accountId","length","registerDynamicWebhook","url","webhooks","events","jqlFilter","parseDynamicWebhookRegistration","refreshDynamicWebhooks","response","put","error","status","undefined","parseDynamicWebhookExpiration","readJiraResponseBody","deleteDynamicWebhook","webhookId","createJiraAgentToolsClient","request","requestJiraRest","operation","jiraRestUrl","path","method","query","searchParams","jiraQueryParams","baseUrl","replace","normalizedPath","startsWith","encodeURIComponent","params","key","value","Object","entries","item","append","String","set","size","text","JSON","parse","webhookRegistrationResult","result","createdWebhookId","errors","warn","providerErrors","filter","slice","providerErrorCount","Number","isSafeInteger","expirationDate","parsed","Date","isNaN","getTime","access_token","expiresAt","parseExpiresAt","expires_in","scopes","parseScopes","scope","id","name","every","split","trim","Boolean","expiresIn","isFinite","now","statusText","retryAfterSeconds","errorCode","readJiraOAuthErrorCode","data","has","errName","Error","message","parseInt"],"mappings":"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"}
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
import type { JiraApiClient } from '#api/client.js';
|
|
2
|
+
import { type JiraInstallation } from '#db/installations.js';
|
|
3
|
+
import type { JiraTokenStore } from './tokens.js';
|
|
4
|
+
export interface DeregisterJiraWebhooksParams {
|
|
5
|
+
connectionId: string;
|
|
6
|
+
tokenStore: Pick<JiraTokenStore, 'getAccessToken'>;
|
|
7
|
+
jira: Pick<JiraApiClient, 'deleteDynamicWebhooks'>;
|
|
8
|
+
getInstallation?: (connectionId: string) => Promise<JiraInstallation | undefined>;
|
|
9
|
+
}
|
|
10
|
+
export declare function prepareJiraWebhookDeregistration(params: DeregisterJiraWebhooksParams): Promise<(() => Promise<void>) | undefined>;
|
|
11
|
+
export declare function deregisterJiraWebhooks(params: DeregisterJiraWebhooksParams): Promise<void>;
|
|
1
12
|
export interface DisconnectJiraInstallationParams<Tx = unknown> {
|
|
2
13
|
connectionId: string;
|
|
3
14
|
getConnection(connectionId: string): Promise<{
|
|
@@ -7,6 +18,7 @@ export interface DisconnectJiraInstallationParams<Tx = unknown> {
|
|
|
7
18
|
workspaceId: string;
|
|
8
19
|
namespace: string;
|
|
9
20
|
}): Promise<number>;
|
|
21
|
+
deregisterWebhooks?: (() => Promise<void>) | undefined;
|
|
10
22
|
transaction<T>(fn: (tx: Tx) => Promise<T>): Promise<T>;
|
|
11
23
|
deleteConnection(params: {
|
|
12
24
|
connectionId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"disconnect.d.ts","sourceRoot":"","sources":["../../src/core/disconnect.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"disconnect.d.ts","sourceRoot":"","sources":["../../src/core/disconnect.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,aAAa,CAAC;AAGhD,MAAM,WAAW,4BAA4B;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,IAAI,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;IACnD,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,uBAAuB,CAAC,CAAC;IACnD,eAAe,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAC;CACnF;AAED,wBAAsB,gCAAgC,CACpD,MAAM,EAAE,4BAA4B,GACnC,OAAO,CAAC,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC,CA+B5C;AAED,wBAAsB,sBAAsB,CAAC,MAAM,EAAE,4BAA4B,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhG;AAED,MAAM,WAAW,gCAAgC,CAAC,EAAE,GAAG,OAAO;IAC5D,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAC,GAAG,SAAS,CAAC,CAAC;IAChF,aAAa,CAAC,MAAM,EAAE;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjF,kBAAkB,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAC;IACvD,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACvD,gBAAgB,CAAC,MAAM,EAAE;QAAC,YAAY,EAAE,MAAM,CAAA;KAAC,EAAE,OAAO,EAAE;QAAC,EAAE,EAAE,EAAE,CAAA;KAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACvF;AAED,wBAAsB,0BAA0B,CAAC,EAAE,GAAG,OAAO,EAC3D,MAAM,EAAE,gCAAgC,CAAC,EAAE,CAAC,GAC3C,OAAO,CAAC,IAAI,CAAC,CAmBf"}
|
package/dist/core/disconnect.js
CHANGED
|
@@ -1,7 +1,51 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { logger } from '@shipfox/node-opentelemetry';
|
|
2
|
+
import { deleteJiraInstallationByConnectionId, getJiraInstallationByConnectionId } from '#db/installations.js';
|
|
2
3
|
import { jiraSecretsNamespace } from './tokens.js';
|
|
4
|
+
export async function prepareJiraWebhookDeregistration(params) {
|
|
5
|
+
try {
|
|
6
|
+
const getInstallation = params.getInstallation ?? getJiraInstallationByConnectionId;
|
|
7
|
+
const installation = await getInstallation(params.connectionId);
|
|
8
|
+
if (!installation || installation.webhookIds.length === 0) return undefined;
|
|
9
|
+
const accessToken = await params.tokenStore.getAccessToken({
|
|
10
|
+
connectionId: params.connectionId,
|
|
11
|
+
allowInactive: true
|
|
12
|
+
});
|
|
13
|
+
return async ()=>{
|
|
14
|
+
try {
|
|
15
|
+
await params.jira.deleteDynamicWebhooks({
|
|
16
|
+
accessToken,
|
|
17
|
+
cloudId: installation.cloudId,
|
|
18
|
+
webhookIds: installation.webhookIds
|
|
19
|
+
});
|
|
20
|
+
} catch (error) {
|
|
21
|
+
logger().warn({
|
|
22
|
+
err: error,
|
|
23
|
+
connectionId: params.connectionId
|
|
24
|
+
}, 'Jira webhook deregistration failed during disconnect');
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
} catch (error) {
|
|
28
|
+
logger().warn({
|
|
29
|
+
err: error,
|
|
30
|
+
connectionId: params.connectionId
|
|
31
|
+
}, 'Jira webhook deregistration preparation failed during disconnect');
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export async function deregisterJiraWebhooks(params) {
|
|
36
|
+
const cleanup = await prepareJiraWebhookDeregistration(params);
|
|
37
|
+
await cleanup?.();
|
|
38
|
+
}
|
|
3
39
|
export async function disconnectJiraInstallation(params) {
|
|
4
40
|
const connection = await params.getConnection(params.connectionId);
|
|
41
|
+
try {
|
|
42
|
+
await params.deregisterWebhooks?.();
|
|
43
|
+
} catch (error) {
|
|
44
|
+
logger().warn({
|
|
45
|
+
err: error,
|
|
46
|
+
connectionId: params.connectionId
|
|
47
|
+
}, 'Jira webhook deregistration hook failed during disconnect');
|
|
48
|
+
}
|
|
5
49
|
if (connection) await params.deleteSecrets({
|
|
6
50
|
workspaceId: connection.workspaceId,
|
|
7
51
|
namespace: jiraSecretsNamespace(params.connectionId)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/disconnect.ts"],"sourcesContent":["import {deleteJiraInstallationByConnectionId} from '#db/installations.js';\nimport {jiraSecretsNamespace} from './tokens.js';\n\nexport interface DisconnectJiraInstallationParams<Tx = unknown> {\n connectionId: string;\n getConnection(connectionId: string): Promise<{workspaceId: string} | undefined>;\n deleteSecrets(params: {workspaceId: string; namespace: string}): Promise<number>;\n transaction<T>(fn: (tx: Tx) => Promise<T>): Promise<T>;\n deleteConnection(params: {connectionId: string}, options: {tx: Tx}): Promise<boolean>;\n}\n\nexport async function disconnectJiraInstallation<Tx = unknown>(\n params: DisconnectJiraInstallationParams<Tx>,\n): Promise<void> {\n const connection = await params.getConnection(params.connectionId);\n if (connection)\n await params.deleteSecrets({\n workspaceId: connection.workspaceId,\n namespace: jiraSecretsNamespace(params.connectionId),\n });\n await params.transaction(async (tx) => {\n await deleteJiraInstallationByConnectionId(params.connectionId, {tx});\n await params.deleteConnection({connectionId: params.connectionId}, {tx});\n });\n}\n"],"names":["deleteJiraInstallationByConnectionId","jiraSecretsNamespace","
|
|
1
|
+
{"version":3,"sources":["../../src/core/disconnect.ts"],"sourcesContent":["import {logger} from '@shipfox/node-opentelemetry';\nimport type {JiraApiClient} from '#api/client.js';\nimport {\n deleteJiraInstallationByConnectionId,\n getJiraInstallationByConnectionId,\n type JiraInstallation,\n} from '#db/installations.js';\nimport type {JiraTokenStore} from './tokens.js';\nimport {jiraSecretsNamespace} from './tokens.js';\n\nexport interface DeregisterJiraWebhooksParams {\n connectionId: string;\n tokenStore: Pick<JiraTokenStore, 'getAccessToken'>;\n jira: Pick<JiraApiClient, 'deleteDynamicWebhooks'>;\n getInstallation?: (connectionId: string) => Promise<JiraInstallation | undefined>;\n}\n\nexport async function prepareJiraWebhookDeregistration(\n params: DeregisterJiraWebhooksParams,\n): Promise<(() => Promise<void>) | undefined> {\n try {\n const getInstallation = params.getInstallation ?? getJiraInstallationByConnectionId;\n const installation = await getInstallation(params.connectionId);\n if (!installation || installation.webhookIds.length === 0) return undefined;\n\n const accessToken = await params.tokenStore.getAccessToken({\n connectionId: params.connectionId,\n allowInactive: true,\n });\n return async () => {\n try {\n await params.jira.deleteDynamicWebhooks({\n accessToken,\n cloudId: installation.cloudId,\n webhookIds: installation.webhookIds,\n });\n } catch (error) {\n logger().warn(\n {err: error, connectionId: params.connectionId},\n 'Jira webhook deregistration failed during disconnect',\n );\n }\n };\n } catch (error) {\n logger().warn(\n {err: error, connectionId: params.connectionId},\n 'Jira webhook deregistration preparation failed during disconnect',\n );\n return undefined;\n }\n}\n\nexport async function deregisterJiraWebhooks(params: DeregisterJiraWebhooksParams): Promise<void> {\n const cleanup = await prepareJiraWebhookDeregistration(params);\n await cleanup?.();\n}\n\nexport interface DisconnectJiraInstallationParams<Tx = unknown> {\n connectionId: string;\n getConnection(connectionId: string): Promise<{workspaceId: string} | undefined>;\n deleteSecrets(params: {workspaceId: string; namespace: string}): Promise<number>;\n deregisterWebhooks?: (() => Promise<void>) | undefined;\n transaction<T>(fn: (tx: Tx) => Promise<T>): Promise<T>;\n deleteConnection(params: {connectionId: string}, options: {tx: Tx}): Promise<boolean>;\n}\n\nexport async function disconnectJiraInstallation<Tx = unknown>(\n params: DisconnectJiraInstallationParams<Tx>,\n): Promise<void> {\n const connection = await params.getConnection(params.connectionId);\n try {\n await params.deregisterWebhooks?.();\n } catch (error) {\n logger().warn(\n {err: error, connectionId: params.connectionId},\n 'Jira webhook deregistration hook failed during disconnect',\n );\n }\n if (connection)\n await params.deleteSecrets({\n workspaceId: connection.workspaceId,\n namespace: jiraSecretsNamespace(params.connectionId),\n });\n await params.transaction(async (tx) => {\n await deleteJiraInstallationByConnectionId(params.connectionId, {tx});\n await params.deleteConnection({connectionId: params.connectionId}, {tx});\n });\n}\n"],"names":["logger","deleteJiraInstallationByConnectionId","getJiraInstallationByConnectionId","jiraSecretsNamespace","prepareJiraWebhookDeregistration","params","getInstallation","installation","connectionId","webhookIds","length","undefined","accessToken","tokenStore","getAccessToken","allowInactive","jira","deleteDynamicWebhooks","cloudId","error","warn","err","deregisterJiraWebhooks","cleanup","disconnectJiraInstallation","connection","getConnection","deregisterWebhooks","deleteSecrets","workspaceId","namespace","transaction","tx","deleteConnection"],"mappings":"AAAA,SAAQA,MAAM,QAAO,8BAA8B;AAEnD,SACEC,oCAAoC,EACpCC,iCAAiC,QAE5B,uBAAuB;AAE9B,SAAQC,oBAAoB,QAAO,cAAc;AASjD,OAAO,eAAeC,iCACpBC,MAAoC;IAEpC,IAAI;QACF,MAAMC,kBAAkBD,OAAOC,eAAe,IAAIJ;QAClD,MAAMK,eAAe,MAAMD,gBAAgBD,OAAOG,YAAY;QAC9D,IAAI,CAACD,gBAAgBA,aAAaE,UAAU,CAACC,MAAM,KAAK,GAAG,OAAOC;QAElE,MAAMC,cAAc,MAAMP,OAAOQ,UAAU,CAACC,cAAc,CAAC;YACzDN,cAAcH,OAAOG,YAAY;YACjCO,eAAe;QACjB;QACA,OAAO;YACL,IAAI;gBACF,MAAMV,OAAOW,IAAI,CAACC,qBAAqB,CAAC;oBACtCL;oBACAM,SAASX,aAAaW,OAAO;oBAC7BT,YAAYF,aAAaE,UAAU;gBACrC;YACF,EAAE,OAAOU,OAAO;gBACdnB,SAASoB,IAAI,CACX;oBAACC,KAAKF;oBAAOX,cAAcH,OAAOG,YAAY;gBAAA,GAC9C;YAEJ;QACF;IACF,EAAE,OAAOW,OAAO;QACdnB,SAASoB,IAAI,CACX;YAACC,KAAKF;YAAOX,cAAcH,OAAOG,YAAY;QAAA,GAC9C;QAEF,OAAOG;IACT;AACF;AAEA,OAAO,eAAeW,uBAAuBjB,MAAoC;IAC/E,MAAMkB,UAAU,MAAMnB,iCAAiCC;IACvD,MAAMkB;AACR;AAWA,OAAO,eAAeC,2BACpBnB,MAA4C;IAE5C,MAAMoB,aAAa,MAAMpB,OAAOqB,aAAa,CAACrB,OAAOG,YAAY;IACjE,IAAI;QACF,MAAMH,OAAOsB,kBAAkB;IACjC,EAAE,OAAOR,OAAO;QACdnB,SAASoB,IAAI,CACX;YAACC,KAAKF;YAAOX,cAAcH,OAAOG,YAAY;QAAA,GAC9C;IAEJ;IACA,IAAIiB,YACF,MAAMpB,OAAOuB,aAAa,CAAC;QACzBC,aAAaJ,WAAWI,WAAW;QACnCC,WAAW3B,qBAAqBE,OAAOG,YAAY;IACrD;IACF,MAAMH,OAAO0B,WAAW,CAAC,OAAOC;QAC9B,MAAM/B,qCAAqCI,OAAOG,YAAY,EAAE;YAACwB;QAAE;QACnE,MAAM3B,OAAO4B,gBAAgB,CAAC;YAACzB,cAAcH,OAAOG,YAAY;QAAA,GAAG;YAACwB;QAAE;IACxE;AACF"}
|
package/dist/core/install.d.ts
CHANGED
|
@@ -54,6 +54,7 @@ export interface HandleJiraCallbackParams {
|
|
|
54
54
|
}): Promise<void>;
|
|
55
55
|
disconnectJiraInstallation(input: {
|
|
56
56
|
connectionId: string;
|
|
57
|
+
lockAlreadyHeld?: boolean | undefined;
|
|
57
58
|
}): Promise<void>;
|
|
58
59
|
}
|
|
59
60
|
export declare function handleJiraCallback(params: HandleJiraCallbackParams): Promise<IntegrationConnection<'jira'> | {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/core/install.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAC,qBAAqB,EAAC,MAAM,8BAA8B,CAAC;AAExE,OAAO,KAAK,EAAC,sBAAsB,EAAE,aAAa,EAAoB,MAAM,gBAAgB,CAAC;AAC7F,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,kBAAkB,CAAC;AAChE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAC,KAAK,oBAAoB,EAA2B,MAAM,sBAAsB,CAAC;AAazF,MAAM,WAAW,4BAA4B;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,aAAa,CAAC;IACpB,UAAU,EAAE,IAAI,CAAC,cAAc,EAAE,aAAa,CAAC,CAAC;IAChD,YAAY,EAAE,IAAI,CAAC,yBAAyB,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;IACzD,0BAA0B,CAAC,KAAK,EAAE;QAChC,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;KACnD,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrB,yBAAyB,CAAC,KAAK,EAAE;QAC/B,OAAO,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC;IACvD,uBAAuB,CACrB,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1C,mBAAmB,CAAC,KAAK,EAAE;QACzB,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;QAC5C,qBAAqB,EAAE,CAAC,KAAK,EAAE;YAAC,EAAE,CAAC,EAAE,OAAO,CAAA;SAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;QAChE,qBAAqB,EAAE,CAAC,KAAK,EAAE;YAAC,EAAE,CAAC,EAAE,OAAO,CAAA;SAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KACjE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClB,wBAAwB,CAAC,EAAE,oBAAoB,CAAC;IAChD,oBAAoB,CAAC,KAAK,EAAE;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,OAAO,CAAA;KAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjF,mBAAmB,CAAC,KAAK,EAAE;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,OAAO,CAAA;KAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChF,0BAA0B,CAAC,KAAK,EAAE;
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/core/install.ts"],"names":[],"mappings":"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"}
|
package/dist/core/install.js
CHANGED
|
@@ -136,7 +136,8 @@ function resolveJiraSite(params) {
|
|
|
136
136
|
async function bestEffortDisconnect(params, connectionId) {
|
|
137
137
|
try {
|
|
138
138
|
await params.disconnectJiraInstallation({
|
|
139
|
-
connectionId
|
|
139
|
+
connectionId,
|
|
140
|
+
lockAlreadyHeld: true
|
|
140
141
|
});
|
|
141
142
|
} catch (error) {
|
|
142
143
|
logger().warn({
|
package/dist/core/install.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/install.ts"],"sourcesContent":["import type {UserContextMembership} from '@shipfox/api-auth-context';\nimport type {IntegrationConnection} from '@shipfox/api-integration-spi';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport type {JiraAccessibleResource, JiraApiClient, JiraAuthorization} from '#api/client.js';\nimport type {JiraPendingSelectionStore} from '#core/pending.js';\nimport type {JiraTokenStore} from '#core/tokens.js';\nimport {type JiraInstallationLock, withJiraInstallationLock} from '#db/installations.js';\nimport {\n JiraInstallationAlreadyLinkedError,\n JiraInstallStateActorMismatchError,\n JiraIntegrationProviderError,\n JiraOAuthCallbackError,\n JiraOfflineAccessNotGrantedError,\n JiraPendingSelectionNotFoundError,\n JiraSiteSelectionMismatchError,\n} from './errors.js';\nimport {assertJiraAuthorizationScopes} from './scopes.js';\nimport {verifyJiraInstallState} from './state.js';\n\nexport interface ConnectJiraInstallationInput {\n workspaceId: string;\n cloudId: string;\n siteUrl: string;\n siteName: string;\n authorizingAccountId: string;\n scopes: string[];\n tokenExpiresAt: Date | null;\n displayName: string;\n}\n\nexport interface HandleJiraCallbackParams {\n jira: JiraApiClient;\n tokenStore: Pick<JiraTokenStore, 'storeTokens'>;\n pendingStore: Pick<JiraPendingSelectionStore, 'save' | 'load' | 'clear'>;\n code: string;\n state: string;\n sessionUserId: string;\n sessionMemberships: ReadonlyArray<UserContextMembership>;\n requireWorkspaceMembership(input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<UserContextMembership>;\n }): Promise<unknown>;\n getExistingJiraConnection(input: {\n cloudId: string;\n }): Promise<IntegrationConnection<'jira'> | undefined>;\n connectJiraInstallation(\n input: ConnectJiraInstallationInput,\n ): Promise<IntegrationConnection<'jira'>>;\n registerJiraWebhook(input: {\n connectionId: string;\n cloudId: string;\n accessToken: string;\n withRegistrationLock?: JiraInstallationLock;\n onRegistrationSuccess: (input: {tx?: unknown}) => Promise<void>;\n onRegistrationFailure: (input: {tx?: unknown}) => Promise<void>;\n }): Promise<void>;\n withJiraInstallationLock?: JiraInstallationLock;\n markConnectionActive(input: {connectionId: string; tx?: unknown}): Promise<void>;\n markConnectionError(input: {connectionId: string; tx?: unknown}): Promise<void>;\n disconnectJiraInstallation(input: {connectionId: string}): Promise<void>;\n}\n\nexport async function handleJiraCallback(\n params: HandleJiraCallbackParams,\n): Promise<IntegrationConnection<'jira'> | {sites: JiraAccessibleResource[]}> {\n const claims = await verifyClaims(params, params.state);\n const authorization = await params.jira.exchangeAuthorizationCode({code: params.code});\n if (!authorization.refreshToken) throw new JiraOfflineAccessNotGrantedError();\n const sites = await params.jira.getAccessibleResources({accessToken: authorization.accessToken});\n if (sites.length === 0)\n throw new JiraIntegrationProviderError(\n 'access-denied',\n 'Jira authorization did not grant access to a site',\n );\n if (sites.length > 1) {\n await params.pendingStore.save({\n workspaceId: claims.workspaceId,\n state: params.state,\n authorization,\n sites,\n });\n return {sites};\n }\n const site = sites[0];\n if (!site) {\n throw new JiraIntegrationProviderError(\n 'malformed-provider-response',\n 'Jira accessible-resources response did not contain a selected site',\n );\n }\n assertJiraAuthorizationScopes(site.scopes);\n return resolveJiraSite({...params, authorization, site, claims});\n}\n\nexport async function handleJiraSiteSelection(\n params: Omit<HandleJiraCallbackParams, 'code'> & {cloudId: string},\n): Promise<IntegrationConnection<'jira'>> {\n const claims = await verifyClaims(params, params.state);\n const pending = await params.pendingStore.load({\n workspaceId: claims.workspaceId,\n state: params.state,\n });\n if (!pending) throw new JiraPendingSelectionNotFoundError();\n const site = pending.sites.find((candidate) => candidate.cloudId === params.cloudId);\n if (!site) throw new JiraSiteSelectionMismatchError(params.cloudId);\n assertJiraAuthorizationScopes(site.scopes);\n const connection = await resolveJiraSite({\n ...params,\n authorization: pending.authorization,\n site,\n claims,\n });\n await params.pendingStore.clear({workspaceId: claims.workspaceId, state: params.state});\n return connection;\n}\n\nexport async function handleJiraOAuthCallbackError(params: {\n state: string;\n error: string;\n errorDescription?: string | undefined;\n sessionUserId: string;\n sessionMemberships: ReadonlyArray<UserContextMembership>;\n requireWorkspaceMembership(input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<UserContextMembership>;\n }): Promise<unknown>;\n}): Promise<never> {\n await verifyClaims(params, params.state);\n throw new JiraOAuthCallbackError(params.error, params.errorDescription);\n}\n\nasync function verifyClaims(\n params: Pick<\n HandleJiraCallbackParams,\n 'sessionUserId' | 'sessionMemberships' | 'requireWorkspaceMembership'\n >,\n state: string,\n) {\n const claims = verifyJiraInstallState(state);\n if (claims.userId !== params.sessionUserId) throw new JiraInstallStateActorMismatchError();\n await params.requireWorkspaceMembership({\n workspaceId: claims.workspaceId,\n userId: claims.userId,\n memberships: params.sessionMemberships,\n });\n return claims;\n}\n\nfunction resolveJiraSite(\n params: Omit<HandleJiraCallbackParams, 'code'> & {\n authorization: JiraAuthorization;\n site: JiraAccessibleResource;\n claims: {workspaceId: string; userId: string};\n },\n): Promise<IntegrationConnection<'jira'>> {\n const withInstallationLock = params.withJiraInstallationLock ?? withJiraInstallationLock;\n return withInstallationLock(params.site.cloudId, async () => {\n const existing = await params.getExistingJiraConnection({cloudId: params.site.cloudId});\n if (existing && existing.workspaceId !== params.claims.workspaceId)\n throw new JiraInstallationAlreadyLinkedError(params.site.cloudId);\n const identity = await params.jira.getMyself({\n accessToken: params.authorization.accessToken,\n cloudId: params.site.cloudId,\n });\n const installationInput = {\n workspaceId: params.claims.workspaceId,\n cloudId: params.site.cloudId,\n siteUrl: params.site.url,\n siteName: params.site.name,\n authorizingAccountId: identity.accountId,\n scopes: params.site.scopes,\n tokenExpiresAt: params.authorization.expiresAt ?? null,\n displayName: `Jira ${params.site.name}`,\n };\n let connection = existing;\n try {\n if (!connection) connection = await params.connectJiraInstallation(installationInput);\n await params.tokenStore.storeTokens({\n connectionId: connection.id,\n accessToken: params.authorization.accessToken,\n refreshToken: params.authorization.refreshToken,\n editedBy: params.claims.userId,\n });\n if (existing) connection = await params.connectJiraInstallation(installationInput);\n } catch (error) {\n if (existing) {\n const markedError = await bestEffortMarkConnectionError(params, existing.id);\n if (!markedError) await bestEffortMarkConnectionError(params, existing.id);\n } else if (connection) {\n await bestEffortDisconnect(params, connection.id);\n }\n throw error;\n }\n\n let registrationFailureHandled = false;\n try {\n await params.registerJiraWebhook({\n connectionId: connection.id,\n cloudId: params.site.cloudId,\n accessToken: params.authorization.accessToken,\n withRegistrationLock: async (_lockKey, fn) => fn(),\n onRegistrationSuccess: async ({tx}) => {\n await params.markConnectionActive(connectionLifecycleInput(connection.id, tx));\n },\n onRegistrationFailure: async ({tx}) => {\n registrationFailureHandled = await bestEffortMarkConnectionError(\n params,\n connection.id,\n tx,\n );\n },\n });\n } catch (error) {\n if (!registrationFailureHandled) await bestEffortMarkConnectionError(params, connection.id);\n throw error;\n }\n\n return connection;\n });\n}\n\nasync function bestEffortDisconnect(\n params: Pick<HandleJiraCallbackParams, 'disconnectJiraInstallation'>,\n connectionId: string,\n): Promise<void> {\n try {\n await params.disconnectJiraInstallation({connectionId});\n } catch (error) {\n logger().warn(\n {err: error, connectionId},\n 'Jira connect compensation failed after token storage rejection',\n );\n }\n}\n\nasync function bestEffortMarkConnectionError(\n params: Pick<HandleJiraCallbackParams, 'markConnectionError'>,\n connectionId: string,\n tx?: unknown,\n): Promise<boolean> {\n try {\n await params.markConnectionError(connectionLifecycleInput(connectionId, tx));\n return true;\n } catch (error) {\n logger().warn(\n {err: error, connectionId},\n 'Jira connection error-state update failed after webhook registration rejection',\n );\n return false;\n }\n}\n\nfunction connectionLifecycleInput(connectionId: string, tx?: unknown) {\n return tx === undefined ? {connectionId} : {connectionId, tx};\n}\n"],"names":["logger","withJiraInstallationLock","JiraInstallationAlreadyLinkedError","JiraInstallStateActorMismatchError","JiraIntegrationProviderError","JiraOAuthCallbackError","JiraOfflineAccessNotGrantedError","JiraPendingSelectionNotFoundError","JiraSiteSelectionMismatchError","assertJiraAuthorizationScopes","verifyJiraInstallState","handleJiraCallback","params","claims","verifyClaims","state","authorization","jira","exchangeAuthorizationCode","code","refreshToken","sites","getAccessibleResources","accessToken","length","pendingStore","save","workspaceId","site","scopes","resolveJiraSite","handleJiraSiteSelection","pending","load","find","candidate","cloudId","connection","clear","handleJiraOAuthCallbackError","error","errorDescription","userId","sessionUserId","requireWorkspaceMembership","memberships","sessionMemberships","withInstallationLock","existing","getExistingJiraConnection","identity","getMyself","installationInput","siteUrl","url","siteName","name","authorizingAccountId","accountId","tokenExpiresAt","expiresAt","displayName","connectJiraInstallation","tokenStore","storeTokens","connectionId","id","editedBy","markedError","bestEffortMarkConnectionError","bestEffortDisconnect","registrationFailureHandled","registerJiraWebhook","withRegistrationLock","_lockKey","fn","onRegistrationSuccess","tx","markConnectionActive","connectionLifecycleInput","onRegistrationFailure","disconnectJiraInstallation","warn","err","markConnectionError","undefined"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/core/install.ts"],"sourcesContent":["import type {UserContextMembership} from '@shipfox/api-auth-context';\nimport type {IntegrationConnection} from '@shipfox/api-integration-spi';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport type {JiraAccessibleResource, JiraApiClient, JiraAuthorization} from '#api/client.js';\nimport type {JiraPendingSelectionStore} from '#core/pending.js';\nimport type {JiraTokenStore} from '#core/tokens.js';\nimport {type JiraInstallationLock, withJiraInstallationLock} from '#db/installations.js';\nimport {\n JiraInstallationAlreadyLinkedError,\n JiraInstallStateActorMismatchError,\n JiraIntegrationProviderError,\n JiraOAuthCallbackError,\n JiraOfflineAccessNotGrantedError,\n JiraPendingSelectionNotFoundError,\n JiraSiteSelectionMismatchError,\n} from './errors.js';\nimport {assertJiraAuthorizationScopes} from './scopes.js';\nimport {verifyJiraInstallState} from './state.js';\n\nexport interface ConnectJiraInstallationInput {\n workspaceId: string;\n cloudId: string;\n siteUrl: string;\n siteName: string;\n authorizingAccountId: string;\n scopes: string[];\n tokenExpiresAt: Date | null;\n displayName: string;\n}\n\nexport interface HandleJiraCallbackParams {\n jira: JiraApiClient;\n tokenStore: Pick<JiraTokenStore, 'storeTokens'>;\n pendingStore: Pick<JiraPendingSelectionStore, 'save' | 'load' | 'clear'>;\n code: string;\n state: string;\n sessionUserId: string;\n sessionMemberships: ReadonlyArray<UserContextMembership>;\n requireWorkspaceMembership(input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<UserContextMembership>;\n }): Promise<unknown>;\n getExistingJiraConnection(input: {\n cloudId: string;\n }): Promise<IntegrationConnection<'jira'> | undefined>;\n connectJiraInstallation(\n input: ConnectJiraInstallationInput,\n ): Promise<IntegrationConnection<'jira'>>;\n registerJiraWebhook(input: {\n connectionId: string;\n cloudId: string;\n accessToken: string;\n withRegistrationLock?: JiraInstallationLock;\n onRegistrationSuccess: (input: {tx?: unknown}) => Promise<void>;\n onRegistrationFailure: (input: {tx?: unknown}) => Promise<void>;\n }): Promise<void>;\n withJiraInstallationLock?: JiraInstallationLock;\n markConnectionActive(input: {connectionId: string; tx?: unknown}): Promise<void>;\n markConnectionError(input: {connectionId: string; tx?: unknown}): Promise<void>;\n disconnectJiraInstallation(input: {\n connectionId: string;\n lockAlreadyHeld?: boolean | undefined;\n }): Promise<void>;\n}\n\nexport async function handleJiraCallback(\n params: HandleJiraCallbackParams,\n): Promise<IntegrationConnection<'jira'> | {sites: JiraAccessibleResource[]}> {\n const claims = await verifyClaims(params, params.state);\n const authorization = await params.jira.exchangeAuthorizationCode({code: params.code});\n if (!authorization.refreshToken) throw new JiraOfflineAccessNotGrantedError();\n const sites = await params.jira.getAccessibleResources({accessToken: authorization.accessToken});\n if (sites.length === 0)\n throw new JiraIntegrationProviderError(\n 'access-denied',\n 'Jira authorization did not grant access to a site',\n );\n if (sites.length > 1) {\n await params.pendingStore.save({\n workspaceId: claims.workspaceId,\n state: params.state,\n authorization,\n sites,\n });\n return {sites};\n }\n const site = sites[0];\n if (!site) {\n throw new JiraIntegrationProviderError(\n 'malformed-provider-response',\n 'Jira accessible-resources response did not contain a selected site',\n );\n }\n assertJiraAuthorizationScopes(site.scopes);\n return resolveJiraSite({...params, authorization, site, claims});\n}\n\nexport async function handleJiraSiteSelection(\n params: Omit<HandleJiraCallbackParams, 'code'> & {cloudId: string},\n): Promise<IntegrationConnection<'jira'>> {\n const claims = await verifyClaims(params, params.state);\n const pending = await params.pendingStore.load({\n workspaceId: claims.workspaceId,\n state: params.state,\n });\n if (!pending) throw new JiraPendingSelectionNotFoundError();\n const site = pending.sites.find((candidate) => candidate.cloudId === params.cloudId);\n if (!site) throw new JiraSiteSelectionMismatchError(params.cloudId);\n assertJiraAuthorizationScopes(site.scopes);\n const connection = await resolveJiraSite({\n ...params,\n authorization: pending.authorization,\n site,\n claims,\n });\n await params.pendingStore.clear({workspaceId: claims.workspaceId, state: params.state});\n return connection;\n}\n\nexport async function handleJiraOAuthCallbackError(params: {\n state: string;\n error: string;\n errorDescription?: string | undefined;\n sessionUserId: string;\n sessionMemberships: ReadonlyArray<UserContextMembership>;\n requireWorkspaceMembership(input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<UserContextMembership>;\n }): Promise<unknown>;\n}): Promise<never> {\n await verifyClaims(params, params.state);\n throw new JiraOAuthCallbackError(params.error, params.errorDescription);\n}\n\nasync function verifyClaims(\n params: Pick<\n HandleJiraCallbackParams,\n 'sessionUserId' | 'sessionMemberships' | 'requireWorkspaceMembership'\n >,\n state: string,\n) {\n const claims = verifyJiraInstallState(state);\n if (claims.userId !== params.sessionUserId) throw new JiraInstallStateActorMismatchError();\n await params.requireWorkspaceMembership({\n workspaceId: claims.workspaceId,\n userId: claims.userId,\n memberships: params.sessionMemberships,\n });\n return claims;\n}\n\nfunction resolveJiraSite(\n params: Omit<HandleJiraCallbackParams, 'code'> & {\n authorization: JiraAuthorization;\n site: JiraAccessibleResource;\n claims: {workspaceId: string; userId: string};\n },\n): Promise<IntegrationConnection<'jira'>> {\n const withInstallationLock = params.withJiraInstallationLock ?? withJiraInstallationLock;\n return withInstallationLock(params.site.cloudId, async () => {\n const existing = await params.getExistingJiraConnection({cloudId: params.site.cloudId});\n if (existing && existing.workspaceId !== params.claims.workspaceId)\n throw new JiraInstallationAlreadyLinkedError(params.site.cloudId);\n const identity = await params.jira.getMyself({\n accessToken: params.authorization.accessToken,\n cloudId: params.site.cloudId,\n });\n const installationInput = {\n workspaceId: params.claims.workspaceId,\n cloudId: params.site.cloudId,\n siteUrl: params.site.url,\n siteName: params.site.name,\n authorizingAccountId: identity.accountId,\n scopes: params.site.scopes,\n tokenExpiresAt: params.authorization.expiresAt ?? null,\n displayName: `Jira ${params.site.name}`,\n };\n let connection = existing;\n try {\n if (!connection) connection = await params.connectJiraInstallation(installationInput);\n await params.tokenStore.storeTokens({\n connectionId: connection.id,\n accessToken: params.authorization.accessToken,\n refreshToken: params.authorization.refreshToken,\n editedBy: params.claims.userId,\n });\n if (existing) connection = await params.connectJiraInstallation(installationInput);\n } catch (error) {\n if (existing) {\n const markedError = await bestEffortMarkConnectionError(params, existing.id);\n if (!markedError) await bestEffortMarkConnectionError(params, existing.id);\n } else if (connection) {\n await bestEffortDisconnect(params, connection.id);\n }\n throw error;\n }\n\n let registrationFailureHandled = false;\n try {\n await params.registerJiraWebhook({\n connectionId: connection.id,\n cloudId: params.site.cloudId,\n accessToken: params.authorization.accessToken,\n withRegistrationLock: async (_lockKey, fn) => fn(),\n onRegistrationSuccess: async ({tx}) => {\n await params.markConnectionActive(connectionLifecycleInput(connection.id, tx));\n },\n onRegistrationFailure: async ({tx}) => {\n registrationFailureHandled = await bestEffortMarkConnectionError(\n params,\n connection.id,\n tx,\n );\n },\n });\n } catch (error) {\n if (!registrationFailureHandled) await bestEffortMarkConnectionError(params, connection.id);\n throw error;\n }\n\n return connection;\n });\n}\n\nasync function bestEffortDisconnect(\n params: Pick<HandleJiraCallbackParams, 'disconnectJiraInstallation'>,\n connectionId: string,\n): Promise<void> {\n try {\n await params.disconnectJiraInstallation({connectionId, lockAlreadyHeld: true});\n } catch (error) {\n logger().warn(\n {err: error, connectionId},\n 'Jira connect compensation failed after token storage rejection',\n );\n }\n}\n\nasync function bestEffortMarkConnectionError(\n params: Pick<HandleJiraCallbackParams, 'markConnectionError'>,\n connectionId: string,\n tx?: unknown,\n): Promise<boolean> {\n try {\n await params.markConnectionError(connectionLifecycleInput(connectionId, tx));\n return true;\n } catch (error) {\n logger().warn(\n {err: error, connectionId},\n 'Jira connection error-state update failed after webhook registration rejection',\n );\n return false;\n }\n}\n\nfunction connectionLifecycleInput(connectionId: string, tx?: unknown) {\n return tx === undefined ? {connectionId} : {connectionId, tx};\n}\n"],"names":["logger","withJiraInstallationLock","JiraInstallationAlreadyLinkedError","JiraInstallStateActorMismatchError","JiraIntegrationProviderError","JiraOAuthCallbackError","JiraOfflineAccessNotGrantedError","JiraPendingSelectionNotFoundError","JiraSiteSelectionMismatchError","assertJiraAuthorizationScopes","verifyJiraInstallState","handleJiraCallback","params","claims","verifyClaims","state","authorization","jira","exchangeAuthorizationCode","code","refreshToken","sites","getAccessibleResources","accessToken","length","pendingStore","save","workspaceId","site","scopes","resolveJiraSite","handleJiraSiteSelection","pending","load","find","candidate","cloudId","connection","clear","handleJiraOAuthCallbackError","error","errorDescription","userId","sessionUserId","requireWorkspaceMembership","memberships","sessionMemberships","withInstallationLock","existing","getExistingJiraConnection","identity","getMyself","installationInput","siteUrl","url","siteName","name","authorizingAccountId","accountId","tokenExpiresAt","expiresAt","displayName","connectJiraInstallation","tokenStore","storeTokens","connectionId","id","editedBy","markedError","bestEffortMarkConnectionError","bestEffortDisconnect","registrationFailureHandled","registerJiraWebhook","withRegistrationLock","_lockKey","fn","onRegistrationSuccess","tx","markConnectionActive","connectionLifecycleInput","onRegistrationFailure","disconnectJiraInstallation","lockAlreadyHeld","warn","err","markConnectionError","undefined"],"mappings":"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"}
|
package/dist/core/tokens.d.ts
CHANGED
|
@@ -34,6 +34,8 @@ export interface StoreJiraTokensParams {
|
|
|
34
34
|
export interface GetJiraAccessTokenParams {
|
|
35
35
|
connectionId: string;
|
|
36
36
|
forceRefresh?: boolean | undefined;
|
|
37
|
+
/** Allows cleanup to use the stored token after a connection leaves the active state. */
|
|
38
|
+
allowInactive?: boolean | undefined;
|
|
37
39
|
}
|
|
38
40
|
export interface JiraTokenStore {
|
|
39
41
|
storeTokens(params: StoreJiraTokensParams): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/core/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,oCAAoC,EAAC,MAAM,8BAA8B,CAAC;AAEvF,OAAO,EAAsB,KAAK,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAkBvE,MAAM,WAAW,4BAA4B;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,oCAAoC,CAAC;CACvD;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,MAAM,EAAE;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACjG,UAAU,CAAC,MAAM,EAAE;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC/B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;KACtC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnB;AAED,MAAM,WAAW,0BAA0B;IACzC,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,4BAA4B,GAAG,SAAS,CAAC,CAAC;IAC3F,OAAO,EAAE,gBAAgB,CAAC;IAC1B,MAAM,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACnC,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE;QAAC,YAAY,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzE;AAED,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,wBAAwB;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/core/tokens.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,oCAAoC,EAAC,MAAM,8BAA8B,CAAC;AAEvF,OAAO,EAAsB,KAAK,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAkBvE,MAAM,WAAW,4BAA4B;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,oCAAoC,CAAC;CACvD;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,MAAM,EAAE;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACjG,UAAU,CAAC,MAAM,EAAE;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC/B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;KACtC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnB;AAED,MAAM,WAAW,0BAA0B;IACzC,iBAAiB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,4BAA4B,GAAG,SAAS,CAAC,CAAC;IAC3F,OAAO,EAAE,gBAAgB,CAAC;IAC1B,MAAM,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACnC,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE;QAAC,YAAY,EAAE,MAAM,CAAA;KAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzE;AAED,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACtC;AAED,MAAM,WAAW,wBAAwB;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,yFAAyF;IACzF,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,cAAc,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACnE;AAED,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,0BAA0B,GAAG,cAAc,CAgIvF"}
|
package/dist/core/tokens.js
CHANGED
|
@@ -81,11 +81,11 @@ export function createJiraTokenStore(params) {
|
|
|
81
81
|
async getAccessToken (input) {
|
|
82
82
|
await waitForCredentialWrites(input.connectionId);
|
|
83
83
|
const connection = await resolveConnection(input.connectionId);
|
|
84
|
-
if (connection.lifecycleStatus !== 'active') {
|
|
84
|
+
if (!input.allowInactive && connection.lifecycleStatus !== 'active') {
|
|
85
85
|
throw new JiraTokenUnrefreshableError(input.connectionId);
|
|
86
86
|
}
|
|
87
87
|
await waitForCredentialWrites(input.connectionId);
|
|
88
|
-
if (refreshStateUnknownConnections.has(input.connectionId)) {
|
|
88
|
+
if (!input.allowInactive && refreshStateUnknownConnections.has(input.connectionId)) {
|
|
89
89
|
throw new JiraTokenUnrefreshableError(input.connectionId);
|
|
90
90
|
}
|
|
91
91
|
const credentialGeneration = currentCredentialGeneration(input.connectionId);
|