@shipfox/api-integration-jira 11.0.0 → 12.1.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 +17 -2
- package/CHANGELOG.md +36 -0
- package/README.md +58 -2
- package/dist/api/client.d.ts +34 -0
- package/dist/api/client.d.ts.map +1 -1
- package/dist/api/client.js +131 -0
- package/dist/api/client.js.map +1 -1
- package/dist/config.d.ts +0 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -4
- package/dist/config.js.map +1 -1
- package/dist/core/agent-tools-provider.d.ts +26 -0
- package/dist/core/agent-tools-provider.d.ts.map +1 -0
- package/dist/core/agent-tools-provider.js +130 -0
- package/dist/core/agent-tools-provider.js.map +1 -0
- package/dist/core/agent-tools.d.ts +16 -0
- package/dist/core/agent-tools.d.ts.map +1 -0
- package/dist/core/agent-tools.js +441 -0
- package/dist/core/agent-tools.js.map +1 -0
- package/dist/core/install.d.ts +22 -0
- package/dist/core/install.d.ts.map +1 -1
- package/dist/core/install.js +68 -20
- 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 +129 -37
- package/dist/core/tokens.js.map +1 -1
- package/dist/core/webhook-processor.d.ts +15 -0
- package/dist/core/webhook-processor.d.ts.map +1 -0
- package/dist/core/webhook-processor.js +137 -0
- package/dist/core/webhook-processor.js.map +1 -0
- package/dist/core/webhook-registration.d.ts +26 -0
- package/dist/core/webhook-registration.d.ts.map +1 -0
- package/dist/core/webhook-registration.js +165 -0
- package/dist/core/webhook-registration.js.map +1 -0
- package/dist/core/webhook.d.ts +18 -0
- package/dist/core/webhook.d.ts.map +1 -0
- package/dist/core/webhook.js +38 -0
- package/dist/core/webhook.js.map +1 -0
- package/dist/db/db.d.ts +68 -0
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/installations.d.ts +33 -6
- package/dist/db/installations.d.ts.map +1 -1
- package/dist/db/installations.js +79 -7
- package/dist/db/installations.js.map +1 -1
- package/dist/db/schema/installations.d.ts +34 -0
- package/dist/db/schema/installations.d.ts.map +1 -1
- package/dist/db/schema/installations.js +12 -2
- package/dist/db/schema/installations.js.map +1 -1
- package/dist/index.d.ts +34 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +76 -8
- package/dist/index.js.map +1 -1
- package/dist/presentation/routes/install.d.ts +22 -0
- package/dist/presentation/routes/install.d.ts.map +1 -1
- package/dist/presentation/routes/install.js.map +1 -1
- package/dist/presentation/routes/webhooks.d.ts +8 -0
- package/dist/presentation/routes/webhooks.d.ts.map +1 -0
- package/dist/presentation/routes/webhooks.js +108 -0
- package/dist/presentation/routes/webhooks.js.map +1 -0
- package/dist/temporal/activities/index.d.ts +2 -0
- package/dist/temporal/activities/index.d.ts.map +1 -0
- package/dist/temporal/activities/index.js +3 -0
- package/dist/temporal/activities/index.js.map +1 -0
- package/dist/temporal/activities/refresh-jira-tokens.d.ts +20 -0
- package/dist/temporal/activities/refresh-jira-tokens.d.ts.map +1 -0
- package/dist/temporal/activities/refresh-jira-tokens.js +58 -0
- package/dist/temporal/activities/refresh-jira-tokens.js.map +1 -0
- package/dist/temporal/constants.d.ts +6 -0
- package/dist/temporal/constants.d.ts.map +1 -0
- package/dist/temporal/constants.js +8 -0
- package/dist/temporal/constants.js.map +1 -0
- package/dist/temporal/worker.d.ts +9 -0
- package/dist/temporal/worker.d.ts.map +1 -0
- package/dist/temporal/worker.js +26 -0
- package/dist/temporal/worker.js.map +1 -0
- package/dist/temporal/workflows/index.bundle.js +25225 -0
- package/dist/temporal/workflows/index.bundle.meta.json +1 -0
- package/dist/temporal/workflows/index.d.ts +2 -0
- package/dist/temporal/workflows/index.d.ts.map +1 -0
- package/dist/temporal/workflows/index.js +3 -0
- package/dist/temporal/workflows/index.js.map +1 -0
- package/dist/temporal/workflows/refresh-jira-tokens-cron.d.ts +2 -0
- package/dist/temporal/workflows/refresh-jira-tokens-cron.d.ts.map +1 -0
- package/dist/temporal/workflows/refresh-jira-tokens-cron.js +20 -0
- package/dist/temporal/workflows/refresh-jira-tokens-cron.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/drizzle/0001_jira-token-refresh-state.sql +12 -0
- package/drizzle/meta/0001_snapshot.json +245 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +17 -8
- package/src/api/client.test.ts +236 -9
- package/src/api/client.ts +192 -0
- package/src/config.test.ts +0 -1
- package/src/config.ts +1 -4
- package/src/core/agent-tools-provider.test.ts +299 -0
- package/src/core/agent-tools-provider.ts +175 -0
- package/src/core/agent-tools.ts +450 -0
- package/src/core/install.test.ts +179 -0
- package/src/core/install.ts +85 -19
- package/src/core/tokens-refresh.test.ts +223 -4
- package/src/core/tokens.test.ts +1 -1
- package/src/core/tokens.ts +153 -33
- package/src/core/webhook-processor.test.ts +418 -0
- package/src/core/webhook-processor.ts +166 -0
- package/src/core/webhook-registration.test.ts +375 -0
- package/src/core/webhook-registration.ts +216 -0
- package/src/core/webhook.ts +69 -0
- package/src/db/installations.test.ts +148 -2
- package/src/db/installations.ts +133 -7
- package/src/db/schema/installations.ts +21 -1
- package/src/index.test.ts +42 -0
- package/src/index.ts +148 -5
- package/src/presentation/routes/install.ts +12 -0
- package/src/presentation/routes/webhooks.test.ts +132 -0
- package/src/presentation/routes/webhooks.ts +115 -0
- package/src/temporal/activities/index.ts +5 -0
- package/src/temporal/activities/refresh-jira-tokens.test.ts +156 -0
- package/src/temporal/activities/refresh-jira-tokens.ts +88 -0
- package/src/temporal/constants.ts +8 -0
- package/src/temporal/worker.ts +39 -0
- package/src/temporal/workflows/index.ts +1 -0
- package/src/temporal/workflows/refresh-jira-tokens-cron.ts +19 -0
- package/test/env.ts +0 -1
- package/test/factories/jira-installation.ts +4 -0
- package/tsconfig.build.tsbuildinfo +1 -1
package/dist/db/db.d.ts
CHANGED
|
@@ -197,6 +197,40 @@ export declare const schema: {
|
|
|
197
197
|
identity: undefined;
|
|
198
198
|
generated: undefined;
|
|
199
199
|
}, {}, {}>;
|
|
200
|
+
refreshTokenLastUsedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
201
|
+
name: "refresh_token_last_used_at";
|
|
202
|
+
tableName: "installations";
|
|
203
|
+
dataType: "date";
|
|
204
|
+
columnType: "PgTimestamp";
|
|
205
|
+
data: Date;
|
|
206
|
+
driverParam: string;
|
|
207
|
+
notNull: true;
|
|
208
|
+
hasDefault: true;
|
|
209
|
+
isPrimaryKey: false;
|
|
210
|
+
isAutoincrement: false;
|
|
211
|
+
hasRuntimeDefault: false;
|
|
212
|
+
enumValues: undefined;
|
|
213
|
+
baseColumn: never;
|
|
214
|
+
identity: undefined;
|
|
215
|
+
generated: undefined;
|
|
216
|
+
}, {}, {}>;
|
|
217
|
+
refreshTokenLastAttemptedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
218
|
+
name: "refresh_token_last_attempted_at";
|
|
219
|
+
tableName: "installations";
|
|
220
|
+
dataType: "date";
|
|
221
|
+
columnType: "PgTimestamp";
|
|
222
|
+
data: Date;
|
|
223
|
+
driverParam: string;
|
|
224
|
+
notNull: true;
|
|
225
|
+
hasDefault: true;
|
|
226
|
+
isPrimaryKey: false;
|
|
227
|
+
isAutoincrement: false;
|
|
228
|
+
hasRuntimeDefault: false;
|
|
229
|
+
enumValues: undefined;
|
|
230
|
+
baseColumn: never;
|
|
231
|
+
identity: undefined;
|
|
232
|
+
generated: undefined;
|
|
233
|
+
}, {}, {}>;
|
|
200
234
|
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
201
235
|
name: "created_at";
|
|
202
236
|
tableName: "installations";
|
|
@@ -527,6 +561,40 @@ export declare function db(): NodePgDatabase<{
|
|
|
527
561
|
identity: undefined;
|
|
528
562
|
generated: undefined;
|
|
529
563
|
}, {}, {}>;
|
|
564
|
+
refreshTokenLastUsedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
565
|
+
name: "refresh_token_last_used_at";
|
|
566
|
+
tableName: "installations";
|
|
567
|
+
dataType: "date";
|
|
568
|
+
columnType: "PgTimestamp";
|
|
569
|
+
data: Date;
|
|
570
|
+
driverParam: string;
|
|
571
|
+
notNull: true;
|
|
572
|
+
hasDefault: true;
|
|
573
|
+
isPrimaryKey: false;
|
|
574
|
+
isAutoincrement: false;
|
|
575
|
+
hasRuntimeDefault: false;
|
|
576
|
+
enumValues: undefined;
|
|
577
|
+
baseColumn: never;
|
|
578
|
+
identity: undefined;
|
|
579
|
+
generated: undefined;
|
|
580
|
+
}, {}, {}>;
|
|
581
|
+
refreshTokenLastAttemptedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
582
|
+
name: "refresh_token_last_attempted_at";
|
|
583
|
+
tableName: "installations";
|
|
584
|
+
dataType: "date";
|
|
585
|
+
columnType: "PgTimestamp";
|
|
586
|
+
data: Date;
|
|
587
|
+
driverParam: string;
|
|
588
|
+
notNull: true;
|
|
589
|
+
hasDefault: true;
|
|
590
|
+
isPrimaryKey: false;
|
|
591
|
+
isAutoincrement: false;
|
|
592
|
+
hasRuntimeDefault: false;
|
|
593
|
+
enumValues: undefined;
|
|
594
|
+
baseColumn: never;
|
|
595
|
+
identity: undefined;
|
|
596
|
+
generated: undefined;
|
|
597
|
+
}, {}, {}>;
|
|
530
598
|
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
531
599
|
name: "created_at";
|
|
532
600
|
tableName: "installations";
|
package/dist/db/db.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/db/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAKnE,eAAO,MAAM,MAAM
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/db/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAKnE,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAA6C,CAAC;AAIjE,wBAAgB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAGjB;AAED,wBAAgB,OAAO,IAAI,IAAI,CAE9B"}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
export type JiraInstallationLock = <T>(lockKey: string, fn: () => Promise<T>) => Promise<T>;
|
|
2
|
+
type JiraAdvisoryLockAttempt<T> = {
|
|
3
|
+
acquired: true;
|
|
4
|
+
value: T;
|
|
5
|
+
} | {
|
|
6
|
+
acquired: false;
|
|
7
|
+
};
|
|
8
|
+
export declare function withJiraInstallationLock<T>(lockKey: string, fn: () => Promise<T>): Promise<Awaited<T>>;
|
|
1
9
|
export type JiraInstallationStatus = 'installed' | 'revoked';
|
|
2
10
|
export interface JiraInstallation {
|
|
3
11
|
id: string;
|
|
@@ -11,6 +19,8 @@ export interface JiraInstallation {
|
|
|
11
19
|
webhookExpiresAt: Date | null;
|
|
12
20
|
status: JiraInstallationStatus;
|
|
13
21
|
tokenExpiresAt: Date | null;
|
|
22
|
+
refreshTokenLastUsedAt: Date;
|
|
23
|
+
refreshTokenLastAttemptedAt: Date;
|
|
14
24
|
createdAt: Date;
|
|
15
25
|
updatedAt: Date;
|
|
16
26
|
}
|
|
@@ -25,12 +35,19 @@ export interface UpsertJiraInstallationParams {
|
|
|
25
35
|
webhookExpiresAt?: Date | null | undefined;
|
|
26
36
|
status: JiraInstallationStatus;
|
|
27
37
|
tokenExpiresAt?: Date | null | undefined;
|
|
38
|
+
refreshTokenLastUsedAt?: Date | undefined;
|
|
39
|
+
refreshTokenLastAttemptedAt?: Date | undefined;
|
|
28
40
|
}
|
|
29
41
|
export interface UpdateJiraInstallationTokenExpiryParams {
|
|
30
42
|
connectionId: string;
|
|
31
43
|
tokenExpiresAt: Date | null;
|
|
32
44
|
scopes?: string[] | undefined;
|
|
33
45
|
}
|
|
46
|
+
export interface UpdateJiraInstallationWebhookParams {
|
|
47
|
+
connectionId: string;
|
|
48
|
+
webhookIds: number[];
|
|
49
|
+
webhookExpiresAt: Date | null;
|
|
50
|
+
}
|
|
34
51
|
export declare function upsertJiraInstallation(params: UpsertJiraInstallationParams, options?: {
|
|
35
52
|
tx?: unknown;
|
|
36
53
|
}): Promise<JiraInstallation>;
|
|
@@ -40,16 +57,25 @@ export declare function getJiraInstallationByCloudId(cloudId: string, options?:
|
|
|
40
57
|
export declare function updateJiraInstallationTokenExpiry(params: UpdateJiraInstallationTokenExpiryParams, options?: {
|
|
41
58
|
tx?: unknown;
|
|
42
59
|
}): Promise<JiraInstallation | undefined>;
|
|
60
|
+
export declare function markJiraInstallationTokenRefreshAttempt(connectionId: string, options?: {
|
|
61
|
+
tx?: unknown;
|
|
62
|
+
}): Promise<JiraInstallation | undefined>;
|
|
63
|
+
export declare function listJiraInstallationsDueForTokenRefresh(params: {
|
|
64
|
+
before: Date;
|
|
65
|
+
limit?: number | undefined;
|
|
66
|
+
}, options?: {
|
|
67
|
+
tx?: unknown;
|
|
68
|
+
}): Promise<JiraInstallation[]>;
|
|
69
|
+
export declare function updateJiraInstallationWebhook(params: UpdateJiraInstallationWebhookParams, options?: {
|
|
70
|
+
tx?: unknown;
|
|
71
|
+
}): Promise<JiraInstallation | undefined>;
|
|
43
72
|
export declare function deleteJiraInstallationByConnectionId(connectionId: string, options?: {
|
|
44
73
|
tx?: unknown;
|
|
45
74
|
}): Promise<boolean>;
|
|
46
|
-
export type JiraRefreshLockResult<T> =
|
|
47
|
-
acquired: true;
|
|
48
|
-
value: T;
|
|
49
|
-
} | {
|
|
50
|
-
acquired: false;
|
|
51
|
-
};
|
|
75
|
+
export type JiraRefreshLockResult<T> = JiraAdvisoryLockAttempt<T>;
|
|
52
76
|
export declare function withJiraRefreshLock<T>(connectionId: string, fn: () => Promise<T>): Promise<JiraRefreshLockResult<T>>;
|
|
77
|
+
export declare function withJiraRefreshLockAndWait<T>(connectionId: string, fn: () => Promise<T>): Promise<T>;
|
|
78
|
+
export declare function withJiraWebhookRegistrationLock<T>(lockKey: string, fn: (tx?: unknown) => Promise<T>): Promise<T>;
|
|
53
79
|
export declare function getJiraInstallationByConnectionId(connectionId: string, options?: {
|
|
54
80
|
tx?: unknown;
|
|
55
81
|
}): Promise<JiraInstallation | undefined>;
|
|
@@ -59,4 +85,5 @@ export declare function getJiraInstallationByWebhookId(webhookId: number, option
|
|
|
59
85
|
export declare function markJiraInstallationRevoked(connectionId: string, options?: {
|
|
60
86
|
tx?: unknown;
|
|
61
87
|
}): Promise<JiraInstallation | undefined>;
|
|
88
|
+
export {};
|
|
62
89
|
//# sourceMappingURL=installations.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installations.d.ts","sourceRoot":"","sources":["../../src/db/installations.ts"],"names":[],"mappings":"AAWA,MAAM,MAAM,sBAAsB,GAAG,WAAW,GAAG,SAAS,CAAC;AAE7D,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,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,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,gBAAgB,EAAE,IAAI,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,sBAAsB,CAAC;IAC/B,cAAc,EAAE,IAAI,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,4BAA4B;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,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,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAClC,gBAAgB,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;IAC3C,MAAM,EAAE,sBAAsB,CAAC;IAC/B,cAAc,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"installations.d.ts","sourceRoot":"","sources":["../../src/db/installations.ts"],"names":[],"mappings":"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"}
|
package/dist/db/installations.js
CHANGED
|
@@ -1,13 +1,36 @@
|
|
|
1
1
|
import { isUniqueViolation } from '@shipfox/node-drizzle';
|
|
2
|
-
import { pgClient } from '@shipfox/node-postgres';
|
|
3
|
-
import { eq, sql } from 'drizzle-orm';
|
|
2
|
+
import { pgClient, withPostgresSession } from '@shipfox/node-postgres';
|
|
3
|
+
import { and, asc, eq, lte, sql } from 'drizzle-orm';
|
|
4
4
|
import { JiraConnectionAlreadyLinkedError, JiraInstallationAlreadyLinkedError, JiraInstallationSiteMismatchError } from '#core/errors.js';
|
|
5
5
|
import { db } from './db.js';
|
|
6
6
|
import { jiraInstallations, toJiraInstallation } from './schema/installations.js';
|
|
7
|
+
const JIRA_ADVISORY_LOCK_RETRY_DELAY_MS = 100;
|
|
8
|
+
const JIRA_ADVISORY_LOCK_MAX_RETRY_DELAY_MS = 1_000;
|
|
9
|
+
const JIRA_ADVISORY_LOCK_TIMEOUT_MS = 30_000;
|
|
10
|
+
export function withJiraInstallationLock(lockKey, fn) {
|
|
11
|
+
const advisoryKey = `jira-installation:${lockKey}`;
|
|
12
|
+
return withJiraAdvisoryLockWait(`Jira installation lock: ${lockKey}`, ()=>withPostgresSession(async (client)=>{
|
|
13
|
+
const lock = await client.query('SELECT pg_try_advisory_lock(hashtext($1)) AS acquired', [
|
|
14
|
+
advisoryKey
|
|
15
|
+
]);
|
|
16
|
+
if (lock.rows[0]?.acquired !== true) return {
|
|
17
|
+
acquired: false
|
|
18
|
+
};
|
|
19
|
+
try {
|
|
20
|
+
return {
|
|
21
|
+
acquired: true,
|
|
22
|
+
value: await fn()
|
|
23
|
+
};
|
|
24
|
+
} finally{
|
|
25
|
+
await client.query('SELECT pg_advisory_unlock(hashtext($1))', [
|
|
26
|
+
advisoryKey
|
|
27
|
+
]);
|
|
28
|
+
}
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
7
31
|
export async function upsertJiraInstallation(params, options = {}) {
|
|
8
32
|
const executor = options.tx ?? db();
|
|
9
33
|
const now = new Date();
|
|
10
|
-
const webhookIds = params.webhookIds ?? [];
|
|
11
34
|
let row;
|
|
12
35
|
try {
|
|
13
36
|
[row] = await executor.insert(jiraInstallations).values({
|
|
@@ -17,10 +40,12 @@ export async function upsertJiraInstallation(params, options = {}) {
|
|
|
17
40
|
siteName: params.siteName,
|
|
18
41
|
authorizingAccountId: params.authorizingAccountId,
|
|
19
42
|
scopes: params.scopes,
|
|
20
|
-
webhookIds,
|
|
43
|
+
webhookIds: params.webhookIds ?? [],
|
|
21
44
|
webhookExpiresAt: params.webhookExpiresAt ?? null,
|
|
22
45
|
status: params.status,
|
|
23
|
-
tokenExpiresAt: params.tokenExpiresAt ?? null
|
|
46
|
+
tokenExpiresAt: params.tokenExpiresAt ?? null,
|
|
47
|
+
refreshTokenLastUsedAt: params.refreshTokenLastUsedAt ?? now,
|
|
48
|
+
refreshTokenLastAttemptedAt: params.refreshTokenLastAttemptedAt ?? now
|
|
24
49
|
}).onConflictDoUpdate({
|
|
25
50
|
target: jiraInstallations.connectionId,
|
|
26
51
|
setWhere: eq(jiraInstallations.cloudId, params.cloudId),
|
|
@@ -30,10 +55,16 @@ export async function upsertJiraInstallation(params, options = {}) {
|
|
|
30
55
|
siteName: params.siteName,
|
|
31
56
|
authorizingAccountId: params.authorizingAccountId,
|
|
32
57
|
scopes: params.scopes,
|
|
33
|
-
webhookIds
|
|
34
|
-
|
|
58
|
+
...params.webhookIds === undefined ? {} : {
|
|
59
|
+
webhookIds: params.webhookIds
|
|
60
|
+
},
|
|
61
|
+
...params.webhookExpiresAt === undefined ? {} : {
|
|
62
|
+
webhookExpiresAt: params.webhookExpiresAt
|
|
63
|
+
},
|
|
35
64
|
status: params.status,
|
|
36
65
|
tokenExpiresAt: params.tokenExpiresAt ?? null,
|
|
66
|
+
refreshTokenLastUsedAt: params.refreshTokenLastUsedAt ?? now,
|
|
67
|
+
refreshTokenLastAttemptedAt: params.refreshTokenLastAttemptedAt ?? now,
|
|
37
68
|
updatedAt: now
|
|
38
69
|
}
|
|
39
70
|
}).returning();
|
|
@@ -57,11 +88,35 @@ export async function getJiraInstallationByCloudId(cloudId, options = {}) {
|
|
|
57
88
|
}
|
|
58
89
|
export async function updateJiraInstallationTokenExpiry(params, options = {}) {
|
|
59
90
|
const executor = options.tx ?? db();
|
|
91
|
+
const now = new Date();
|
|
60
92
|
const [row] = await executor.update(jiraInstallations).set({
|
|
61
93
|
tokenExpiresAt: params.tokenExpiresAt,
|
|
94
|
+
refreshTokenLastUsedAt: now,
|
|
95
|
+
refreshTokenLastAttemptedAt: now,
|
|
62
96
|
...params.scopes === undefined ? {} : {
|
|
63
97
|
scopes: params.scopes
|
|
64
98
|
},
|
|
99
|
+
updatedAt: now
|
|
100
|
+
}).where(eq(jiraInstallations.connectionId, params.connectionId)).returning();
|
|
101
|
+
return row ? toJiraInstallation(row) : undefined;
|
|
102
|
+
}
|
|
103
|
+
export async function markJiraInstallationTokenRefreshAttempt(connectionId, options = {}) {
|
|
104
|
+
const executor = options.tx ?? db();
|
|
105
|
+
const [row] = await executor.update(jiraInstallations).set({
|
|
106
|
+
refreshTokenLastAttemptedAt: new Date()
|
|
107
|
+
}).where(eq(jiraInstallations.connectionId, connectionId)).returning();
|
|
108
|
+
return row ? toJiraInstallation(row) : undefined;
|
|
109
|
+
}
|
|
110
|
+
export async function listJiraInstallationsDueForTokenRefresh(params, options = {}) {
|
|
111
|
+
const executor = options.tx ?? db();
|
|
112
|
+
const rows = await executor.select().from(jiraInstallations).where(and(eq(jiraInstallations.status, 'installed'), lte(jiraInstallations.refreshTokenLastUsedAt, params.before))).orderBy(asc(jiraInstallations.refreshTokenLastAttemptedAt)).limit(params.limit ?? 100);
|
|
113
|
+
return rows.map(toJiraInstallation);
|
|
114
|
+
}
|
|
115
|
+
export async function updateJiraInstallationWebhook(params, options = {}) {
|
|
116
|
+
const executor = options.tx ?? db();
|
|
117
|
+
const [row] = await executor.update(jiraInstallations).set({
|
|
118
|
+
webhookIds: params.webhookIds,
|
|
119
|
+
webhookExpiresAt: params.webhookExpiresAt,
|
|
65
120
|
updatedAt: new Date()
|
|
66
121
|
}).where(eq(jiraInstallations.connectionId, params.connectionId)).returning();
|
|
67
122
|
return row ? toJiraInstallation(row) : undefined;
|
|
@@ -74,6 +129,12 @@ export async function deleteJiraInstallationByConnectionId(connectionId, options
|
|
|
74
129
|
export function withJiraRefreshLock(connectionId, fn) {
|
|
75
130
|
return withJiraRefreshLockClient(connectionId, fn);
|
|
76
131
|
}
|
|
132
|
+
export function withJiraRefreshLockAndWait(connectionId, fn) {
|
|
133
|
+
return withJiraAdvisoryLockWait(`Jira refresh lock: ${connectionId}`, ()=>withJiraRefreshLock(connectionId, fn));
|
|
134
|
+
}
|
|
135
|
+
export function withJiraWebhookRegistrationLock(lockKey, fn) {
|
|
136
|
+
return withJiraInstallationLock(lockKey, ()=>fn());
|
|
137
|
+
}
|
|
77
138
|
async function withJiraRefreshLockClient(connectionId, fn) {
|
|
78
139
|
const client = await pgClient().connect();
|
|
79
140
|
let acquired = false;
|
|
@@ -99,6 +160,17 @@ async function withJiraRefreshLockClient(connectionId, fn) {
|
|
|
99
160
|
}
|
|
100
161
|
}
|
|
101
162
|
}
|
|
163
|
+
async function withJiraAdvisoryLockWait(lockDescription, attemptLock) {
|
|
164
|
+
const deadline = Date.now() + JIRA_ADVISORY_LOCK_TIMEOUT_MS;
|
|
165
|
+
let retryDelayMs = JIRA_ADVISORY_LOCK_RETRY_DELAY_MS;
|
|
166
|
+
while(true){
|
|
167
|
+
const attempt = await attemptLock();
|
|
168
|
+
if (attempt.acquired) return attempt.value;
|
|
169
|
+
if (Date.now() >= deadline) throw new Error(`Timed out waiting for ${lockDescription}`);
|
|
170
|
+
await new Promise((resolve)=>setTimeout(resolve, retryDelayMs));
|
|
171
|
+
retryDelayMs = Math.min(retryDelayMs * 2, JIRA_ADVISORY_LOCK_MAX_RETRY_DELAY_MS);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
102
174
|
export async function getJiraInstallationByConnectionId(connectionId, options = {}) {
|
|
103
175
|
const executor = options.tx ?? db();
|
|
104
176
|
const rows = await executor.select().from(jiraInstallations).where(eq(jiraInstallations.connectionId, connectionId)).limit(1);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/installations.ts"],"sourcesContent":["import {isUniqueViolation} from '@shipfox/node-drizzle';\nimport {pgClient} from '@shipfox/node-postgres';\nimport {eq, sql} from 'drizzle-orm';\nimport {\n JiraConnectionAlreadyLinkedError,\n JiraInstallationAlreadyLinkedError,\n JiraInstallationSiteMismatchError,\n} from '#core/errors.js';\nimport {db} from './db.js';\nimport {jiraInstallations, toJiraInstallation} from './schema/installations.js';\n\nexport type JiraInstallationStatus = 'installed' | 'revoked';\n\nexport interface JiraInstallation {\n id: string;\n connectionId: string;\n cloudId: string;\n siteUrl: string;\n siteName: string;\n authorizingAccountId: string;\n scopes: string[];\n webhookIds: number[];\n webhookExpiresAt: Date | null;\n status: JiraInstallationStatus;\n tokenExpiresAt: Date | null;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface UpsertJiraInstallationParams {\n connectionId: string;\n cloudId: string;\n siteUrl: string;\n siteName: string;\n authorizingAccountId: string;\n scopes: string[];\n webhookIds?: number[] | undefined;\n webhookExpiresAt?: Date | null | undefined;\n status: JiraInstallationStatus;\n tokenExpiresAt?: Date | null | undefined;\n}\n\nexport interface UpdateJiraInstallationTokenExpiryParams {\n connectionId: string;\n tokenExpiresAt: Date | null;\n scopes?: string[] | undefined;\n}\n\ntype JiraDb = ReturnType<typeof db>;\ntype JiraTx = Parameters<Parameters<JiraDb['transaction']>[0]>[0];\n\nexport async function upsertJiraInstallation(\n params: UpsertJiraInstallationParams,\n options: {tx?: unknown} = {},\n): Promise<JiraInstallation> {\n const executor = (options.tx ?? db()) as JiraDb | JiraTx;\n const now = new Date();\n const webhookIds = params.webhookIds ?? [];\n let row: typeof jiraInstallations.$inferSelect | undefined;\n try {\n [row] = await executor\n .insert(jiraInstallations)\n .values({\n connectionId: params.connectionId,\n cloudId: params.cloudId,\n siteUrl: params.siteUrl,\n siteName: params.siteName,\n authorizingAccountId: params.authorizingAccountId,\n scopes: params.scopes,\n webhookIds,\n webhookExpiresAt: params.webhookExpiresAt ?? null,\n status: params.status,\n tokenExpiresAt: params.tokenExpiresAt ?? null,\n })\n .onConflictDoUpdate({\n target: jiraInstallations.connectionId,\n setWhere: eq(jiraInstallations.cloudId, params.cloudId),\n set: {\n cloudId: params.cloudId,\n siteUrl: params.siteUrl,\n siteName: params.siteName,\n authorizingAccountId: params.authorizingAccountId,\n scopes: params.scopes,\n webhookIds,\n webhookExpiresAt: params.webhookExpiresAt ?? null,\n status: params.status,\n tokenExpiresAt: params.tokenExpiresAt ?? null,\n updatedAt: now,\n },\n })\n .returning();\n } catch (error) {\n if (isUniqueViolation(error, 'integrations_jira_installations_connection_unique')) {\n throw new JiraConnectionAlreadyLinkedError(params.connectionId);\n }\n if (isUniqueViolation(error, 'integrations_jira_installations_cloud_id_unique')) {\n throw new JiraInstallationAlreadyLinkedError(params.cloudId);\n }\n throw error;\n }\n\n if (!row) throw new JiraInstallationSiteMismatchError(params.connectionId, params.cloudId);\n return toJiraInstallation(row);\n}\n\nexport async function getJiraInstallationByCloudId(\n cloudId: string,\n options: {tx?: unknown} = {},\n): Promise<JiraInstallation | undefined> {\n const executor = (options.tx ?? db()) as JiraDb | JiraTx;\n const rows = await executor\n .select()\n .from(jiraInstallations)\n .where(eq(jiraInstallations.cloudId, cloudId))\n .limit(1);\n const row = rows[0];\n return row ? toJiraInstallation(row) : undefined;\n}\n\nexport async function updateJiraInstallationTokenExpiry(\n params: UpdateJiraInstallationTokenExpiryParams,\n options: {tx?: unknown} = {},\n): Promise<JiraInstallation | undefined> {\n const executor = (options.tx ?? db()) as JiraDb | JiraTx;\n const [row] = await executor\n .update(jiraInstallations)\n .set({\n tokenExpiresAt: params.tokenExpiresAt,\n ...(params.scopes === undefined ? {} : {scopes: params.scopes}),\n updatedAt: new Date(),\n })\n .where(eq(jiraInstallations.connectionId, params.connectionId))\n .returning();\n return row ? toJiraInstallation(row) : undefined;\n}\n\nexport async function deleteJiraInstallationByConnectionId(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<boolean> {\n const executor = (options.tx ?? db()) as JiraDb | JiraTx;\n const result = await executor\n .delete(jiraInstallations)\n .where(eq(jiraInstallations.connectionId, connectionId));\n return (result.rowCount ?? 0) > 0;\n}\n\nexport type JiraRefreshLockResult<T> = {acquired: true; value: T} | {acquired: false};\n\nexport function withJiraRefreshLock<T>(\n connectionId: string,\n fn: () => Promise<T>,\n): Promise<JiraRefreshLockResult<T>> {\n return withJiraRefreshLockClient(connectionId, fn);\n}\n\nasync function withJiraRefreshLockClient<T>(\n connectionId: string,\n fn: () => Promise<T>,\n): Promise<JiraRefreshLockResult<T>> {\n const client = await pgClient().connect();\n let acquired = false;\n try {\n const lock = await client.query<{acquired: boolean}>(\n 'SELECT pg_try_advisory_lock(hashtext($1)) AS acquired',\n [connectionId],\n );\n acquired = lock.rows[0]?.acquired === true;\n if (!acquired) return {acquired: false};\n return {acquired: true, value: await fn()};\n } finally {\n try {\n if (acquired) await client.query('SELECT pg_advisory_unlock(hashtext($1))', [connectionId]);\n } finally {\n client.release();\n }\n }\n}\n\nexport async function getJiraInstallationByConnectionId(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<JiraInstallation | undefined> {\n const executor = (options.tx ?? db()) as JiraDb | JiraTx;\n const rows = await executor\n .select()\n .from(jiraInstallations)\n .where(eq(jiraInstallations.connectionId, connectionId))\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toJiraInstallation(row);\n}\n\nexport async function getJiraInstallationByWebhookId(\n webhookId: number,\n options: {tx?: unknown} = {},\n): Promise<JiraInstallation | undefined> {\n const executor = (options.tx ?? db()) as JiraDb | JiraTx;\n const rows = await executor\n .select()\n .from(jiraInstallations)\n .where(sql`${jiraInstallations.webhookIds} @> ${JSON.stringify([webhookId])}::jsonb`)\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toJiraInstallation(row);\n}\n\nexport async function markJiraInstallationRevoked(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<JiraInstallation | undefined> {\n const executor = (options.tx ?? db()) as JiraDb | JiraTx;\n const [row] = await executor\n .update(jiraInstallations)\n .set({status: 'revoked', updatedAt: new Date()})\n .where(eq(jiraInstallations.connectionId, connectionId))\n .returning();\n if (!row) return undefined;\n return toJiraInstallation(row);\n}\n"],"names":["isUniqueViolation","pgClient","eq","sql","JiraConnectionAlreadyLinkedError","JiraInstallationAlreadyLinkedError","JiraInstallationSiteMismatchError","db","jiraInstallations","toJiraInstallation","upsertJiraInstallation","params","options","executor","tx","now","Date","webhookIds","row","insert","values","connectionId","cloudId","siteUrl","siteName","authorizingAccountId","scopes","webhookExpiresAt","status","tokenExpiresAt","onConflictDoUpdate","target","setWhere","set","updatedAt","returning","error","getJiraInstallationByCloudId","rows","select","from","where","limit","undefined","updateJiraInstallationTokenExpiry","update","deleteJiraInstallationByConnectionId","result","delete","rowCount","withJiraRefreshLock","fn","withJiraRefreshLockClient","client","connect","acquired","lock","query","value","release","getJiraInstallationByConnectionId","getJiraInstallationByWebhookId","webhookId","JSON","stringify","markJiraInstallationRevoked"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/db/installations.ts"],"sourcesContent":["import {isUniqueViolation} from '@shipfox/node-drizzle';\nimport {pgClient, withPostgresSession} from '@shipfox/node-postgres';\nimport {and, asc, eq, lte, sql} from 'drizzle-orm';\nimport {\n JiraConnectionAlreadyLinkedError,\n JiraInstallationAlreadyLinkedError,\n JiraInstallationSiteMismatchError,\n} from '#core/errors.js';\nimport {db} from './db.js';\nimport {jiraInstallations, toJiraInstallation} from './schema/installations.js';\n\nexport type JiraInstallationLock = <T>(lockKey: string, fn: () => Promise<T>) => Promise<T>;\n\nconst JIRA_ADVISORY_LOCK_RETRY_DELAY_MS = 100;\nconst JIRA_ADVISORY_LOCK_MAX_RETRY_DELAY_MS = 1_000;\nconst JIRA_ADVISORY_LOCK_TIMEOUT_MS = 30_000;\n\ntype JiraAdvisoryLockAttempt<T> = {acquired: true; value: T} | {acquired: false};\n\nexport function withJiraInstallationLock<T>(lockKey: string, fn: () => Promise<T>) {\n const advisoryKey = `jira-installation:${lockKey}`;\n return withJiraAdvisoryLockWait(`Jira installation lock: ${lockKey}`, () =>\n withPostgresSession(async (client) => {\n const lock = await client.query<{acquired: boolean}>(\n 'SELECT pg_try_advisory_lock(hashtext($1)) AS acquired',\n [advisoryKey],\n );\n if (lock.rows[0]?.acquired !== true) return {acquired: false as const};\n\n try {\n return {acquired: true as const, value: await fn()};\n } finally {\n await client.query('SELECT pg_advisory_unlock(hashtext($1))', [advisoryKey]);\n }\n }),\n );\n}\n\nexport type JiraInstallationStatus = 'installed' | 'revoked';\n\nexport interface JiraInstallation {\n id: string;\n connectionId: string;\n cloudId: string;\n siteUrl: string;\n siteName: string;\n authorizingAccountId: string;\n scopes: string[];\n webhookIds: number[];\n webhookExpiresAt: Date | null;\n status: JiraInstallationStatus;\n tokenExpiresAt: Date | null;\n refreshTokenLastUsedAt: Date;\n refreshTokenLastAttemptedAt: Date;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface UpsertJiraInstallationParams {\n connectionId: string;\n cloudId: string;\n siteUrl: string;\n siteName: string;\n authorizingAccountId: string;\n scopes: string[];\n webhookIds?: number[] | undefined;\n webhookExpiresAt?: Date | null | undefined;\n status: JiraInstallationStatus;\n tokenExpiresAt?: Date | null | undefined;\n refreshTokenLastUsedAt?: Date | undefined;\n refreshTokenLastAttemptedAt?: Date | undefined;\n}\n\nexport interface UpdateJiraInstallationTokenExpiryParams {\n connectionId: string;\n tokenExpiresAt: Date | null;\n scopes?: string[] | undefined;\n}\n\nexport interface UpdateJiraInstallationWebhookParams {\n connectionId: string;\n webhookIds: number[];\n webhookExpiresAt: Date | null;\n}\n\ntype JiraDb = ReturnType<typeof db>;\ntype JiraTx = Parameters<Parameters<JiraDb['transaction']>[0]>[0];\n\nexport async function upsertJiraInstallation(\n params: UpsertJiraInstallationParams,\n options: {tx?: unknown} = {},\n): Promise<JiraInstallation> {\n const executor = (options.tx ?? db()) as JiraDb | JiraTx;\n const now = new Date();\n let row: typeof jiraInstallations.$inferSelect | undefined;\n try {\n [row] = await executor\n .insert(jiraInstallations)\n .values({\n connectionId: params.connectionId,\n cloudId: params.cloudId,\n siteUrl: params.siteUrl,\n siteName: params.siteName,\n authorizingAccountId: params.authorizingAccountId,\n scopes: params.scopes,\n webhookIds: params.webhookIds ?? [],\n webhookExpiresAt: params.webhookExpiresAt ?? null,\n status: params.status,\n tokenExpiresAt: params.tokenExpiresAt ?? null,\n refreshTokenLastUsedAt: params.refreshTokenLastUsedAt ?? now,\n refreshTokenLastAttemptedAt: params.refreshTokenLastAttemptedAt ?? now,\n })\n .onConflictDoUpdate({\n target: jiraInstallations.connectionId,\n setWhere: eq(jiraInstallations.cloudId, params.cloudId),\n set: {\n cloudId: params.cloudId,\n siteUrl: params.siteUrl,\n siteName: params.siteName,\n authorizingAccountId: params.authorizingAccountId,\n scopes: params.scopes,\n ...(params.webhookIds === undefined ? {} : {webhookIds: params.webhookIds}),\n ...(params.webhookExpiresAt === undefined\n ? {}\n : {webhookExpiresAt: params.webhookExpiresAt}),\n status: params.status,\n tokenExpiresAt: params.tokenExpiresAt ?? null,\n refreshTokenLastUsedAt: params.refreshTokenLastUsedAt ?? now,\n refreshTokenLastAttemptedAt: params.refreshTokenLastAttemptedAt ?? now,\n updatedAt: now,\n },\n })\n .returning();\n } catch (error) {\n if (isUniqueViolation(error, 'integrations_jira_installations_connection_unique')) {\n throw new JiraConnectionAlreadyLinkedError(params.connectionId);\n }\n if (isUniqueViolation(error, 'integrations_jira_installations_cloud_id_unique')) {\n throw new JiraInstallationAlreadyLinkedError(params.cloudId);\n }\n throw error;\n }\n\n if (!row) throw new JiraInstallationSiteMismatchError(params.connectionId, params.cloudId);\n return toJiraInstallation(row);\n}\n\nexport async function getJiraInstallationByCloudId(\n cloudId: string,\n options: {tx?: unknown} = {},\n): Promise<JiraInstallation | undefined> {\n const executor = (options.tx ?? db()) as JiraDb | JiraTx;\n const rows = await executor\n .select()\n .from(jiraInstallations)\n .where(eq(jiraInstallations.cloudId, cloudId))\n .limit(1);\n const row = rows[0];\n return row ? toJiraInstallation(row) : undefined;\n}\n\nexport async function updateJiraInstallationTokenExpiry(\n params: UpdateJiraInstallationTokenExpiryParams,\n options: {tx?: unknown} = {},\n): Promise<JiraInstallation | undefined> {\n const executor = (options.tx ?? db()) as JiraDb | JiraTx;\n const now = new Date();\n const [row] = await executor\n .update(jiraInstallations)\n .set({\n tokenExpiresAt: params.tokenExpiresAt,\n refreshTokenLastUsedAt: now,\n refreshTokenLastAttemptedAt: now,\n ...(params.scopes === undefined ? {} : {scopes: params.scopes}),\n updatedAt: now,\n })\n .where(eq(jiraInstallations.connectionId, params.connectionId))\n .returning();\n return row ? toJiraInstallation(row) : undefined;\n}\n\nexport async function markJiraInstallationTokenRefreshAttempt(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<JiraInstallation | undefined> {\n const executor = (options.tx ?? db()) as JiraDb | JiraTx;\n const [row] = await executor\n .update(jiraInstallations)\n .set({refreshTokenLastAttemptedAt: new Date()})\n .where(eq(jiraInstallations.connectionId, connectionId))\n .returning();\n return row ? toJiraInstallation(row) : undefined;\n}\n\nexport async function listJiraInstallationsDueForTokenRefresh(\n params: {before: Date; limit?: number | undefined},\n options: {tx?: unknown} = {},\n): Promise<JiraInstallation[]> {\n const executor = (options.tx ?? db()) as JiraDb | JiraTx;\n const rows = await executor\n .select()\n .from(jiraInstallations)\n .where(\n and(\n eq(jiraInstallations.status, 'installed'),\n lte(jiraInstallations.refreshTokenLastUsedAt, params.before),\n ),\n )\n .orderBy(asc(jiraInstallations.refreshTokenLastAttemptedAt))\n .limit(params.limit ?? 100);\n return rows.map(toJiraInstallation);\n}\n\nexport async function updateJiraInstallationWebhook(\n params: UpdateJiraInstallationWebhookParams,\n options: {tx?: unknown} = {},\n): Promise<JiraInstallation | undefined> {\n const executor = (options.tx ?? db()) as JiraDb | JiraTx;\n const [row] = await executor\n .update(jiraInstallations)\n .set({\n webhookIds: params.webhookIds,\n webhookExpiresAt: params.webhookExpiresAt,\n updatedAt: new Date(),\n })\n .where(eq(jiraInstallations.connectionId, params.connectionId))\n .returning();\n return row ? toJiraInstallation(row) : undefined;\n}\n\nexport async function deleteJiraInstallationByConnectionId(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<boolean> {\n const executor = (options.tx ?? db()) as JiraDb | JiraTx;\n const result = await executor\n .delete(jiraInstallations)\n .where(eq(jiraInstallations.connectionId, connectionId));\n return (result.rowCount ?? 0) > 0;\n}\n\nexport type JiraRefreshLockResult<T> = JiraAdvisoryLockAttempt<T>;\n\nexport function withJiraRefreshLock<T>(\n connectionId: string,\n fn: () => Promise<T>,\n): Promise<JiraRefreshLockResult<T>> {\n return withJiraRefreshLockClient(connectionId, fn);\n}\n\nexport function withJiraRefreshLockAndWait<T>(\n connectionId: string,\n fn: () => Promise<T>,\n): Promise<T> {\n return withJiraAdvisoryLockWait(`Jira refresh lock: ${connectionId}`, () =>\n withJiraRefreshLock(connectionId, fn),\n );\n}\n\nexport function withJiraWebhookRegistrationLock<T>(\n lockKey: string,\n fn: (tx?: unknown) => Promise<T>,\n): Promise<T> {\n return withJiraInstallationLock(lockKey, () => fn());\n}\n\nasync function withJiraRefreshLockClient<T>(\n connectionId: string,\n fn: () => Promise<T>,\n): Promise<JiraRefreshLockResult<T>> {\n const client = await pgClient().connect();\n let acquired = false;\n try {\n const lock = await client.query<{acquired: boolean}>(\n 'SELECT pg_try_advisory_lock(hashtext($1)) AS acquired',\n [connectionId],\n );\n acquired = lock.rows[0]?.acquired === true;\n if (!acquired) return {acquired: false};\n return {acquired: true, value: await fn()};\n } finally {\n try {\n if (acquired) await client.query('SELECT pg_advisory_unlock(hashtext($1))', [connectionId]);\n } finally {\n client.release();\n }\n }\n}\n\nasync function withJiraAdvisoryLockWait<T>(\n lockDescription: string,\n attemptLock: () => Promise<JiraAdvisoryLockAttempt<T>>,\n): Promise<T> {\n const deadline = Date.now() + JIRA_ADVISORY_LOCK_TIMEOUT_MS;\n let retryDelayMs = JIRA_ADVISORY_LOCK_RETRY_DELAY_MS;\n\n while (true) {\n const attempt = await attemptLock();\n if (attempt.acquired) return attempt.value;\n if (Date.now() >= deadline) throw new Error(`Timed out waiting for ${lockDescription}`);\n await new Promise((resolve) => setTimeout(resolve, retryDelayMs));\n retryDelayMs = Math.min(retryDelayMs * 2, JIRA_ADVISORY_LOCK_MAX_RETRY_DELAY_MS);\n }\n}\n\nexport async function getJiraInstallationByConnectionId(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<JiraInstallation | undefined> {\n const executor = (options.tx ?? db()) as JiraDb | JiraTx;\n const rows = await executor\n .select()\n .from(jiraInstallations)\n .where(eq(jiraInstallations.connectionId, connectionId))\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toJiraInstallation(row);\n}\n\nexport async function getJiraInstallationByWebhookId(\n webhookId: number,\n options: {tx?: unknown} = {},\n): Promise<JiraInstallation | undefined> {\n const executor = (options.tx ?? db()) as JiraDb | JiraTx;\n const rows = await executor\n .select()\n .from(jiraInstallations)\n .where(sql`${jiraInstallations.webhookIds} @> ${JSON.stringify([webhookId])}::jsonb`)\n .limit(1);\n const row = rows[0];\n if (!row) return undefined;\n return toJiraInstallation(row);\n}\n\nexport async function markJiraInstallationRevoked(\n connectionId: string,\n options: {tx?: unknown} = {},\n): Promise<JiraInstallation | undefined> {\n const executor = (options.tx ?? db()) as JiraDb | JiraTx;\n const [row] = await executor\n .update(jiraInstallations)\n .set({status: 'revoked', updatedAt: new Date()})\n .where(eq(jiraInstallations.connectionId, connectionId))\n .returning();\n if (!row) return undefined;\n return toJiraInstallation(row);\n}\n"],"names":["isUniqueViolation","pgClient","withPostgresSession","and","asc","eq","lte","sql","JiraConnectionAlreadyLinkedError","JiraInstallationAlreadyLinkedError","JiraInstallationSiteMismatchError","db","jiraInstallations","toJiraInstallation","JIRA_ADVISORY_LOCK_RETRY_DELAY_MS","JIRA_ADVISORY_LOCK_MAX_RETRY_DELAY_MS","JIRA_ADVISORY_LOCK_TIMEOUT_MS","withJiraInstallationLock","lockKey","fn","advisoryKey","withJiraAdvisoryLockWait","client","lock","query","rows","acquired","value","upsertJiraInstallation","params","options","executor","tx","now","Date","row","insert","values","connectionId","cloudId","siteUrl","siteName","authorizingAccountId","scopes","webhookIds","webhookExpiresAt","status","tokenExpiresAt","refreshTokenLastUsedAt","refreshTokenLastAttemptedAt","onConflictDoUpdate","target","setWhere","set","undefined","updatedAt","returning","error","getJiraInstallationByCloudId","select","from","where","limit","updateJiraInstallationTokenExpiry","update","markJiraInstallationTokenRefreshAttempt","listJiraInstallationsDueForTokenRefresh","before","orderBy","map","updateJiraInstallationWebhook","deleteJiraInstallationByConnectionId","result","delete","rowCount","withJiraRefreshLock","withJiraRefreshLockClient","withJiraRefreshLockAndWait","withJiraWebhookRegistrationLock","connect","release","lockDescription","attemptLock","deadline","retryDelayMs","attempt","Error","Promise","resolve","setTimeout","Math","min","getJiraInstallationByConnectionId","getJiraInstallationByWebhookId","webhookId","JSON","stringify","markJiraInstallationRevoked"],"mappings":"AAAA,SAAQA,iBAAiB,QAAO,wBAAwB;AACxD,SAAQC,QAAQ,EAAEC,mBAAmB,QAAO,yBAAyB;AACrE,SAAQC,GAAG,EAAEC,GAAG,EAAEC,EAAE,EAAEC,GAAG,EAAEC,GAAG,QAAO,cAAc;AACnD,SACEC,gCAAgC,EAChCC,kCAAkC,EAClCC,iCAAiC,QAC5B,kBAAkB;AACzB,SAAQC,EAAE,QAAO,UAAU;AAC3B,SAAQC,iBAAiB,EAAEC,kBAAkB,QAAO,4BAA4B;AAIhF,MAAMC,oCAAoC;AAC1C,MAAMC,wCAAwC;AAC9C,MAAMC,gCAAgC;AAItC,OAAO,SAASC,yBAA4BC,OAAe,EAAEC,EAAoB;IAC/E,MAAMC,cAAc,CAAC,kBAAkB,EAAEF,SAAS;IAClD,OAAOG,yBAAyB,CAAC,wBAAwB,EAAEH,SAAS,EAAE,IACpEhB,oBAAoB,OAAOoB;YACzB,MAAMC,OAAO,MAAMD,OAAOE,KAAK,CAC7B,yDACA;gBAACJ;aAAY;YAEf,IAAIG,KAAKE,IAAI,CAAC,EAAE,EAAEC,aAAa,MAAM,OAAO;gBAACA,UAAU;YAAc;YAErE,IAAI;gBACF,OAAO;oBAACA,UAAU;oBAAeC,OAAO,MAAMR;gBAAI;YACpD,SAAU;gBACR,MAAMG,OAAOE,KAAK,CAAC,2CAA2C;oBAACJ;iBAAY;YAC7E;QACF;AAEJ;AAoDA,OAAO,eAAeQ,uBACpBC,MAAoC,EACpCC,UAA0B,CAAC,CAAC;IAE5B,MAAMC,WAAYD,QAAQE,EAAE,IAAIrB;IAChC,MAAMsB,MAAM,IAAIC;IAChB,IAAIC;IACJ,IAAI;QACF,CAACA,IAAI,GAAG,MAAMJ,SACXK,MAAM,CAACxB,mBACPyB,MAAM,CAAC;YACNC,cAAcT,OAAOS,YAAY;YACjCC,SAASV,OAAOU,OAAO;YACvBC,SAASX,OAAOW,OAAO;YACvBC,UAAUZ,OAAOY,QAAQ;YACzBC,sBAAsBb,OAAOa,oBAAoB;YACjDC,QAAQd,OAAOc,MAAM;YACrBC,YAAYf,OAAOe,UAAU,IAAI,EAAE;YACnCC,kBAAkBhB,OAAOgB,gBAAgB,IAAI;YAC7CC,QAAQjB,OAAOiB,MAAM;YACrBC,gBAAgBlB,OAAOkB,cAAc,IAAI;YACzCC,wBAAwBnB,OAAOmB,sBAAsB,IAAIf;YACzDgB,6BAA6BpB,OAAOoB,2BAA2B,IAAIhB;QACrE,GACCiB,kBAAkB,CAAC;YAClBC,QAAQvC,kBAAkB0B,YAAY;YACtCc,UAAU/C,GAAGO,kBAAkB2B,OAAO,EAAEV,OAAOU,OAAO;YACtDc,KAAK;gBACHd,SAASV,OAAOU,OAAO;gBACvBC,SAASX,OAAOW,OAAO;gBACvBC,UAAUZ,OAAOY,QAAQ;gBACzBC,sBAAsBb,OAAOa,oBAAoB;gBACjDC,QAAQd,OAAOc,MAAM;gBACrB,GAAId,OAAOe,UAAU,KAAKU,YAAY,CAAC,IAAI;oBAACV,YAAYf,OAAOe,UAAU;gBAAA,CAAC;gBAC1E,GAAIf,OAAOgB,gBAAgB,KAAKS,YAC5B,CAAC,IACD;oBAACT,kBAAkBhB,OAAOgB,gBAAgB;gBAAA,CAAC;gBAC/CC,QAAQjB,OAAOiB,MAAM;gBACrBC,gBAAgBlB,OAAOkB,cAAc,IAAI;gBACzCC,wBAAwBnB,OAAOmB,sBAAsB,IAAIf;gBACzDgB,6BAA6BpB,OAAOoB,2BAA2B,IAAIhB;gBACnEsB,WAAWtB;YACb;QACF,GACCuB,SAAS;IACd,EAAE,OAAOC,OAAO;QACd,IAAIzD,kBAAkByD,OAAO,sDAAsD;YACjF,MAAM,IAAIjD,iCAAiCqB,OAAOS,YAAY;QAChE;QACA,IAAItC,kBAAkByD,OAAO,oDAAoD;YAC/E,MAAM,IAAIhD,mCAAmCoB,OAAOU,OAAO;QAC7D;QACA,MAAMkB;IACR;IAEA,IAAI,CAACtB,KAAK,MAAM,IAAIzB,kCAAkCmB,OAAOS,YAAY,EAAET,OAAOU,OAAO;IACzF,OAAO1B,mBAAmBsB;AAC5B;AAEA,OAAO,eAAeuB,6BACpBnB,OAAe,EACfT,UAA0B,CAAC,CAAC;IAE5B,MAAMC,WAAYD,QAAQE,EAAE,IAAIrB;IAChC,MAAMc,OAAO,MAAMM,SAChB4B,MAAM,GACNC,IAAI,CAAChD,mBACLiD,KAAK,CAACxD,GAAGO,kBAAkB2B,OAAO,EAAEA,UACpCuB,KAAK,CAAC;IACT,MAAM3B,MAAMV,IAAI,CAAC,EAAE;IACnB,OAAOU,MAAMtB,mBAAmBsB,OAAOmB;AACzC;AAEA,OAAO,eAAeS,kCACpBlC,MAA+C,EAC/CC,UAA0B,CAAC,CAAC;IAE5B,MAAMC,WAAYD,QAAQE,EAAE,IAAIrB;IAChC,MAAMsB,MAAM,IAAIC;IAChB,MAAM,CAACC,IAAI,GAAG,MAAMJ,SACjBiC,MAAM,CAACpD,mBACPyC,GAAG,CAAC;QACHN,gBAAgBlB,OAAOkB,cAAc;QACrCC,wBAAwBf;QACxBgB,6BAA6BhB;QAC7B,GAAIJ,OAAOc,MAAM,KAAKW,YAAY,CAAC,IAAI;YAACX,QAAQd,OAAOc,MAAM;QAAA,CAAC;QAC9DY,WAAWtB;IACb,GACC4B,KAAK,CAACxD,GAAGO,kBAAkB0B,YAAY,EAAET,OAAOS,YAAY,GAC5DkB,SAAS;IACZ,OAAOrB,MAAMtB,mBAAmBsB,OAAOmB;AACzC;AAEA,OAAO,eAAeW,wCACpB3B,YAAoB,EACpBR,UAA0B,CAAC,CAAC;IAE5B,MAAMC,WAAYD,QAAQE,EAAE,IAAIrB;IAChC,MAAM,CAACwB,IAAI,GAAG,MAAMJ,SACjBiC,MAAM,CAACpD,mBACPyC,GAAG,CAAC;QAACJ,6BAA6B,IAAIf;IAAM,GAC5C2B,KAAK,CAACxD,GAAGO,kBAAkB0B,YAAY,EAAEA,eACzCkB,SAAS;IACZ,OAAOrB,MAAMtB,mBAAmBsB,OAAOmB;AACzC;AAEA,OAAO,eAAeY,wCACpBrC,MAAkD,EAClDC,UAA0B,CAAC,CAAC;IAE5B,MAAMC,WAAYD,QAAQE,EAAE,IAAIrB;IAChC,MAAMc,OAAO,MAAMM,SAChB4B,MAAM,GACNC,IAAI,CAAChD,mBACLiD,KAAK,CACJ1D,IACEE,GAAGO,kBAAkBkC,MAAM,EAAE,cAC7BxC,IAAIM,kBAAkBoC,sBAAsB,EAAEnB,OAAOsC,MAAM,IAG9DC,OAAO,CAAChE,IAAIQ,kBAAkBqC,2BAA2B,GACzDa,KAAK,CAACjC,OAAOiC,KAAK,IAAI;IACzB,OAAOrC,KAAK4C,GAAG,CAACxD;AAClB;AAEA,OAAO,eAAeyD,8BACpBzC,MAA2C,EAC3CC,UAA0B,CAAC,CAAC;IAE5B,MAAMC,WAAYD,QAAQE,EAAE,IAAIrB;IAChC,MAAM,CAACwB,IAAI,GAAG,MAAMJ,SACjBiC,MAAM,CAACpD,mBACPyC,GAAG,CAAC;QACHT,YAAYf,OAAOe,UAAU;QAC7BC,kBAAkBhB,OAAOgB,gBAAgB;QACzCU,WAAW,IAAIrB;IACjB,GACC2B,KAAK,CAACxD,GAAGO,kBAAkB0B,YAAY,EAAET,OAAOS,YAAY,GAC5DkB,SAAS;IACZ,OAAOrB,MAAMtB,mBAAmBsB,OAAOmB;AACzC;AAEA,OAAO,eAAeiB,qCACpBjC,YAAoB,EACpBR,UAA0B,CAAC,CAAC;IAE5B,MAAMC,WAAYD,QAAQE,EAAE,IAAIrB;IAChC,MAAM6D,SAAS,MAAMzC,SAClB0C,MAAM,CAAC7D,mBACPiD,KAAK,CAACxD,GAAGO,kBAAkB0B,YAAY,EAAEA;IAC5C,OAAO,AAACkC,CAAAA,OAAOE,QAAQ,IAAI,CAAA,IAAK;AAClC;AAIA,OAAO,SAASC,oBACdrC,YAAoB,EACpBnB,EAAoB;IAEpB,OAAOyD,0BAA0BtC,cAAcnB;AACjD;AAEA,OAAO,SAAS0D,2BACdvC,YAAoB,EACpBnB,EAAoB;IAEpB,OAAOE,yBAAyB,CAAC,mBAAmB,EAAEiB,cAAc,EAAE,IACpEqC,oBAAoBrC,cAAcnB;AAEtC;AAEA,OAAO,SAAS2D,gCACd5D,OAAe,EACfC,EAAgC;IAEhC,OAAOF,yBAAyBC,SAAS,IAAMC;AACjD;AAEA,eAAeyD,0BACbtC,YAAoB,EACpBnB,EAAoB;IAEpB,MAAMG,SAAS,MAAMrB,WAAW8E,OAAO;IACvC,IAAIrD,WAAW;IACf,IAAI;QACF,MAAMH,OAAO,MAAMD,OAAOE,KAAK,CAC7B,yDACA;YAACc;SAAa;QAEhBZ,WAAWH,KAAKE,IAAI,CAAC,EAAE,EAAEC,aAAa;QACtC,IAAI,CAACA,UAAU,OAAO;YAACA,UAAU;QAAK;QACtC,OAAO;YAACA,UAAU;YAAMC,OAAO,MAAMR;QAAI;IAC3C,SAAU;QACR,IAAI;YACF,IAAIO,UAAU,MAAMJ,OAAOE,KAAK,CAAC,2CAA2C;gBAACc;aAAa;QAC5F,SAAU;YACRhB,OAAO0D,OAAO;QAChB;IACF;AACF;AAEA,eAAe3D,yBACb4D,eAAuB,EACvBC,WAAsD;IAEtD,MAAMC,WAAWjD,KAAKD,GAAG,KAAKjB;IAC9B,IAAIoE,eAAetE;IAEnB,MAAO,KAAM;QACX,MAAMuE,UAAU,MAAMH;QACtB,IAAIG,QAAQ3D,QAAQ,EAAE,OAAO2D,QAAQ1D,KAAK;QAC1C,IAAIO,KAAKD,GAAG,MAAMkD,UAAU,MAAM,IAAIG,MAAM,CAAC,sBAAsB,EAAEL,iBAAiB;QACtF,MAAM,IAAIM,QAAQ,CAACC,UAAYC,WAAWD,SAASJ;QACnDA,eAAeM,KAAKC,GAAG,CAACP,eAAe,GAAGrE;IAC5C;AACF;AAEA,OAAO,eAAe6E,kCACpBtD,YAAoB,EACpBR,UAA0B,CAAC,CAAC;IAE5B,MAAMC,WAAYD,QAAQE,EAAE,IAAIrB;IAChC,MAAMc,OAAO,MAAMM,SAChB4B,MAAM,GACNC,IAAI,CAAChD,mBACLiD,KAAK,CAACxD,GAAGO,kBAAkB0B,YAAY,EAAEA,eACzCwB,KAAK,CAAC;IACT,MAAM3B,MAAMV,IAAI,CAAC,EAAE;IACnB,IAAI,CAACU,KAAK,OAAOmB;IACjB,OAAOzC,mBAAmBsB;AAC5B;AAEA,OAAO,eAAe0D,+BACpBC,SAAiB,EACjBhE,UAA0B,CAAC,CAAC;IAE5B,MAAMC,WAAYD,QAAQE,EAAE,IAAIrB;IAChC,MAAMc,OAAO,MAAMM,SAChB4B,MAAM,GACNC,IAAI,CAAChD,mBACLiD,KAAK,CAACtD,GAAG,CAAC,EAAEK,kBAAkBgC,UAAU,CAAC,IAAI,EAAEmD,KAAKC,SAAS,CAAC;QAACF;KAAU,EAAE,OAAO,CAAC,EACnFhC,KAAK,CAAC;IACT,MAAM3B,MAAMV,IAAI,CAAC,EAAE;IACnB,IAAI,CAACU,KAAK,OAAOmB;IACjB,OAAOzC,mBAAmBsB;AAC5B;AAEA,OAAO,eAAe8D,4BACpB3D,YAAoB,EACpBR,UAA0B,CAAC,CAAC;IAE5B,MAAMC,WAAYD,QAAQE,EAAE,IAAIrB;IAChC,MAAM,CAACwB,IAAI,GAAG,MAAMJ,SACjBiC,MAAM,CAACpD,mBACPyC,GAAG,CAAC;QAACP,QAAQ;QAAWS,WAAW,IAAIrB;IAAM,GAC7C2B,KAAK,CAACxD,GAAGO,kBAAkB0B,YAAY,EAAEA,eACzCkB,SAAS;IACZ,IAAI,CAACrB,KAAK,OAAOmB;IACjB,OAAOzC,mBAAmBsB;AAC5B"}
|
|
@@ -196,6 +196,40 @@ export declare const jiraInstallations: import("drizzle-orm/pg-core").PgTableWit
|
|
|
196
196
|
identity: undefined;
|
|
197
197
|
generated: undefined;
|
|
198
198
|
}, {}, {}>;
|
|
199
|
+
refreshTokenLastUsedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
200
|
+
name: "refresh_token_last_used_at";
|
|
201
|
+
tableName: "installations";
|
|
202
|
+
dataType: "date";
|
|
203
|
+
columnType: "PgTimestamp";
|
|
204
|
+
data: Date;
|
|
205
|
+
driverParam: string;
|
|
206
|
+
notNull: true;
|
|
207
|
+
hasDefault: true;
|
|
208
|
+
isPrimaryKey: false;
|
|
209
|
+
isAutoincrement: false;
|
|
210
|
+
hasRuntimeDefault: false;
|
|
211
|
+
enumValues: undefined;
|
|
212
|
+
baseColumn: never;
|
|
213
|
+
identity: undefined;
|
|
214
|
+
generated: undefined;
|
|
215
|
+
}, {}, {}>;
|
|
216
|
+
refreshTokenLastAttemptedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
217
|
+
name: "refresh_token_last_attempted_at";
|
|
218
|
+
tableName: "installations";
|
|
219
|
+
dataType: "date";
|
|
220
|
+
columnType: "PgTimestamp";
|
|
221
|
+
data: Date;
|
|
222
|
+
driverParam: string;
|
|
223
|
+
notNull: true;
|
|
224
|
+
hasDefault: true;
|
|
225
|
+
isPrimaryKey: false;
|
|
226
|
+
isAutoincrement: false;
|
|
227
|
+
hasRuntimeDefault: false;
|
|
228
|
+
enumValues: undefined;
|
|
229
|
+
baseColumn: never;
|
|
230
|
+
identity: undefined;
|
|
231
|
+
generated: undefined;
|
|
232
|
+
}, {}, {}>;
|
|
199
233
|
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
200
234
|
name: "created_at";
|
|
201
235
|
tableName: "installations";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installations.d.ts","sourceRoot":"","sources":["../../../src/db/schema/installations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,sBAAsB,CAAC;AAG3D,eAAO,MAAM,iBAAiB
|
|
1
|
+
{"version":3,"file":"installations.d.ts","sourceRoot":"","sources":["../../../src/db/schema/installations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,sBAAsB,CAAC;AAG3D,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuC7B,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,OAAO,iBAAiB,CAAC,YAAY,CAAC;AACvE,MAAM,MAAM,wBAAwB,GAAG,OAAO,iBAAiB,CAAC,YAAY,CAAC;AAE7E,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,kBAAkB,GAAG,gBAAgB,CAkB5E"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { uuidv7PrimaryKey } from '@shipfox/node-drizzle';
|
|
2
|
-
import { jsonb, text, timestamp, uniqueIndex, uuid } from 'drizzle-orm/pg-core';
|
|
2
|
+
import { index, jsonb, text, timestamp, uniqueIndex, uuid } from 'drizzle-orm/pg-core';
|
|
3
3
|
import { pgTable } from './common.js';
|
|
4
4
|
export const jiraInstallations = pgTable('installations', {
|
|
5
5
|
id: uuidv7PrimaryKey(),
|
|
@@ -17,6 +17,12 @@ export const jiraInstallations = pgTable('installations', {
|
|
|
17
17
|
tokenExpiresAt: timestamp('token_expires_at', {
|
|
18
18
|
withTimezone: true
|
|
19
19
|
}),
|
|
20
|
+
refreshTokenLastUsedAt: timestamp('refresh_token_last_used_at', {
|
|
21
|
+
withTimezone: true
|
|
22
|
+
}).notNull().defaultNow(),
|
|
23
|
+
refreshTokenLastAttemptedAt: timestamp('refresh_token_last_attempted_at', {
|
|
24
|
+
withTimezone: true
|
|
25
|
+
}).notNull().defaultNow(),
|
|
20
26
|
createdAt: timestamp('created_at', {
|
|
21
27
|
withTimezone: true
|
|
22
28
|
}).notNull().defaultNow(),
|
|
@@ -25,7 +31,9 @@ export const jiraInstallations = pgTable('installations', {
|
|
|
25
31
|
}).notNull().defaultNow()
|
|
26
32
|
}, (table)=>[
|
|
27
33
|
uniqueIndex('integrations_jira_installations_connection_unique').on(table.connectionId),
|
|
28
|
-
uniqueIndex('integrations_jira_installations_cloud_id_unique').on(table.cloudId)
|
|
34
|
+
uniqueIndex('integrations_jira_installations_cloud_id_unique').on(table.cloudId),
|
|
35
|
+
index('integrations_jira_installations_token_refresh_due_idx').on(table.status, table.refreshTokenLastUsedAt),
|
|
36
|
+
index('integrations_jira_installations_token_refresh_attempt_idx').on(table.status, table.refreshTokenLastAttemptedAt)
|
|
29
37
|
]);
|
|
30
38
|
export function toJiraInstallation(row) {
|
|
31
39
|
return {
|
|
@@ -40,6 +48,8 @@ export function toJiraInstallation(row) {
|
|
|
40
48
|
webhookExpiresAt: row.webhookExpiresAt,
|
|
41
49
|
status: row.status,
|
|
42
50
|
tokenExpiresAt: row.tokenExpiresAt,
|
|
51
|
+
refreshTokenLastUsedAt: row.refreshTokenLastUsedAt,
|
|
52
|
+
refreshTokenLastAttemptedAt: row.refreshTokenLastAttemptedAt,
|
|
43
53
|
createdAt: row.createdAt,
|
|
44
54
|
updatedAt: row.updatedAt
|
|
45
55
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/db/schema/installations.ts"],"sourcesContent":["import {uuidv7PrimaryKey} from '@shipfox/node-drizzle';\nimport {jsonb, text, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';\nimport type {JiraInstallation} from '#db/installations.js';\nimport {pgTable} from './common.js';\n\nexport const jiraInstallations = pgTable(\n 'installations',\n {\n id: uuidv7PrimaryKey(),\n connectionId: uuid('connection_id').notNull(),\n cloudId: text('cloud_id').notNull(),\n siteUrl: text('site_url').notNull(),\n siteName: text('site_name').notNull(),\n authorizingAccountId: text('authorizing_account_id').notNull(),\n scopes: jsonb('scopes').$type<string[]>().notNull(),\n webhookIds: jsonb('webhook_ids').$type<number[]>().notNull().default([]),\n webhookExpiresAt: timestamp('webhook_expires_at', {withTimezone: true}),\n status: text('status').notNull().$type<JiraInstallation['status']>(),\n tokenExpiresAt: timestamp('token_expires_at', {withTimezone: true}),\n createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),\n updatedAt: timestamp('updated_at', {withTimezone: true}).notNull().defaultNow(),\n },\n (table) => [\n uniqueIndex('integrations_jira_installations_connection_unique').on(table.connectionId),\n uniqueIndex('integrations_jira_installations_cloud_id_unique').on(table.cloudId),\n ],\n);\n\nexport type JiraInstallationDb = typeof jiraInstallations.$inferSelect;\nexport type JiraInstallationCreateDb = typeof jiraInstallations.$inferInsert;\n\nexport function toJiraInstallation(row: JiraInstallationDb): JiraInstallation {\n return {\n id: row.id,\n connectionId: row.connectionId,\n cloudId: row.cloudId,\n siteUrl: row.siteUrl,\n siteName: row.siteName,\n authorizingAccountId: row.authorizingAccountId,\n scopes: row.scopes,\n webhookIds: row.webhookIds,\n webhookExpiresAt: row.webhookExpiresAt,\n status: row.status,\n tokenExpiresAt: row.tokenExpiresAt,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n"],"names":["uuidv7PrimaryKey","jsonb","text","timestamp","uniqueIndex","uuid","pgTable","jiraInstallations","id","connectionId","notNull","cloudId","siteUrl","siteName","authorizingAccountId","scopes","$type","webhookIds","default","webhookExpiresAt","withTimezone","status","tokenExpiresAt","
|
|
1
|
+
{"version":3,"sources":["../../../src/db/schema/installations.ts"],"sourcesContent":["import {uuidv7PrimaryKey} from '@shipfox/node-drizzle';\nimport {index, jsonb, text, timestamp, uniqueIndex, uuid} from 'drizzle-orm/pg-core';\nimport type {JiraInstallation} from '#db/installations.js';\nimport {pgTable} from './common.js';\n\nexport const jiraInstallations = pgTable(\n 'installations',\n {\n id: uuidv7PrimaryKey(),\n connectionId: uuid('connection_id').notNull(),\n cloudId: text('cloud_id').notNull(),\n siteUrl: text('site_url').notNull(),\n siteName: text('site_name').notNull(),\n authorizingAccountId: text('authorizing_account_id').notNull(),\n scopes: jsonb('scopes').$type<string[]>().notNull(),\n webhookIds: jsonb('webhook_ids').$type<number[]>().notNull().default([]),\n webhookExpiresAt: timestamp('webhook_expires_at', {withTimezone: true}),\n status: text('status').notNull().$type<JiraInstallation['status']>(),\n tokenExpiresAt: timestamp('token_expires_at', {withTimezone: true}),\n refreshTokenLastUsedAt: timestamp('refresh_token_last_used_at', {\n withTimezone: true,\n })\n .notNull()\n .defaultNow(),\n refreshTokenLastAttemptedAt: timestamp('refresh_token_last_attempted_at', {\n withTimezone: true,\n })\n .notNull()\n .defaultNow(),\n createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),\n updatedAt: timestamp('updated_at', {withTimezone: true}).notNull().defaultNow(),\n },\n (table) => [\n uniqueIndex('integrations_jira_installations_connection_unique').on(table.connectionId),\n uniqueIndex('integrations_jira_installations_cloud_id_unique').on(table.cloudId),\n index('integrations_jira_installations_token_refresh_due_idx').on(\n table.status,\n table.refreshTokenLastUsedAt,\n ),\n index('integrations_jira_installations_token_refresh_attempt_idx').on(\n table.status,\n table.refreshTokenLastAttemptedAt,\n ),\n ],\n);\n\nexport type JiraInstallationDb = typeof jiraInstallations.$inferSelect;\nexport type JiraInstallationCreateDb = typeof jiraInstallations.$inferInsert;\n\nexport function toJiraInstallation(row: JiraInstallationDb): JiraInstallation {\n return {\n id: row.id,\n connectionId: row.connectionId,\n cloudId: row.cloudId,\n siteUrl: row.siteUrl,\n siteName: row.siteName,\n authorizingAccountId: row.authorizingAccountId,\n scopes: row.scopes,\n webhookIds: row.webhookIds,\n webhookExpiresAt: row.webhookExpiresAt,\n status: row.status,\n tokenExpiresAt: row.tokenExpiresAt,\n refreshTokenLastUsedAt: row.refreshTokenLastUsedAt,\n refreshTokenLastAttemptedAt: row.refreshTokenLastAttemptedAt,\n createdAt: row.createdAt,\n updatedAt: row.updatedAt,\n };\n}\n"],"names":["uuidv7PrimaryKey","index","jsonb","text","timestamp","uniqueIndex","uuid","pgTable","jiraInstallations","id","connectionId","notNull","cloudId","siteUrl","siteName","authorizingAccountId","scopes","$type","webhookIds","default","webhookExpiresAt","withTimezone","status","tokenExpiresAt","refreshTokenLastUsedAt","defaultNow","refreshTokenLastAttemptedAt","createdAt","updatedAt","table","on","toJiraInstallation","row"],"mappings":"AAAA,SAAQA,gBAAgB,QAAO,wBAAwB;AACvD,SAAQC,KAAK,EAAEC,KAAK,EAAEC,IAAI,EAAEC,SAAS,EAAEC,WAAW,EAAEC,IAAI,QAAO,sBAAsB;AAErF,SAAQC,OAAO,QAAO,cAAc;AAEpC,OAAO,MAAMC,oBAAoBD,QAC/B,iBACA;IACEE,IAAIT;IACJU,cAAcJ,KAAK,iBAAiBK,OAAO;IAC3CC,SAAST,KAAK,YAAYQ,OAAO;IACjCE,SAASV,KAAK,YAAYQ,OAAO;IACjCG,UAAUX,KAAK,aAAaQ,OAAO;IACnCI,sBAAsBZ,KAAK,0BAA0BQ,OAAO;IAC5DK,QAAQd,MAAM,UAAUe,KAAK,GAAaN,OAAO;IACjDO,YAAYhB,MAAM,eAAee,KAAK,GAAaN,OAAO,GAAGQ,OAAO,CAAC,EAAE;IACvEC,kBAAkBhB,UAAU,sBAAsB;QAACiB,cAAc;IAAI;IACrEC,QAAQnB,KAAK,UAAUQ,OAAO,GAAGM,KAAK;IACtCM,gBAAgBnB,UAAU,oBAAoB;QAACiB,cAAc;IAAI;IACjEG,wBAAwBpB,UAAU,8BAA8B;QAC9DiB,cAAc;IAChB,GACGV,OAAO,GACPc,UAAU;IACbC,6BAA6BtB,UAAU,mCAAmC;QACxEiB,cAAc;IAChB,GACGV,OAAO,GACPc,UAAU;IACbE,WAAWvB,UAAU,cAAc;QAACiB,cAAc;IAAI,GAAGV,OAAO,GAAGc,UAAU;IAC7EG,WAAWxB,UAAU,cAAc;QAACiB,cAAc;IAAI,GAAGV,OAAO,GAAGc,UAAU;AAC/E,GACA,CAACI,QAAU;QACTxB,YAAY,qDAAqDyB,EAAE,CAACD,MAAMnB,YAAY;QACtFL,YAAY,mDAAmDyB,EAAE,CAACD,MAAMjB,OAAO;QAC/EX,MAAM,yDAAyD6B,EAAE,CAC/DD,MAAMP,MAAM,EACZO,MAAML,sBAAsB;QAE9BvB,MAAM,6DAA6D6B,EAAE,CACnED,MAAMP,MAAM,EACZO,MAAMH,2BAA2B;KAEpC,EACD;AAKF,OAAO,SAASK,mBAAmBC,GAAuB;IACxD,OAAO;QACLvB,IAAIuB,IAAIvB,EAAE;QACVC,cAAcsB,IAAItB,YAAY;QAC9BE,SAASoB,IAAIpB,OAAO;QACpBC,SAASmB,IAAInB,OAAO;QACpBC,UAAUkB,IAAIlB,QAAQ;QACtBC,sBAAsBiB,IAAIjB,oBAAoB;QAC9CC,QAAQgB,IAAIhB,MAAM;QAClBE,YAAYc,IAAId,UAAU;QAC1BE,kBAAkBY,IAAIZ,gBAAgB;QACtCE,QAAQU,IAAIV,MAAM;QAClBC,gBAAgBS,IAAIT,cAAc;QAClCC,wBAAwBQ,IAAIR,sBAAsB;QAClDE,6BAA6BM,IAAIN,2BAA2B;QAC5DC,WAAWK,IAAIL,SAAS;QACxBC,WAAWI,IAAIJ,SAAS;IAC1B;AACF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import { type JiraApiClient } from '#api/client.js';
|
|
1
|
+
import { type JiraAgentToolsClient, type JiraApiClient } from '#api/client.js';
|
|
2
2
|
import { config } from '#config.js';
|
|
3
|
+
import { JiraAgentToolsProvider } from '#core/agent-tools-provider.js';
|
|
4
|
+
import type { JiraTokenStore } from '#core/tokens.js';
|
|
3
5
|
import { closeDb, db } from '#db/db.js';
|
|
4
6
|
import { getJiraInstallationByConnectionId } from '#db/installations.js';
|
|
5
7
|
import { migrationsPath } from '#db/migrations.js';
|
|
6
8
|
import { type CreateJiraIntegrationRoutesOptions } from '#presentation/routes/install.js';
|
|
9
|
+
import { type CreateJiraWebhookRoutesOptions } from '#presentation/routes/webhooks.js';
|
|
7
10
|
export type { JiraProvider } from '@shipfox/api-integration-jira-dto';
|
|
8
|
-
export type { JiraAccessibleResource, JiraApiClient, JiraAuthorization, JiraIdentity, } from '#api/client.js';
|
|
9
|
-
export { createJiraApiClient, mapJiraError } from '#api/client.js';
|
|
11
|
+
export type { JiraAccessibleResource, JiraAgentToolHttpMethod, JiraAgentToolQueryValue, JiraAgentToolRequest, JiraAgentToolResponse, JiraAgentToolsClient, JiraApiClient, JiraAuthorization, JiraDynamicWebhookRegistration, JiraIdentity, } from '#api/client.js';
|
|
12
|
+
export { createJiraAgentToolsClient, createJiraApiClient, JIRA_DYNAMIC_WEBHOOK_EVENTS, JIRA_DYNAMIC_WEBHOOK_JQL, mapJiraError, } from '#api/client.js';
|
|
13
|
+
export type { JiraAgentToolCatalogEntry, JiraAgentToolId, JiraAgentToolRequiredScope, } from '#core/agent-tools.js';
|
|
14
|
+
export { jiraAgentToolCatalog, jiraAgentToolSelectionCatalog, jiraPlainTextToAdf, } from '#core/agent-tools.js';
|
|
15
|
+
export type { JiraAgentToolsProviderOptions, JiraToolCallResult, } from '#core/agent-tools-provider.js';
|
|
16
|
+
export { JiraAgentToolsProvider } from '#core/agent-tools-provider.js';
|
|
10
17
|
export type { DisconnectJiraInstallationParams } from '#core/disconnect.js';
|
|
11
18
|
export { disconnectJiraInstallation } from '#core/disconnect.js';
|
|
12
19
|
export { JiraAccessTokenMissingError, JiraAuthorizationScopeMismatchError, JiraConnectionAlreadyLinkedError, JiraConnectionNotFoundError, JiraInstallationAlreadyLinkedError, JiraInstallationSiteMismatchError, JiraInstallStateActorMismatchError, JiraInstallStateError, JiraIntegrationProviderError, JiraOAuthCallbackError, JiraOfflineAccessNotGrantedError, JiraPendingSelectionNotFoundError, JiraSiteSelectionMismatchError, JiraTokenUnrefreshableError, } from '#core/errors.js';
|
|
@@ -18,21 +25,41 @@ export { assertJiraAuthorizationScopes, formatJiraOAuthScopes, JIRA_OAUTH_SCOPES
|
|
|
18
25
|
export { signJiraInstallState, verifyJiraInstallState } from '#core/state.js';
|
|
19
26
|
export type { CreateJiraTokenStoreParams, GetJiraAccessTokenParams, JiraConnectionResolverResult, JiraSecretsStore, JiraTokenStore, StoreJiraTokensParams, } from '#core/tokens.js';
|
|
20
27
|
export { createJiraTokenStore, jiraSecretsNamespace } from '#core/tokens.js';
|
|
21
|
-
export type {
|
|
22
|
-
export {
|
|
28
|
+
export type { CreateJiraWebhookProcessorOptions, JiraWebhookProcessor, } from '#core/webhook-processor.js';
|
|
29
|
+
export { createJiraWebhookProcessor } from '#core/webhook-processor.js';
|
|
30
|
+
export type { RegisterJiraWebhookParams } from '#core/webhook-registration.js';
|
|
31
|
+
export { JIRA_WEBHOOK_TTL_MS, registerJiraWebhook } from '#core/webhook-registration.js';
|
|
32
|
+
export type { JiraInstallation, JiraInstallationLock, JiraInstallationStatus, UpdateJiraInstallationWebhookParams, UpsertJiraInstallationParams, } from '#db/installations.js';
|
|
33
|
+
export { deleteJiraInstallationByConnectionId, getJiraInstallationByCloudId, getJiraInstallationByConnectionId, getJiraInstallationByWebhookId, listJiraInstallationsDueForTokenRefresh, markJiraInstallationRevoked, updateJiraInstallationTokenExpiry, updateJiraInstallationWebhook, upsertJiraInstallation, withJiraRefreshLock, withJiraWebhookRegistrationLock, } from '#db/installations.js';
|
|
34
|
+
export type { CreateJiraWebhookRoutesOptions } from '#presentation/routes/webhooks.js';
|
|
35
|
+
export { createJiraWebhookRoutes } from '#presentation/routes/webhooks.js';
|
|
36
|
+
export { createJiraMaintenanceWorker } from '#temporal/worker.js';
|
|
23
37
|
export { closeDb, config, db, migrationsPath };
|
|
24
38
|
export interface CreateJiraIntegrationProviderOptions {
|
|
25
39
|
jira?: JiraApiClient | undefined;
|
|
40
|
+
agentTools?: {
|
|
41
|
+
tokenStore: Pick<JiraTokenStore, 'getAccessToken'>;
|
|
42
|
+
jira?: JiraAgentToolsClient | undefined;
|
|
43
|
+
} | undefined;
|
|
26
44
|
getJiraInstallationByConnectionId?: typeof getJiraInstallationByConnectionId | undefined;
|
|
27
|
-
routes?:
|
|
45
|
+
routes?: JiraIntegrationProviderRoutesOptions | undefined;
|
|
28
46
|
}
|
|
47
|
+
type JiraIntegrationProviderRoutesOptions = Omit<CreateJiraIntegrationRoutesOptions, 'jira' | 'connectionCapabilities' | 'registerJiraWebhook'> & Omit<CreateJiraWebhookRoutesOptions, 'processor'>;
|
|
29
48
|
export declare function createJiraIntegrationProvider(options?: CreateJiraIntegrationProviderOptions): {
|
|
30
49
|
provider: string;
|
|
31
50
|
displayName: string;
|
|
32
|
-
adapters: {
|
|
51
|
+
adapters: {
|
|
52
|
+
agent_tools: JiraAgentToolsProvider;
|
|
53
|
+
} | {
|
|
54
|
+
agent_tools?: never;
|
|
55
|
+
};
|
|
33
56
|
connectionExternalUrl(connection: {
|
|
34
57
|
id: string;
|
|
35
58
|
}): Promise<string | undefined>;
|
|
36
59
|
routes: import("@shipfox/node-fastify").RouteGroup[];
|
|
60
|
+
webhookProcessors: {
|
|
61
|
+
routeIds: readonly ["jira"];
|
|
62
|
+
processor: import("#core/webhook-processor.js").JiraWebhookProcessor;
|
|
63
|
+
}[] | undefined;
|
|
37
64
|
};
|
|
38
65
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|