@shipfox/api-integration-jira 4.0.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +52 -0
  3. package/dist/api/client.d.ts +33 -0
  4. package/dist/api/client.d.ts.map +1 -0
  5. package/dist/api/client.js +143 -0
  6. package/dist/api/client.js.map +1 -0
  7. package/dist/core/disconnect.d.ts +18 -0
  8. package/dist/core/disconnect.d.ts.map +1 -0
  9. package/dist/core/disconnect.js +21 -0
  10. package/dist/core/disconnect.js.map +1 -0
  11. package/dist/core/errors.d.ts +37 -0
  12. package/dist/core/errors.d.ts.map +1 -1
  13. package/dist/core/errors.js +63 -0
  14. package/dist/core/errors.js.map +1 -1
  15. package/dist/core/install.d.ts +55 -0
  16. package/dist/core/install.d.ts.map +1 -0
  17. package/dist/core/install.js +121 -0
  18. package/dist/core/install.js.map +1 -0
  19. package/dist/core/pending.d.ts +43 -0
  20. package/dist/core/pending.d.ts.map +1 -0
  21. package/dist/core/pending.js +107 -0
  22. package/dist/core/pending.js.map +1 -0
  23. package/dist/core/scopes.d.ts +4 -0
  24. package/dist/core/scopes.d.ts.map +1 -0
  25. package/dist/core/scopes.js +23 -0
  26. package/dist/core/scopes.js.map +1 -0
  27. package/dist/core/state.d.ts +12 -0
  28. package/dist/core/state.d.ts.map +1 -0
  29. package/dist/core/state.js +51 -0
  30. package/dist/core/state.js.map +1 -0
  31. package/dist/core/tokens.d.ts +6 -0
  32. package/dist/core/tokens.d.ts.map +1 -1
  33. package/dist/core/tokens.js +82 -6
  34. package/dist/core/tokens.js.map +1 -1
  35. package/dist/db/db.d.ts +188 -0
  36. package/dist/db/db.d.ts.map +1 -1
  37. package/dist/db/db.js +3 -1
  38. package/dist/db/db.js.map +1 -1
  39. package/dist/db/installations.d.ts +21 -0
  40. package/dist/db/installations.d.ts.map +1 -1
  41. package/dist/db/installations.js +82 -19
  42. package/dist/db/installations.js.map +1 -1
  43. package/dist/db/pending-selections.d.ts +17 -0
  44. package/dist/db/pending-selections.d.ts.map +1 -0
  45. package/dist/db/pending-selections.js +27 -0
  46. package/dist/db/pending-selections.js.map +1 -0
  47. package/dist/db/schema/installations.js +2 -2
  48. package/dist/db/schema/installations.js.map +1 -1
  49. package/dist/db/schema/pending-selections.d.ts +105 -0
  50. package/dist/db/schema/pending-selections.d.ts.map +1 -0
  51. package/dist/db/schema/pending-selections.js +24 -0
  52. package/dist/db/schema/pending-selections.js.map +1 -0
  53. package/dist/index.d.ts +26 -3
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +27 -4
  56. package/dist/index.js.map +1 -1
  57. package/dist/presentation/dto/integrations.d.ts +2 -0
  58. package/dist/presentation/dto/integrations.d.ts.map +1 -0
  59. package/dist/presentation/dto/integrations.js +3 -0
  60. package/dist/presentation/dto/integrations.js.map +1 -0
  61. package/dist/presentation/routes/errors.d.ts +2 -0
  62. package/dist/presentation/routes/errors.d.ts.map +1 -0
  63. package/dist/presentation/routes/errors.js +58 -0
  64. package/dist/presentation/routes/errors.js.map +1 -0
  65. package/dist/presentation/routes/install.d.ts +26 -0
  66. package/dist/presentation/routes/install.d.ts.map +1 -0
  67. package/dist/presentation/routes/install.js +133 -0
  68. package/dist/presentation/routes/install.js.map +1 -0
  69. package/dist/tsconfig.test.tsbuildinfo +1 -1
  70. package/drizzle/0000_whole_master_chief.sql +8 -1
  71. package/drizzle/meta/0000_snapshot.json +47 -3
  72. package/package.json +22 -26
  73. package/src/api/client.test.ts +29 -0
  74. package/src/api/client.ts +213 -0
  75. package/src/core/disconnect.test.ts +24 -0
  76. package/src/core/disconnect.ts +25 -0
  77. package/src/core/errors.ts +79 -0
  78. package/src/core/install.test.ts +129 -0
  79. package/src/core/install.ts +191 -0
  80. package/src/core/pending.ts +146 -0
  81. package/src/core/scopes.test.ts +19 -0
  82. package/src/core/scopes.ts +20 -0
  83. package/src/core/state.test.ts +38 -0
  84. package/src/core/state.ts +79 -0
  85. package/src/core/tokens-refresh.test.ts +106 -0
  86. package/src/core/tokens.test.ts +16 -2
  87. package/src/core/tokens.ts +145 -8
  88. package/src/db/db.ts +2 -1
  89. package/src/db/installations.test.ts +37 -1
  90. package/src/db/installations.ts +120 -23
  91. package/src/db/pending-selections.ts +55 -0
  92. package/src/db/schema/installations.ts +2 -2
  93. package/src/db/schema/pending-selections.ts +31 -0
  94. package/src/index.test.ts +7 -2
  95. package/src/index.ts +66 -2
  96. package/src/presentation/dto/integrations.ts +1 -0
  97. package/src/presentation/routes/errors.ts +61 -0
  98. package/src/presentation/routes/install.ts +161 -0
  99. package/test/globalSetup.ts +0 -9
  100. package/tsconfig.build.tsbuildinfo +1 -1
  101. package/turbo.json +9 -0
  102. package/test/api-secrets.d.ts +0 -26
