@shipfox/api-integration-core 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.
- package/.turbo/turbo-build.log +4 -4
- package/CHANGELOG.md +100 -0
- package/dist/core/entities/provider.d.ts +7 -2
- package/dist/core/entities/provider.d.ts.map +1 -1
- package/dist/core/entities/provider.js.map +1 -1
- package/dist/core/errors.d.ts +7 -0
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +8 -2
- package/dist/core/errors.js.map +1 -1
- package/dist/db/webhook-deliveries.d.ts +4 -0
- package/dist/db/webhook-deliveries.d.ts.map +1 -1
- package/dist/db/webhook-deliveries.js +10 -2
- package/dist/db/webhook-deliveries.js.map +1 -1
- package/dist/index.d.ts +19 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +51 -6
- package/dist/index.js.map +1 -1
- package/dist/presentation/inter-module.d.ts +9 -0
- package/dist/presentation/inter-module.d.ts.map +1 -0
- package/dist/presentation/inter-module.js +121 -0
- package/dist/presentation/inter-module.js.map +1 -0
- package/dist/presentation/routes/agent-tools-gateway/index.d.ts +1 -0
- package/dist/presentation/routes/agent-tools-gateway/index.d.ts.map +1 -1
- package/dist/presentation/routes/agent-tools-gateway/index.js +1 -0
- package/dist/presentation/routes/agent-tools-gateway/index.js.map +1 -1
- package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.d.ts +5 -2
- package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.d.ts.map +1 -1
- package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.js +53 -17
- package/dist/presentation/routes/agent-tools-gateway/resolve-authorized-tools.js.map +1 -1
- package/dist/presentation/routes/index.d.ts +2 -3
- package/dist/presentation/routes/index.d.ts.map +1 -1
- package/dist/presentation/routes/index.js +3 -3
- package/dist/presentation/routes/index.js.map +1 -1
- package/dist/presentation/routes/manage-connections.d.ts +1 -1
- package/dist/presentation/routes/manage-connections.d.ts.map +1 -1
- package/dist/presentation/routes/manage-connections.js +28 -3
- package/dist/presentation/routes/manage-connections.js.map +1 -1
- package/dist/providers/gitea.d.ts.map +1 -1
- package/dist/providers/gitea.js +10 -8
- package/dist/providers/gitea.js.map +1 -1
- package/dist/providers/github.d.ts.map +1 -1
- package/dist/providers/github.js +18 -13
- package/dist/providers/github.js.map +1 -1
- package/dist/providers/jira.d.ts.map +1 -1
- package/dist/providers/jira.js +113 -3
- package/dist/providers/jira.js.map +1 -1
- package/dist/providers/linear.d.ts.map +1 -1
- package/dist/providers/linear.js +35 -16
- package/dist/providers/linear.js.map +1 -1
- package/dist/providers/sentry.d.ts.map +1 -1
- package/dist/providers/sentry.js +13 -11
- package/dist/providers/sentry.js.map +1 -1
- package/dist/providers/slack.d.ts.map +1 -1
- package/dist/providers/slack.js +36 -16
- package/dist/providers/slack.js.map +1 -1
- package/dist/providers/types.d.ts +12 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/types.js.map +1 -1
- package/dist/providers/webhook.d.ts.map +1 -1
- package/dist/providers/webhook.js +11 -9
- package/dist/providers/webhook.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +33 -38
- package/src/core/entities/provider.ts +7 -1
- package/src/core/errors.ts +12 -2
- package/src/db/webhook-deliveries.test.ts +11 -0
- package/src/db/webhook-deliveries.ts +12 -3
- package/src/index.test.ts +112 -0
- package/src/index.ts +104 -10
- package/src/presentation/inter-module.ts +155 -0
- package/src/presentation/routes/agent-tools-gateway/index.ts +1 -0
- package/src/presentation/routes/agent-tools-gateway/resolve-authorized-tools.test.ts +36 -0
- package/src/presentation/routes/agent-tools-gateway/resolve-authorized-tools.ts +64 -22
- package/src/presentation/routes/index.ts +5 -6
- package/src/presentation/routes/manage-connections.test.ts +113 -0
- package/src/presentation/routes/manage-connections.ts +26 -2
- package/src/providers/gitea.ts +11 -8
- package/src/providers/github.ts +17 -11
- package/src/providers/jira.ts +155 -7
- package/src/providers/linear.test.ts +136 -0
- package/src/providers/linear.ts +33 -13
- package/src/providers/sentry.ts +15 -12
- package/src/providers/slack.test.ts +139 -0
- package/src/providers/slack.ts +39 -14
- package/src/providers/types.ts +15 -0
- package/src/providers/webhook.ts +11 -9
- package/tsconfig.build.tsbuildinfo +1 -1
package/dist/providers/gitea.js
CHANGED
|
@@ -50,15 +50,17 @@ async function loadGiteaModuleParts() {
|
|
|
50
50
|
return connection;
|
|
51
51
|
}));
|
|
52
52
|
}
|
|
53
|
+
const integrationProvider = createGiteaIntegrationProvider({
|
|
54
|
+
getExistingGiteaConnection,
|
|
55
|
+
connectGiteaConnection,
|
|
56
|
+
publishSourcePush,
|
|
57
|
+
recordDeliveryOnly,
|
|
58
|
+
getIntegrationConnectionById,
|
|
59
|
+
coreDb: db
|
|
60
|
+
});
|
|
53
61
|
return {
|
|
54
|
-
provider:
|
|
55
|
-
|
|
56
|
-
connectGiteaConnection,
|
|
57
|
-
publishSourcePush,
|
|
58
|
-
recordDeliveryOnly,
|
|
59
|
-
getIntegrationConnectionById,
|
|
60
|
-
coreDb: db
|
|
61
|
-
}),
|
|
62
|
+
provider: integrationProvider,
|
|
63
|
+
webhookProcessors: integrationProvider.webhookProcessors,
|
|
62
64
|
database: {
|
|
63
65
|
db: giteaDb,
|
|
64
66
|
migrationsPath: giteaMigrationsPath,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/providers/gitea.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {ConnectGiteaConnectionInput} from '@shipfox/api-integration-gitea';\nimport {config} from '#config.js';\nimport {\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {publishSourcePush, recordDeliveryOnly} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\n// Stable migration-tracking table name for the Gitea provider database. This\n// must NOT depend on the provider's position in the module `database` array. A\n// positional name would shift if a provider is flag-disabled and silently\n// re-run migrations against existing tables.\nconst GITEA_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_gitea';\n\nasync function loadGiteaModuleParts(): Promise<IntegrationModuleParts> {\n const {\n createGiteaIntegrationProvider,\n getGiteaConnectionByOrg,\n upsertGiteaConnection,\n db: giteaDb,\n migrationsPath: giteaMigrationsPath,\n } = await import('@shipfox/api-integration-gitea');\n\n async function getExistingGiteaConnection(input: {\n org: string;\n }): Promise<CoreIntegrationConnection<'gitea'> | undefined> {\n const row = await getGiteaConnectionByOrg(input.org);\n if (!row) return undefined;\n const connection = await getIntegrationConnectionById(row.connectionId);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'gitea'>;\n }\n\n async function connectGiteaConnection(\n input: ConnectGiteaConnectionInput,\n ): Promise<CoreIntegrationConnection<'gitea'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`gitea_${input.org}`, {fallback: 'gitea'});\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'gitea',\n externalAccountId: input.org,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'gitea',\n externalAccountId: input.org,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n\n await upsertGiteaConnection(\n {\n connectionId: connection.id,\n org: input.org,\n },\n {tx},\n );\n\n return connection as CoreIntegrationConnection<'gitea'>;\n }),\n );\n }\n\n
|
|
1
|
+
{"version":3,"sources":["../../src/providers/gitea.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {ConnectGiteaConnectionInput} from '@shipfox/api-integration-gitea';\nimport {config} from '#config.js';\nimport {\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {publishSourcePush, recordDeliveryOnly} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\n// Stable migration-tracking table name for the Gitea provider database. This\n// must NOT depend on the provider's position in the module `database` array. A\n// positional name would shift if a provider is flag-disabled and silently\n// re-run migrations against existing tables.\nconst GITEA_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_gitea';\n\nasync function loadGiteaModuleParts(): Promise<IntegrationModuleParts> {\n const {\n createGiteaIntegrationProvider,\n getGiteaConnectionByOrg,\n upsertGiteaConnection,\n db: giteaDb,\n migrationsPath: giteaMigrationsPath,\n } = await import('@shipfox/api-integration-gitea');\n\n async function getExistingGiteaConnection(input: {\n org: string;\n }): Promise<CoreIntegrationConnection<'gitea'> | undefined> {\n const row = await getGiteaConnectionByOrg(input.org);\n if (!row) return undefined;\n const connection = await getIntegrationConnectionById(row.connectionId);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'gitea'>;\n }\n\n async function connectGiteaConnection(\n input: ConnectGiteaConnectionInput,\n ): Promise<CoreIntegrationConnection<'gitea'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`gitea_${input.org}`, {fallback: 'gitea'});\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'gitea',\n externalAccountId: input.org,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'gitea',\n externalAccountId: input.org,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n\n await upsertGiteaConnection(\n {\n connectionId: connection.id,\n org: input.org,\n },\n {tx},\n );\n\n return connection as CoreIntegrationConnection<'gitea'>;\n }),\n );\n }\n\n const integrationProvider = createGiteaIntegrationProvider({\n getExistingGiteaConnection,\n connectGiteaConnection,\n publishSourcePush,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n coreDb: db,\n });\n\n return {\n provider: integrationProvider,\n webhookProcessors: integrationProvider.webhookProcessors,\n database: {\n db: giteaDb,\n migrationsPath: giteaMigrationsPath,\n migrationsTableName: GITEA_MIGRATIONS_TABLE,\n },\n };\n}\n\nexport const giteaProviderModule: IntegrationProviderModule = {\n id: 'gitea',\n enabled: config.INTEGRATIONS_ENABLE_GITEA_PROVIDER,\n load: loadGiteaModuleParts,\n};\n"],"names":["slugifyConnectionSlug","config","getIntegrationConnectionById","resolveUniqueConnectionSlug","upsertIntegrationConnection","db","publishSourcePush","recordDeliveryOnly","retryConnectionSlugCollision","GITEA_MIGRATIONS_TABLE","loadGiteaModuleParts","createGiteaIntegrationProvider","getGiteaConnectionByOrg","upsertGiteaConnection","giteaDb","migrationsPath","giteaMigrationsPath","getExistingGiteaConnection","input","row","org","undefined","connection","connectionId","connectGiteaConnection","transaction","tx","baseSlug","fallback","slug","workspaceId","provider","externalAccountId","displayName","lifecycleStatus","id","integrationProvider","coreDb","webhookProcessors","database","migrationsTableName","giteaProviderModule","enabled","INTEGRATIONS_ENABLE_GITEA_PROVIDER","load"],"mappings":"AAAA,SAEEA,qBAAqB,QAChB,oCAAoC;AAE3C,SAAQC,MAAM,QAAO,aAAa;AAClC,SACEC,4BAA4B,EAC5BC,2BAA2B,EAC3BC,2BAA2B,QACtB,qBAAqB;AAC5B,SAAQC,EAAE,QAAO,YAAY;AAC7B,SAAQC,iBAAiB,EAAEC,kBAAkB,QAAO,4BAA4B;AAChF,SAAQC,4BAA4B,QAAO,gCAAgC;AAG3E,6EAA6E;AAC7E,+EAA+E;AAC/E,0EAA0E;AAC1E,6CAA6C;AAC7C,MAAMC,yBAAyB;AAE/B,eAAeC;IACb,MAAM,EACJC,8BAA8B,EAC9BC,uBAAuB,EACvBC,qBAAqB,EACrBR,IAAIS,OAAO,EACXC,gBAAgBC,mBAAmB,EACpC,GAAG,MAAM,MAAM,CAAC;IAEjB,eAAeC,2BAA2BC,KAEzC;QACC,MAAMC,MAAM,MAAMP,wBAAwBM,MAAME,GAAG;QACnD,IAAI,CAACD,KAAK,OAAOE;QACjB,MAAMC,aAAa,MAAMpB,6BAA6BiB,IAAII,YAAY;QACtE,IAAI,CAACD,YAAY,OAAOD;QACxB,OAAOC;IACT;IAEA,eAAeE,uBACbN,KAAkC;QAElC,OAAO,MAAMV,6BAA6B,IACxCH,KAAKoB,WAAW,CAAC,OAAOC;gBACtB,MAAMC,WAAW3B,sBAAsB,CAAC,MAAM,EAAEkB,MAAME,GAAG,EAAE,EAAE;oBAACQ,UAAU;gBAAO;gBAC/E,MAAMC,OAAO,MAAM1B,4BACjB;oBACE2B,aAAaZ,MAAMY,WAAW;oBAC9BC,UAAU;oBACVC,mBAAmBd,MAAME,GAAG;oBAC5BO;gBACF,GACA;oBAACD;gBAAE;gBAEL,MAAMJ,aAAa,MAAMlB,4BACvB;oBACE0B,aAAaZ,MAAMY,WAAW;oBAC9BC,UAAU;oBACVC,mBAAmBd,MAAME,GAAG;oBAC5BS;oBACAI,aAAaf,MAAMe,WAAW;oBAC9BC,iBAAiB;gBACnB,GACA;oBAACR;gBAAE;gBAGL,MAAMb,sBACJ;oBACEU,cAAcD,WAAWa,EAAE;oBAC3Bf,KAAKF,MAAME,GAAG;gBAChB,GACA;oBAACM;gBAAE;gBAGL,OAAOJ;YACT;IAEJ;IAEA,MAAMc,sBAAsBzB,+BAA+B;QACzDM;QACAO;QACAlB;QACAC;QACAL;QACAmC,QAAQhC;IACV;IAEA,OAAO;QACL0B,UAAUK;QACVE,mBAAmBF,oBAAoBE,iBAAiB;QACxDC,UAAU;YACRlC,IAAIS;YACJC,gBAAgBC;YAChBwB,qBAAqB/B;QACvB;IACF;AACF;AAEA,OAAO,MAAMgC,sBAAiD;IAC5DN,IAAI;IACJO,SAASzC,OAAO0C,kCAAkC;IAClDC,MAAMlC;AACR,EAAE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/providers/github.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAEV,yBAAyB,EAE1B,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/providers/github.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAEV,yBAAyB,EAE1B,MAAM,qBAAqB,CAAC;AAiI7B,eAAO,MAAM,oBAAoB,EAAE,yBAIlC,CAAC"}
|
package/dist/providers/github.js
CHANGED
|
@@ -69,20 +69,25 @@ async function loadGithubModuleParts(options = {}) {
|
|
|
69
69
|
return connection;
|
|
70
70
|
}));
|
|
71
71
|
}
|
|
72
|
+
const integrationProvider = createGithubIntegrationProvider({
|
|
73
|
+
getExistingGithubConnection,
|
|
74
|
+
connectGithubInstallation,
|
|
75
|
+
publishIntegrationEventReceived,
|
|
76
|
+
publishSourcePush,
|
|
77
|
+
recordDeliveryOnly,
|
|
78
|
+
getIntegrationConnectionById,
|
|
79
|
+
coreDb: db,
|
|
80
|
+
deleteSecrets: options.secrets?.deleteSecrets,
|
|
81
|
+
agentTools: {
|
|
82
|
+
tokenProvider
|
|
83
|
+
},
|
|
84
|
+
...options.requireActiveWorkspaceMembership ? {
|
|
85
|
+
requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership
|
|
86
|
+
} : {}
|
|
87
|
+
});
|
|
72
88
|
return {
|
|
73
|
-
provider:
|
|
74
|
-
|
|
75
|
-
connectGithubInstallation,
|
|
76
|
-
publishIntegrationEventReceived,
|
|
77
|
-
publishSourcePush,
|
|
78
|
-
recordDeliveryOnly,
|
|
79
|
-
getIntegrationConnectionById,
|
|
80
|
-
coreDb: db,
|
|
81
|
-
deleteSecrets: options.secrets?.deleteSecrets,
|
|
82
|
-
agentTools: {
|
|
83
|
-
tokenProvider
|
|
84
|
-
}
|
|
85
|
-
}),
|
|
89
|
+
provider: integrationProvider,
|
|
90
|
+
webhookProcessors: integrationProvider.webhookProcessors,
|
|
86
91
|
e2eRoutes: [
|
|
87
92
|
createGithubE2eRoutes({
|
|
88
93
|
getExistingGithubConnection,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/providers/github.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {ConnectGithubInstallationInput} from '@shipfox/api-integration-github';\nimport {config} from '#config.js';\nimport {\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {\n publishIntegrationEventReceived,\n publishSourcePush,\n recordDeliveryOnly,\n} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {\n IntegrationModuleParts,\n IntegrationProviderModule,\n IntegrationProviderModuleLoadOptions,\n} from '#providers/types.js';\n\n// Stable migration-tracking table name for the GitHub provider database. This\n// must NOT depend on the provider's position in the module `database` array. A\n// positional name would shift if a provider is flag-disabled and silently\n// re-run migrations against existing tables.\nconst GITHUB_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_github';\n\nasync function loadGithubModuleParts(\n options: IntegrationProviderModuleLoadOptions = {},\n): Promise<IntegrationModuleParts> {\n const {\n createGithubInstallationTokenProvider,\n createGithubE2eRoutes,\n encodeInstallationTokenEnvelope,\n createGithubIntegrationProvider,\n getGithubInstallationByInstallationId,\n githubInstallationTokenNamespace,\n db: githubDb,\n migrationsPath: githubMigrationsPath,\n upsertGithubInstallation,\n } = await import('@shipfox/api-integration-github');\n\n const tokenProvider = createGithubInstallationTokenProvider({\n getIntegrationConnectionById,\n secretStore: options.secrets?.github\n ? {\n read: async (workspaceId, installationId) =>\n (await options.secrets?.github?.getSecret({\n workspaceId,\n namespace: githubInstallationTokenNamespace(installationId),\n key: 'envelope',\n })) ?? null,\n write: async (workspaceId, installationId, envelope) => {\n await options.secrets?.github?.setSecrets({\n workspaceId,\n namespace: githubInstallationTokenNamespace(installationId),\n values: {envelope: encodeInstallationTokenEnvelope(envelope)},\n });\n },\n }\n : undefined,\n });\n\n async function getExistingGithubConnection(input: {\n installationId: string;\n }): Promise<CoreIntegrationConnection<'github'> | undefined> {\n const installation = await getGithubInstallationByInstallationId(input.installationId);\n if (!installation) return undefined;\n const connection = await getIntegrationConnectionById(installation.connectionId);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'github'>;\n }\n\n async function connectGithubInstallation(\n input: ConnectGithubInstallationInput,\n ): Promise<CoreIntegrationConnection<'github'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`github_${input.installation.accountLogin}`, {\n fallback: 'github',\n });\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'github',\n externalAccountId: input.installationId,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'github',\n externalAccountId: input.installationId,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n\n await upsertGithubInstallation(\n {\n connectionId: connection.id,\n ...input.installation,\n },\n {tx},\n );\n\n return connection as CoreIntegrationConnection<'github'>;\n }),\n );\n }\n\n
|
|
1
|
+
{"version":3,"sources":["../../src/providers/github.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {ConnectGithubInstallationInput} from '@shipfox/api-integration-github';\nimport {config} from '#config.js';\nimport {\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {\n publishIntegrationEventReceived,\n publishSourcePush,\n recordDeliveryOnly,\n} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {\n IntegrationModuleParts,\n IntegrationProviderModule,\n IntegrationProviderModuleLoadOptions,\n} from '#providers/types.js';\n\n// Stable migration-tracking table name for the GitHub provider database. This\n// must NOT depend on the provider's position in the module `database` array. A\n// positional name would shift if a provider is flag-disabled and silently\n// re-run migrations against existing tables.\nconst GITHUB_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_github';\n\nasync function loadGithubModuleParts(\n options: IntegrationProviderModuleLoadOptions = {},\n): Promise<IntegrationModuleParts> {\n const {\n createGithubInstallationTokenProvider,\n createGithubE2eRoutes,\n encodeInstallationTokenEnvelope,\n createGithubIntegrationProvider,\n getGithubInstallationByInstallationId,\n githubInstallationTokenNamespace,\n db: githubDb,\n migrationsPath: githubMigrationsPath,\n upsertGithubInstallation,\n } = await import('@shipfox/api-integration-github');\n\n const tokenProvider = createGithubInstallationTokenProvider({\n getIntegrationConnectionById,\n secretStore: options.secrets?.github\n ? {\n read: async (workspaceId, installationId) =>\n (await options.secrets?.github?.getSecret({\n workspaceId,\n namespace: githubInstallationTokenNamespace(installationId),\n key: 'envelope',\n })) ?? null,\n write: async (workspaceId, installationId, envelope) => {\n await options.secrets?.github?.setSecrets({\n workspaceId,\n namespace: githubInstallationTokenNamespace(installationId),\n values: {envelope: encodeInstallationTokenEnvelope(envelope)},\n });\n },\n }\n : undefined,\n });\n\n async function getExistingGithubConnection(input: {\n installationId: string;\n }): Promise<CoreIntegrationConnection<'github'> | undefined> {\n const installation = await getGithubInstallationByInstallationId(input.installationId);\n if (!installation) return undefined;\n const connection = await getIntegrationConnectionById(installation.connectionId);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'github'>;\n }\n\n async function connectGithubInstallation(\n input: ConnectGithubInstallationInput,\n ): Promise<CoreIntegrationConnection<'github'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`github_${input.installation.accountLogin}`, {\n fallback: 'github',\n });\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'github',\n externalAccountId: input.installationId,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'github',\n externalAccountId: input.installationId,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n\n await upsertGithubInstallation(\n {\n connectionId: connection.id,\n ...input.installation,\n },\n {tx},\n );\n\n return connection as CoreIntegrationConnection<'github'>;\n }),\n );\n }\n\n const integrationProvider = createGithubIntegrationProvider({\n getExistingGithubConnection,\n connectGithubInstallation,\n publishIntegrationEventReceived,\n publishSourcePush,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n coreDb: db,\n deleteSecrets: options.secrets?.deleteSecrets,\n agentTools: {tokenProvider},\n ...(options.requireActiveWorkspaceMembership\n ? {requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership}\n : {}),\n });\n\n return {\n provider: integrationProvider,\n webhookProcessors: integrationProvider.webhookProcessors,\n e2eRoutes: [\n createGithubE2eRoutes({\n getExistingGithubConnection,\n connectGithubInstallation,\n connectionCapabilities: ['source_control', 'agent_tools'],\n }),\n ],\n database: {\n db: githubDb,\n migrationsPath: githubMigrationsPath,\n migrationsTableName: GITHUB_MIGRATIONS_TABLE,\n },\n };\n}\n\nexport const githubProviderModule: IntegrationProviderModule = {\n id: 'github',\n enabled: config.INTEGRATIONS_ENABLE_GITHUB_PROVIDER,\n load: loadGithubModuleParts,\n};\n"],"names":["slugifyConnectionSlug","config","getIntegrationConnectionById","resolveUniqueConnectionSlug","upsertIntegrationConnection","db","publishIntegrationEventReceived","publishSourcePush","recordDeliveryOnly","retryConnectionSlugCollision","GITHUB_MIGRATIONS_TABLE","loadGithubModuleParts","options","createGithubInstallationTokenProvider","createGithubE2eRoutes","encodeInstallationTokenEnvelope","createGithubIntegrationProvider","getGithubInstallationByInstallationId","githubInstallationTokenNamespace","githubDb","migrationsPath","githubMigrationsPath","upsertGithubInstallation","tokenProvider","secretStore","secrets","github","read","workspaceId","installationId","getSecret","namespace","key","write","envelope","setSecrets","values","undefined","getExistingGithubConnection","input","installation","connection","connectionId","connectGithubInstallation","transaction","tx","baseSlug","accountLogin","fallback","slug","provider","externalAccountId","displayName","lifecycleStatus","id","integrationProvider","coreDb","deleteSecrets","agentTools","requireActiveWorkspaceMembership","webhookProcessors","e2eRoutes","connectionCapabilities","database","migrationsTableName","githubProviderModule","enabled","INTEGRATIONS_ENABLE_GITHUB_PROVIDER","load"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jira.d.ts","sourceRoot":"","sources":["../../src/providers/jira.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"jira.d.ts","sourceRoot":"","sources":["../../src/providers/jira.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;AAgJ3F,eAAO,MAAM,kBAAkB,EAAE,yBAIhC,CAAC"}
|
package/dist/providers/jira.js
CHANGED
|
@@ -1,9 +1,113 @@
|
|
|
1
|
+
import { slugifyConnectionSlug } from '@shipfox/api-integration-core-dto';
|
|
1
2
|
import { config } from '#config.js';
|
|
3
|
+
import { deleteIntegrationConnection, getIntegrationConnectionById, resolveUniqueConnectionSlug, updateIntegrationConnectionLifecycleStatus, upsertIntegrationConnection } from '#db/connections.js';
|
|
4
|
+
import { db } from '#db/db.js';
|
|
5
|
+
import { retryConnectionSlugCollision } from '#providers/connection-slug.js';
|
|
2
6
|
const JIRA_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_jira';
|
|
3
|
-
|
|
4
|
-
|
|
7
|
+
const JIRA_SECRETS_NAMESPACE_PREFIX = 'system/integrations/jira/';
|
|
8
|
+
async function loadJiraModuleParts(options = {}) {
|
|
9
|
+
const { createJiraIntegrationProvider, createJiraPendingSelectionStore, createJiraTokenStore, db: jiraDb, disconnectJiraInstallation: disconnectJiraInstallationRecords, getJiraInstallationByCloudId, migrationsPath, upsertJiraInstallation } = await import('@shipfox/api-integration-jira');
|
|
10
|
+
async function getExistingJiraConnection(input) {
|
|
11
|
+
const installation = await getJiraInstallationByCloudId(input.cloudId);
|
|
12
|
+
if (!installation) return undefined;
|
|
13
|
+
return await getIntegrationConnectionById(installation.connectionId);
|
|
14
|
+
}
|
|
15
|
+
function connectJiraInstallation(input) {
|
|
16
|
+
return retryConnectionSlugCollision(()=>db().transaction(async (tx)=>{
|
|
17
|
+
const slug = await resolveUniqueConnectionSlug({
|
|
18
|
+
workspaceId: input.workspaceId,
|
|
19
|
+
provider: 'jira',
|
|
20
|
+
externalAccountId: input.cloudId,
|
|
21
|
+
baseSlug: slugifyConnectionSlug(`jira_${input.siteName || input.cloudId}`, {
|
|
22
|
+
fallback: 'jira'
|
|
23
|
+
})
|
|
24
|
+
}, {
|
|
25
|
+
tx
|
|
26
|
+
});
|
|
27
|
+
const connection = await upsertIntegrationConnection({
|
|
28
|
+
workspaceId: input.workspaceId,
|
|
29
|
+
provider: 'jira',
|
|
30
|
+
externalAccountId: input.cloudId,
|
|
31
|
+
slug,
|
|
32
|
+
displayName: input.displayName,
|
|
33
|
+
lifecycleStatus: 'active'
|
|
34
|
+
}, {
|
|
35
|
+
tx
|
|
36
|
+
});
|
|
37
|
+
await upsertJiraInstallation({
|
|
38
|
+
connectionId: connection.id,
|
|
39
|
+
cloudId: input.cloudId,
|
|
40
|
+
siteUrl: input.siteUrl,
|
|
41
|
+
siteName: input.siteName,
|
|
42
|
+
authorizingAccountId: input.authorizingAccountId,
|
|
43
|
+
scopes: input.scopes,
|
|
44
|
+
status: 'installed',
|
|
45
|
+
tokenExpiresAt: input.tokenExpiresAt
|
|
46
|
+
}, {
|
|
47
|
+
tx
|
|
48
|
+
});
|
|
49
|
+
return connection;
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
async function disconnectJiraInstallation(input) {
|
|
53
|
+
await disconnectJiraInstallationRecords({
|
|
54
|
+
connectionId: input.connectionId,
|
|
55
|
+
getConnection: getIntegrationConnectionById,
|
|
56
|
+
deleteSecrets: (params)=>options.secrets?.jira?.deleteSecrets({
|
|
57
|
+
...params,
|
|
58
|
+
namespace: jiraNamespaceSuffix(params.namespace)
|
|
59
|
+
}) ?? Promise.resolve(0),
|
|
60
|
+
transaction: (fn)=>db().transaction((tx)=>fn(tx)),
|
|
61
|
+
deleteConnection: (params, transactionOptions)=>deleteIntegrationConnection({
|
|
62
|
+
id: params.connectionId
|
|
63
|
+
}, transactionOptions)
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const fallbackSecrets = {
|
|
67
|
+
getSecret: ()=>Promise.resolve(null),
|
|
68
|
+
setSecrets: ()=>Promise.reject(new Error('Jira token storage is not configured')),
|
|
69
|
+
deleteSecrets: ()=>Promise.resolve(0)
|
|
70
|
+
};
|
|
71
|
+
const secrets = options.secrets?.jira ? {
|
|
72
|
+
getSecret: (params)=>options.secrets?.jira?.getSecret({
|
|
73
|
+
...params,
|
|
74
|
+
namespace: jiraNamespaceSuffix(params.namespace)
|
|
75
|
+
}) ?? Promise.resolve(null),
|
|
76
|
+
setSecrets: (params)=>options.secrets?.jira?.setSecrets({
|
|
77
|
+
...params,
|
|
78
|
+
namespace: jiraNamespaceSuffix(params.namespace)
|
|
79
|
+
}) ?? Promise.resolve(),
|
|
80
|
+
deleteSecrets: (params)=>options.secrets?.jira?.deleteSecrets({
|
|
81
|
+
...params,
|
|
82
|
+
namespace: jiraNamespaceSuffix(params.namespace)
|
|
83
|
+
}) ?? Promise.resolve(0)
|
|
84
|
+
} : fallbackSecrets;
|
|
85
|
+
const tokenStore = createJiraTokenStore({
|
|
86
|
+
resolveConnection: getIntegrationConnectionById,
|
|
87
|
+
secrets,
|
|
88
|
+
markConnectionError: async ({ connectionId })=>{
|
|
89
|
+
await updateIntegrationConnectionLifecycleStatus({
|
|
90
|
+
id: connectionId,
|
|
91
|
+
lifecycleStatus: 'error'
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
const pendingStore = createJiraPendingSelectionStore({
|
|
96
|
+
secrets
|
|
97
|
+
});
|
|
5
98
|
return {
|
|
6
|
-
provider: createJiraIntegrationProvider(
|
|
99
|
+
provider: createJiraIntegrationProvider({
|
|
100
|
+
routes: {
|
|
101
|
+
tokenStore,
|
|
102
|
+
pendingStore,
|
|
103
|
+
getExistingJiraConnection,
|
|
104
|
+
connectJiraInstallation,
|
|
105
|
+
disconnectJiraInstallation,
|
|
106
|
+
...options.requireActiveWorkspaceMembership ? {
|
|
107
|
+
requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership
|
|
108
|
+
} : {}
|
|
109
|
+
}
|
|
110
|
+
}),
|
|
7
111
|
database: {
|
|
8
112
|
db: jiraDb,
|
|
9
113
|
migrationsPath,
|
|
@@ -16,5 +120,11 @@ export const jiraProviderModule = {
|
|
|
16
120
|
enabled: config.INTEGRATIONS_ENABLE_JIRA_PROVIDER,
|
|
17
121
|
load: loadJiraModuleParts
|
|
18
122
|
};
|
|
123
|
+
function jiraNamespaceSuffix(namespace) {
|
|
124
|
+
if (!namespace.startsWith(JIRA_SECRETS_NAMESPACE_PREFIX)) {
|
|
125
|
+
throw new Error('Jira provider attempted to access an unscoped secret namespace');
|
|
126
|
+
}
|
|
127
|
+
return namespace.slice(JIRA_SECRETS_NAMESPACE_PREFIX.length);
|
|
128
|
+
}
|
|
19
129
|
|
|
20
130
|
//# sourceMappingURL=jira.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/providers/jira.ts"],"sourcesContent":["import {config} from '#config.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\nconst JIRA_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_jira';\n\nasync function loadJiraModuleParts(): Promise<IntegrationModuleParts> {\n const {\n createJiraIntegrationProvider,\n db: jiraDb,\n migrationsPath,\n } = await import('@shipfox/api-integration-jira');\n\n return {\n provider: createJiraIntegrationProvider(),\n database: {\n db: jiraDb,\n migrationsPath,\n migrationsTableName: JIRA_MIGRATIONS_TABLE,\n },\n };\n}\n\nexport const jiraProviderModule: IntegrationProviderModule = {\n id: 'jira',\n enabled: config.INTEGRATIONS_ENABLE_JIRA_PROVIDER,\n load: loadJiraModuleParts,\n};\n"],"names":["config","JIRA_MIGRATIONS_TABLE","loadJiraModuleParts","createJiraIntegrationProvider","db","jiraDb","migrationsPath","provider","database","migrationsTableName","jiraProviderModule","id","enabled","INTEGRATIONS_ENABLE_JIRA_PROVIDER","load"],"mappings":"AAAA,SAAQA,MAAM,QAAO,aAAa;AAGlC,MAAMC,wBAAwB;AAE9B,eAAeC;IACb,MAAM,EACJC,6BAA6B,EAC7BC,IAAIC,MAAM,EACVC,cAAc,EACf,GAAG,MAAM,MAAM,CAAC;IAEjB,OAAO;QACLC,UAAUJ;QACVK,UAAU;YACRJ,IAAIC;YACJC;YACAG,qBAAqBR;QACvB;IACF;AACF;AAEA,OAAO,MAAMS,qBAAgD;IAC3DC,IAAI;IACJC,SAASZ,OAAOa,iCAAiC;IACjDC,MAAMZ;AACR,EAAE"}
|
|
1
|
+
{"version":3,"sources":["../../src/providers/jira.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {\n ConnectJiraInstallationInput,\n JiraPendingSelectionSecretsStore,\n JiraSecretsStore,\n} from '@shipfox/api-integration-jira';\nimport {config} from '#config.js';\nimport {\n deleteIntegrationConnection,\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n updateIntegrationConnectionLifecycleStatus,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\nconst JIRA_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_jira';\nconst JIRA_SECRETS_NAMESPACE_PREFIX = 'system/integrations/jira/';\ntype IntegrationDb = ReturnType<typeof db>;\ntype IntegrationTx = Parameters<Parameters<IntegrationDb['transaction']>[0]>[0];\n\nasync function loadJiraModuleParts(\n options: Parameters<IntegrationProviderModule['load']>[0] = {},\n): Promise<IntegrationModuleParts> {\n const {\n createJiraIntegrationProvider,\n createJiraPendingSelectionStore,\n createJiraTokenStore,\n db: jiraDb,\n disconnectJiraInstallation: disconnectJiraInstallationRecords,\n getJiraInstallationByCloudId,\n migrationsPath,\n upsertJiraInstallation,\n } = await import('@shipfox/api-integration-jira');\n\n async function getExistingJiraConnection(input: {\n cloudId: string;\n }): Promise<CoreIntegrationConnection<'jira'> | undefined> {\n const installation = await getJiraInstallationByCloudId(input.cloudId);\n if (!installation) return undefined;\n return (await getIntegrationConnectionById(installation.connectionId)) as\n | CoreIntegrationConnection<'jira'>\n | undefined;\n }\n\n function connectJiraInstallation(\n input: ConnectJiraInstallationInput,\n ): Promise<CoreIntegrationConnection<'jira'>> {\n return retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'jira',\n externalAccountId: input.cloudId,\n baseSlug: slugifyConnectionSlug(`jira_${input.siteName || input.cloudId}`, {\n fallback: 'jira',\n }),\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'jira',\n externalAccountId: input.cloudId,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n await upsertJiraInstallation(\n {\n connectionId: connection.id,\n cloudId: input.cloudId,\n siteUrl: input.siteUrl,\n siteName: input.siteName,\n authorizingAccountId: input.authorizingAccountId,\n scopes: input.scopes,\n status: 'installed',\n tokenExpiresAt: input.tokenExpiresAt,\n },\n {tx},\n );\n return connection as CoreIntegrationConnection<'jira'>;\n }),\n );\n }\n\n async function disconnectJiraInstallation(input: {connectionId: string}): Promise<void> {\n await disconnectJiraInstallationRecords<IntegrationTx>({\n connectionId: input.connectionId,\n getConnection: getIntegrationConnectionById,\n deleteSecrets: (params) =>\n options.secrets?.jira?.deleteSecrets({\n ...params,\n namespace: jiraNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(0),\n transaction: (fn) => db().transaction((tx) => fn(tx)),\n deleteConnection: (params, transactionOptions) =>\n deleteIntegrationConnection({id: params.connectionId}, transactionOptions),\n });\n }\n\n const fallbackSecrets: JiraSecretsStore & JiraPendingSelectionSecretsStore = {\n getSecret: () => Promise.resolve(null),\n setSecrets: () => Promise.reject(new Error('Jira token storage is not configured')),\n deleteSecrets: () => Promise.resolve(0),\n };\n const secrets: JiraSecretsStore & JiraPendingSelectionSecretsStore = options.secrets?.jira\n ? {\n getSecret: (params) =>\n options.secrets?.jira?.getSecret({\n ...params,\n namespace: jiraNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(null),\n setSecrets: (params) =>\n options.secrets?.jira?.setSecrets({\n ...params,\n namespace: jiraNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(),\n deleteSecrets: (params) =>\n options.secrets?.jira?.deleteSecrets({\n ...params,\n namespace: jiraNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(0),\n }\n : fallbackSecrets;\n const tokenStore = createJiraTokenStore({\n resolveConnection: getIntegrationConnectionById,\n secrets,\n markConnectionError: async ({connectionId}) => {\n await updateIntegrationConnectionLifecycleStatus({\n id: connectionId,\n lifecycleStatus: 'error',\n });\n },\n });\n const pendingStore = createJiraPendingSelectionStore({secrets});\n\n return {\n provider: createJiraIntegrationProvider({\n routes: {\n tokenStore,\n pendingStore,\n getExistingJiraConnection,\n connectJiraInstallation,\n disconnectJiraInstallation,\n ...(options.requireActiveWorkspaceMembership\n ? {requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership}\n : {}),\n },\n }),\n database: {db: jiraDb, migrationsPath, migrationsTableName: JIRA_MIGRATIONS_TABLE},\n };\n}\n\nexport const jiraProviderModule: IntegrationProviderModule = {\n id: 'jira',\n enabled: config.INTEGRATIONS_ENABLE_JIRA_PROVIDER,\n load: loadJiraModuleParts,\n};\n\nfunction jiraNamespaceSuffix(namespace: string): string {\n if (!namespace.startsWith(JIRA_SECRETS_NAMESPACE_PREFIX)) {\n throw new Error('Jira provider attempted to access an unscoped secret namespace');\n }\n return namespace.slice(JIRA_SECRETS_NAMESPACE_PREFIX.length);\n}\n"],"names":["slugifyConnectionSlug","config","deleteIntegrationConnection","getIntegrationConnectionById","resolveUniqueConnectionSlug","updateIntegrationConnectionLifecycleStatus","upsertIntegrationConnection","db","retryConnectionSlugCollision","JIRA_MIGRATIONS_TABLE","JIRA_SECRETS_NAMESPACE_PREFIX","loadJiraModuleParts","options","createJiraIntegrationProvider","createJiraPendingSelectionStore","createJiraTokenStore","jiraDb","disconnectJiraInstallation","disconnectJiraInstallationRecords","getJiraInstallationByCloudId","migrationsPath","upsertJiraInstallation","getExistingJiraConnection","input","installation","cloudId","undefined","connectionId","connectJiraInstallation","transaction","tx","slug","workspaceId","provider","externalAccountId","baseSlug","siteName","fallback","connection","displayName","lifecycleStatus","id","siteUrl","authorizingAccountId","scopes","status","tokenExpiresAt","getConnection","deleteSecrets","params","secrets","jira","namespace","jiraNamespaceSuffix","Promise","resolve","fn","deleteConnection","transactionOptions","fallbackSecrets","getSecret","setSecrets","reject","Error","tokenStore","resolveConnection","markConnectionError","pendingStore","routes","requireActiveWorkspaceMembership","database","migrationsTableName","jiraProviderModule","enabled","INTEGRATIONS_ENABLE_JIRA_PROVIDER","load","startsWith","slice","length"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"linear.d.ts","sourceRoot":"","sources":["../../src/providers/linear.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"linear.d.ts","sourceRoot":"","sources":["../../src/providers/linear.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;AAyK3F,eAAO,MAAM,oBAAoB,EAAE,yBAIlC,CAAC"}
|
package/dist/providers/linear.js
CHANGED
|
@@ -7,7 +7,7 @@ import { retryConnectionSlugCollision } from '#providers/connection-slug.js';
|
|
|
7
7
|
const LINEAR_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_linear';
|
|
8
8
|
const LINEAR_SECRETS_NAMESPACE_PREFIX = 'system/integrations/linear/';
|
|
9
9
|
async function loadLinearModuleParts(options = {}) {
|
|
10
|
-
const { createLinearTokenStore, createLinearE2eRoutes, createLinearIntegrationProvider, config: linearConfig, disconnectLinearInstallation: disconnectLinearInstallationRecords, getLinearInstallationByOrganizationId, db: linearDb, migrationsPath: linearMigrationsPath, upsertLinearInstallation } = await import('@shipfox/api-integration-linear');
|
|
10
|
+
const { createLinearTokenStore, createLinearE2eRoutes, createLinearIntegrationProvider, config: linearConfig, deleteLinearInstallationByConnectionId, disconnectLinearInstallation: disconnectLinearInstallationRecords, getLinearInstallationByOrganizationId, db: linearDb, linearSecretsNamespace, migrationsPath: linearMigrationsPath, upsertLinearInstallation } = await import('@shipfox/api-integration-linear');
|
|
11
11
|
async function getExistingLinearConnection(input) {
|
|
12
12
|
const installation = await getLinearInstallationByOrganizationId(input.organizationId);
|
|
13
13
|
if (!installation) return undefined;
|
|
@@ -84,23 +84,42 @@ async function loadLinearModuleParts(options = {}) {
|
|
|
84
84
|
resolveConnection: async (connectionId)=>getIntegrationConnectionById(connectionId),
|
|
85
85
|
secrets
|
|
86
86
|
});
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
const integrationProvider = createLinearIntegrationProvider({
|
|
88
|
+
agentTools: {
|
|
89
|
+
tokenStore,
|
|
90
|
+
endpoint: linearConfig.LINEAR_MCP_ENDPOINT
|
|
91
|
+
},
|
|
92
|
+
cleanup: {
|
|
93
|
+
deleteConnectionRecords: async (connection, { tx })=>{
|
|
94
|
+
await deleteLinearInstallationByConnectionId(connection.id, {
|
|
95
|
+
tx
|
|
96
|
+
});
|
|
92
97
|
},
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
recordDeliveryOnly,
|
|
100
|
-
getIntegrationConnectionById,
|
|
101
|
-
coreDb: db
|
|
98
|
+
deleteConnectionSecrets: async (connection)=>{
|
|
99
|
+
// Scoped secrets accept the provider-local suffix, after this helper validates its prefix.
|
|
100
|
+
await (options.secrets?.linear?.deleteSecrets({
|
|
101
|
+
workspaceId: connection.workspaceId,
|
|
102
|
+
namespace: linearNamespaceSuffix(linearSecretsNamespace(connection.id))
|
|
103
|
+
}) ?? Promise.resolve());
|
|
102
104
|
}
|
|
103
|
-
}
|
|
105
|
+
},
|
|
106
|
+
routes: {
|
|
107
|
+
tokenStore,
|
|
108
|
+
getExistingLinearConnection,
|
|
109
|
+
connectLinearInstallation,
|
|
110
|
+
disconnectLinearInstallation,
|
|
111
|
+
publishIntegrationEventReceived,
|
|
112
|
+
recordDeliveryOnly,
|
|
113
|
+
getIntegrationConnectionById,
|
|
114
|
+
coreDb: db,
|
|
115
|
+
...options.requireActiveWorkspaceMembership ? {
|
|
116
|
+
requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership
|
|
117
|
+
} : {}
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
provider: integrationProvider,
|
|
122
|
+
webhookProcessors: integrationProvider.webhookProcessors,
|
|
104
123
|
e2eRoutes: [
|
|
105
124
|
createLinearE2eRoutes({
|
|
106
125
|
tokenStore,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/providers/linear.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {\n ConnectLinearInstallationInput,\n LinearSecretsStore,\n} from '@shipfox/api-integration-linear';\nimport {config} from '#config.js';\nimport {\n deleteIntegrationConnection,\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {publishIntegrationEventReceived, recordDeliveryOnly} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\nconst LINEAR_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_linear';\nconst LINEAR_SECRETS_NAMESPACE_PREFIX = 'system/integrations/linear/';\n\ntype IntegrationDb = ReturnType<typeof db>;\ntype IntegrationTx = Parameters<Parameters<IntegrationDb['transaction']>[0]>[0];\n\nasync function loadLinearModuleParts(\n options: Parameters<IntegrationProviderModule['load']>[0] = {},\n): Promise<IntegrationModuleParts> {\n const {\n createLinearTokenStore,\n createLinearE2eRoutes,\n createLinearIntegrationProvider,\n config: linearConfig,\n disconnectLinearInstallation: disconnectLinearInstallationRecords,\n getLinearInstallationByOrganizationId,\n db: linearDb,\n migrationsPath: linearMigrationsPath,\n upsertLinearInstallation,\n } = await import('@shipfox/api-integration-linear');\n\n async function getExistingLinearConnection(input: {\n organizationId: string;\n }): Promise<CoreIntegrationConnection<'linear'> | undefined> {\n const installation = await getLinearInstallationByOrganizationId(input.organizationId);\n if (!installation) return undefined;\n const connection = await getIntegrationConnectionById(installation.connectionId);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'linear'>;\n }\n\n async function connectLinearInstallation(\n input: ConnectLinearInstallationInput,\n ): Promise<CoreIntegrationConnection<'linear'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`linear_${input.organizationUrlKey}`, {\n fallback: 'linear',\n });\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'linear',\n externalAccountId: input.organizationId,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'linear',\n externalAccountId: input.organizationId,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n\n await upsertLinearInstallation(\n {\n connectionId: connection.id,\n organizationId: input.organizationId,\n organizationUrlKey: input.organizationUrlKey,\n appUserId: input.appUserId,\n scopes: input.scopes,\n tokenExpiresAt: input.tokenExpiresAt,\n status: 'installed',\n },\n {tx},\n );\n\n return connection as CoreIntegrationConnection<'linear'>;\n }),\n );\n }\n\n async function disconnectLinearInstallation(input: {connectionId: string}): Promise<void> {\n await disconnectLinearInstallationRecords<IntegrationTx>({\n connectionId: input.connectionId,\n getConnection: getIntegrationConnectionById,\n deleteSecrets: (params) =>\n options.secrets?.linear?.deleteSecrets({\n ...params,\n namespace: linearNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(0),\n transaction: (fn) => db().transaction((tx) => fn(tx)),\n deleteConnection: (params, options) =>\n deleteIntegrationConnection({id: params.connectionId}, options),\n });\n }\n\n const fallbackSecrets: LinearSecretsStore = {\n getSecret: () => Promise.resolve(null),\n setSecrets: () => Promise.reject(new Error('Linear token storage is not configured')),\n };\n const secrets: LinearSecretsStore = options.secrets?.linear\n ? {\n getSecret: (params: Parameters<LinearSecretsStore['getSecret']>[0]) =>\n options.secrets?.linear?.getSecret({\n ...params,\n namespace: linearNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(null),\n setSecrets: (params: Parameters<LinearSecretsStore['setSecrets']>[0]) =>\n options.secrets?.linear?.setSecrets({\n ...params,\n namespace: linearNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(),\n }\n : fallbackSecrets;\n const tokenStore = createLinearTokenStore({\n resolveConnection: async (connectionId) => getIntegrationConnectionById(connectionId),\n secrets,\n });\n\n return {\n provider: createLinearIntegrationProvider({\n agentTools: {tokenStore, endpoint: linearConfig.LINEAR_MCP_ENDPOINT},\n routes: {\n tokenStore,\n getExistingLinearConnection,\n connectLinearInstallation,\n disconnectLinearInstallation,\n publishIntegrationEventReceived,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n coreDb: db,\n },\n }),\n e2eRoutes: [\n createLinearE2eRoutes({\n tokenStore,\n getExistingLinearConnection,\n connectLinearInstallation,\n disconnectLinearInstallation,\n connectionCapabilities: ['agent_tools'],\n }),\n ],\n database: {\n db: linearDb,\n migrationsPath: linearMigrationsPath,\n migrationsTableName: LINEAR_MIGRATIONS_TABLE,\n },\n };\n}\n\nexport const linearProviderModule: IntegrationProviderModule = {\n id: 'linear',\n enabled: config.INTEGRATIONS_ENABLE_LINEAR_PROVIDER,\n load: loadLinearModuleParts,\n};\n\nfunction linearNamespaceSuffix(namespace: string): string {\n if (!namespace.startsWith(LINEAR_SECRETS_NAMESPACE_PREFIX)) {\n throw new Error('Linear provider attempted to access an unscoped secret namespace');\n }\n return namespace.slice(LINEAR_SECRETS_NAMESPACE_PREFIX.length);\n}\n"],"names":["slugifyConnectionSlug","config","deleteIntegrationConnection","getIntegrationConnectionById","resolveUniqueConnectionSlug","upsertIntegrationConnection","db","publishIntegrationEventReceived","recordDeliveryOnly","retryConnectionSlugCollision","LINEAR_MIGRATIONS_TABLE","LINEAR_SECRETS_NAMESPACE_PREFIX","loadLinearModuleParts","options","createLinearTokenStore","createLinearE2eRoutes","createLinearIntegrationProvider","linearConfig","disconnectLinearInstallation","disconnectLinearInstallationRecords","getLinearInstallationByOrganizationId","linearDb","migrationsPath","linearMigrationsPath","upsertLinearInstallation","getExistingLinearConnection","input","installation","organizationId","undefined","connection","connectionId","connectLinearInstallation","transaction","tx","baseSlug","organizationUrlKey","fallback","slug","workspaceId","provider","externalAccountId","displayName","lifecycleStatus","id","appUserId","scopes","tokenExpiresAt","status","getConnection","deleteSecrets","params","secrets","linear","namespace","linearNamespaceSuffix","Promise","resolve","fn","deleteConnection","fallbackSecrets","getSecret","setSecrets","reject","Error","tokenStore","resolveConnection","agentTools","endpoint","LINEAR_MCP_ENDPOINT","routes","coreDb","e2eRoutes","connectionCapabilities","database","migrationsTableName","linearProviderModule","enabled","INTEGRATIONS_ENABLE_LINEAR_PROVIDER","load","startsWith","slice","length"],"mappings":"AAAA,SAEEA,qBAAqB,QAChB,oCAAoC;AAK3C,SAAQC,MAAM,QAAO,aAAa;AAClC,SACEC,2BAA2B,EAC3BC,4BAA4B,EAC5BC,2BAA2B,EAC3BC,2BAA2B,QACtB,qBAAqB;AAC5B,SAAQC,EAAE,QAAO,YAAY;AAC7B,SAAQC,+BAA+B,EAAEC,kBAAkB,QAAO,4BAA4B;AAC9F,SAAQC,4BAA4B,QAAO,gCAAgC;AAG3E,MAAMC,0BAA0B;AAChC,MAAMC,kCAAkC;AAKxC,eAAeC,sBACbC,UAA4D,CAAC,CAAC;IAE9D,MAAM,EACJC,sBAAsB,EACtBC,qBAAqB,EACrBC,+BAA+B,EAC/Bf,QAAQgB,YAAY,EACpBC,8BAA8BC,mCAAmC,EACjEC,qCAAqC,EACrCd,IAAIe,QAAQ,EACZC,gBAAgBC,oBAAoB,EACpCC,wBAAwB,EACzB,GAAG,MAAM,MAAM,CAAC;IAEjB,eAAeC,4BAA4BC,KAE1C;QACC,MAAMC,eAAe,MAAMP,sCAAsCM,MAAME,cAAc;QACrF,IAAI,CAACD,cAAc,OAAOE;QAC1B,MAAMC,aAAa,MAAM3B,6BAA6BwB,aAAaI,YAAY;QAC/E,IAAI,CAACD,YAAY,OAAOD;QACxB,OAAOC;IACT;IAEA,eAAeE,0BACbN,KAAqC;QAErC,OAAO,MAAMjB,6BAA6B,IACxCH,KAAK2B,WAAW,CAAC,OAAOC;gBACtB,MAAMC,WAAWnC,sBAAsB,CAAC,OAAO,EAAE0B,MAAMU,kBAAkB,EAAE,EAAE;oBAC3EC,UAAU;gBACZ;gBACA,MAAMC,OAAO,MAAMlC,4BACjB;oBACEmC,aAAab,MAAMa,WAAW;oBAC9BC,UAAU;oBACVC,mBAAmBf,MAAME,cAAc;oBACvCO;gBACF,GACA;oBAACD;gBAAE;gBAEL,MAAMJ,aAAa,MAAMzB,4BACvB;oBACEkC,aAAab,MAAMa,WAAW;oBAC9BC,UAAU;oBACVC,mBAAmBf,MAAME,cAAc;oBACvCU;oBACAI,aAAahB,MAAMgB,WAAW;oBAC9BC,iBAAiB;gBACnB,GACA;oBAACT;gBAAE;gBAGL,MAAMV,yBACJ;oBACEO,cAAcD,WAAWc,EAAE;oBAC3BhB,gBAAgBF,MAAME,cAAc;oBACpCQ,oBAAoBV,MAAMU,kBAAkB;oBAC5CS,WAAWnB,MAAMmB,SAAS;oBAC1BC,QAAQpB,MAAMoB,MAAM;oBACpBC,gBAAgBrB,MAAMqB,cAAc;oBACpCC,QAAQ;gBACV,GACA;oBAACd;gBAAE;gBAGL,OAAOJ;YACT;IAEJ;IAEA,eAAeZ,6BAA6BQ,KAA6B;QACvE,MAAMP,oCAAmD;YACvDY,cAAcL,MAAMK,YAAY;YAChCkB,eAAe9C;YACf+C,eAAe,CAACC,SACdtC,QAAQuC,OAAO,EAAEC,QAAQH,cAAc;oBACrC,GAAGC,MAAM;oBACTG,WAAWC,sBAAsBJ,OAAOG,SAAS;gBACnD,MAAME,QAAQC,OAAO,CAAC;YACxBxB,aAAa,CAACyB,KAAOpD,KAAK2B,WAAW,CAAC,CAACC,KAAOwB,GAAGxB;YACjDyB,kBAAkB,CAACR,QAAQtC,UACzBX,4BAA4B;oBAAC0C,IAAIO,OAAOpB,YAAY;gBAAA,GAAGlB;QAC3D;IACF;IAEA,MAAM+C,kBAAsC;QAC1CC,WAAW,IAAML,QAAQC,OAAO,CAAC;QACjCK,YAAY,IAAMN,QAAQO,MAAM,CAAC,IAAIC,MAAM;IAC7C;IACA,MAAMZ,UAA8BvC,QAAQuC,OAAO,EAAEC,SACjD;QACEQ,WAAW,CAACV,SACVtC,QAAQuC,OAAO,EAAEC,QAAQQ,UAAU;gBACjC,GAAGV,MAAM;gBACTG,WAAWC,sBAAsBJ,OAAOG,SAAS;YACnD,MAAME,QAAQC,OAAO,CAAC;QACxBK,YAAY,CAACX,SACXtC,QAAQuC,OAAO,EAAEC,QAAQS,WAAW;gBAClC,GAAGX,MAAM;gBACTG,WAAWC,sBAAsBJ,OAAOG,SAAS;YACnD,MAAME,QAAQC,OAAO;IACzB,IACAG;IACJ,MAAMK,aAAanD,uBAAuB;QACxCoD,mBAAmB,OAAOnC,eAAiB5B,6BAA6B4B;QACxEqB;IACF;IAEA,OAAO;QACLZ,UAAUxB,gCAAgC;YACxCmD,YAAY;gBAACF;gBAAYG,UAAUnD,aAAaoD,mBAAmB;YAAA;YACnEC,QAAQ;gBACNL;gBACAxC;gBACAO;gBACAd;gBACAX;gBACAC;gBACAL;gBACAoE,QAAQjE;YACV;QACF;QACAkE,WAAW;YACTzD,sBAAsB;gBACpBkD;gBACAxC;gBACAO;gBACAd;gBACAuD,wBAAwB;oBAAC;iBAAc;YACzC;SACD;QACDC,UAAU;YACRpE,IAAIe;YACJC,gBAAgBC;YAChBoD,qBAAqBjE;QACvB;IACF;AACF;AAEA,OAAO,MAAMkE,uBAAkD;IAC7DhC,IAAI;IACJiC,SAAS5E,OAAO6E,mCAAmC;IACnDC,MAAMnE;AACR,EAAE;AAEF,SAAS2C,sBAAsBD,SAAiB;IAC9C,IAAI,CAACA,UAAU0B,UAAU,CAACrE,kCAAkC;QAC1D,MAAM,IAAIqD,MAAM;IAClB;IACA,OAAOV,UAAU2B,KAAK,CAACtE,gCAAgCuE,MAAM;AAC/D"}
|
|
1
|
+
{"version":3,"sources":["../../src/providers/linear.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {\n ConnectLinearInstallationInput,\n LinearSecretsStore,\n} from '@shipfox/api-integration-linear';\nimport {config} from '#config.js';\nimport {\n deleteIntegrationConnection,\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {publishIntegrationEventReceived, recordDeliveryOnly} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\nconst LINEAR_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_linear';\nconst LINEAR_SECRETS_NAMESPACE_PREFIX = 'system/integrations/linear/';\n\ntype IntegrationDb = ReturnType<typeof db>;\ntype IntegrationTx = Parameters<Parameters<IntegrationDb['transaction']>[0]>[0];\n\nasync function loadLinearModuleParts(\n options: Parameters<IntegrationProviderModule['load']>[0] = {},\n): Promise<IntegrationModuleParts> {\n const {\n createLinearTokenStore,\n createLinearE2eRoutes,\n createLinearIntegrationProvider,\n config: linearConfig,\n deleteLinearInstallationByConnectionId,\n disconnectLinearInstallation: disconnectLinearInstallationRecords,\n getLinearInstallationByOrganizationId,\n db: linearDb,\n linearSecretsNamespace,\n migrationsPath: linearMigrationsPath,\n upsertLinearInstallation,\n } = await import('@shipfox/api-integration-linear');\n\n async function getExistingLinearConnection(input: {\n organizationId: string;\n }): Promise<CoreIntegrationConnection<'linear'> | undefined> {\n const installation = await getLinearInstallationByOrganizationId(input.organizationId);\n if (!installation) return undefined;\n const connection = await getIntegrationConnectionById(installation.connectionId);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'linear'>;\n }\n\n async function connectLinearInstallation(\n input: ConnectLinearInstallationInput,\n ): Promise<CoreIntegrationConnection<'linear'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`linear_${input.organizationUrlKey}`, {\n fallback: 'linear',\n });\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'linear',\n externalAccountId: input.organizationId,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'linear',\n externalAccountId: input.organizationId,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n\n await upsertLinearInstallation(\n {\n connectionId: connection.id,\n organizationId: input.organizationId,\n organizationUrlKey: input.organizationUrlKey,\n appUserId: input.appUserId,\n scopes: input.scopes,\n tokenExpiresAt: input.tokenExpiresAt,\n status: 'installed',\n },\n {tx},\n );\n\n return connection as CoreIntegrationConnection<'linear'>;\n }),\n );\n }\n\n async function disconnectLinearInstallation(input: {connectionId: string}): Promise<void> {\n await disconnectLinearInstallationRecords<IntegrationTx>({\n connectionId: input.connectionId,\n getConnection: getIntegrationConnectionById,\n deleteSecrets: (params) =>\n options.secrets?.linear?.deleteSecrets({\n ...params,\n namespace: linearNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(0),\n transaction: (fn) => db().transaction((tx) => fn(tx)),\n deleteConnection: (params, options) =>\n deleteIntegrationConnection({id: params.connectionId}, options),\n });\n }\n\n const fallbackSecrets: LinearSecretsStore = {\n getSecret: () => Promise.resolve(null),\n setSecrets: () => Promise.reject(new Error('Linear token storage is not configured')),\n };\n const secrets: LinearSecretsStore = options.secrets?.linear\n ? {\n getSecret: (params: Parameters<LinearSecretsStore['getSecret']>[0]) =>\n options.secrets?.linear?.getSecret({\n ...params,\n namespace: linearNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(null),\n setSecrets: (params: Parameters<LinearSecretsStore['setSecrets']>[0]) =>\n options.secrets?.linear?.setSecrets({\n ...params,\n namespace: linearNamespaceSuffix(params.namespace),\n }) ?? Promise.resolve(),\n }\n : fallbackSecrets;\n const tokenStore = createLinearTokenStore({\n resolveConnection: async (connectionId) => getIntegrationConnectionById(connectionId),\n secrets,\n });\n\n const integrationProvider = createLinearIntegrationProvider({\n agentTools: {tokenStore, endpoint: linearConfig.LINEAR_MCP_ENDPOINT},\n cleanup: {\n deleteConnectionRecords: async (connection, {tx}) => {\n await deleteLinearInstallationByConnectionId(connection.id, {tx});\n },\n deleteConnectionSecrets: async (connection) => {\n // Scoped secrets accept the provider-local suffix, after this helper validates its prefix.\n await (options.secrets?.linear?.deleteSecrets({\n workspaceId: connection.workspaceId,\n namespace: linearNamespaceSuffix(linearSecretsNamespace(connection.id)),\n }) ?? Promise.resolve());\n },\n },\n routes: {\n tokenStore,\n getExistingLinearConnection,\n connectLinearInstallation,\n disconnectLinearInstallation,\n publishIntegrationEventReceived,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n coreDb: db,\n ...(options.requireActiveWorkspaceMembership\n ? {requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership}\n : {}),\n },\n });\n\n return {\n provider: integrationProvider,\n webhookProcessors: integrationProvider.webhookProcessors,\n e2eRoutes: [\n createLinearE2eRoutes({\n tokenStore,\n getExistingLinearConnection,\n connectLinearInstallation,\n disconnectLinearInstallation,\n connectionCapabilities: ['agent_tools'],\n }),\n ],\n database: {\n db: linearDb,\n migrationsPath: linearMigrationsPath,\n migrationsTableName: LINEAR_MIGRATIONS_TABLE,\n },\n };\n}\n\nexport const linearProviderModule: IntegrationProviderModule = {\n id: 'linear',\n enabled: config.INTEGRATIONS_ENABLE_LINEAR_PROVIDER,\n load: loadLinearModuleParts,\n};\n\nfunction linearNamespaceSuffix(namespace: string): string {\n if (!namespace.startsWith(LINEAR_SECRETS_NAMESPACE_PREFIX)) {\n throw new Error('Linear provider attempted to access an unscoped secret namespace');\n }\n return namespace.slice(LINEAR_SECRETS_NAMESPACE_PREFIX.length);\n}\n"],"names":["slugifyConnectionSlug","config","deleteIntegrationConnection","getIntegrationConnectionById","resolveUniqueConnectionSlug","upsertIntegrationConnection","db","publishIntegrationEventReceived","recordDeliveryOnly","retryConnectionSlugCollision","LINEAR_MIGRATIONS_TABLE","LINEAR_SECRETS_NAMESPACE_PREFIX","loadLinearModuleParts","options","createLinearTokenStore","createLinearE2eRoutes","createLinearIntegrationProvider","linearConfig","deleteLinearInstallationByConnectionId","disconnectLinearInstallation","disconnectLinearInstallationRecords","getLinearInstallationByOrganizationId","linearDb","linearSecretsNamespace","migrationsPath","linearMigrationsPath","upsertLinearInstallation","getExistingLinearConnection","input","installation","organizationId","undefined","connection","connectionId","connectLinearInstallation","transaction","tx","baseSlug","organizationUrlKey","fallback","slug","workspaceId","provider","externalAccountId","displayName","lifecycleStatus","id","appUserId","scopes","tokenExpiresAt","status","getConnection","deleteSecrets","params","secrets","linear","namespace","linearNamespaceSuffix","Promise","resolve","fn","deleteConnection","fallbackSecrets","getSecret","setSecrets","reject","Error","tokenStore","resolveConnection","integrationProvider","agentTools","endpoint","LINEAR_MCP_ENDPOINT","cleanup","deleteConnectionRecords","deleteConnectionSecrets","routes","coreDb","requireActiveWorkspaceMembership","webhookProcessors","e2eRoutes","connectionCapabilities","database","migrationsTableName","linearProviderModule","enabled","INTEGRATIONS_ENABLE_LINEAR_PROVIDER","load","startsWith","slice","length"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sentry.d.ts","sourceRoot":"","sources":["../../src/providers/sentry.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"sentry.d.ts","sourceRoot":"","sources":["../../src/providers/sentry.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;AAgG3F,eAAO,MAAM,oBAAoB,EAAE,yBAIlC,CAAC"}
|
package/dist/providers/sentry.js
CHANGED
|
@@ -52,18 +52,20 @@ async function loadSentryModuleParts() {
|
|
|
52
52
|
return connection;
|
|
53
53
|
}));
|
|
54
54
|
}
|
|
55
|
+
const integrationProvider = createSentryIntegrationProvider({
|
|
56
|
+
getSentryInstallation: ({ installationUuid })=>getSentryInstallationByInstallationUuid(installationUuid),
|
|
57
|
+
getConnectionById,
|
|
58
|
+
connectSentryInstallation,
|
|
59
|
+
persistVerifiedUnclaimedInstallation,
|
|
60
|
+
coreDb: db,
|
|
61
|
+
publishIntegrationEventReceived,
|
|
62
|
+
recordDeliveryOnly,
|
|
63
|
+
getIntegrationConnectionById,
|
|
64
|
+
updateConnectionLifecycleStatus: updateIntegrationConnectionLifecycleStatus
|
|
65
|
+
});
|
|
55
66
|
return {
|
|
56
|
-
provider:
|
|
57
|
-
|
|
58
|
-
getConnectionById,
|
|
59
|
-
connectSentryInstallation,
|
|
60
|
-
persistVerifiedUnclaimedInstallation,
|
|
61
|
-
coreDb: db,
|
|
62
|
-
publishIntegrationEventReceived,
|
|
63
|
-
recordDeliveryOnly,
|
|
64
|
-
getIntegrationConnectionById,
|
|
65
|
-
updateConnectionLifecycleStatus: updateIntegrationConnectionLifecycleStatus
|
|
66
|
-
}),
|
|
67
|
+
provider: integrationProvider,
|
|
68
|
+
webhookProcessors: integrationProvider.webhookProcessors,
|
|
67
69
|
database: {
|
|
68
70
|
db: sentryDb,
|
|
69
71
|
migrationsPath: sentryMigrationsPath,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/providers/sentry.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {ConnectSentryInstallationInput} from '@shipfox/api-integration-sentry';\nimport {config} from '#config.js';\nimport {\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n updateIntegrationConnectionLifecycleStatus,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {publishIntegrationEventReceived, recordDeliveryOnly} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\n// Stable migration-tracking table name for the Sentry provider database. This\n// must NOT depend on the provider's position in the module `database` array. A\n// positional name would shift if a provider is flag-disabled and silently\n// re-run migrations against existing tables.\nconst SENTRY_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_sentry';\n\nasync function loadSentryModuleParts(): Promise<IntegrationModuleParts> {\n const {\n createSentryIntegrationProvider,\n createSentryMaintenanceWorker,\n getSentryInstallationByInstallationUuid,\n persistVerifiedUnclaimedInstallation,\n upsertSentryInstallation,\n db: sentryDb,\n migrationsPath: sentryMigrationsPath,\n } = await import('@shipfox/api-integration-sentry');\n\n async function getConnectionById(\n id: string,\n ): Promise<CoreIntegrationConnection<'sentry'> | undefined> {\n const connection = await getIntegrationConnectionById(id);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'sentry'>;\n }\n\n async function connectSentryInstallation(\n input: ConnectSentryInstallationInput,\n ): Promise<CoreIntegrationConnection<'sentry'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`sentry_${input.orgSlug}`, {fallback: 'sentry'});\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'sentry',\n externalAccountId: input.installationUuid,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'sentry',\n externalAccountId: input.installationUuid,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n\n await upsertSentryInstallation(\n {\n connectionId: connection.id,\n installationUuid: input.installationUuid,\n orgSlug: input.orgSlug,\n status: 'installed',\n codeHash: input.codeHash,\n installerUserId: input.installerUserId,\n },\n {tx},\n );\n\n return connection as CoreIntegrationConnection<'sentry'>;\n }),\n );\n }\n\n
|
|
1
|
+
{"version":3,"sources":["../../src/providers/sentry.ts"],"sourcesContent":["import {\n type IntegrationConnection as CoreIntegrationConnection,\n slugifyConnectionSlug,\n} from '@shipfox/api-integration-core-dto';\nimport type {ConnectSentryInstallationInput} from '@shipfox/api-integration-sentry';\nimport {config} from '#config.js';\nimport {\n getIntegrationConnectionById,\n resolveUniqueConnectionSlug,\n updateIntegrationConnectionLifecycleStatus,\n upsertIntegrationConnection,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {publishIntegrationEventReceived, recordDeliveryOnly} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\n// Stable migration-tracking table name for the Sentry provider database. This\n// must NOT depend on the provider's position in the module `database` array. A\n// positional name would shift if a provider is flag-disabled and silently\n// re-run migrations against existing tables.\nconst SENTRY_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_sentry';\n\nasync function loadSentryModuleParts(): Promise<IntegrationModuleParts> {\n const {\n createSentryIntegrationProvider,\n createSentryMaintenanceWorker,\n getSentryInstallationByInstallationUuid,\n persistVerifiedUnclaimedInstallation,\n upsertSentryInstallation,\n db: sentryDb,\n migrationsPath: sentryMigrationsPath,\n } = await import('@shipfox/api-integration-sentry');\n\n async function getConnectionById(\n id: string,\n ): Promise<CoreIntegrationConnection<'sentry'> | undefined> {\n const connection = await getIntegrationConnectionById(id);\n if (!connection) return undefined;\n return connection as CoreIntegrationConnection<'sentry'>;\n }\n\n async function connectSentryInstallation(\n input: ConnectSentryInstallationInput,\n ): Promise<CoreIntegrationConnection<'sentry'>> {\n return await retryConnectionSlugCollision(() =>\n db().transaction(async (tx) => {\n const baseSlug = slugifyConnectionSlug(`sentry_${input.orgSlug}`, {fallback: 'sentry'});\n const slug = await resolveUniqueConnectionSlug(\n {\n workspaceId: input.workspaceId,\n provider: 'sentry',\n externalAccountId: input.installationUuid,\n baseSlug,\n },\n {tx},\n );\n const connection = await upsertIntegrationConnection(\n {\n workspaceId: input.workspaceId,\n provider: 'sentry',\n externalAccountId: input.installationUuid,\n slug,\n displayName: input.displayName,\n lifecycleStatus: 'active',\n },\n {tx},\n );\n\n await upsertSentryInstallation(\n {\n connectionId: connection.id,\n installationUuid: input.installationUuid,\n orgSlug: input.orgSlug,\n status: 'installed',\n codeHash: input.codeHash,\n installerUserId: input.installerUserId,\n },\n {tx},\n );\n\n return connection as CoreIntegrationConnection<'sentry'>;\n }),\n );\n }\n\n const integrationProvider = createSentryIntegrationProvider({\n getSentryInstallation: ({installationUuid}) =>\n getSentryInstallationByInstallationUuid(installationUuid),\n getConnectionById,\n connectSentryInstallation,\n persistVerifiedUnclaimedInstallation,\n coreDb: db,\n publishIntegrationEventReceived,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n updateConnectionLifecycleStatus: updateIntegrationConnectionLifecycleStatus,\n });\n\n return {\n provider: integrationProvider,\n webhookProcessors: integrationProvider.webhookProcessors,\n database: {\n db: sentryDb,\n migrationsPath: sentryMigrationsPath,\n migrationsTableName: SENTRY_MIGRATIONS_TABLE,\n },\n workers: [createSentryMaintenanceWorker()],\n };\n}\n\nexport const sentryProviderModule: IntegrationProviderModule = {\n id: 'sentry',\n enabled: config.INTEGRATIONS_ENABLE_SENTRY_PROVIDER,\n load: loadSentryModuleParts,\n};\n"],"names":["slugifyConnectionSlug","config","getIntegrationConnectionById","resolveUniqueConnectionSlug","updateIntegrationConnectionLifecycleStatus","upsertIntegrationConnection","db","publishIntegrationEventReceived","recordDeliveryOnly","retryConnectionSlugCollision","SENTRY_MIGRATIONS_TABLE","loadSentryModuleParts","createSentryIntegrationProvider","createSentryMaintenanceWorker","getSentryInstallationByInstallationUuid","persistVerifiedUnclaimedInstallation","upsertSentryInstallation","sentryDb","migrationsPath","sentryMigrationsPath","getConnectionById","id","connection","undefined","connectSentryInstallation","input","transaction","tx","baseSlug","orgSlug","fallback","slug","workspaceId","provider","externalAccountId","installationUuid","displayName","lifecycleStatus","connectionId","status","codeHash","installerUserId","integrationProvider","getSentryInstallation","coreDb","updateConnectionLifecycleStatus","webhookProcessors","database","migrationsTableName","workers","sentryProviderModule","enabled","INTEGRATIONS_ENABLE_SENTRY_PROVIDER","load"],"mappings":"AAAA,SAEEA,qBAAqB,QAChB,oCAAoC;AAE3C,SAAQC,MAAM,QAAO,aAAa;AAClC,SACEC,4BAA4B,EAC5BC,2BAA2B,EAC3BC,0CAA0C,EAC1CC,2BAA2B,QACtB,qBAAqB;AAC5B,SAAQC,EAAE,QAAO,YAAY;AAC7B,SAAQC,+BAA+B,EAAEC,kBAAkB,QAAO,4BAA4B;AAC9F,SAAQC,4BAA4B,QAAO,gCAAgC;AAG3E,8EAA8E;AAC9E,+EAA+E;AAC/E,0EAA0E;AAC1E,6CAA6C;AAC7C,MAAMC,0BAA0B;AAEhC,eAAeC;IACb,MAAM,EACJC,+BAA+B,EAC/BC,6BAA6B,EAC7BC,uCAAuC,EACvCC,oCAAoC,EACpCC,wBAAwB,EACxBV,IAAIW,QAAQ,EACZC,gBAAgBC,oBAAoB,EACrC,GAAG,MAAM,MAAM,CAAC;IAEjB,eAAeC,kBACbC,EAAU;QAEV,MAAMC,aAAa,MAAMpB,6BAA6BmB;QACtD,IAAI,CAACC,YAAY,OAAOC;QACxB,OAAOD;IACT;IAEA,eAAeE,0BACbC,KAAqC;QAErC,OAAO,MAAMhB,6BAA6B,IACxCH,KAAKoB,WAAW,CAAC,OAAOC;gBACtB,MAAMC,WAAW5B,sBAAsB,CAAC,OAAO,EAAEyB,MAAMI,OAAO,EAAE,EAAE;oBAACC,UAAU;gBAAQ;gBACrF,MAAMC,OAAO,MAAM5B,4BACjB;oBACE6B,aAAaP,MAAMO,WAAW;oBAC9BC,UAAU;oBACVC,mBAAmBT,MAAMU,gBAAgB;oBACzCP;gBACF,GACA;oBAACD;gBAAE;gBAEL,MAAML,aAAa,MAAMjB,4BACvB;oBACE2B,aAAaP,MAAMO,WAAW;oBAC9BC,UAAU;oBACVC,mBAAmBT,MAAMU,gBAAgB;oBACzCJ;oBACAK,aAAaX,MAAMW,WAAW;oBAC9BC,iBAAiB;gBACnB,GACA;oBAACV;gBAAE;gBAGL,MAAMX,yBACJ;oBACEsB,cAAchB,WAAWD,EAAE;oBAC3Bc,kBAAkBV,MAAMU,gBAAgB;oBACxCN,SAASJ,MAAMI,OAAO;oBACtBU,QAAQ;oBACRC,UAAUf,MAAMe,QAAQ;oBACxBC,iBAAiBhB,MAAMgB,eAAe;gBACxC,GACA;oBAACd;gBAAE;gBAGL,OAAOL;YACT;IAEJ;IAEA,MAAMoB,sBAAsB9B,gCAAgC;QAC1D+B,uBAAuB,CAAC,EAACR,gBAAgB,EAAC,GACxCrB,wCAAwCqB;QAC1Cf;QACAI;QACAT;QACA6B,QAAQtC;QACRC;QACAC;QACAN;QACA2C,iCAAiCzC;IACnC;IAEA,OAAO;QACL6B,UAAUS;QACVI,mBAAmBJ,oBAAoBI,iBAAiB;QACxDC,UAAU;YACRzC,IAAIW;YACJC,gBAAgBC;YAChB6B,qBAAqBtC;QACvB;QACAuC,SAAS;YAACpC;SAAgC;IAC5C;AACF;AAEA,OAAO,MAAMqC,uBAAkD;IAC7D7B,IAAI;IACJ8B,SAASlD,OAAOmD,mCAAmC;IACnDC,MAAM1C;AACR,EAAE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../src/providers/slack.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../src/providers/slack.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;AAwK3F,eAAO,MAAM,mBAAmB,EAAE,yBAIjC,CAAC"}
|
package/dist/providers/slack.js
CHANGED
|
@@ -2,12 +2,12 @@ import { slugifyConnectionSlug } from '@shipfox/api-integration-core-dto';
|
|
|
2
2
|
import { config } from '#config.js';
|
|
3
3
|
import { deleteIntegrationConnection, getIntegrationConnectionById, resolveUniqueConnectionSlug, upsertIntegrationConnection } from '#db/connections.js';
|
|
4
4
|
import { db } from '#db/db.js';
|
|
5
|
-
import { publishIntegrationEventReceived, recordDeliveryOnly } from '#db/webhook-deliveries.js';
|
|
5
|
+
import { claimWebhookDelivery, publishIntegrationEventReceived, recordDeliveryOnly } from '#db/webhook-deliveries.js';
|
|
6
6
|
import { retryConnectionSlugCollision } from '#providers/connection-slug.js';
|
|
7
7
|
const SLACK_MIGRATIONS_TABLE = '__drizzle_migrations_integrations_slack';
|
|
8
8
|
const SLACK_SECRETS_NAMESPACE_PREFIX = 'system/integrations/slack/';
|
|
9
9
|
async function loadSlackModuleParts(options = {}) {
|
|
10
|
-
const { createSlackE2eRoutes, createSlackIntegrationProvider, createSlackTokenStore, db: slackDb, disconnectSlackInstallation: disconnectSlackInstallationRecords, getSlackInstallationByTeamId, migrationsPath: slackMigrationsPath, upsertSlackInstallation } = await import('@shipfox/api-integration-slack');
|
|
10
|
+
const { createSlackE2eRoutes, createSlackIntegrationProvider, createSlackTokenStore, db: slackDb, deleteSlackInstallationByConnectionId, disconnectSlackInstallation: disconnectSlackInstallationRecords, getSlackInstallationByTeamId, migrationsPath: slackMigrationsPath, slackSecretsNamespace, upsertSlackInstallation } = await import('@shipfox/api-integration-slack');
|
|
11
11
|
async function getExistingSlackConnection(input) {
|
|
12
12
|
const installation = await getSlackInstallationByTeamId(input.teamId);
|
|
13
13
|
if (!installation) return undefined;
|
|
@@ -85,22 +85,42 @@ async function loadSlackModuleParts(options = {}) {
|
|
|
85
85
|
resolveConnection: async (connectionId)=>getIntegrationConnectionById(connectionId),
|
|
86
86
|
secrets
|
|
87
87
|
});
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
const integrationProvider = createSlackIntegrationProvider({
|
|
89
|
+
agentTools: {
|
|
90
|
+
tokenStore
|
|
91
|
+
},
|
|
92
|
+
cleanup: {
|
|
93
|
+
deleteConnectionRecords: async (connection, { tx })=>{
|
|
94
|
+
await deleteSlackInstallationByConnectionId(connection.id, {
|
|
95
|
+
tx
|
|
96
|
+
});
|
|
92
97
|
},
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
publishIntegrationEventReceived,
|
|
100
|
-
recordDeliveryOnly,
|
|
101
|
-
getIntegrationConnectionById
|
|
98
|
+
deleteConnectionSecrets: async (connection)=>{
|
|
99
|
+
// Scoped secrets accept the provider-local suffix, after this helper validates its prefix.
|
|
100
|
+
await (options.secrets?.slack?.deleteSecrets({
|
|
101
|
+
workspaceId: connection.workspaceId,
|
|
102
|
+
namespace: slackNamespaceSuffix(slackSecretsNamespace(connection.id))
|
|
103
|
+
}) ?? Promise.resolve());
|
|
102
104
|
}
|
|
103
|
-
}
|
|
105
|
+
},
|
|
106
|
+
routes: {
|
|
107
|
+
tokenStore,
|
|
108
|
+
getExistingSlackConnection,
|
|
109
|
+
connectSlackInstallation,
|
|
110
|
+
disconnectSlackInstallation,
|
|
111
|
+
coreDb: db,
|
|
112
|
+
claimWebhookDelivery,
|
|
113
|
+
publishIntegrationEventReceived,
|
|
114
|
+
recordDeliveryOnly,
|
|
115
|
+
getIntegrationConnectionById,
|
|
116
|
+
...options.requireActiveWorkspaceMembership ? {
|
|
117
|
+
requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership
|
|
118
|
+
} : {}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
return {
|
|
122
|
+
provider: integrationProvider,
|
|
123
|
+
webhookProcessors: integrationProvider.webhookProcessors,
|
|
104
124
|
e2eRoutes: [
|
|
105
125
|
createSlackE2eRoutes({
|
|
106
126
|
tokenStore,
|