@shipfox/api-integration-jira 4.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +21 -0
- package/dist/api/client.d.ts +33 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +143 -0
- package/dist/api/client.js.map +1 -0
- package/dist/core/disconnect.d.ts +18 -0
- package/dist/core/disconnect.d.ts.map +1 -0
- package/dist/core/disconnect.js +21 -0
- package/dist/core/disconnect.js.map +1 -0
- package/dist/core/errors.d.ts +37 -0
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +63 -0
- package/dist/core/errors.js.map +1 -1
- package/dist/core/install.d.ts +55 -0
- package/dist/core/install.d.ts.map +1 -0
- package/dist/core/install.js +121 -0
- package/dist/core/install.js.map +1 -0
- package/dist/core/pending.d.ts +43 -0
- package/dist/core/pending.d.ts.map +1 -0
- package/dist/core/pending.js +107 -0
- package/dist/core/pending.js.map +1 -0
- package/dist/core/scopes.d.ts +4 -0
- package/dist/core/scopes.d.ts.map +1 -0
- package/dist/core/scopes.js +23 -0
- package/dist/core/scopes.js.map +1 -0
- package/dist/core/state.d.ts +12 -0
- package/dist/core/state.d.ts.map +1 -0
- package/dist/core/state.js +51 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/tokens.d.ts +6 -0
- package/dist/core/tokens.d.ts.map +1 -1
- package/dist/core/tokens.js +82 -6
- package/dist/core/tokens.js.map +1 -1
- package/dist/db/db.d.ts +188 -0
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/db.js +3 -1
- package/dist/db/db.js.map +1 -1
- package/dist/db/installations.d.ts +21 -0
- package/dist/db/installations.d.ts.map +1 -1
- package/dist/db/installations.js +82 -19
- package/dist/db/installations.js.map +1 -1
- package/dist/db/pending-selections.d.ts +17 -0
- package/dist/db/pending-selections.d.ts.map +1 -0
- package/dist/db/pending-selections.js +27 -0
- package/dist/db/pending-selections.js.map +1 -0
- package/dist/db/schema/installations.js +2 -2
- package/dist/db/schema/installations.js.map +1 -1
- package/dist/db/schema/pending-selections.d.ts +105 -0
- package/dist/db/schema/pending-selections.d.ts.map +1 -0
- package/dist/db/schema/pending-selections.js +24 -0
- package/dist/db/schema/pending-selections.js.map +1 -0
- package/dist/index.d.ts +26 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -4
- package/dist/index.js.map +1 -1
- package/dist/presentation/dto/integrations.d.ts +2 -0
- package/dist/presentation/dto/integrations.d.ts.map +1 -0
- package/dist/presentation/dto/integrations.js +3 -0
- package/dist/presentation/dto/integrations.js.map +1 -0
- package/dist/presentation/routes/errors.d.ts +2 -0
- package/dist/presentation/routes/errors.d.ts.map +1 -0
- package/dist/presentation/routes/errors.js +58 -0
- package/dist/presentation/routes/errors.js.map +1 -0
- package/dist/presentation/routes/install.d.ts +21 -0
- package/dist/presentation/routes/install.d.ts.map +1 -0
- package/dist/presentation/routes/install.js +130 -0
- package/dist/presentation/routes/install.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/drizzle/0000_whole_master_chief.sql +8 -1
- package/drizzle/meta/0000_snapshot.json +47 -3
- package/package.json +21 -24
- package/src/api/client.test.ts +29 -0
- package/src/api/client.ts +213 -0
- package/src/core/disconnect.test.ts +24 -0
- package/src/core/disconnect.ts +25 -0
- package/src/core/errors.ts +79 -0
- package/src/core/install.test.ts +129 -0
- package/src/core/install.ts +191 -0
- package/src/core/pending.ts +146 -0
- package/src/core/scopes.test.ts +19 -0
- package/src/core/scopes.ts +20 -0
- package/src/core/state.test.ts +38 -0
- package/src/core/state.ts +79 -0
- package/src/core/tokens-refresh.test.ts +106 -0
- package/src/core/tokens.ts +145 -8
- package/src/db/db.ts +2 -1
- package/src/db/installations.test.ts +37 -1
- package/src/db/installations.ts +120 -23
- package/src/db/pending-selections.ts +55 -0
- package/src/db/schema/installations.ts +2 -2
- package/src/db/schema/pending-selections.ts +31 -0
- package/src/index.test.ts +7 -2
- package/src/index.ts +66 -2
- package/src/presentation/dto/integrations.ts +1 -0
- package/src/presentation/routes/errors.ts +61 -0
- package/src/presentation/routes/install.ts +147 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/turbo.json +9 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pending-selections.d.ts","sourceRoot":"","sources":["../../../src/db/schema/pending-selections.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,gBAAgB,CAAC;AAG3D,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAMhC,CAAC;AAEH,MAAM,MAAM,sBAAsB,GAAG,OAAO,qBAAqB,CAAC,YAAY,CAAC;AAE/E,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,IAAI,CAAC;IAChB,KAAK,EAAE,sBAAsB,EAAE,CAAC;IAChC,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,sBAAsB,GAAG,oBAAoB,CAQxF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsonb, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
|
2
|
+
import { pgTable } from './common.js';
|
|
3
|
+
export const jiraPendingSelections = pgTable('pending_selections', {
|
|
4
|
+
stateHash: text('state_hash').primaryKey(),
|
|
5
|
+
workspaceId: uuid('workspace_id').notNull(),
|
|
6
|
+
expiresAt: timestamp('expires_at', {
|
|
7
|
+
withTimezone: true
|
|
8
|
+
}).notNull(),
|
|
9
|
+
sites: jsonb('sites').$type().notNull(),
|
|
10
|
+
createdAt: timestamp('created_at', {
|
|
11
|
+
withTimezone: true
|
|
12
|
+
}).notNull().defaultNow()
|
|
13
|
+
});
|
|
14
|
+
export function toJiraPendingSelection(row) {
|
|
15
|
+
return {
|
|
16
|
+
stateHash: row.stateHash,
|
|
17
|
+
workspaceId: row.workspaceId,
|
|
18
|
+
expiresAt: row.expiresAt,
|
|
19
|
+
sites: row.sites,
|
|
20
|
+
createdAt: row.createdAt
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=pending-selections.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/db/schema/pending-selections.ts"],"sourcesContent":["import {jsonb, text, timestamp, uuid} from 'drizzle-orm/pg-core';\nimport type {JiraAccessibleResource} from '#api/client.js';\nimport {pgTable} from './common.js';\n\nexport const jiraPendingSelections = pgTable('pending_selections', {\n stateHash: text('state_hash').primaryKey(),\n workspaceId: uuid('workspace_id').notNull(),\n expiresAt: timestamp('expires_at', {withTimezone: true}).notNull(),\n sites: jsonb('sites').$type<JiraAccessibleResource[]>().notNull(),\n createdAt: timestamp('created_at', {withTimezone: true}).notNull().defaultNow(),\n});\n\nexport type JiraPendingSelectionDb = typeof jiraPendingSelections.$inferSelect;\n\nexport interface JiraPendingSelection {\n stateHash: string;\n workspaceId: string;\n expiresAt: Date;\n sites: JiraAccessibleResource[];\n createdAt: Date;\n}\n\nexport function toJiraPendingSelection(row: JiraPendingSelectionDb): JiraPendingSelection {\n return {\n stateHash: row.stateHash,\n workspaceId: row.workspaceId,\n expiresAt: row.expiresAt,\n sites: row.sites,\n createdAt: row.createdAt,\n };\n}\n"],"names":["jsonb","text","timestamp","uuid","pgTable","jiraPendingSelections","stateHash","primaryKey","workspaceId","notNull","expiresAt","withTimezone","sites","$type","createdAt","defaultNow","toJiraPendingSelection","row"],"mappings":"AAAA,SAAQA,KAAK,EAAEC,IAAI,EAAEC,SAAS,EAAEC,IAAI,QAAO,sBAAsB;AAEjE,SAAQC,OAAO,QAAO,cAAc;AAEpC,OAAO,MAAMC,wBAAwBD,QAAQ,sBAAsB;IACjEE,WAAWL,KAAK,cAAcM,UAAU;IACxCC,aAAaL,KAAK,gBAAgBM,OAAO;IACzCC,WAAWR,UAAU,cAAc;QAACS,cAAc;IAAI,GAAGF,OAAO;IAChEG,OAAOZ,MAAM,SAASa,KAAK,GAA6BJ,OAAO;IAC/DK,WAAWZ,UAAU,cAAc;QAACS,cAAc;IAAI,GAAGF,OAAO,GAAGM,UAAU;AAC/E,GAAG;AAYH,OAAO,SAASC,uBAAuBC,GAA2B;IAChE,OAAO;QACLX,WAAWW,IAAIX,SAAS;QACxBE,aAAaS,IAAIT,WAAW;QAC5BE,WAAWO,IAAIP,SAAS;QACxBE,OAAOK,IAAIL,KAAK;QAChBE,WAAWG,IAAIH,SAAS;IAC1B;AACF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,38 @@
|
|
|
1
|
+
import { type JiraApiClient } from '#api/client.js';
|
|
1
2
|
import { config } from '#config.js';
|
|
2
3
|
import { closeDb, db } from '#db/db.js';
|
|
4
|
+
import { getJiraInstallationByConnectionId } from '#db/installations.js';
|
|
3
5
|
import { migrationsPath } from '#db/migrations.js';
|
|
6
|
+
import { type CreateJiraIntegrationRoutesOptions } from '#presentation/routes/install.js';
|
|
4
7
|
export type { JiraProvider } from '@shipfox/api-integration-jira-dto';
|
|
5
|
-
export {
|
|
8
|
+
export type { JiraAccessibleResource, JiraApiClient, JiraAuthorization, JiraIdentity, } from '#api/client.js';
|
|
9
|
+
export { createJiraApiClient, mapJiraError } from '#api/client.js';
|
|
10
|
+
export type { DisconnectJiraInstallationParams } from '#core/disconnect.js';
|
|
11
|
+
export { disconnectJiraInstallation } from '#core/disconnect.js';
|
|
12
|
+
export { JiraAccessTokenMissingError, JiraAuthorizationScopeMismatchError, JiraConnectionAlreadyLinkedError, JiraConnectionNotFoundError, JiraInstallationAlreadyLinkedError, JiraInstallationSiteMismatchError, JiraInstallStateActorMismatchError, JiraInstallStateError, JiraIntegrationProviderError, JiraOAuthCallbackError, JiraOfflineAccessNotGrantedError, JiraPendingSelectionNotFoundError, JiraSiteSelectionMismatchError, JiraTokenUnrefreshableError, } from '#core/errors.js';
|
|
13
|
+
export type { ConnectJiraInstallationInput, HandleJiraCallbackParams } from '#core/install.js';
|
|
14
|
+
export { handleJiraCallback, handleJiraOAuthCallbackError, handleJiraSiteSelection, } from '#core/install.js';
|
|
15
|
+
export type { JiraPendingSelectionSecretsStore, JiraPendingSelectionStore } from '#core/pending.js';
|
|
16
|
+
export { createJiraPendingSelectionStore, jiraPendingSecretsNamespace } from '#core/pending.js';
|
|
17
|
+
export { assertJiraAuthorizationScopes, formatJiraOAuthScopes, JIRA_OAUTH_SCOPES, } from '#core/scopes.js';
|
|
18
|
+
export { signJiraInstallState, verifyJiraInstallState } from '#core/state.js';
|
|
6
19
|
export type { CreateJiraTokenStoreParams, GetJiraAccessTokenParams, JiraConnectionResolverResult, JiraSecretsStore, JiraTokenStore, StoreJiraTokensParams, } from '#core/tokens.js';
|
|
7
20
|
export { createJiraTokenStore, jiraSecretsNamespace } from '#core/tokens.js';
|
|
8
21
|
export type { JiraInstallation, JiraInstallationStatus, UpsertJiraInstallationParams, } from '#db/installations.js';
|
|
9
|
-
export { getJiraInstallationByConnectionId, getJiraInstallationByWebhookId, markJiraInstallationRevoked, upsertJiraInstallation, } from '#db/installations.js';
|
|
22
|
+
export { deleteJiraInstallationByConnectionId, getJiraInstallationByCloudId, getJiraInstallationByConnectionId, getJiraInstallationByWebhookId, markJiraInstallationRevoked, updateJiraInstallationTokenExpiry, upsertJiraInstallation, withJiraRefreshLock, } from '#db/installations.js';
|
|
10
23
|
export { closeDb, config, db, migrationsPath };
|
|
11
|
-
export
|
|
24
|
+
export interface CreateJiraIntegrationProviderOptions {
|
|
25
|
+
jira?: JiraApiClient | undefined;
|
|
26
|
+
getJiraInstallationByConnectionId?: typeof getJiraInstallationByConnectionId | undefined;
|
|
27
|
+
routes?: Omit<CreateJiraIntegrationRoutesOptions, 'jira' | 'connectionCapabilities'> | undefined;
|
|
28
|
+
}
|
|
29
|
+
export declare function createJiraIntegrationProvider(options?: CreateJiraIntegrationProviderOptions): {
|
|
12
30
|
provider: string;
|
|
13
31
|
displayName: string;
|
|
32
|
+
adapters: {};
|
|
33
|
+
connectionExternalUrl(connection: {
|
|
34
|
+
id: string;
|
|
35
|
+
}): Promise<string | undefined>;
|
|
36
|
+
routes: import("@shipfox/node-fastify").RouteGroup[];
|
|
14
37
|
};
|
|
15
38
|
//# 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,EAAC,MAAM,EAAC,MAAM,YAAY,CAAC;AAClC,OAAO,EAAC,OAAO,EAAE,EAAE,EAAC,MAAM,WAAW,CAAC;AACtC,OAAO,EAAC,cAAc,EAAC,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,KAAK,aAAa,EAAC,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAAC,MAAM,EAAC,MAAM,YAAY,CAAC;AAClC,OAAO,EAAC,OAAO,EAAE,EAAE,EAAC,MAAM,WAAW,CAAC;AACtC,OAAO,EAAC,iCAAiC,EAAC,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAC,cAAc,EAAC,MAAM,mBAAmB,CAAC;AACjD,OAAO,EACL,KAAK,kCAAkC,EAExC,MAAM,iCAAiC,CAAC;AAEzC,YAAY,EAAC,YAAY,EAAC,MAAM,mCAAmC,CAAC;AACpE,YAAY,EACV,sBAAsB,EACtB,aAAa,EACb,iBAAiB,EACjB,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAC,mBAAmB,EAAE,YAAY,EAAC,MAAM,gBAAgB,CAAC;AACjE,YAAY,EAAC,gCAAgC,EAAC,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAC,0BAA0B,EAAC,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EACL,2BAA2B,EAC3B,mCAAmC,EACnC,gCAAgC,EAChC,2BAA2B,EAC3B,kCAAkC,EAClC,iCAAiC,EACjC,kCAAkC,EAClC,qBAAqB,EACrB,4BAA4B,EAC5B,sBAAsB,EACtB,gCAAgC,EAChC,iCAAiC,EACjC,8BAA8B,EAC9B,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAC,4BAA4B,EAAE,wBAAwB,EAAC,MAAM,kBAAkB,CAAC;AAC7F,OAAO,EACL,kBAAkB,EAClB,4BAA4B,EAC5B,uBAAuB,GACxB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAC,gCAAgC,EAAE,yBAAyB,EAAC,MAAM,kBAAkB,CAAC;AAClG,OAAO,EAAC,+BAA+B,EAAE,2BAA2B,EAAC,MAAM,kBAAkB,CAAC;AAC9F,OAAO,EACL,6BAA6B,EAC7B,qBAAqB,EACrB,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAC,oBAAoB,EAAE,sBAAsB,EAAC,MAAM,gBAAgB,CAAC;AAC5E,YAAY,EACV,0BAA0B,EAC1B,wBAAwB,EACxB,4BAA4B,EAC5B,gBAAgB,EAChB,cAAc,EACd,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAC,oBAAoB,EAAE,oBAAoB,EAAC,MAAM,iBAAiB,CAAC;AAC3E,YAAY,EACV,gBAAgB,EAChB,sBAAsB,EACtB,4BAA4B,GAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,oCAAoC,EACpC,4BAA4B,EAC5B,iCAAiC,EACjC,8BAA8B,EAC9B,2BAA2B,EAC3B,iCAAiC,EACjC,sBAAsB,EACtB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,EAAC,CAAC;AAE7C,MAAM,WAAW,oCAAoC;IACnD,IAAI,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IACjC,iCAAiC,CAAC,EAAE,OAAO,iCAAiC,GAAG,SAAS,CAAC;IACzF,MAAM,CAAC,EAAE,IAAI,CAAC,kCAAkC,EAAE,MAAM,GAAG,wBAAwB,CAAC,GAAG,SAAS,CAAC;CAClG;AAED,wBAAgB,6BAA6B,CAAC,OAAO,GAAE,oCAAyC;;;;sCAWpD;QAAC,EAAE,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;;EAKrF"}
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,38 @@
|
|
|
1
1
|
import { JIRA_PROVIDER } from '@shipfox/api-integration-jira-dto';
|
|
2
|
+
import { createJiraApiClient } from '#api/client.js';
|
|
2
3
|
import { config } from '#config.js';
|
|
3
4
|
import { closeDb, db } from '#db/db.js';
|
|
5
|
+
import { getJiraInstallationByConnectionId } from '#db/installations.js';
|
|
4
6
|
import { migrationsPath } from '#db/migrations.js';
|
|
5
|
-
|
|
7
|
+
import { createJiraIntegrationRoutes } from '#presentation/routes/install.js';
|
|
8
|
+
export { createJiraApiClient, mapJiraError } from '#api/client.js';
|
|
9
|
+
export { disconnectJiraInstallation } from '#core/disconnect.js';
|
|
10
|
+
export { JiraAccessTokenMissingError, JiraAuthorizationScopeMismatchError, JiraConnectionAlreadyLinkedError, JiraConnectionNotFoundError, JiraInstallationAlreadyLinkedError, JiraInstallationSiteMismatchError, JiraInstallStateActorMismatchError, JiraInstallStateError, JiraIntegrationProviderError, JiraOAuthCallbackError, JiraOfflineAccessNotGrantedError, JiraPendingSelectionNotFoundError, JiraSiteSelectionMismatchError, JiraTokenUnrefreshableError } from '#core/errors.js';
|
|
11
|
+
export { handleJiraCallback, handleJiraOAuthCallbackError, handleJiraSiteSelection } from '#core/install.js';
|
|
12
|
+
export { createJiraPendingSelectionStore, jiraPendingSecretsNamespace } from '#core/pending.js';
|
|
13
|
+
export { assertJiraAuthorizationScopes, formatJiraOAuthScopes, JIRA_OAUTH_SCOPES } from '#core/scopes.js';
|
|
14
|
+
export { signJiraInstallState, verifyJiraInstallState } from '#core/state.js';
|
|
6
15
|
export { createJiraTokenStore, jiraSecretsNamespace } from '#core/tokens.js';
|
|
7
|
-
export { getJiraInstallationByConnectionId, getJiraInstallationByWebhookId, markJiraInstallationRevoked, upsertJiraInstallation } from '#db/installations.js';
|
|
16
|
+
export { deleteJiraInstallationByConnectionId, getJiraInstallationByCloudId, getJiraInstallationByConnectionId, getJiraInstallationByWebhookId, markJiraInstallationRevoked, updateJiraInstallationTokenExpiry, upsertJiraInstallation, withJiraRefreshLock } from '#db/installations.js';
|
|
8
17
|
export { closeDb, config, db, migrationsPath };
|
|
9
|
-
export function createJiraIntegrationProvider() {
|
|
18
|
+
export function createJiraIntegrationProvider(options = {}) {
|
|
19
|
+
const jira = options.jira ?? createJiraApiClient();
|
|
20
|
+
const getInstallationByConnectionId = options.getJiraInstallationByConnectionId ?? getJiraInstallationByConnectionId;
|
|
21
|
+
const routes = options.routes ? [
|
|
22
|
+
createJiraIntegrationRoutes({
|
|
23
|
+
jira,
|
|
24
|
+
connectionCapabilities: [],
|
|
25
|
+
...options.routes
|
|
26
|
+
})
|
|
27
|
+
] : [];
|
|
10
28
|
return {
|
|
11
29
|
provider: JIRA_PROVIDER,
|
|
12
|
-
displayName: 'Jira'
|
|
30
|
+
displayName: 'Jira',
|
|
31
|
+
adapters: {},
|
|
32
|
+
async connectionExternalUrl (connection) {
|
|
33
|
+
return (await getInstallationByConnectionId(connection.id))?.siteUrl;
|
|
34
|
+
},
|
|
35
|
+
routes
|
|
13
36
|
};
|
|
14
37
|
}
|
|
15
38
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {JIRA_PROVIDER} from '@shipfox/api-integration-jira-dto';\nimport {config} from '#config.js';\nimport {closeDb, db} from '#db/db.js';\nimport {migrationsPath} from '#db/migrations.js';\n\nexport type {JiraProvider} from '@shipfox/api-integration-jira-dto';\nexport {\n JiraAccessTokenMissingError,\n JiraConnectionNotFoundError,\n JiraInstallationSiteMismatchError,\n} from '#core/errors.js';\nexport type {\n CreateJiraTokenStoreParams,\n GetJiraAccessTokenParams,\n JiraConnectionResolverResult,\n JiraSecretsStore,\n JiraTokenStore,\n StoreJiraTokensParams,\n} from '#core/tokens.js';\nexport {createJiraTokenStore, jiraSecretsNamespace} from '#core/tokens.js';\nexport type {\n JiraInstallation,\n JiraInstallationStatus,\n UpsertJiraInstallationParams,\n} from '#db/installations.js';\nexport {\n getJiraInstallationByConnectionId,\n getJiraInstallationByWebhookId,\n markJiraInstallationRevoked,\n upsertJiraInstallation,\n} from '#db/installations.js';\nexport {closeDb, config, db, migrationsPath};\n\nexport function createJiraIntegrationProvider() {\n return {provider: JIRA_PROVIDER
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {JIRA_PROVIDER} from '@shipfox/api-integration-jira-dto';\nimport {createJiraApiClient, type JiraApiClient} from '#api/client.js';\nimport {config} from '#config.js';\nimport {closeDb, db} from '#db/db.js';\nimport {getJiraInstallationByConnectionId} from '#db/installations.js';\nimport {migrationsPath} from '#db/migrations.js';\nimport {\n type CreateJiraIntegrationRoutesOptions,\n createJiraIntegrationRoutes,\n} from '#presentation/routes/install.js';\n\nexport type {JiraProvider} from '@shipfox/api-integration-jira-dto';\nexport type {\n JiraAccessibleResource,\n JiraApiClient,\n JiraAuthorization,\n JiraIdentity,\n} from '#api/client.js';\nexport {createJiraApiClient, mapJiraError} from '#api/client.js';\nexport type {DisconnectJiraInstallationParams} from '#core/disconnect.js';\nexport {disconnectJiraInstallation} from '#core/disconnect.js';\nexport {\n JiraAccessTokenMissingError,\n JiraAuthorizationScopeMismatchError,\n JiraConnectionAlreadyLinkedError,\n JiraConnectionNotFoundError,\n JiraInstallationAlreadyLinkedError,\n JiraInstallationSiteMismatchError,\n JiraInstallStateActorMismatchError,\n JiraInstallStateError,\n JiraIntegrationProviderError,\n JiraOAuthCallbackError,\n JiraOfflineAccessNotGrantedError,\n JiraPendingSelectionNotFoundError,\n JiraSiteSelectionMismatchError,\n JiraTokenUnrefreshableError,\n} from '#core/errors.js';\nexport type {ConnectJiraInstallationInput, HandleJiraCallbackParams} from '#core/install.js';\nexport {\n handleJiraCallback,\n handleJiraOAuthCallbackError,\n handleJiraSiteSelection,\n} from '#core/install.js';\nexport type {JiraPendingSelectionSecretsStore, JiraPendingSelectionStore} from '#core/pending.js';\nexport {createJiraPendingSelectionStore, jiraPendingSecretsNamespace} from '#core/pending.js';\nexport {\n assertJiraAuthorizationScopes,\n formatJiraOAuthScopes,\n JIRA_OAUTH_SCOPES,\n} from '#core/scopes.js';\nexport {signJiraInstallState, verifyJiraInstallState} from '#core/state.js';\nexport type {\n CreateJiraTokenStoreParams,\n GetJiraAccessTokenParams,\n JiraConnectionResolverResult,\n JiraSecretsStore,\n JiraTokenStore,\n StoreJiraTokensParams,\n} from '#core/tokens.js';\nexport {createJiraTokenStore, jiraSecretsNamespace} from '#core/tokens.js';\nexport type {\n JiraInstallation,\n JiraInstallationStatus,\n UpsertJiraInstallationParams,\n} from '#db/installations.js';\nexport {\n deleteJiraInstallationByConnectionId,\n getJiraInstallationByCloudId,\n getJiraInstallationByConnectionId,\n getJiraInstallationByWebhookId,\n markJiraInstallationRevoked,\n updateJiraInstallationTokenExpiry,\n upsertJiraInstallation,\n withJiraRefreshLock,\n} from '#db/installations.js';\nexport {closeDb, config, db, migrationsPath};\n\nexport interface CreateJiraIntegrationProviderOptions {\n jira?: JiraApiClient | undefined;\n getJiraInstallationByConnectionId?: typeof getJiraInstallationByConnectionId | undefined;\n routes?: Omit<CreateJiraIntegrationRoutesOptions, 'jira' | 'connectionCapabilities'> | undefined;\n}\n\nexport function createJiraIntegrationProvider(options: CreateJiraIntegrationProviderOptions = {}) {\n const jira = options.jira ?? createJiraApiClient();\n const getInstallationByConnectionId =\n options.getJiraInstallationByConnectionId ?? getJiraInstallationByConnectionId;\n const routes = options.routes\n ? [createJiraIntegrationRoutes({jira, connectionCapabilities: [], ...options.routes})]\n : [];\n return {\n provider: JIRA_PROVIDER,\n displayName: 'Jira',\n adapters: {},\n async connectionExternalUrl(connection: {id: string}): Promise<string | undefined> {\n return (await getInstallationByConnectionId(connection.id))?.siteUrl;\n },\n routes,\n };\n}\n"],"names":["JIRA_PROVIDER","createJiraApiClient","config","closeDb","db","getJiraInstallationByConnectionId","migrationsPath","createJiraIntegrationRoutes","mapJiraError","disconnectJiraInstallation","JiraAccessTokenMissingError","JiraAuthorizationScopeMismatchError","JiraConnectionAlreadyLinkedError","JiraConnectionNotFoundError","JiraInstallationAlreadyLinkedError","JiraInstallationSiteMismatchError","JiraInstallStateActorMismatchError","JiraInstallStateError","JiraIntegrationProviderError","JiraOAuthCallbackError","JiraOfflineAccessNotGrantedError","JiraPendingSelectionNotFoundError","JiraSiteSelectionMismatchError","JiraTokenUnrefreshableError","handleJiraCallback","handleJiraOAuthCallbackError","handleJiraSiteSelection","createJiraPendingSelectionStore","jiraPendingSecretsNamespace","assertJiraAuthorizationScopes","formatJiraOAuthScopes","JIRA_OAUTH_SCOPES","signJiraInstallState","verifyJiraInstallState","createJiraTokenStore","jiraSecretsNamespace","deleteJiraInstallationByConnectionId","getJiraInstallationByCloudId","getJiraInstallationByWebhookId","markJiraInstallationRevoked","updateJiraInstallationTokenExpiry","upsertJiraInstallation","withJiraRefreshLock","createJiraIntegrationProvider","options","jira","getInstallationByConnectionId","routes","connectionCapabilities","provider","displayName","adapters","connectionExternalUrl","connection","id","siteUrl"],"mappings":"AAAA,SAAQA,aAAa,QAAO,oCAAoC;AAChE,SAAQC,mBAAmB,QAA2B,iBAAiB;AACvE,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,OAAO,EAAEC,EAAE,QAAO,YAAY;AACtC,SAAQC,iCAAiC,QAAO,uBAAuB;AACvE,SAAQC,cAAc,QAAO,oBAAoB;AACjD,SAEEC,2BAA2B,QACtB,kCAAkC;AASzC,SAAQN,mBAAmB,EAAEO,YAAY,QAAO,iBAAiB;AAEjE,SAAQC,0BAA0B,QAAO,sBAAsB;AAC/D,SACEC,2BAA2B,EAC3BC,mCAAmC,EACnCC,gCAAgC,EAChCC,2BAA2B,EAC3BC,kCAAkC,EAClCC,iCAAiC,EACjCC,kCAAkC,EAClCC,qBAAqB,EACrBC,4BAA4B,EAC5BC,sBAAsB,EACtBC,gCAAgC,EAChCC,iCAAiC,EACjCC,8BAA8B,EAC9BC,2BAA2B,QACtB,kBAAkB;AAEzB,SACEC,kBAAkB,EAClBC,4BAA4B,EAC5BC,uBAAuB,QAClB,mBAAmB;AAE1B,SAAQC,+BAA+B,EAAEC,2BAA2B,QAAO,mBAAmB;AAC9F,SACEC,6BAA6B,EAC7BC,qBAAqB,EACrBC,iBAAiB,QACZ,kBAAkB;AACzB,SAAQC,oBAAoB,EAAEC,sBAAsB,QAAO,iBAAiB;AAS5E,SAAQC,oBAAoB,EAAEC,oBAAoB,QAAO,kBAAkB;AAM3E,SACEC,oCAAoC,EACpCC,4BAA4B,EAC5BhC,iCAAiC,EACjCiC,8BAA8B,EAC9BC,2BAA2B,EAC3BC,iCAAiC,EACjCC,sBAAsB,EACtBC,mBAAmB,QACd,uBAAuB;AAC9B,SAAQvC,OAAO,EAAED,MAAM,EAAEE,EAAE,EAAEE,cAAc,GAAE;AAQ7C,OAAO,SAASqC,8BAA8BC,UAAgD,CAAC,CAAC;IAC9F,MAAMC,OAAOD,QAAQC,IAAI,IAAI5C;IAC7B,MAAM6C,gCACJF,QAAQvC,iCAAiC,IAAIA;IAC/C,MAAM0C,SAASH,QAAQG,MAAM,GACzB;QAACxC,4BAA4B;YAACsC;YAAMG,wBAAwB,EAAE;YAAE,GAAGJ,QAAQG,MAAM;QAAA;KAAG,GACpF,EAAE;IACN,OAAO;QACLE,UAAUjD;QACVkD,aAAa;QACbC,UAAU,CAAC;QACX,MAAMC,uBAAsBC,UAAwB;YAClD,OAAQ,CAAA,MAAMP,8BAA8BO,WAAWC,EAAE,CAAA,GAAIC;QAC/D;QACAR;IACF;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integrations.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/integrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,0BAA0B,EAAC,MAAM,mCAAmC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/presentation/dto/integrations.ts"],"sourcesContent":["export {toIntegrationConnectionDto} from '@shipfox/api-integration-core-dto';\n"],"names":["toIntegrationConnectionDto"],"mappings":"AAAA,SAAQA,0BAA0B,QAAO,oCAAoC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/errors.ts"],"names":[],"mappings":"AAwBA,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAoC3D"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ConnectionSlugConflictError } from '@shipfox/api-integration-core-dto';
|
|
2
|
+
import { ClientError } from '@shipfox/node-fastify';
|
|
3
|
+
import { JiraAuthorizationScopeMismatchError, JiraConnectionAlreadyLinkedError, JiraInstallationAlreadyLinkedError, JiraInstallStateActorMismatchError, JiraInstallStateError, JiraIntegrationProviderError, JiraOAuthCallbackError, JiraOfflineAccessNotGrantedError, JiraPendingSelectionNotFoundError, JiraSiteSelectionMismatchError } from '#core/errors.js';
|
|
4
|
+
function providerStatus(reason) {
|
|
5
|
+
if (reason === 'rate-limited') return 429;
|
|
6
|
+
if (reason === 'timeout' || reason === 'provider-unavailable') return 503;
|
|
7
|
+
return 422;
|
|
8
|
+
}
|
|
9
|
+
export function jiraRouteErrorHandler(error) {
|
|
10
|
+
if (error instanceof JiraInstallStateError) throw new ClientError(error.message, 'invalid-jira-install-state', {
|
|
11
|
+
status: 400
|
|
12
|
+
});
|
|
13
|
+
if (error instanceof JiraInstallStateActorMismatchError) throw new ClientError(error.message, 'jira-install-state-actor-mismatch', {
|
|
14
|
+
status: 403
|
|
15
|
+
});
|
|
16
|
+
if (error instanceof JiraInstallationAlreadyLinkedError) throw new ClientError(error.message, 'jira-installation-already-linked', {
|
|
17
|
+
status: 409
|
|
18
|
+
});
|
|
19
|
+
if (error instanceof JiraConnectionAlreadyLinkedError) throw new ClientError(error.message, 'jira-connection-already-linked', {
|
|
20
|
+
status: 409
|
|
21
|
+
});
|
|
22
|
+
if (error instanceof JiraAuthorizationScopeMismatchError) throw new ClientError(error.message, 'jira-authorization-scope-mismatch', {
|
|
23
|
+
status: 422,
|
|
24
|
+
details: {
|
|
25
|
+
missing_scopes: error.missingScopes
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
if (error instanceof JiraOfflineAccessNotGrantedError) throw new ClientError(error.message, 'jira-offline-access-not-granted', {
|
|
29
|
+
status: 422
|
|
30
|
+
});
|
|
31
|
+
if (error instanceof JiraSiteSelectionMismatchError) throw new ClientError(error.message, 'jira-site-selection-mismatch', {
|
|
32
|
+
status: 422
|
|
33
|
+
});
|
|
34
|
+
if (error instanceof JiraPendingSelectionNotFoundError) throw new ClientError(error.message, 'jira-pending-selection-not-found', {
|
|
35
|
+
status: 410
|
|
36
|
+
});
|
|
37
|
+
if (error instanceof JiraOAuthCallbackError) throw new ClientError(error.message, 'jira-oauth-callback-error', {
|
|
38
|
+
status: 422,
|
|
39
|
+
details: {
|
|
40
|
+
error: error.providerError,
|
|
41
|
+
...error.providerDescription ? {
|
|
42
|
+
error_description: error.providerDescription
|
|
43
|
+
} : {}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
if (error instanceof ConnectionSlugConflictError) throw new ClientError(error.message, 'slug-conflict', {
|
|
47
|
+
status: 409
|
|
48
|
+
});
|
|
49
|
+
if (error instanceof JiraIntegrationProviderError) throw new ClientError(error.message, error.reason, {
|
|
50
|
+
status: providerStatus(error.reason),
|
|
51
|
+
details: {
|
|
52
|
+
retry_after_seconds: error.retryAfterSeconds
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/presentation/routes/errors.ts"],"sourcesContent":["import {\n ConnectionSlugConflictError,\n type IntegrationProviderErrorReason,\n} from '@shipfox/api-integration-core-dto';\nimport {ClientError} from '@shipfox/node-fastify';\nimport {\n JiraAuthorizationScopeMismatchError,\n JiraConnectionAlreadyLinkedError,\n JiraInstallationAlreadyLinkedError,\n JiraInstallStateActorMismatchError,\n JiraInstallStateError,\n JiraIntegrationProviderError,\n JiraOAuthCallbackError,\n JiraOfflineAccessNotGrantedError,\n JiraPendingSelectionNotFoundError,\n JiraSiteSelectionMismatchError,\n} from '#core/errors.js';\n\nfunction providerStatus(reason: IntegrationProviderErrorReason): number {\n if (reason === 'rate-limited') return 429;\n if (reason === 'timeout' || reason === 'provider-unavailable') return 503;\n return 422;\n}\n\nexport function jiraRouteErrorHandler(error: unknown): never {\n if (error instanceof JiraInstallStateError)\n throw new ClientError(error.message, 'invalid-jira-install-state', {status: 400});\n if (error instanceof JiraInstallStateActorMismatchError)\n throw new ClientError(error.message, 'jira-install-state-actor-mismatch', {status: 403});\n if (error instanceof JiraInstallationAlreadyLinkedError)\n throw new ClientError(error.message, 'jira-installation-already-linked', {status: 409});\n if (error instanceof JiraConnectionAlreadyLinkedError)\n throw new ClientError(error.message, 'jira-connection-already-linked', {status: 409});\n if (error instanceof JiraAuthorizationScopeMismatchError)\n throw new ClientError(error.message, 'jira-authorization-scope-mismatch', {\n status: 422,\n details: {missing_scopes: error.missingScopes},\n });\n if (error instanceof JiraOfflineAccessNotGrantedError)\n throw new ClientError(error.message, 'jira-offline-access-not-granted', {status: 422});\n if (error instanceof JiraSiteSelectionMismatchError)\n throw new ClientError(error.message, 'jira-site-selection-mismatch', {status: 422});\n if (error instanceof JiraPendingSelectionNotFoundError)\n throw new ClientError(error.message, 'jira-pending-selection-not-found', {status: 410});\n if (error instanceof JiraOAuthCallbackError)\n throw new ClientError(error.message, 'jira-oauth-callback-error', {\n status: 422,\n details: {\n error: error.providerError,\n ...(error.providerDescription ? {error_description: error.providerDescription} : {}),\n },\n });\n if (error instanceof ConnectionSlugConflictError)\n throw new ClientError(error.message, 'slug-conflict', {status: 409});\n if (error instanceof JiraIntegrationProviderError)\n throw new ClientError(error.message, error.reason, {\n status: providerStatus(error.reason),\n details: {retry_after_seconds: error.retryAfterSeconds},\n });\n throw error;\n}\n"],"names":["ConnectionSlugConflictError","ClientError","JiraAuthorizationScopeMismatchError","JiraConnectionAlreadyLinkedError","JiraInstallationAlreadyLinkedError","JiraInstallStateActorMismatchError","JiraInstallStateError","JiraIntegrationProviderError","JiraOAuthCallbackError","JiraOfflineAccessNotGrantedError","JiraPendingSelectionNotFoundError","JiraSiteSelectionMismatchError","providerStatus","reason","jiraRouteErrorHandler","error","message","status","details","missing_scopes","missingScopes","providerError","providerDescription","error_description","retry_after_seconds","retryAfterSeconds"],"mappings":"AAAA,SACEA,2BAA2B,QAEtB,oCAAoC;AAC3C,SAAQC,WAAW,QAAO,wBAAwB;AAClD,SACEC,mCAAmC,EACnCC,gCAAgC,EAChCC,kCAAkC,EAClCC,kCAAkC,EAClCC,qBAAqB,EACrBC,4BAA4B,EAC5BC,sBAAsB,EACtBC,gCAAgC,EAChCC,iCAAiC,EACjCC,8BAA8B,QACzB,kBAAkB;AAEzB,SAASC,eAAeC,MAAsC;IAC5D,IAAIA,WAAW,gBAAgB,OAAO;IACtC,IAAIA,WAAW,aAAaA,WAAW,wBAAwB,OAAO;IACtE,OAAO;AACT;AAEA,OAAO,SAASC,sBAAsBC,KAAc;IAClD,IAAIA,iBAAiBT,uBACnB,MAAM,IAAIL,YAAYc,MAAMC,OAAO,EAAE,8BAA8B;QAACC,QAAQ;IAAG;IACjF,IAAIF,iBAAiBV,oCACnB,MAAM,IAAIJ,YAAYc,MAAMC,OAAO,EAAE,qCAAqC;QAACC,QAAQ;IAAG;IACxF,IAAIF,iBAAiBX,oCACnB,MAAM,IAAIH,YAAYc,MAAMC,OAAO,EAAE,oCAAoC;QAACC,QAAQ;IAAG;IACvF,IAAIF,iBAAiBZ,kCACnB,MAAM,IAAIF,YAAYc,MAAMC,OAAO,EAAE,kCAAkC;QAACC,QAAQ;IAAG;IACrF,IAAIF,iBAAiBb,qCACnB,MAAM,IAAID,YAAYc,MAAMC,OAAO,EAAE,qCAAqC;QACxEC,QAAQ;QACRC,SAAS;YAACC,gBAAgBJ,MAAMK,aAAa;QAAA;IAC/C;IACF,IAAIL,iBAAiBN,kCACnB,MAAM,IAAIR,YAAYc,MAAMC,OAAO,EAAE,mCAAmC;QAACC,QAAQ;IAAG;IACtF,IAAIF,iBAAiBJ,gCACnB,MAAM,IAAIV,YAAYc,MAAMC,OAAO,EAAE,gCAAgC;QAACC,QAAQ;IAAG;IACnF,IAAIF,iBAAiBL,mCACnB,MAAM,IAAIT,YAAYc,MAAMC,OAAO,EAAE,oCAAoC;QAACC,QAAQ;IAAG;IACvF,IAAIF,iBAAiBP,wBACnB,MAAM,IAAIP,YAAYc,MAAMC,OAAO,EAAE,6BAA6B;QAChEC,QAAQ;QACRC,SAAS;YACPH,OAAOA,MAAMM,aAAa;YAC1B,GAAIN,MAAMO,mBAAmB,GAAG;gBAACC,mBAAmBR,MAAMO,mBAAmB;YAAA,IAAI,CAAC,CAAC;QACrF;IACF;IACF,IAAIP,iBAAiBf,6BACnB,MAAM,IAAIC,YAAYc,MAAMC,OAAO,EAAE,iBAAiB;QAACC,QAAQ;IAAG;IACpE,IAAIF,iBAAiBR,8BACnB,MAAM,IAAIN,YAAYc,MAAMC,OAAO,EAAED,MAAMF,MAAM,EAAE;QACjDI,QAAQL,eAAeG,MAAMF,MAAM;QACnCK,SAAS;YAACM,qBAAqBT,MAAMU,iBAAiB;QAAA;IACxD;IACF,MAAMV;AACR"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type IntegrationCapability, type IntegrationConnection } from '@shipfox/api-integration-core-dto';
|
|
2
|
+
import { type RouteGroup } from '@shipfox/node-fastify';
|
|
3
|
+
import type { JiraApiClient } from '#api/client.js';
|
|
4
|
+
import { type ConnectJiraInstallationInput } from '#core/install.js';
|
|
5
|
+
import type { JiraPendingSelectionStore } from '#core/pending.js';
|
|
6
|
+
import type { JiraTokenStore } from '#core/tokens.js';
|
|
7
|
+
export interface CreateJiraIntegrationRoutesOptions {
|
|
8
|
+
jira: JiraApiClient;
|
|
9
|
+
tokenStore: Pick<JiraTokenStore, 'storeTokens'>;
|
|
10
|
+
pendingStore: Pick<JiraPendingSelectionStore, 'save' | 'load' | 'clear'>;
|
|
11
|
+
getExistingJiraConnection(input: {
|
|
12
|
+
cloudId: string;
|
|
13
|
+
}): Promise<IntegrationConnection<'jira'> | undefined>;
|
|
14
|
+
connectJiraInstallation(input: ConnectJiraInstallationInput): Promise<IntegrationConnection<'jira'>>;
|
|
15
|
+
disconnectJiraInstallation(input: {
|
|
16
|
+
connectionId: string;
|
|
17
|
+
}): Promise<void>;
|
|
18
|
+
connectionCapabilities: IntegrationCapability[];
|
|
19
|
+
}
|
|
20
|
+
export declare function createJiraIntegrationRoutes(options: CreateJiraIntegrationRoutesOptions): RouteGroup;
|
|
21
|
+
//# sourceMappingURL=install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/install.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAE3B,MAAM,mCAAmC,CAAC;AAU3C,OAAO,EAAc,KAAK,UAAU,EAAC,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAElD,OAAO,EACL,KAAK,4BAA4B,EAIlC,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,kBAAkB,CAAC;AAGhE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAIpD,MAAM,WAAW,kCAAkC;IACjD,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,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,0BAA0B,CAAC,KAAK,EAAE;QAAC,YAAY,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzE,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;CACjD;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,kCAAkC,GAC1C,UAAU,CA2FZ"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { AUTH_USER, requireUserContext, requireWorkspaceAccess } from '@shipfox/api-auth-context';
|
|
2
|
+
import { integrationConnectionDtoSchema } from '@shipfox/api-integration-core-dto';
|
|
3
|
+
import { completeJiraSiteSelectionBodySchema, createJiraInstallBodySchema, createJiraInstallResponseSchema, jiraCallbackQuerySchema, jiraCallbackResponseSchema } from '@shipfox/api-integration-jira-dto';
|
|
4
|
+
import { requireWorkspaceMembership } from '@shipfox/api-workspaces';
|
|
5
|
+
import { defineRoute } from '@shipfox/node-fastify';
|
|
6
|
+
import { config } from '#config.js';
|
|
7
|
+
import { handleJiraCallback, handleJiraOAuthCallbackError, handleJiraSiteSelection } from '#core/install.js';
|
|
8
|
+
import { formatJiraOAuthScopes } from '#core/scopes.js';
|
|
9
|
+
import { signJiraInstallState } from '#core/state.js';
|
|
10
|
+
import { toIntegrationConnectionDto } from '#presentation/dto/integrations.js';
|
|
11
|
+
import { jiraRouteErrorHandler } from './errors.js';
|
|
12
|
+
export function createJiraIntegrationRoutes(options) {
|
|
13
|
+
const installRoute = defineRoute({
|
|
14
|
+
method: 'POST',
|
|
15
|
+
path: '/install',
|
|
16
|
+
auth: AUTH_USER,
|
|
17
|
+
description: 'Create a Jira OAuth authorization URL for a workspace.',
|
|
18
|
+
schema: {
|
|
19
|
+
body: createJiraInstallBodySchema,
|
|
20
|
+
response: {
|
|
21
|
+
200: createJiraInstallResponseSchema
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
handler: (request)=>{
|
|
25
|
+
const { workspace_id: workspaceId } = request.body;
|
|
26
|
+
const actor = requireUserContext(request);
|
|
27
|
+
requireWorkspaceAccess({
|
|
28
|
+
request,
|
|
29
|
+
workspaceId
|
|
30
|
+
});
|
|
31
|
+
const installUrl = new URL(`${config.JIRA_AUTH_BASE_URL}/authorize`);
|
|
32
|
+
installUrl.searchParams.set('audience', 'api.atlassian.com');
|
|
33
|
+
installUrl.searchParams.set('client_id', config.JIRA_OAUTH_CLIENT_ID);
|
|
34
|
+
installUrl.searchParams.set('scope', formatJiraOAuthScopes());
|
|
35
|
+
installUrl.searchParams.set('redirect_uri', config.JIRA_OAUTH_REDIRECT_URL);
|
|
36
|
+
installUrl.searchParams.set('state', signJiraInstallState({
|
|
37
|
+
workspaceId,
|
|
38
|
+
userId: actor.userId
|
|
39
|
+
}));
|
|
40
|
+
installUrl.searchParams.set('response_type', 'code');
|
|
41
|
+
installUrl.searchParams.set('prompt', 'consent');
|
|
42
|
+
return {
|
|
43
|
+
install_url: installUrl.toString()
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
const callbackApiRoute = defineRoute({
|
|
48
|
+
method: 'GET',
|
|
49
|
+
path: '/callback/api',
|
|
50
|
+
auth: AUTH_USER,
|
|
51
|
+
description: 'Handle the Jira OAuth callback.',
|
|
52
|
+
schema: {
|
|
53
|
+
querystring: jiraCallbackQuerySchema,
|
|
54
|
+
response: {
|
|
55
|
+
200: jiraCallbackResponseSchema
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
errorHandler: jiraRouteErrorHandler,
|
|
59
|
+
handler: async (request)=>{
|
|
60
|
+
const actor = requireUserContext(request);
|
|
61
|
+
const query = request.query;
|
|
62
|
+
if (isJiraOAuthErrorCallback(query)) return await handleJiraOAuthCallbackError({
|
|
63
|
+
state: query.state,
|
|
64
|
+
error: query.error,
|
|
65
|
+
errorDescription: query.error_description,
|
|
66
|
+
sessionUserId: actor.userId,
|
|
67
|
+
sessionMemberships: actor.memberships,
|
|
68
|
+
requireWorkspaceMembership
|
|
69
|
+
});
|
|
70
|
+
const result = await handleJiraCallback({
|
|
71
|
+
...options,
|
|
72
|
+
code: query.code,
|
|
73
|
+
state: query.state,
|
|
74
|
+
sessionUserId: actor.userId,
|
|
75
|
+
sessionMemberships: actor.memberships,
|
|
76
|
+
requireWorkspaceMembership
|
|
77
|
+
});
|
|
78
|
+
return 'sites' in result ? {
|
|
79
|
+
sites: result.sites.map((site)=>({
|
|
80
|
+
cloud_id: site.cloudId,
|
|
81
|
+
name: site.name,
|
|
82
|
+
url: site.url,
|
|
83
|
+
scopes: site.scopes
|
|
84
|
+
}))
|
|
85
|
+
} : toIntegrationConnectionDto(result, {
|
|
86
|
+
capabilities: options.connectionCapabilities
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
const siteSelectionRoute = defineRoute({
|
|
91
|
+
method: 'POST',
|
|
92
|
+
path: '/callback/site',
|
|
93
|
+
auth: AUTH_USER,
|
|
94
|
+
description: 'Complete Jira OAuth site selection.',
|
|
95
|
+
schema: {
|
|
96
|
+
body: completeJiraSiteSelectionBodySchema,
|
|
97
|
+
response: {
|
|
98
|
+
200: integrationConnectionDtoSchema
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
errorHandler: jiraRouteErrorHandler,
|
|
102
|
+
handler: async (request)=>{
|
|
103
|
+
const actor = requireUserContext(request);
|
|
104
|
+
const connection = await handleJiraSiteSelection({
|
|
105
|
+
...options,
|
|
106
|
+
cloudId: request.body.cloud_id,
|
|
107
|
+
state: request.body.state,
|
|
108
|
+
sessionUserId: actor.userId,
|
|
109
|
+
sessionMemberships: actor.memberships,
|
|
110
|
+
requireWorkspaceMembership
|
|
111
|
+
});
|
|
112
|
+
return toIntegrationConnectionDto(connection, {
|
|
113
|
+
capabilities: options.connectionCapabilities
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
return {
|
|
118
|
+
prefix: '/integrations/jira',
|
|
119
|
+
routes: [
|
|
120
|
+
installRoute,
|
|
121
|
+
callbackApiRoute,
|
|
122
|
+
siteSelectionRoute
|
|
123
|
+
]
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
function isJiraOAuthErrorCallback(query) {
|
|
127
|
+
return 'error' in query && typeof query.error === 'string';
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
//# sourceMappingURL=install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/presentation/routes/install.ts"],"sourcesContent":["import {AUTH_USER, requireUserContext, requireWorkspaceAccess} from '@shipfox/api-auth-context';\nimport {\n type IntegrationCapability,\n type IntegrationConnection,\n integrationConnectionDtoSchema,\n} from '@shipfox/api-integration-core-dto';\nimport {\n completeJiraSiteSelectionBodySchema,\n createJiraInstallBodySchema,\n createJiraInstallResponseSchema,\n type JiraCallbackQueryDto,\n jiraCallbackQuerySchema,\n jiraCallbackResponseSchema,\n} from '@shipfox/api-integration-jira-dto';\nimport {requireWorkspaceMembership} from '@shipfox/api-workspaces';\nimport {defineRoute, type RouteGroup} from '@shipfox/node-fastify';\nimport type {JiraApiClient} from '#api/client.js';\nimport {config} from '#config.js';\nimport {\n type ConnectJiraInstallationInput,\n handleJiraCallback,\n handleJiraOAuthCallbackError,\n handleJiraSiteSelection,\n} from '#core/install.js';\nimport type {JiraPendingSelectionStore} from '#core/pending.js';\nimport {formatJiraOAuthScopes} from '#core/scopes.js';\nimport {signJiraInstallState} from '#core/state.js';\nimport type {JiraTokenStore} from '#core/tokens.js';\nimport {toIntegrationConnectionDto} from '#presentation/dto/integrations.js';\nimport {jiraRouteErrorHandler} from './errors.js';\n\nexport interface CreateJiraIntegrationRoutesOptions {\n jira: JiraApiClient;\n tokenStore: Pick<JiraTokenStore, 'storeTokens'>;\n pendingStore: Pick<JiraPendingSelectionStore, 'save' | 'load' | 'clear'>;\n getExistingJiraConnection(input: {\n cloudId: string;\n }): Promise<IntegrationConnection<'jira'> | undefined>;\n connectJiraInstallation(\n input: ConnectJiraInstallationInput,\n ): Promise<IntegrationConnection<'jira'>>;\n disconnectJiraInstallation(input: {connectionId: string}): Promise<void>;\n connectionCapabilities: IntegrationCapability[];\n}\n\nexport function createJiraIntegrationRoutes(\n options: CreateJiraIntegrationRoutesOptions,\n): RouteGroup {\n const installRoute = defineRoute({\n method: 'POST',\n path: '/install',\n auth: AUTH_USER,\n description: 'Create a Jira OAuth authorization URL for a workspace.',\n schema: {body: createJiraInstallBodySchema, response: {200: createJiraInstallResponseSchema}},\n handler: (request) => {\n const {workspace_id: workspaceId} = request.body;\n const actor = requireUserContext(request);\n requireWorkspaceAccess({request, workspaceId});\n const installUrl = new URL(`${config.JIRA_AUTH_BASE_URL}/authorize`);\n installUrl.searchParams.set('audience', 'api.atlassian.com');\n installUrl.searchParams.set('client_id', config.JIRA_OAUTH_CLIENT_ID);\n installUrl.searchParams.set('scope', formatJiraOAuthScopes());\n installUrl.searchParams.set('redirect_uri', config.JIRA_OAUTH_REDIRECT_URL);\n installUrl.searchParams.set(\n 'state',\n signJiraInstallState({workspaceId, userId: actor.userId}),\n );\n installUrl.searchParams.set('response_type', 'code');\n installUrl.searchParams.set('prompt', 'consent');\n return {install_url: installUrl.toString()};\n },\n });\n const callbackApiRoute = defineRoute({\n method: 'GET',\n path: '/callback/api',\n auth: AUTH_USER,\n description: 'Handle the Jira OAuth callback.',\n schema: {querystring: jiraCallbackQuerySchema, response: {200: jiraCallbackResponseSchema}},\n errorHandler: jiraRouteErrorHandler,\n handler: async (request) => {\n const actor = requireUserContext(request);\n const query = request.query;\n if (isJiraOAuthErrorCallback(query))\n return await handleJiraOAuthCallbackError({\n state: query.state,\n error: query.error,\n errorDescription: query.error_description,\n sessionUserId: actor.userId,\n sessionMemberships: actor.memberships,\n requireWorkspaceMembership,\n });\n const result = await handleJiraCallback({\n ...options,\n code: query.code,\n state: query.state,\n sessionUserId: actor.userId,\n sessionMemberships: actor.memberships,\n requireWorkspaceMembership,\n });\n return 'sites' in result\n ? {\n sites: result.sites.map((site) => ({\n cloud_id: site.cloudId,\n name: site.name,\n url: site.url,\n scopes: site.scopes,\n })),\n }\n : toIntegrationConnectionDto(result, {capabilities: options.connectionCapabilities});\n },\n });\n const siteSelectionRoute = defineRoute({\n method: 'POST',\n path: '/callback/site',\n auth: AUTH_USER,\n description: 'Complete Jira OAuth site selection.',\n schema: {\n body: completeJiraSiteSelectionBodySchema,\n response: {200: integrationConnectionDtoSchema},\n },\n errorHandler: jiraRouteErrorHandler,\n handler: async (request) => {\n const actor = requireUserContext(request);\n const connection = await handleJiraSiteSelection({\n ...options,\n cloudId: request.body.cloud_id,\n state: request.body.state,\n sessionUserId: actor.userId,\n sessionMemberships: actor.memberships,\n requireWorkspaceMembership,\n });\n return toIntegrationConnectionDto(connection, {capabilities: options.connectionCapabilities});\n },\n });\n return {\n prefix: '/integrations/jira',\n routes: [installRoute, callbackApiRoute, siteSelectionRoute],\n };\n}\n\nfunction isJiraOAuthErrorCallback(query: JiraCallbackQueryDto): query is JiraCallbackQueryDto & {\n error: string;\n error_description?: string | undefined;\n state: string;\n} {\n return 'error' in query && typeof query.error === 'string';\n}\n"],"names":["AUTH_USER","requireUserContext","requireWorkspaceAccess","integrationConnectionDtoSchema","completeJiraSiteSelectionBodySchema","createJiraInstallBodySchema","createJiraInstallResponseSchema","jiraCallbackQuerySchema","jiraCallbackResponseSchema","requireWorkspaceMembership","defineRoute","config","handleJiraCallback","handleJiraOAuthCallbackError","handleJiraSiteSelection","formatJiraOAuthScopes","signJiraInstallState","toIntegrationConnectionDto","jiraRouteErrorHandler","createJiraIntegrationRoutes","options","installRoute","method","path","auth","description","schema","body","response","handler","request","workspace_id","workspaceId","actor","installUrl","URL","JIRA_AUTH_BASE_URL","searchParams","set","JIRA_OAUTH_CLIENT_ID","JIRA_OAUTH_REDIRECT_URL","userId","install_url","toString","callbackApiRoute","querystring","errorHandler","query","isJiraOAuthErrorCallback","state","error","errorDescription","error_description","sessionUserId","sessionMemberships","memberships","result","code","sites","map","site","cloud_id","cloudId","name","url","scopes","capabilities","connectionCapabilities","siteSelectionRoute","connection","prefix","routes"],"mappings":"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"}
|