@shipfox/api-integration-core 12.2.0 → 12.4.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 +11 -8
- package/CHANGELOG.md +25 -0
- package/dist/core/secret-cleanup.d.ts +20 -0
- package/dist/core/secret-cleanup.d.ts.map +1 -0
- package/dist/core/secret-cleanup.js +178 -0
- package/dist/core/secret-cleanup.js.map +1 -0
- package/dist/db/db.d.ts +562 -0
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/db.js +2 -0
- package/dist/db/db.js.map +1 -1
- package/dist/db/schema/secret-cleanups.d.ts +284 -0
- package/dist/db/schema/secret-cleanups.d.ts.map +1 -0
- package/dist/db/schema/secret-cleanups.js +39 -0
- package/dist/db/schema/secret-cleanups.js.map +1 -0
- package/dist/db/secret-cleanups.d.ts +52 -0
- package/dist/db/secret-cleanups.d.ts.map +1 -0
- package/dist/db/secret-cleanups.js +105 -0
- package/dist/db/secret-cleanups.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/presentation/routes/manage-connections.d.ts.map +1 -1
- package/dist/presentation/routes/manage-connections.js +57 -18
- package/dist/presentation/routes/manage-connections.js.map +1 -1
- package/dist/providers/jira.d.ts.map +1 -1
- package/dist/providers/jira.js +51 -14
- package/dist/providers/jira.js.map +1 -1
- package/dist/temporal/activities/index.d.ts +5 -1
- package/dist/temporal/activities/index.d.ts.map +1 -1
- package/dist/temporal/activities/index.js +8 -2
- package/dist/temporal/activities/index.js.map +1 -1
- package/dist/temporal/constants.d.ts +1 -0
- package/dist/temporal/constants.d.ts.map +1 -1
- package/dist/temporal/constants.js +1 -0
- package/dist/temporal/constants.js.map +1 -1
- package/dist/temporal/workflows/cleanup-integration-secrets-cron.d.ts +2 -0
- package/dist/temporal/workflows/cleanup-integration-secrets-cron.d.ts.map +1 -0
- package/dist/temporal/workflows/cleanup-integration-secrets-cron.js +21 -0
- package/dist/temporal/workflows/cleanup-integration-secrets-cron.js.map +1 -0
- package/dist/temporal/workflows/index.bundle.js +64 -3
- package/dist/temporal/workflows/index.d.ts +1 -0
- package/dist/temporal/workflows/index.d.ts.map +1 -1
- package/dist/temporal/workflows/index.js +1 -0
- package/dist/temporal/workflows/index.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/drizzle/0001_durable_secret_cleanup.sql +22 -0
- package/drizzle/meta/0001_snapshot.json +180 -2
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +11 -10
- package/src/core/secret-cleanup.test.ts +129 -0
- package/src/core/secret-cleanup.ts +229 -0
- package/src/db/db.ts +2 -0
- package/src/db/schema/secret-cleanups.ts +42 -0
- package/src/db/secret-cleanups.test.ts +113 -0
- package/src/db/secret-cleanups.ts +209 -0
- package/src/index.ts +6 -1
- package/src/presentation/routes/manage-connections.test.ts +232 -0
- package/src/presentation/routes/manage-connections.ts +45 -11
- package/src/providers/jira.ts +61 -13
- package/src/temporal/activities/index.ts +11 -1
- package/src/temporal/constants.ts +2 -0
- package/src/temporal/workflows/cleanup-integration-secrets-cron.ts +20 -0
- package/src/temporal/workflows/index.ts +1 -0
- package/test/env.ts +3 -0
- package/test/globalSetup.ts +1 -0
- package/test/route-utils.ts +3 -0
- package/test/setup.ts +12 -0
- package/tsconfig.build.tsbuildinfo +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {dirname, resolve} from 'node:path';\nimport {fileURLToPath} from 'node:url';\nimport {\n integrationsEventSchemas,\n type StoredWebhookRequest,\n type WebhookProcessingResult,\n type WebhookRequestProcessor,\n type WebhookRouteId,\n} from '@shipfox/api-integration-spi';\nimport type {WorkflowsModuleClient} from '@shipfox/api-workflows-dto/inter-module';\nimport type {WorkspacesInterModuleClient} from '@shipfox/api-workspaces-dto/inter-module';\nimport {reportError} from '@shipfox/node-error-monitoring';\nimport type {ModuleService, ShipfoxModule} from '@shipfox/node-module';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport type {IntegrationProvider} from '#core/entities/provider.js';\nimport {WebhookProcessorNotConfiguredError} from '#core/errors.js';\nimport {\n createIntegrationProviderRegistry,\n type IntegrationProviderRegistry,\n} from '#core/providers/registry.js';\nimport {\n createSourceControlIntegrationService,\n type IntegrationSourceControlService,\n} from '#core/source-control-service.js';\nimport {getIntegrationConnectionById} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {migrationsPath} from '#db/migrations.js';\nimport {integrationsOutbox} from '#db/schema/outbox.js';\nimport {createIntegrationsInterModulePresentation} from '#presentation/inter-module.js';\nimport {createIntegrationRoutes} from '#presentation/routes/index.js';\nimport {loadEnabledProviderModules} from '#providers/modules.js';\nimport type {\n IntegrationModuleParts,\n IntegrationProviderSecrets,\n WebhookProcessorRegistration,\n} from '#providers/types.js';\nimport {createIntegrationsMaintenanceActivities} from '#temporal/activities/index.js';\nimport {INTEGRATIONS_MAINTENANCE_TASK_QUEUE} from '#temporal/constants.js';\n\nconst packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');\nconst maintenanceWorkflowsPath = resolve(packageRoot, 'dist/temporal/workflows/index.js');\n\nexport type {\n StoredWebhookRequest,\n WebhookProcessingResult,\n WebhookRequestProcessor,\n WebhookRouteId,\n} from '@shipfox/api-integration-spi';\nexport {\n buildProviderRepositoryId,\n MAX_REPOSITORY_FILE_BYTES,\n parseProviderRepositoryId,\n} from '@shipfox/api-integration-spi';\nexport type {\n AgentToolCatalogs,\n AgentToolSelectionCatalogs,\n LoadWorkspaceConnectionSnapshot,\n WorkspaceConnectionSnapshot,\n WorkspaceConnectionSnapshotEntry,\n} from '#core/agent-tool-selection.js';\nexport {\n buildAgentToolCatalogs,\n buildAgentToolSelectionCatalogs,\n createWorkspaceConnectionSnapshotLoader,\n} from '#core/agent-tool-selection.js';\nexport type {\n IntegrationConnection,\n IntegrationConnectionLifecycleStatus,\n} from '#core/entities/connection.js';\nexport type {\n IntegrationCapability,\n IntegrationProvider,\n IntegrationProviderAdapters,\n IntegrationProviderKind,\n RegisteredIntegrationProvider,\n} from '#core/entities/provider.js';\nexport type {IntegrationProviderErrorReason} from '#core/errors.js';\nexport {\n ConnectionSlugConflictError,\n IntegrationCapabilityUnavailableError,\n IntegrationCheckoutUnsupportedError,\n IntegrationConnectionInactiveError,\n IntegrationConnectionNotFoundError,\n IntegrationConnectionWorkspaceMismatchError,\n IntegrationProviderError,\n IntegrationProviderUnavailableError,\n WebhookProcessorNotConfiguredError,\n} from '#core/errors.js';\nexport type {\n AgentToolCallInput,\n AgentToolCatalogEntry,\n AgentToolCatalogMethod,\n AgentToolJsonSchema,\n AgentToolSensitivity,\n AgentToolSession,\n AgentToolsProvider,\n OpenAgentToolsSessionInput,\n} from '#core/providers/agent-tools.js';\nexport {redactCheckoutSpec} from '#core/providers/redact-checkout-spec.js';\nexport type {IntegrationProviderRegistry} from '#core/providers/registry.js';\nexport type {\n CheckoutCredentials,\n CheckoutPermissions,\n CheckoutSpec,\n CreateCheckoutSpecInput,\n FetchFileInput,\n FileEntry,\n FilePage,\n FileSnapshot,\n ListFilesInput,\n ListRepositoriesInput,\n RepositoryPage,\n RepositorySnapshot,\n RepositoryVisibility,\n ResolveRepositoryInput,\n SourceControlProvider,\n TriggerReference,\n} from '#core/providers/source-control.js';\nexport type {IntegrationSourceControlService} from '#core/source-control-service.js';\nexport {createSourceControlIntegrationService} from '#core/source-control-service.js';\nexport type {\n GetIntegrationConnectionByIdFn,\n GetIntegrationConnectionBySlugFn,\n} from '#db/connections.js';\nexport {getIntegrationConnectionById, getIntegrationConnectionBySlug} from '#db/connections.js';\nexport type {\n ClaimWebhookDeliveryFn,\n PublishIntegrationEventReceivedFn,\n PublishIntegrationEventReceivedParams,\n PublishIntegrationEventReceivedResult,\n PublishSourcePushFn,\n PublishSourcePushParams,\n PublishSourceRepositoryUpdatedFn,\n PublishSourceRepositoryUpdatedParams,\n RecordDeliveryOnlyFn,\n RecordDeliveryOnlyParams,\n} from '#db/webhook-deliveries.js';\nexport {\n claimWebhookDelivery,\n pruneWebhookDeliveries,\n publishSourceRepositoryUpdated,\n} from '#db/webhook-deliveries.js';\nexport {integrationRouteErrorHandler} from '#presentation/routes/errors.js';\n\nexport interface CreateIntegrationsModuleOptions {\n providers?: IntegrationProvider[] | undefined;\n /**\n * Pre-built module parts, bypassing config-gated provider loading. Test-only seam\n * for exercising a provider's database, workers, or startup tasks directly. Takes\n * precedence over `providers`.\n */\n parts?: IntegrationModuleParts[] | undefined;\n secrets?: IntegrationProviderSecrets | undefined;\n workspaces?: WorkspacesInterModuleClient | undefined;\n agentTools?:\n | {\n workflows: WorkflowsModuleClient;\n }\n | undefined;\n webhookDeliverySource?: WebhookDeliverySource | undefined;\n}\n\n/**\n * Hosted runtimes implement this port to receive stored webhook requests. The\n * integration module starts its returned service after migrations complete.\n */\nexport interface WebhookDeliverySource {\n createService(processor: WebhookRequestProcessor): ModuleService;\n}\n\nexport interface IntegrationsContext {\n module: ShipfoxModule;\n registry: IntegrationProviderRegistry;\n capabilities: {\n sourceControl: IntegrationSourceControlService;\n };\n sourceControl: IntegrationSourceControlService;\n webhookProcessor: WebhookRequestProcessor;\n /**\n * Runs every enabled provider's one-shot boot-time tasks, after modules are initialized\n * (migrations done). Failures are isolated and logged, never rethrown, so a provider task\n * can never gate API boot. No-op when no enabled provider contributes a task.\n */\n runStartupTasks: () => Promise<void>;\n}\n\nexport async function createIntegrationsModule(\n options: CreateIntegrationsModuleOptions = {},\n): Promise<ShipfoxModule> {\n return (await createIntegrationsContext(options)).module;\n}\n\nexport async function createIntegrationsContext(\n options: CreateIntegrationsModuleOptions = {},\n): Promise<IntegrationsContext> {\n const workspaces = options.workspaces;\n const parts: IntegrationModuleParts[] =\n options.parts ??\n (options.providers\n ? options.providers.map((provider) => ({\n provider,\n webhookProcessors: provider.webhookProcessors,\n }))\n : await loadEnabledProviderModules({\n secrets: options.secrets,\n ...(workspaces\n ? {\n requireActiveWorkspaceMembership: (input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<\n import('@shipfox/api-auth-context').UserContextMembership\n >;\n }) =>\n workspaces.requireActiveMembership({\n ...input,\n memberships: [...input.memberships],\n }),\n }\n : {}),\n }));\n\n const registry = createIntegrationProviderRegistry(parts.map((part) => part.provider));\n const sourceControl = createSourceControlIntegrationService({\n registry,\n getIntegrationConnectionById,\n });\n const webhookProcessor = createComposedWebhookProcessor(\n parts.flatMap((part) => part.webhookProcessors ?? []),\n );\n\n async function runStartupTasks(): Promise<void> {\n for (const task of parts.flatMap((part) => part.startupTasks ?? [])) {\n // A provider convenience must never gate API boot.\n try {\n await task();\n } catch (error) {\n logger().error({err: error}, 'Integration startup task failed, continuing boot');\n reportError(error, {boundary: 'integration.startup'});\n }\n }\n }\n\n const module: ShipfoxModule = {\n name: 'integrations',\n interModulePresentations: [\n createIntegrationsInterModulePresentation({registry, sourceControl}),\n ],\n startupTasks: runStartupTasks,\n database: [\n {db, migrationsPath, databaseNamespace: 'integrations'},\n ...parts.flatMap((part) => (part.database ? [part.database] : [])),\n ],\n routes: createIntegrationRoutes(registry, sourceControl, {\n agentTools: options.agentTools\n ? {\n workflows: options.agentTools.workflows,\n getIntegrationConnectionById,\n }\n : undefined,\n }),\n e2eRoutes: parts.flatMap((part) => part.e2eRoutes ?? []),\n publishers: [\n {name: 'integrations', table: integrationsOutbox, db, eventSchemas: integrationsEventSchemas},\n ],\n workers: [\n {\n taskQueue: INTEGRATIONS_MAINTENANCE_TASK_QUEUE,\n workflowsPath: maintenanceWorkflowsPath,\n activities: createIntegrationsMaintenanceActivities,\n workflows: [\n {\n name: 'pruneWebhookDeliveriesCron',\n id: 'integrations-prune-webhook-deliveries',\n cronSchedule: '0 3 * * *',\n },\n ],\n },\n ...parts.flatMap((part) => part.workers ?? []),\n ],\n ...(options.webhookDeliverySource\n ? {services: [options.webhookDeliverySource.createService(webhookProcessor)]}\n : {}),\n };\n\n return {\n module,\n registry,\n capabilities: {sourceControl},\n sourceControl,\n webhookProcessor,\n runStartupTasks,\n };\n}\n\nfunction createComposedWebhookProcessor(\n registrations: WebhookProcessorRegistration[],\n): WebhookRequestProcessor {\n const processors = new Map<WebhookRouteId, WebhookRequestProcessor>();\n for (const registration of registrations) {\n for (const routeId of registration.routeIds) {\n if (processors.has(routeId)) {\n throw new Error(`Webhook processor is registered more than once for ${routeId}`);\n }\n processors.set(routeId, registration.processor);\n }\n }\n\n return {\n async process(request: StoredWebhookRequest): Promise<WebhookProcessingResult> {\n const processor = processors.get(request.route_id);\n if (!processor) {\n throw new WebhookProcessorNotConfiguredError(request.route_id);\n }\n return await processor.process(request);\n },\n };\n}\n"],"names":["dirname","resolve","fileURLToPath","integrationsEventSchemas","reportError","logger","WebhookProcessorNotConfiguredError","createIntegrationProviderRegistry","createSourceControlIntegrationService","getIntegrationConnectionById","db","migrationsPath","integrationsOutbox","createIntegrationsInterModulePresentation","createIntegrationRoutes","loadEnabledProviderModules","createIntegrationsMaintenanceActivities","INTEGRATIONS_MAINTENANCE_TASK_QUEUE","packageRoot","url","maintenanceWorkflowsPath","buildProviderRepositoryId","MAX_REPOSITORY_FILE_BYTES","parseProviderRepositoryId","buildAgentToolCatalogs","buildAgentToolSelectionCatalogs","createWorkspaceConnectionSnapshotLoader","ConnectionSlugConflictError","IntegrationCapabilityUnavailableError","IntegrationCheckoutUnsupportedError","IntegrationConnectionInactiveError","IntegrationConnectionNotFoundError","IntegrationConnectionWorkspaceMismatchError","IntegrationProviderError","IntegrationProviderUnavailableError","redactCheckoutSpec","getIntegrationConnectionBySlug","claimWebhookDelivery","pruneWebhookDeliveries","publishSourceRepositoryUpdated","integrationRouteErrorHandler","createIntegrationsModule","options","createIntegrationsContext","module","workspaces","parts","providers","map","provider","webhookProcessors","secrets","requireActiveWorkspaceMembership","input","requireActiveMembership","memberships","registry","part","sourceControl","webhookProcessor","createComposedWebhookProcessor","flatMap","runStartupTasks","task","startupTasks","error","err","boundary","name","interModulePresentations","database","databaseNamespace","routes","agentTools","workflows","undefined","e2eRoutes","publishers","table","eventSchemas","workers","taskQueue","workflowsPath","activities","id","cronSchedule","webhookDeliverySource","services","createService","capabilities","registrations","processors","Map","registration","routeId","routeIds","has","Error","set","processor","process","request","get","route_id"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {dirname, resolve} from 'node:path';\nimport {fileURLToPath} from 'node:url';\nimport {\n integrationsEventSchemas,\n type StoredWebhookRequest,\n type WebhookProcessingResult,\n type WebhookRequestProcessor,\n type WebhookRouteId,\n} from '@shipfox/api-integration-spi';\nimport type {WorkflowsModuleClient} from '@shipfox/api-workflows-dto/inter-module';\nimport type {WorkspacesInterModuleClient} from '@shipfox/api-workspaces-dto/inter-module';\nimport {reportError} from '@shipfox/node-error-monitoring';\nimport type {ModuleService, ShipfoxModule} from '@shipfox/node-module';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport type {IntegrationProvider} from '#core/entities/provider.js';\nimport {WebhookProcessorNotConfiguredError} from '#core/errors.js';\nimport {\n createIntegrationProviderRegistry,\n type IntegrationProviderRegistry,\n} from '#core/providers/registry.js';\nimport {\n createSourceControlIntegrationService,\n type IntegrationSourceControlService,\n} from '#core/source-control-service.js';\nimport {getIntegrationConnectionById} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {migrationsPath} from '#db/migrations.js';\nimport {integrationsOutbox} from '#db/schema/outbox.js';\nimport {createIntegrationsInterModulePresentation} from '#presentation/inter-module.js';\nimport {createIntegrationRoutes} from '#presentation/routes/index.js';\nimport {loadEnabledProviderModules} from '#providers/modules.js';\nimport type {\n IntegrationModuleParts,\n IntegrationProviderSecrets,\n WebhookProcessorRegistration,\n} from '#providers/types.js';\nimport {createIntegrationsMaintenanceActivities} from '#temporal/activities/index.js';\nimport {INTEGRATIONS_MAINTENANCE_TASK_QUEUE} from '#temporal/constants.js';\n\nconst packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');\nconst maintenanceWorkflowsPath = resolve(packageRoot, 'dist/temporal/workflows/index.js');\n\nexport type {\n StoredWebhookRequest,\n WebhookProcessingResult,\n WebhookRequestProcessor,\n WebhookRouteId,\n} from '@shipfox/api-integration-spi';\nexport {\n buildProviderRepositoryId,\n MAX_REPOSITORY_FILE_BYTES,\n parseProviderRepositoryId,\n} from '@shipfox/api-integration-spi';\nexport type {\n AgentToolCatalogs,\n AgentToolSelectionCatalogs,\n LoadWorkspaceConnectionSnapshot,\n WorkspaceConnectionSnapshot,\n WorkspaceConnectionSnapshotEntry,\n} from '#core/agent-tool-selection.js';\nexport {\n buildAgentToolCatalogs,\n buildAgentToolSelectionCatalogs,\n createWorkspaceConnectionSnapshotLoader,\n} from '#core/agent-tool-selection.js';\nexport type {\n IntegrationConnection,\n IntegrationConnectionLifecycleStatus,\n} from '#core/entities/connection.js';\nexport type {\n IntegrationCapability,\n IntegrationProvider,\n IntegrationProviderAdapters,\n IntegrationProviderKind,\n RegisteredIntegrationProvider,\n} from '#core/entities/provider.js';\nexport type {IntegrationProviderErrorReason} from '#core/errors.js';\nexport {\n ConnectionSlugConflictError,\n IntegrationCapabilityUnavailableError,\n IntegrationCheckoutUnsupportedError,\n IntegrationConnectionInactiveError,\n IntegrationConnectionNotFoundError,\n IntegrationConnectionWorkspaceMismatchError,\n IntegrationProviderError,\n IntegrationProviderUnavailableError,\n WebhookProcessorNotConfiguredError,\n} from '#core/errors.js';\nexport type {\n AgentToolCallInput,\n AgentToolCatalogEntry,\n AgentToolCatalogMethod,\n AgentToolJsonSchema,\n AgentToolSensitivity,\n AgentToolSession,\n AgentToolsProvider,\n OpenAgentToolsSessionInput,\n} from '#core/providers/agent-tools.js';\nexport {redactCheckoutSpec} from '#core/providers/redact-checkout-spec.js';\nexport type {IntegrationProviderRegistry} from '#core/providers/registry.js';\nexport type {\n CheckoutCredentials,\n CheckoutPermissions,\n CheckoutSpec,\n CreateCheckoutSpecInput,\n FetchFileInput,\n FileEntry,\n FilePage,\n FileSnapshot,\n ListFilesInput,\n ListRepositoriesInput,\n RepositoryPage,\n RepositorySnapshot,\n RepositoryVisibility,\n ResolveRepositoryInput,\n SourceControlProvider,\n TriggerReference,\n} from '#core/providers/source-control.js';\nexport type {IntegrationSourceControlService} from '#core/source-control-service.js';\nexport {createSourceControlIntegrationService} from '#core/source-control-service.js';\nexport type {\n GetIntegrationConnectionByIdFn,\n GetIntegrationConnectionBySlugFn,\n} from '#db/connections.js';\nexport {getIntegrationConnectionById, getIntegrationConnectionBySlug} from '#db/connections.js';\nexport type {\n ClaimWebhookDeliveryFn,\n PublishIntegrationEventReceivedFn,\n PublishIntegrationEventReceivedParams,\n PublishIntegrationEventReceivedResult,\n PublishSourcePushFn,\n PublishSourcePushParams,\n PublishSourceRepositoryUpdatedFn,\n PublishSourceRepositoryUpdatedParams,\n RecordDeliveryOnlyFn,\n RecordDeliveryOnlyParams,\n} from '#db/webhook-deliveries.js';\nexport {\n claimWebhookDelivery,\n pruneWebhookDeliveries,\n publishSourceRepositoryUpdated,\n} from '#db/webhook-deliveries.js';\nexport {integrationRouteErrorHandler} from '#presentation/routes/errors.js';\n\nexport interface CreateIntegrationsModuleOptions {\n providers?: IntegrationProvider[] | undefined;\n /**\n * Pre-built module parts, bypassing config-gated provider loading. Test-only seam\n * for exercising a provider's database, workers, or startup tasks directly. Takes\n * precedence over `providers`.\n */\n parts?: IntegrationModuleParts[] | undefined;\n secrets?: IntegrationProviderSecrets | undefined;\n workspaces?: WorkspacesInterModuleClient | undefined;\n agentTools?:\n | {\n workflows: WorkflowsModuleClient;\n }\n | undefined;\n webhookDeliverySource?: WebhookDeliverySource | undefined;\n}\n\n/**\n * Hosted runtimes implement this port to receive stored webhook requests. The\n * integration module starts its returned service after migrations complete.\n */\nexport interface WebhookDeliverySource {\n createService(processor: WebhookRequestProcessor): ModuleService;\n}\n\nexport interface IntegrationsContext {\n module: ShipfoxModule;\n registry: IntegrationProviderRegistry;\n capabilities: {\n sourceControl: IntegrationSourceControlService;\n };\n sourceControl: IntegrationSourceControlService;\n webhookProcessor: WebhookRequestProcessor;\n /**\n * Runs every enabled provider's one-shot boot-time tasks, after modules are initialized\n * (migrations done). Failures are isolated and logged, never rethrown, so a provider task\n * can never gate API boot. No-op when no enabled provider contributes a task.\n */\n runStartupTasks: () => Promise<void>;\n}\n\nexport async function createIntegrationsModule(\n options: CreateIntegrationsModuleOptions = {},\n): Promise<ShipfoxModule> {\n return (await createIntegrationsContext(options)).module;\n}\n\nexport async function createIntegrationsContext(\n options: CreateIntegrationsModuleOptions = {},\n): Promise<IntegrationsContext> {\n const workspaces = options.workspaces;\n const parts: IntegrationModuleParts[] =\n options.parts ??\n (options.providers\n ? options.providers.map((provider) => ({\n provider,\n webhookProcessors: provider.webhookProcessors,\n }))\n : await loadEnabledProviderModules({\n secrets: options.secrets,\n ...(workspaces\n ? {\n requireActiveWorkspaceMembership: (input: {\n workspaceId: string;\n userId: string;\n memberships: ReadonlyArray<\n import('@shipfox/api-auth-context').UserContextMembership\n >;\n }) =>\n workspaces.requireActiveMembership({\n ...input,\n memberships: [...input.memberships],\n }),\n }\n : {}),\n }));\n\n const registry = createIntegrationProviderRegistry(parts.map((part) => part.provider));\n const sourceControl = createSourceControlIntegrationService({\n registry,\n getIntegrationConnectionById,\n });\n const webhookProcessor = createComposedWebhookProcessor(\n parts.flatMap((part) => part.webhookProcessors ?? []),\n );\n\n async function runStartupTasks(): Promise<void> {\n for (const task of parts.flatMap((part) => part.startupTasks ?? [])) {\n // A provider convenience must never gate API boot.\n try {\n await task();\n } catch (error) {\n logger().error({err: error}, 'Integration startup task failed, continuing boot');\n reportError(error, {boundary: 'integration.startup'});\n }\n }\n }\n\n const module: ShipfoxModule = {\n name: 'integrations',\n interModulePresentations: [\n createIntegrationsInterModulePresentation({registry, sourceControl}),\n ],\n startupTasks: runStartupTasks,\n database: [\n {db, migrationsPath, databaseNamespace: 'integrations'},\n ...parts.flatMap((part) => (part.database ? [part.database] : [])),\n ],\n routes: createIntegrationRoutes(registry, sourceControl, {\n agentTools: options.agentTools\n ? {\n workflows: options.agentTools.workflows,\n getIntegrationConnectionById,\n }\n : undefined,\n }),\n e2eRoutes: parts.flatMap((part) => part.e2eRoutes ?? []),\n publishers: [\n {name: 'integrations', table: integrationsOutbox, db, eventSchemas: integrationsEventSchemas},\n ],\n workers: [\n {\n taskQueue: INTEGRATIONS_MAINTENANCE_TASK_QUEUE,\n workflowsPath: maintenanceWorkflowsPath,\n activities: () => createIntegrationsMaintenanceActivities({registry}),\n workflows: [\n {\n name: 'pruneWebhookDeliveriesCron',\n id: 'integrations-prune-webhook-deliveries',\n cronSchedule: '0 3 * * *',\n },\n {\n name: 'cleanupIntegrationSecretsCron',\n id: 'integrations-cleanup-secret-namespaces',\n cronSchedule: '*/5 * * * *',\n },\n ],\n },\n ...parts.flatMap((part) => part.workers ?? []),\n ],\n ...(options.webhookDeliverySource\n ? {services: [options.webhookDeliverySource.createService(webhookProcessor)]}\n : {}),\n };\n\n return {\n module,\n registry,\n capabilities: {sourceControl},\n sourceControl,\n webhookProcessor,\n runStartupTasks,\n };\n}\n\nfunction createComposedWebhookProcessor(\n registrations: WebhookProcessorRegistration[],\n): WebhookRequestProcessor {\n const processors = new Map<WebhookRouteId, WebhookRequestProcessor>();\n for (const registration of registrations) {\n for (const routeId of registration.routeIds) {\n if (processors.has(routeId)) {\n throw new Error(`Webhook processor is registered more than once for ${routeId}`);\n }\n processors.set(routeId, registration.processor);\n }\n }\n\n return {\n async process(request: StoredWebhookRequest): Promise<WebhookProcessingResult> {\n const processor = processors.get(request.route_id);\n if (!processor) {\n throw new WebhookProcessorNotConfiguredError(request.route_id);\n }\n return await processor.process(request);\n },\n };\n}\n"],"names":["dirname","resolve","fileURLToPath","integrationsEventSchemas","reportError","logger","WebhookProcessorNotConfiguredError","createIntegrationProviderRegistry","createSourceControlIntegrationService","getIntegrationConnectionById","db","migrationsPath","integrationsOutbox","createIntegrationsInterModulePresentation","createIntegrationRoutes","loadEnabledProviderModules","createIntegrationsMaintenanceActivities","INTEGRATIONS_MAINTENANCE_TASK_QUEUE","packageRoot","url","maintenanceWorkflowsPath","buildProviderRepositoryId","MAX_REPOSITORY_FILE_BYTES","parseProviderRepositoryId","buildAgentToolCatalogs","buildAgentToolSelectionCatalogs","createWorkspaceConnectionSnapshotLoader","ConnectionSlugConflictError","IntegrationCapabilityUnavailableError","IntegrationCheckoutUnsupportedError","IntegrationConnectionInactiveError","IntegrationConnectionNotFoundError","IntegrationConnectionWorkspaceMismatchError","IntegrationProviderError","IntegrationProviderUnavailableError","redactCheckoutSpec","getIntegrationConnectionBySlug","claimWebhookDelivery","pruneWebhookDeliveries","publishSourceRepositoryUpdated","integrationRouteErrorHandler","createIntegrationsModule","options","createIntegrationsContext","module","workspaces","parts","providers","map","provider","webhookProcessors","secrets","requireActiveWorkspaceMembership","input","requireActiveMembership","memberships","registry","part","sourceControl","webhookProcessor","createComposedWebhookProcessor","flatMap","runStartupTasks","task","startupTasks","error","err","boundary","name","interModulePresentations","database","databaseNamespace","routes","agentTools","workflows","undefined","e2eRoutes","publishers","table","eventSchemas","workers","taskQueue","workflowsPath","activities","id","cronSchedule","webhookDeliverySource","services","createService","capabilities","registrations","processors","Map","registration","routeId","routeIds","has","Error","set","processor","process","request","get","route_id"],"mappings":"AAAA,SAAQA,OAAO,EAAEC,OAAO,QAAO,YAAY;AAC3C,SAAQC,aAAa,QAAO,WAAW;AACvC,SACEC,wBAAwB,QAKnB,+BAA+B;AAGtC,SAAQC,WAAW,QAAO,iCAAiC;AAE3D,SAAQC,MAAM,QAAO,8BAA8B;AAEnD,SAAQC,kCAAkC,QAAO,kBAAkB;AACnE,SACEC,iCAAiC,QAE5B,8BAA8B;AACrC,SACEC,qCAAqC,QAEhC,kCAAkC;AACzC,SAAQC,4BAA4B,QAAO,qBAAqB;AAChE,SAAQC,EAAE,QAAO,YAAY;AAC7B,SAAQC,cAAc,QAAO,oBAAoB;AACjD,SAAQC,kBAAkB,QAAO,uBAAuB;AACxD,SAAQC,yCAAyC,QAAO,gCAAgC;AACxF,SAAQC,uBAAuB,QAAO,gCAAgC;AACtE,SAAQC,0BAA0B,QAAO,wBAAwB;AAMjE,SAAQC,uCAAuC,QAAO,gCAAgC;AACtF,SAAQC,mCAAmC,QAAO,yBAAyB;AAE3E,MAAMC,cAAcjB,QAAQD,QAAQE,cAAc,YAAYiB,GAAG,IAAI;AACrE,MAAMC,2BAA2BnB,QAAQiB,aAAa;AAQtD,SACEG,yBAAyB,EACzBC,yBAAyB,EACzBC,yBAAyB,QACpB,+BAA+B;AAQtC,SACEC,sBAAsB,EACtBC,+BAA+B,EAC/BC,uCAAuC,QAClC,gCAAgC;AAavC,SACEC,2BAA2B,EAC3BC,qCAAqC,EACrCC,mCAAmC,EACnCC,kCAAkC,EAClCC,kCAAkC,EAClCC,2CAA2C,EAC3CC,wBAAwB,EACxBC,mCAAmC,EACnC5B,kCAAkC,QAC7B,kBAAkB;AAWzB,SAAQ6B,kBAAkB,QAAO,0CAA0C;AAqB3E,SAAQ3B,qCAAqC,QAAO,kCAAkC;AAKtF,SAAQC,4BAA4B,EAAE2B,8BAA8B,QAAO,qBAAqB;AAahG,SACEC,oBAAoB,EACpBC,sBAAsB,EACtBC,8BAA8B,QACzB,4BAA4B;AACnC,SAAQC,4BAA4B,QAAO,iCAAiC;AA4C5E,OAAO,eAAeC,yBACpBC,UAA2C,CAAC,CAAC;IAE7C,OAAO,AAAC,CAAA,MAAMC,0BAA0BD,QAAO,EAAGE,MAAM;AAC1D;AAEA,OAAO,eAAeD,0BACpBD,UAA2C,CAAC,CAAC;IAE7C,MAAMG,aAAaH,QAAQG,UAAU;IACrC,MAAMC,QACJJ,QAAQI,KAAK,IACZJ,CAAAA,QAAQK,SAAS,GACdL,QAAQK,SAAS,CAACC,GAAG,CAAC,CAACC,WAAc,CAAA;YACnCA;YACAC,mBAAmBD,SAASC,iBAAiB;QAC/C,CAAA,KACA,MAAMnC,2BAA2B;QAC/BoC,SAAST,QAAQS,OAAO;QACxB,GAAIN,aACA;YACEO,kCAAkC,CAACC,QAOjCR,WAAWS,uBAAuB,CAAC;oBACjC,GAAGD,KAAK;oBACRE,aAAa;2BAAIF,MAAME,WAAW;qBAAC;gBACrC;QACJ,IACA,CAAC,CAAC;IACR,EAAC;IAEP,MAAMC,WAAWjD,kCAAkCuC,MAAME,GAAG,CAAC,CAACS,OAASA,KAAKR,QAAQ;IACpF,MAAMS,gBAAgBlD,sCAAsC;QAC1DgD;QACA/C;IACF;IACA,MAAMkD,mBAAmBC,+BACvBd,MAAMe,OAAO,CAAC,CAACJ,OAASA,KAAKP,iBAAiB,IAAI,EAAE;IAGtD,eAAeY;QACb,KAAK,MAAMC,QAAQjB,MAAMe,OAAO,CAAC,CAACJ,OAASA,KAAKO,YAAY,IAAI,EAAE,EAAG;YACnE,mDAAmD;YACnD,IAAI;gBACF,MAAMD;YACR,EAAE,OAAOE,OAAO;gBACd5D,SAAS4D,KAAK,CAAC;oBAACC,KAAKD;gBAAK,GAAG;gBAC7B7D,YAAY6D,OAAO;oBAACE,UAAU;gBAAqB;YACrD;QACF;IACF;IAEA,MAAMvB,SAAwB;QAC5BwB,MAAM;QACNC,0BAA0B;YACxBxD,0CAA0C;gBAAC2C;gBAAUE;YAAa;SACnE;QACDM,cAAcF;QACdQ,UAAU;YACR;gBAAC5D;gBAAIC;gBAAgB4D,mBAAmB;YAAc;eACnDzB,MAAMe,OAAO,CAAC,CAACJ,OAAUA,KAAKa,QAAQ,GAAG;oBAACb,KAAKa,QAAQ;iBAAC,GAAG,EAAE;SACjE;QACDE,QAAQ1D,wBAAwB0C,UAAUE,eAAe;YACvDe,YAAY/B,QAAQ+B,UAAU,GAC1B;gBACEC,WAAWhC,QAAQ+B,UAAU,CAACC,SAAS;gBACvCjE;YACF,IACAkE;QACN;QACAC,WAAW9B,MAAMe,OAAO,CAAC,CAACJ,OAASA,KAAKmB,SAAS,IAAI,EAAE;QACvDC,YAAY;YACV;gBAACT,MAAM;gBAAgBU,OAAOlE;gBAAoBF;gBAAIqE,cAAc5E;YAAwB;SAC7F;QACD6E,SAAS;YACP;gBACEC,WAAWhE;gBACXiE,eAAe9D;gBACf+D,YAAY,IAAMnE,wCAAwC;wBAACwC;oBAAQ;gBACnEkB,WAAW;oBACT;wBACEN,MAAM;wBACNgB,IAAI;wBACJC,cAAc;oBAChB;oBACA;wBACEjB,MAAM;wBACNgB,IAAI;wBACJC,cAAc;oBAChB;iBACD;YACH;eACGvC,MAAMe,OAAO,CAAC,CAACJ,OAASA,KAAKuB,OAAO,IAAI,EAAE;SAC9C;QACD,GAAItC,QAAQ4C,qBAAqB,GAC7B;YAACC,UAAU;gBAAC7C,QAAQ4C,qBAAqB,CAACE,aAAa,CAAC7B;aAAkB;QAAA,IAC1E,CAAC,CAAC;IACR;IAEA,OAAO;QACLf;QACAY;QACAiC,cAAc;YAAC/B;QAAa;QAC5BA;QACAC;QACAG;IACF;AACF;AAEA,SAASF,+BACP8B,aAA6C;IAE7C,MAAMC,aAAa,IAAIC;IACvB,KAAK,MAAMC,gBAAgBH,cAAe;QACxC,KAAK,MAAMI,WAAWD,aAAaE,QAAQ,CAAE;YAC3C,IAAIJ,WAAWK,GAAG,CAACF,UAAU;gBAC3B,MAAM,IAAIG,MAAM,CAAC,mDAAmD,EAAEH,SAAS;YACjF;YACAH,WAAWO,GAAG,CAACJ,SAASD,aAAaM,SAAS;QAChD;IACF;IAEA,OAAO;QACL,MAAMC,SAAQC,OAA6B;YACzC,MAAMF,YAAYR,WAAWW,GAAG,CAACD,QAAQE,QAAQ;YACjD,IAAI,CAACJ,WAAW;gBACd,MAAM,IAAI7F,mCAAmC+F,QAAQE,QAAQ;YAC/D;YACA,OAAO,MAAMJ,UAAUC,OAAO,CAACC;QACjC;IACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manage-connections.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/manage-connections.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"manage-connections.d.ts","sourceRoot":"","sources":["../../../src/presentation/routes/manage-connections.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,6BAA6B,CAAC;AAe7E,wBAAgB,sCAAsC,CAAC,QAAQ,EAAE,2BAA2B,mDAgC3F;AAED,wBAAgB,sCAAsC,CAAC,QAAQ,EAAE,2BAA2B,mDA8E3F"}
|
|
@@ -2,8 +2,10 @@ import { AUTH_USER, requireWorkspaceAccess } from '@shipfox/api-auth-context';
|
|
|
2
2
|
import { integrationConnectionDtoSchema, updateIntegrationConnectionBodySchema } from '@shipfox/api-integration-spi';
|
|
3
3
|
import { ClientError, defineRoute } from '@shipfox/node-fastify';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
+
import { processIntegrationSecretCleanups } from '#core/secret-cleanup.js';
|
|
5
6
|
import { deleteIntegrationConnection, getIntegrationConnectionById, updateIntegrationConnectionLifecycleStatus } from '#db/connections.js';
|
|
6
7
|
import { db } from '#db/db.js';
|
|
8
|
+
import { enqueueIntegrationSecretCleanup } from '#db/secret-cleanups.js';
|
|
7
9
|
import { toIntegrationConnectionDto } from '#presentation/dto/integrations.js';
|
|
8
10
|
const connectionParamsSchema = z.object({
|
|
9
11
|
connectionId: z.string().uuid()
|
|
@@ -72,31 +74,68 @@ export function createDeleteIntegrationConnectionRoute(registry) {
|
|
|
72
74
|
workspaceId: connection.workspaceId
|
|
73
75
|
});
|
|
74
76
|
const provider = registry.list().find((candidate)=>candidate.provider === connection.provider);
|
|
75
|
-
const hasCleanupHooks = provider?.deleteConnectionRecords !== undefined || provider?.deleteConnectionSecrets !== undefined;
|
|
77
|
+
const hasCleanupHooks = provider?.deleteConnectionRemoteResources !== undefined || provider?.deleteConnectionRecords !== undefined || provider?.deleteConnectionSecrets !== undefined;
|
|
76
78
|
if (!hasCleanupHooks) {
|
|
77
79
|
request.log.warn({
|
|
78
80
|
connectionId: connection.id,
|
|
79
81
|
provider: connection.provider
|
|
80
82
|
}, 'Deleting integration connection without provider cleanup');
|
|
81
83
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
const deleteConnection = async ()=>{
|
|
85
|
+
let deleteRemoteResources;
|
|
86
|
+
try {
|
|
87
|
+
deleteRemoteResources = await provider?.deleteConnectionRemoteResources?.(connection);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
request.log.error({
|
|
90
|
+
connectionId: connection.id,
|
|
91
|
+
provider: connection.provider,
|
|
92
|
+
err: error
|
|
93
|
+
}, 'Integration connection remote cleanup preparation failed');
|
|
94
|
+
}
|
|
95
|
+
await db().transaction(async (tx)=>{
|
|
96
|
+
await provider?.deleteConnectionRecords?.(connection, {
|
|
97
|
+
tx
|
|
98
|
+
});
|
|
99
|
+
await enqueueIntegrationSecretCleanup({
|
|
100
|
+
connection
|
|
101
|
+
}, {
|
|
102
|
+
tx
|
|
103
|
+
});
|
|
104
|
+
await deleteIntegrationConnection({
|
|
105
|
+
id: connection.id
|
|
106
|
+
}, {
|
|
107
|
+
tx
|
|
108
|
+
});
|
|
85
109
|
});
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
110
|
+
try {
|
|
111
|
+
await deleteRemoteResources?.();
|
|
112
|
+
} catch (error) {
|
|
113
|
+
request.log.error({
|
|
114
|
+
connectionId: connection.id,
|
|
115
|
+
provider: connection.provider,
|
|
116
|
+
err: error
|
|
117
|
+
}, 'Integration connection remote cleanup failed after deletion');
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
await processIntegrationSecretCleanups({
|
|
121
|
+
registry,
|
|
122
|
+
connectionId: connection.id,
|
|
123
|
+
connection,
|
|
124
|
+
limit: 1
|
|
125
|
+
});
|
|
126
|
+
} catch (error) {
|
|
127
|
+
// The durable cleanup row survives, so a later sweep retries this connection.
|
|
128
|
+
request.log.error({
|
|
129
|
+
connectionId: connection.id,
|
|
130
|
+
provider: connection.provider,
|
|
131
|
+
err: error
|
|
132
|
+
}, 'Integration connection secret cleanup failed after connection deletion');
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
if (provider?.withConnectionDeletionLock) {
|
|
136
|
+
await provider.withConnectionDeletionLock(connection, deleteConnection);
|
|
137
|
+
} else {
|
|
138
|
+
await deleteConnection();
|
|
100
139
|
}
|
|
101
140
|
reply.status(204);
|
|
102
141
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/presentation/routes/manage-connections.ts"],"sourcesContent":["import {AUTH_USER, requireWorkspaceAccess} from '@shipfox/api-auth-context';\nimport {\n integrationConnectionDtoSchema,\n updateIntegrationConnectionBodySchema,\n} from '@shipfox/api-integration-spi';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {z} from 'zod';\nimport type {IntegrationProviderRegistry} from '#core/providers/registry.js';\nimport {\n deleteIntegrationConnection,\n getIntegrationConnectionById,\n updateIntegrationConnectionLifecycleStatus,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {toIntegrationConnectionDto} from '#presentation/dto/integrations.js';\n\nconst connectionParamsSchema = z.object({\n connectionId: z.string().uuid(),\n});\n\nexport function createUpdateIntegrationConnectionRoute(registry: IntegrationProviderRegistry) {\n return defineRoute({\n method: 'PATCH',\n path: '/integration-connections/:connectionId',\n auth: AUTH_USER,\n description: 'Update an integration connection.',\n schema: {\n params: connectionParamsSchema,\n body: updateIntegrationConnectionBodySchema,\n response: {\n 200: integrationConnectionDtoSchema,\n },\n },\n handler: async (request) => {\n const connection = await getIntegrationConnectionById(request.params.connectionId);\n if (!connection) {\n throw new ClientError('Integration connection not found', 'not-found', {status: 404});\n }\n\n requireWorkspaceAccess({request, workspaceId: connection.workspaceId});\n const updated = await updateIntegrationConnectionLifecycleStatus({\n id: connection.id,\n lifecycleStatus: request.body.lifecycle_status,\n });\n if (!updated) {\n throw new ClientError('Integration connection not found', 'not-found', {status: 404});\n }\n\n const provider = registry.list().find((candidate) => candidate.provider === updated.provider);\n return toIntegrationConnectionDto(updated, {capabilities: provider?.capabilities ?? []});\n },\n });\n}\n\nexport function createDeleteIntegrationConnectionRoute(registry: IntegrationProviderRegistry) {\n return defineRoute({\n method: 'DELETE',\n path: '/integration-connections/:connectionId',\n auth: AUTH_USER,\n description: 'Delete an integration connection.',\n schema: {\n params: connectionParamsSchema,\n response: {\n 204: z.void(),\n },\n },\n handler: async (request, reply) => {\n const connection = await getIntegrationConnectionById(request.params.connectionId);\n if (!connection) {\n throw new ClientError('Integration connection not found', 'not-found', {status: 404});\n }\n\n requireWorkspaceAccess({request, workspaceId: connection.workspaceId});\n const provider = registry\n .list()\n .find((candidate) => candidate.provider === connection.provider);\n const hasCleanupHooks =\n provider?.deleteConnectionRecords !== undefined ||\n provider?.deleteConnectionSecrets !== undefined;\n if (!hasCleanupHooks) {\n request.log.warn(\n {connectionId: connection.id, provider: connection.provider},\n 'Deleting integration connection without provider cleanup',\n );\n }\n await db().transaction(async (tx) => {\n
|
|
1
|
+
{"version":3,"sources":["../../../src/presentation/routes/manage-connections.ts"],"sourcesContent":["import {AUTH_USER, requireWorkspaceAccess} from '@shipfox/api-auth-context';\nimport {\n integrationConnectionDtoSchema,\n updateIntegrationConnectionBodySchema,\n} from '@shipfox/api-integration-spi';\nimport {ClientError, defineRoute} from '@shipfox/node-fastify';\nimport {z} from 'zod';\nimport type {IntegrationProviderRegistry} from '#core/providers/registry.js';\nimport {processIntegrationSecretCleanups} from '#core/secret-cleanup.js';\nimport {\n deleteIntegrationConnection,\n getIntegrationConnectionById,\n updateIntegrationConnectionLifecycleStatus,\n} from '#db/connections.js';\nimport {db} from '#db/db.js';\nimport {enqueueIntegrationSecretCleanup} from '#db/secret-cleanups.js';\nimport {toIntegrationConnectionDto} from '#presentation/dto/integrations.js';\n\nconst connectionParamsSchema = z.object({\n connectionId: z.string().uuid(),\n});\n\nexport function createUpdateIntegrationConnectionRoute(registry: IntegrationProviderRegistry) {\n return defineRoute({\n method: 'PATCH',\n path: '/integration-connections/:connectionId',\n auth: AUTH_USER,\n description: 'Update an integration connection.',\n schema: {\n params: connectionParamsSchema,\n body: updateIntegrationConnectionBodySchema,\n response: {\n 200: integrationConnectionDtoSchema,\n },\n },\n handler: async (request) => {\n const connection = await getIntegrationConnectionById(request.params.connectionId);\n if (!connection) {\n throw new ClientError('Integration connection not found', 'not-found', {status: 404});\n }\n\n requireWorkspaceAccess({request, workspaceId: connection.workspaceId});\n const updated = await updateIntegrationConnectionLifecycleStatus({\n id: connection.id,\n lifecycleStatus: request.body.lifecycle_status,\n });\n if (!updated) {\n throw new ClientError('Integration connection not found', 'not-found', {status: 404});\n }\n\n const provider = registry.list().find((candidate) => candidate.provider === updated.provider);\n return toIntegrationConnectionDto(updated, {capabilities: provider?.capabilities ?? []});\n },\n });\n}\n\nexport function createDeleteIntegrationConnectionRoute(registry: IntegrationProviderRegistry) {\n return defineRoute({\n method: 'DELETE',\n path: '/integration-connections/:connectionId',\n auth: AUTH_USER,\n description: 'Delete an integration connection.',\n schema: {\n params: connectionParamsSchema,\n response: {\n 204: z.void(),\n },\n },\n handler: async (request, reply) => {\n const connection = await getIntegrationConnectionById(request.params.connectionId);\n if (!connection) {\n throw new ClientError('Integration connection not found', 'not-found', {status: 404});\n }\n\n requireWorkspaceAccess({request, workspaceId: connection.workspaceId});\n const provider = registry\n .list()\n .find((candidate) => candidate.provider === connection.provider);\n const hasCleanupHooks =\n provider?.deleteConnectionRemoteResources !== undefined ||\n provider?.deleteConnectionRecords !== undefined ||\n provider?.deleteConnectionSecrets !== undefined;\n if (!hasCleanupHooks) {\n request.log.warn(\n {connectionId: connection.id, provider: connection.provider},\n 'Deleting integration connection without provider cleanup',\n );\n }\n const deleteConnection = async (): Promise<void> => {\n let deleteRemoteResources: (() => Promise<void>) | undefined;\n try {\n deleteRemoteResources = await provider?.deleteConnectionRemoteResources?.(connection);\n } catch (error) {\n request.log.error(\n {connectionId: connection.id, provider: connection.provider, err: error},\n 'Integration connection remote cleanup preparation failed',\n );\n }\n await db().transaction(async (tx) => {\n await provider?.deleteConnectionRecords?.(connection, {tx});\n await enqueueIntegrationSecretCleanup({connection}, {tx});\n await deleteIntegrationConnection({id: connection.id}, {tx});\n });\n try {\n await deleteRemoteResources?.();\n } catch (error) {\n request.log.error(\n {connectionId: connection.id, provider: connection.provider, err: error},\n 'Integration connection remote cleanup failed after deletion',\n );\n }\n try {\n await processIntegrationSecretCleanups({\n registry,\n connectionId: connection.id,\n connection,\n limit: 1,\n });\n } catch (error) {\n // The durable cleanup row survives, so a later sweep retries this connection.\n request.log.error(\n {connectionId: connection.id, provider: connection.provider, err: error},\n 'Integration connection secret cleanup failed after connection deletion',\n );\n }\n };\n if (provider?.withConnectionDeletionLock) {\n await provider.withConnectionDeletionLock(connection, deleteConnection);\n } else {\n await deleteConnection();\n }\n reply.status(204);\n },\n });\n}\n"],"names":["AUTH_USER","requireWorkspaceAccess","integrationConnectionDtoSchema","updateIntegrationConnectionBodySchema","ClientError","defineRoute","z","processIntegrationSecretCleanups","deleteIntegrationConnection","getIntegrationConnectionById","updateIntegrationConnectionLifecycleStatus","db","enqueueIntegrationSecretCleanup","toIntegrationConnectionDto","connectionParamsSchema","object","connectionId","string","uuid","createUpdateIntegrationConnectionRoute","registry","method","path","auth","description","schema","params","body","response","handler","request","connection","status","workspaceId","updated","id","lifecycleStatus","lifecycle_status","provider","list","find","candidate","capabilities","createDeleteIntegrationConnectionRoute","void","reply","hasCleanupHooks","deleteConnectionRemoteResources","undefined","deleteConnectionRecords","deleteConnectionSecrets","log","warn","deleteConnection","deleteRemoteResources","error","err","transaction","tx","limit","withConnectionDeletionLock"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jira.d.ts","sourceRoot":"","sources":["../../src/providers/jira.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"jira.d.ts","sourceRoot":"","sources":["../../src/providers/jira.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAyB,yBAAyB,EAAC,MAAM,qBAAqB,CAAC;AAiP3F,eAAO,MAAM,kBAAkB,EAAE,yBAIhC,CAAC"}
|
package/dist/providers/jira.js
CHANGED
|
@@ -5,7 +5,8 @@ import { publishIntegrationEventReceived, recordDeliveryOnly } from '#db/webhook
|
|
|
5
5
|
import { retryConnectionSlugCollision, slugifyConnectionSlug } from '#providers/connection-slug.js';
|
|
6
6
|
const JIRA_SECRETS_NAMESPACE_PREFIX = 'system/integrations/jira/';
|
|
7
7
|
async function loadJiraModuleParts(options = {}) {
|
|
8
|
-
const { createJiraIntegrationProvider, createJiraMaintenanceWorker, createJiraPendingSelectionStore, createJiraTokenStore, db: jiraDb, deleteJiraInstallationByConnectionId, disconnectJiraInstallation: disconnectJiraInstallationRecords, getJiraInstallationByCloudId, jiraSecretsNamespace, migrationsPath, upsertJiraInstallation, withJiraRefreshLockAndWait } = await import('@shipfox/api-integration-jira');
|
|
8
|
+
const { createJiraApiClient, createJiraIntegrationProvider, createJiraMaintenanceWorker, createJiraPendingSelectionStore, createJiraTokenStore, db: jiraDb, deregisterJiraWebhooks, deleteJiraInstallationByConnectionId, disconnectJiraInstallation: disconnectJiraInstallationRecords, getJiraInstallationByCloudId, getJiraInstallationByConnectionId, jiraSecretsNamespace, jiraWebhookUrl, migrationsPath, prepareJiraWebhookDeregistration, upsertJiraInstallation, withJiraRefreshLockAndWait, withJiraWebhookRegistrationLock } = await import('@shipfox/api-integration-jira');
|
|
9
|
+
const jira = createJiraApiClient();
|
|
9
10
|
async function getExistingJiraConnection(input) {
|
|
10
11
|
const installation = await getJiraInstallationByCloudId(input.cloudId);
|
|
11
12
|
if (!installation) return undefined;
|
|
@@ -49,18 +50,34 @@ async function loadJiraModuleParts(options = {}) {
|
|
|
49
50
|
}));
|
|
50
51
|
}
|
|
51
52
|
async function disconnectJiraInstallation(input) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
53
|
+
const disconnect = ()=>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
|
+
deregisterWebhooks: ()=>deregisterJiraWebhooks({
|
|
61
|
+
connectionId: input.connectionId,
|
|
62
|
+
getInstallation: getJiraInstallationByConnectionId,
|
|
63
|
+
tokenStore,
|
|
64
|
+
jira
|
|
65
|
+
}),
|
|
66
|
+
transaction: (fn)=>db().transaction((tx)=>fn(tx)),
|
|
67
|
+
deleteConnection: (params, transactionOptions)=>deleteIntegrationConnection({
|
|
68
|
+
id: params.connectionId
|
|
69
|
+
}, transactionOptions)
|
|
70
|
+
});
|
|
71
|
+
if (input.lockAlreadyHeld) {
|
|
72
|
+
await disconnect();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const installation = await getJiraInstallationByConnectionId(input.connectionId);
|
|
76
|
+
if (!installation) {
|
|
77
|
+
await disconnect();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
await withJiraWebhookRegistrationLock(installation.cloudId, disconnect);
|
|
64
81
|
}
|
|
65
82
|
const fallbackSecrets = {
|
|
66
83
|
getSecret: ()=>Promise.resolve(null),
|
|
@@ -82,6 +99,7 @@ async function loadJiraModuleParts(options = {}) {
|
|
|
82
99
|
}) ?? Promise.resolve(0)
|
|
83
100
|
} : fallbackSecrets;
|
|
84
101
|
const tokenStore = createJiraTokenStore({
|
|
102
|
+
client: jira,
|
|
85
103
|
resolveConnection: getIntegrationConnectionById,
|
|
86
104
|
secrets,
|
|
87
105
|
markConnectionError: async ({ connectionId })=>{
|
|
@@ -95,10 +113,27 @@ async function loadJiraModuleParts(options = {}) {
|
|
|
95
113
|
secrets
|
|
96
114
|
});
|
|
97
115
|
const integrationProvider = createJiraIntegrationProvider({
|
|
116
|
+
jira,
|
|
98
117
|
agentTools: {
|
|
99
118
|
tokenStore
|
|
100
119
|
},
|
|
101
120
|
cleanup: {
|
|
121
|
+
deleteConnectionRemoteResources: async (connection)=>{
|
|
122
|
+
return await prepareJiraWebhookDeregistration({
|
|
123
|
+
connectionId: connection.id,
|
|
124
|
+
getInstallation: getJiraInstallationByConnectionId,
|
|
125
|
+
tokenStore,
|
|
126
|
+
jira
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
withConnectionDeletionLock: async (connection, fn)=>{
|
|
130
|
+
const installation = await getJiraInstallationByConnectionId(connection.id);
|
|
131
|
+
if (!installation) {
|
|
132
|
+
await fn();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
await withJiraWebhookRegistrationLock(installation.cloudId, fn);
|
|
136
|
+
},
|
|
102
137
|
deleteConnectionRecords: async (connection, { tx })=>{
|
|
103
138
|
await deleteJiraInstallationByConnectionId(connection.id, {
|
|
104
139
|
tx
|
|
@@ -149,8 +184,10 @@ async function loadJiraModuleParts(options = {}) {
|
|
|
149
184
|
provider: integrationProvider,
|
|
150
185
|
workers: [
|
|
151
186
|
createJiraMaintenanceWorker({
|
|
187
|
+
jira,
|
|
152
188
|
tokenStore,
|
|
153
|
-
resolveConnection: getIntegrationConnectionById
|
|
189
|
+
resolveConnection: getIntegrationConnectionById,
|
|
190
|
+
webhookUrlForConnection: jiraWebhookUrl
|
|
154
191
|
})
|
|
155
192
|
],
|
|
156
193
|
webhookProcessors: integrationProvider.webhookProcessors,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/providers/jira.ts"],"sourcesContent":["import type {\n ConnectJiraInstallationInput,\n JiraPendingSelectionSecretsStore,\n JiraSecretsStore,\n} from '@shipfox/api-integration-jira';\nimport type {IntegrationConnection as CoreIntegrationConnection} from '@shipfox/api-integration-spi';\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 {publishIntegrationEventReceived, recordDeliveryOnly} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision, slugifyConnectionSlug} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\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 createJiraMaintenanceWorker,\n createJiraPendingSelectionStore,\n createJiraTokenStore,\n db: jiraDb,\n deleteJiraInstallationByConnectionId,\n disconnectJiraInstallation: disconnectJiraInstallationRecords,\n getJiraInstallationByCloudId,\n jiraSecretsNamespace,\n migrationsPath,\n upsertJiraInstallation,\n withJiraRefreshLockAndWait,\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 const integrationProvider = createJiraIntegrationProvider({\n agentTools: {tokenStore},\n cleanup: {\n deleteConnectionRecords: async (connection, {tx}) => {\n await deleteJiraInstallationByConnectionId(connection.id, {tx});\n },\n deleteConnectionSecrets: async (connection) => {\n await withJiraRefreshLockAndWait(connection.id, async () => {\n // Scoped secrets accept the provider-local suffix, after this helper validates its prefix.\n await (options.secrets?.jira?.deleteSecrets({\n workspaceId: connection.workspaceId,\n namespace: jiraNamespaceSuffix(jiraSecretsNamespace(connection.id)),\n }) ?? Promise.resolve());\n });\n },\n },\n routes: {\n tokenStore,\n pendingStore,\n getExistingJiraConnection,\n connectJiraInstallation,\n disconnectJiraInstallation,\n coreDb: db,\n publishIntegrationEventReceived,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n markConnectionActive: async ({connectionId, tx}) => {\n await updateIntegrationConnectionLifecycleStatus(\n {\n id: connectionId,\n lifecycleStatus: 'active',\n },\n tx === undefined ? {} : {tx: tx as IntegrationTx},\n );\n },\n markConnectionError: async ({connectionId, tx}) => {\n await updateIntegrationConnectionLifecycleStatus(\n {\n id: connectionId,\n lifecycleStatus: 'error',\n },\n tx === undefined ? {} : {tx: tx as IntegrationTx},\n );\n },\n ...(options.requireActiveWorkspaceMembership\n ? {requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership}\n : {}),\n },\n });\n\n return {\n provider: integrationProvider,\n workers: [\n createJiraMaintenanceWorker({\n tokenStore,\n resolveConnection: getIntegrationConnectionById,\n }),\n ],\n webhookProcessors: integrationProvider.webhookProcessors,\n database: {db: jiraDb, migrationsPath, databaseNamespace: 'integrations_jira'},\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":["config","deleteIntegrationConnection","getIntegrationConnectionById","resolveUniqueConnectionSlug","updateIntegrationConnectionLifecycleStatus","upsertIntegrationConnection","db","publishIntegrationEventReceived","recordDeliveryOnly","retryConnectionSlugCollision","slugifyConnectionSlug","JIRA_SECRETS_NAMESPACE_PREFIX","loadJiraModuleParts","options","createJiraIntegrationProvider","createJiraMaintenanceWorker","createJiraPendingSelectionStore","createJiraTokenStore","jiraDb","deleteJiraInstallationByConnectionId","disconnectJiraInstallation","disconnectJiraInstallationRecords","getJiraInstallationByCloudId","jiraSecretsNamespace","migrationsPath","upsertJiraInstallation","withJiraRefreshLockAndWait","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","integrationProvider","agentTools","cleanup","deleteConnectionRecords","deleteConnectionSecrets","routes","coreDb","markConnectionActive","requireActiveWorkspaceMembership","workers","webhookProcessors","database","databaseNamespace","jiraProviderModule","enabled","INTEGRATIONS_ENABLE_JIRA_PROVIDER","load","startsWith","slice","length"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/providers/jira.ts"],"sourcesContent":["import type {\n ConnectJiraInstallationInput,\n JiraPendingSelectionSecretsStore,\n JiraSecretsStore,\n} from '@shipfox/api-integration-jira';\nimport type {IntegrationConnection as CoreIntegrationConnection} from '@shipfox/api-integration-spi';\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 {publishIntegrationEventReceived, recordDeliveryOnly} from '#db/webhook-deliveries.js';\nimport {retryConnectionSlugCollision, slugifyConnectionSlug} from '#providers/connection-slug.js';\nimport type {IntegrationModuleParts, IntegrationProviderModule} from '#providers/types.js';\n\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 createJiraApiClient,\n createJiraIntegrationProvider,\n createJiraMaintenanceWorker,\n createJiraPendingSelectionStore,\n createJiraTokenStore,\n db: jiraDb,\n deregisterJiraWebhooks,\n deleteJiraInstallationByConnectionId,\n disconnectJiraInstallation: disconnectJiraInstallationRecords,\n getJiraInstallationByCloudId,\n getJiraInstallationByConnectionId,\n jiraSecretsNamespace,\n jiraWebhookUrl,\n migrationsPath,\n prepareJiraWebhookDeregistration,\n upsertJiraInstallation,\n withJiraRefreshLockAndWait,\n withJiraWebhookRegistrationLock,\n } = await import('@shipfox/api-integration-jira');\n const jira = createJiraApiClient();\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: {\n connectionId: string;\n lockAlreadyHeld?: boolean | undefined;\n }): Promise<void> {\n const disconnect = () =>\n 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 deregisterWebhooks: () =>\n deregisterJiraWebhooks({\n connectionId: input.connectionId,\n getInstallation: getJiraInstallationByConnectionId,\n tokenStore,\n jira,\n }),\n transaction: (fn) => db().transaction((tx) => fn(tx)),\n deleteConnection: (params, transactionOptions) =>\n deleteIntegrationConnection({id: params.connectionId}, transactionOptions),\n });\n if (input.lockAlreadyHeld) {\n await disconnect();\n return;\n }\n const installation = await getJiraInstallationByConnectionId(input.connectionId);\n if (!installation) {\n await disconnect();\n return;\n }\n await withJiraWebhookRegistrationLock(installation.cloudId, disconnect);\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 client: jira,\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 const integrationProvider = createJiraIntegrationProvider({\n jira,\n agentTools: {tokenStore},\n cleanup: {\n deleteConnectionRemoteResources: async (connection) => {\n return await prepareJiraWebhookDeregistration({\n connectionId: connection.id,\n getInstallation: getJiraInstallationByConnectionId,\n tokenStore,\n jira,\n });\n },\n withConnectionDeletionLock: async (connection, fn) => {\n const installation = await getJiraInstallationByConnectionId(connection.id);\n if (!installation) {\n await fn();\n return;\n }\n await withJiraWebhookRegistrationLock(installation.cloudId, fn);\n },\n deleteConnectionRecords: async (connection, {tx}) => {\n await deleteJiraInstallationByConnectionId(connection.id, {tx});\n },\n deleteConnectionSecrets: async (connection) => {\n await withJiraRefreshLockAndWait(connection.id, async () => {\n // Scoped secrets accept the provider-local suffix, after this helper validates its prefix.\n await (options.secrets?.jira?.deleteSecrets({\n workspaceId: connection.workspaceId,\n namespace: jiraNamespaceSuffix(jiraSecretsNamespace(connection.id)),\n }) ?? Promise.resolve());\n });\n },\n },\n routes: {\n tokenStore,\n pendingStore,\n getExistingJiraConnection,\n connectJiraInstallation,\n disconnectJiraInstallation,\n coreDb: db,\n publishIntegrationEventReceived,\n recordDeliveryOnly,\n getIntegrationConnectionById,\n markConnectionActive: async ({connectionId, tx}) => {\n await updateIntegrationConnectionLifecycleStatus(\n {\n id: connectionId,\n lifecycleStatus: 'active',\n },\n tx === undefined ? {} : {tx: tx as IntegrationTx},\n );\n },\n markConnectionError: async ({connectionId, tx}) => {\n await updateIntegrationConnectionLifecycleStatus(\n {\n id: connectionId,\n lifecycleStatus: 'error',\n },\n tx === undefined ? {} : {tx: tx as IntegrationTx},\n );\n },\n ...(options.requireActiveWorkspaceMembership\n ? {requireActiveWorkspaceMembership: options.requireActiveWorkspaceMembership}\n : {}),\n },\n });\n\n return {\n provider: integrationProvider,\n workers: [\n createJiraMaintenanceWorker({\n jira,\n tokenStore,\n resolveConnection: getIntegrationConnectionById,\n webhookUrlForConnection: jiraWebhookUrl,\n }),\n ],\n webhookProcessors: integrationProvider.webhookProcessors,\n database: {db: jiraDb, migrationsPath, databaseNamespace: 'integrations_jira'},\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":["config","deleteIntegrationConnection","getIntegrationConnectionById","resolveUniqueConnectionSlug","updateIntegrationConnectionLifecycleStatus","upsertIntegrationConnection","db","publishIntegrationEventReceived","recordDeliveryOnly","retryConnectionSlugCollision","slugifyConnectionSlug","JIRA_SECRETS_NAMESPACE_PREFIX","loadJiraModuleParts","options","createJiraApiClient","createJiraIntegrationProvider","createJiraMaintenanceWorker","createJiraPendingSelectionStore","createJiraTokenStore","jiraDb","deregisterJiraWebhooks","deleteJiraInstallationByConnectionId","disconnectJiraInstallation","disconnectJiraInstallationRecords","getJiraInstallationByCloudId","getJiraInstallationByConnectionId","jiraSecretsNamespace","jiraWebhookUrl","migrationsPath","prepareJiraWebhookDeregistration","upsertJiraInstallation","withJiraRefreshLockAndWait","withJiraWebhookRegistrationLock","jira","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","disconnect","getConnection","deleteSecrets","params","secrets","namespace","jiraNamespaceSuffix","Promise","resolve","deregisterWebhooks","getInstallation","tokenStore","fn","deleteConnection","transactionOptions","lockAlreadyHeld","fallbackSecrets","getSecret","setSecrets","reject","Error","client","resolveConnection","markConnectionError","pendingStore","integrationProvider","agentTools","cleanup","deleteConnectionRemoteResources","withConnectionDeletionLock","deleteConnectionRecords","deleteConnectionSecrets","routes","coreDb","markConnectionActive","requireActiveWorkspaceMembership","workers","webhookUrlForConnection","webhookProcessors","database","databaseNamespace","jiraProviderModule","enabled","INTEGRATIONS_ENABLE_JIRA_PROVIDER","load","startsWith","slice","length"],"mappings":"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"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import type { IntegrationProviderRegistry } from '#core/providers/registry.js';
|
|
1
2
|
import { pruneWebhookDeliveriesActivity } from './prune-webhook-deliveries.js';
|
|
2
|
-
export declare function createIntegrationsMaintenanceActivities(
|
|
3
|
+
export declare function createIntegrationsMaintenanceActivities(options: {
|
|
4
|
+
registry: IntegrationProviderRegistry;
|
|
5
|
+
}): {
|
|
3
6
|
pruneWebhookDeliveriesActivity: typeof pruneWebhookDeliveriesActivity;
|
|
7
|
+
cleanupIntegrationSecretsActivity: () => Promise<import("#core/secret-cleanup.js").ProcessIntegrationSecretCleanupsResult>;
|
|
4
8
|
};
|
|
5
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/temporal/activities/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/temporal/activities/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,2BAA2B,EAAC,MAAM,6BAA6B,CAAC;AAE7E,OAAO,EAAC,8BAA8B,EAAC,MAAM,+BAA+B,CAAC;AAE7E,wBAAgB,uCAAuC,CAAC,OAAO,EAAE;IAC/D,QAAQ,EAAE,2BAA2B,CAAC;CACvC;;;EASA"}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
import { Context } from '@temporalio/activity';
|
|
2
|
+
import { processIntegrationSecretCleanups } from '#core/secret-cleanup.js';
|
|
1
3
|
import { pruneWebhookDeliveriesActivity } from './prune-webhook-deliveries.js';
|
|
2
|
-
export function createIntegrationsMaintenanceActivities() {
|
|
4
|
+
export function createIntegrationsMaintenanceActivities(options) {
|
|
3
5
|
return {
|
|
4
|
-
pruneWebhookDeliveriesActivity
|
|
6
|
+
pruneWebhookDeliveriesActivity,
|
|
7
|
+
cleanupIntegrationSecretsActivity: ()=>processIntegrationSecretCleanups({
|
|
8
|
+
registry: options.registry,
|
|
9
|
+
heartbeat: ()=>Context.current().heartbeat()
|
|
10
|
+
})
|
|
5
11
|
};
|
|
6
12
|
}
|
|
7
13
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/temporal/activities/index.ts"],"sourcesContent":["import {pruneWebhookDeliveriesActivity} from './prune-webhook-deliveries.js';\n\nexport function createIntegrationsMaintenanceActivities() {\n return {\n pruneWebhookDeliveriesActivity,\n };\n}\n"],"names":["pruneWebhookDeliveriesActivity","createIntegrationsMaintenanceActivities"],"mappings":"AAAA,SAAQA,8BAA8B,QAAO,gCAAgC;AAE7E,OAAO,SAASC;
|
|
1
|
+
{"version":3,"sources":["../../../src/temporal/activities/index.ts"],"sourcesContent":["import {Context} from '@temporalio/activity';\nimport type {IntegrationProviderRegistry} from '#core/providers/registry.js';\nimport {processIntegrationSecretCleanups} from '#core/secret-cleanup.js';\nimport {pruneWebhookDeliveriesActivity} from './prune-webhook-deliveries.js';\n\nexport function createIntegrationsMaintenanceActivities(options: {\n registry: IntegrationProviderRegistry;\n}) {\n return {\n pruneWebhookDeliveriesActivity,\n cleanupIntegrationSecretsActivity: () =>\n processIntegrationSecretCleanups({\n registry: options.registry,\n heartbeat: () => Context.current().heartbeat(),\n }),\n };\n}\n"],"names":["Context","processIntegrationSecretCleanups","pruneWebhookDeliveriesActivity","createIntegrationsMaintenanceActivities","options","cleanupIntegrationSecretsActivity","registry","heartbeat","current"],"mappings":"AAAA,SAAQA,OAAO,QAAO,uBAAuB;AAE7C,SAAQC,gCAAgC,QAAO,0BAA0B;AACzE,SAAQC,8BAA8B,QAAO,gCAAgC;AAE7E,OAAO,SAASC,wCAAwCC,OAEvD;IACC,OAAO;QACLF;QACAG,mCAAmC,IACjCJ,iCAAiC;gBAC/BK,UAAUF,QAAQE,QAAQ;gBAC1BC,WAAW,IAAMP,QAAQQ,OAAO,GAAGD,SAAS;YAC9C;IACJ;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/temporal/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mCAAmC,6BAA6B,CAAC;AAE9E,eAAO,MAAM,+BAA+B,KAAK,CAAC"}
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/temporal/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mCAAmC,6BAA6B,CAAC;AAE9E,eAAO,MAAM,+BAA+B,KAAK,CAAC;AAElD,eAAO,MAAM,mCAAmC,QAAiB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/temporal/constants.ts"],"sourcesContent":["export const INTEGRATIONS_MAINTENANCE_TASK_QUEUE = 'integrations-maintenance';\n\nexport const WEBHOOK_DELIVERY_RETENTION_DAYS = 30;\n"],"names":["INTEGRATIONS_MAINTENANCE_TASK_QUEUE","WEBHOOK_DELIVERY_RETENTION_DAYS"],"mappings":"AAAA,OAAO,MAAMA,sCAAsC,2BAA2B;AAE9E,OAAO,MAAMC,kCAAkC,GAAG"}
|
|
1
|
+
{"version":3,"sources":["../../src/temporal/constants.ts"],"sourcesContent":["export const INTEGRATIONS_MAINTENANCE_TASK_QUEUE = 'integrations-maintenance';\n\nexport const WEBHOOK_DELIVERY_RETENTION_DAYS = 30;\n\nexport const CLEANUP_SECRETS_ACTIVITY_TIMEOUT_MS = 5 * 60 * 1_000;\n"],"names":["INTEGRATIONS_MAINTENANCE_TASK_QUEUE","WEBHOOK_DELIVERY_RETENTION_DAYS","CLEANUP_SECRETS_ACTIVITY_TIMEOUT_MS"],"mappings":"AAAA,OAAO,MAAMA,sCAAsC,2BAA2B;AAE9E,OAAO,MAAMC,kCAAkC,GAAG;AAElD,OAAO,MAAMC,sCAAsC,IAAI,KAAK,MAAM"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cleanup-integration-secrets-cron.d.ts","sourceRoot":"","sources":["../../../src/temporal/workflows/cleanup-integration-secrets-cron.ts"],"names":[],"mappings":"AAcA,wBAAsB,6BAA6B,IAAI,OAAO,CAAC,IAAI,CAAC,CAKnE"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { log, proxyActivities } from '@temporalio/workflow';
|
|
2
|
+
import { CLEANUP_SECRETS_ACTIVITY_TIMEOUT_MS } from '../constants.js';
|
|
3
|
+
const { cleanupIntegrationSecretsActivity } = proxyActivities({
|
|
4
|
+
startToCloseTimeout: CLEANUP_SECRETS_ACTIVITY_TIMEOUT_MS,
|
|
5
|
+
heartbeatTimeout: '1 minute',
|
|
6
|
+
// The activity records a per-row retry schedule. A later cron run retries the
|
|
7
|
+
// row without replaying the whole sweep in Temporal.
|
|
8
|
+
retry: {
|
|
9
|
+
maximumAttempts: 1
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
export async function cleanupIntegrationSecretsCron() {
|
|
13
|
+
const result = await cleanupIntegrationSecretsActivity();
|
|
14
|
+
if (result.claimed > 0) {
|
|
15
|
+
log.info('Completed integration connection secret cleanup sweep', {
|
|
16
|
+
...result
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=cleanup-integration-secrets-cron.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/temporal/workflows/cleanup-integration-secrets-cron.ts"],"sourcesContent":["import {log, proxyActivities} from '@temporalio/workflow';\nimport type {createIntegrationsMaintenanceActivities} from '../activities/index.js';\nimport {CLEANUP_SECRETS_ACTIVITY_TIMEOUT_MS} from '../constants.js';\n\nconst {cleanupIntegrationSecretsActivity} = proxyActivities<\n ReturnType<typeof createIntegrationsMaintenanceActivities>\n>({\n startToCloseTimeout: CLEANUP_SECRETS_ACTIVITY_TIMEOUT_MS,\n heartbeatTimeout: '1 minute',\n // The activity records a per-row retry schedule. A later cron run retries the\n // row without replaying the whole sweep in Temporal.\n retry: {maximumAttempts: 1},\n});\n\nexport async function cleanupIntegrationSecretsCron(): Promise<void> {\n const result = await cleanupIntegrationSecretsActivity();\n if (result.claimed > 0) {\n log.info('Completed integration connection secret cleanup sweep', {...result});\n }\n}\n"],"names":["log","proxyActivities","CLEANUP_SECRETS_ACTIVITY_TIMEOUT_MS","cleanupIntegrationSecretsActivity","startToCloseTimeout","heartbeatTimeout","retry","maximumAttempts","cleanupIntegrationSecretsCron","result","claimed","info"],"mappings":"AAAA,SAAQA,GAAG,EAAEC,eAAe,QAAO,uBAAuB;AAE1D,SAAQC,mCAAmC,QAAO,kBAAkB;AAEpE,MAAM,EAACC,iCAAiC,EAAC,GAAGF,gBAE1C;IACAG,qBAAqBF;IACrBG,kBAAkB;IAClB,8EAA8E;IAC9E,qDAAqD;IACrDC,OAAO;QAACC,iBAAiB;IAAC;AAC5B;AAEA,OAAO,eAAeC;IACpB,MAAMC,SAAS,MAAMN;IACrB,IAAIM,OAAOC,OAAO,GAAG,GAAG;QACtBV,IAAIW,IAAI,CAAC,yDAAyD;YAAC,GAAGF,MAAM;QAAA;IAC9E;AACF"}
|