@@ -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 { JiraAccessTokenMissingError, JiraConnectionNotFoundError, JiraInstallationSiteMismatchError, } from '#core/errors.js';
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 declare function createJiraIntegrationProvider(): {
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
@@ -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;AAEjD,YAAY,EAAC,YAAY,EAAC,MAAM,mCAAmC,CAAC;AACpE,OAAO,EACL,2BAA2B,EAC3B,2BAA2B,EAC3B,iCAAiC,GAClC,MAAM,iBAAiB,CAAC;AACzB,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,iCAAiC,EACjC,8BAA8B,EAC9B,2BAA2B,EAC3B,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,EAAC,CAAC;AAE7C,wBAAgB,6BAA6B;;;EAE5C"}
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
- export { JiraAccessTokenMissingError, JiraConnectionNotFoundError, JiraInstallationSiteMismatchError } from '#core/errors.js';
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, displayName: 'Jira'};\n}\n"],"names":["JIRA_PROVIDER","config","closeDb","db","migrationsPath","JiraAccessTokenMissingError","JiraConnectionNotFoundError","JiraInstallationSiteMismatchError","createJiraTokenStore","jiraSecretsNamespace","getJiraInstallationByConnectionId","getJiraInstallationByWebhookId","markJiraInstallationRevoked","upsertJiraInstallation","createJiraIntegrationProvider","provider","displayName"],"mappings":"AAAA,SAAQA,aAAa,QAAO,oCAAoC;AAChE,SAAQC,MAAM,QAAO,aAAa;AAClC,SAAQC,OAAO,EAAEC,EAAE,QAAO,YAAY;AACtC,SAAQC,cAAc,QAAO,oBAAoB;AAGjD,SACEC,2BAA2B,EAC3BC,2BAA2B,EAC3BC,iCAAiC,QAC5B,kBAAkB;AASzB,SAAQC,oBAAoB,EAAEC,oBAAoB,QAAO,kBAAkB;AAM3E,SACEC,iCAAiC,EACjCC,8BAA8B,EAC9BC,2BAA2B,EAC3BC,sBAAsB,QACjB,uBAAuB;AAC9B,SAAQX,OAAO,EAAED,MAAM,EAAEE,EAAE,EAAEC,cAAc,GAAE;AAE7C,OAAO,SAASU;IACd,OAAO;QAACC,UAAUf;QAAegB,aAAa;IAAM;AACtD"}
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,2 @@
1
+ export { toIntegrationConnectionDto } from '@shipfox/api-integration-core-dto';
2
+ //# sourceMappingURL=integrations.d.ts.map
@@ -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,3 @@
1
+ export { toIntegrationConnectionDto } from '@shipfox/api-integration-core-dto';
2
+
3
+ //# sourceMappingURL=integrations.js.map
@@ -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,2 @@
1
+ export declare function jiraRouteErrorHandler(error: unknown): never;
2
+ //# sourceMappingURL=errors.d.ts.map
@@ -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,26 @@
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
+ requireActiveWorkspaceMembership?: (input: {
20
+ workspaceId: string;
21
+ userId: string;
22
+ memberships: ReadonlyArray<import('@shipfox/api-auth-context').UserContextMembership>;
23
+ }) => Promise<unknown>;
24
+ }
25
+ export declare function createJiraIntegrationRoutes(options: CreateJiraIntegrationRoutesOptions): RouteGroup;
26
+ //# 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;AAS3C,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;IAChD,gCAAgC,CAAC,EAAE,CAAC,KAAK,EAAE;QACzC,WAAW,EAAE,MAAM,CAAC;QACpB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,aAAa,CAAC,OAAO,2BAA2B,EAAE,qBAAqB,CAAC,CAAC;KACvF,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACxB;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,kCAAkC,GAC1C,UAAU,CA6FZ"}
@@ -0,0 +1,133 @@
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 { defineRoute } from '@shipfox/node-fastify';
5
+ import { config } from '#config.js';
6
+ import { handleJiraCallback, handleJiraOAuthCallbackError, handleJiraSiteSelection } from '#core/install.js';
7
+ import { formatJiraOAuthScopes } from '#core/scopes.js';
8
+ import { signJiraInstallState } from '#core/state.js';
9
+ import { toIntegrationConnectionDto } from '#presentation/dto/integrations.js';
10
+ import { jiraRouteErrorHandler } from './errors.js';
11
+ export function createJiraIntegrationRoutes(options) {
12
+ const requireMembership = options.requireActiveWorkspaceMembership ?? unavailableWorkspaceMembershipCheck;
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: requireMembership
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: requireMembership
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: requireMembership
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 unavailableWorkspaceMembershipCheck(_input) {
127
+ return Promise.reject(new Error('Workspaces inter-module client is not configured'));
128
+ }
129
+ function isJiraOAuthErrorCallback(query) {
130
+ return 'error' in query && typeof query.error === 'string';
131
+ }
132
+
133
+ //# 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 {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 requireActiveWorkspaceMembership?: (input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<import('@shipfox/api-auth-context').UserContextMembership>;\n }) => Promise<unknown>;\n}\n\nexport function createJiraIntegrationRoutes(\n options: CreateJiraIntegrationRoutesOptions,\n): RouteGroup {\n const requireMembership =\n options.requireActiveWorkspaceMembership ?? unavailableWorkspaceMembershipCheck;\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: requireMembership,\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: requireMembership,\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: requireMembership,\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 unavailableWorkspaceMembershipCheck(_input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<import('@shipfox/api-auth-context').UserContextMembership>;\n}): Promise<never> {\n return Promise.reject(new Error('Workspaces inter-module client is not configured'));\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","defineRoute","config","handleJiraCallback","handleJiraOAuthCallbackError","handleJiraSiteSelection","formatJiraOAuthScopes","signJiraInstallState","toIntegrationConnectionDto","jiraRouteErrorHandler","createJiraIntegrationRoutes","options","requireMembership","requireActiveWorkspaceMembership","unavailableWorkspaceMembershipCheck","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","requireWorkspaceMembership","result","code","sites","map","site","cloud_id","cloudId","name","url","scopes","capabilities","connectionCapabilities","siteSelectionRoute","connection","prefix","routes","_input","Promise","reject","Error"],"mappings":"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"}