@friggframework/core 2.0.0-next.44 → 2.0.0-next.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/README.md +28 -0
  2. package/application/commands/integration-commands.js +19 -0
  3. package/core/Worker.js +8 -21
  4. package/credential/repositories/credential-repository-mongo.js +14 -8
  5. package/credential/repositories/credential-repository-postgres.js +14 -8
  6. package/credential/repositories/credential-repository.js +3 -8
  7. package/database/MONGODB_TRANSACTION_FIX.md +198 -0
  8. package/database/adapters/lambda-invoker.js +97 -0
  9. package/database/config.js +11 -2
  10. package/database/models/WebsocketConnection.js +11 -10
  11. package/database/prisma.js +63 -3
  12. package/database/repositories/health-check-repository-mongodb.js +3 -0
  13. package/database/repositories/migration-status-repository-s3.js +137 -0
  14. package/database/use-cases/check-database-state-use-case.js +81 -0
  15. package/database/use-cases/check-encryption-health-use-case.js +3 -2
  16. package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
  17. package/database/use-cases/get-migration-status-use-case.js +93 -0
  18. package/database/use-cases/run-database-migration-use-case.js +137 -0
  19. package/database/use-cases/trigger-database-migration-use-case.js +157 -0
  20. package/database/utils/mongodb-collection-utils.js +91 -0
  21. package/database/utils/mongodb-schema-init.js +106 -0
  22. package/database/utils/prisma-runner.js +400 -0
  23. package/database/utils/prisma-schema-parser.js +182 -0
  24. package/encrypt/Cryptor.js +14 -16
  25. package/generated/prisma-mongodb/client.d.ts +1 -0
  26. package/generated/prisma-mongodb/client.js +4 -0
  27. package/generated/prisma-mongodb/default.d.ts +1 -0
  28. package/generated/prisma-mongodb/default.js +4 -0
  29. package/generated/prisma-mongodb/edge.d.ts +1 -0
  30. package/generated/prisma-mongodb/edge.js +334 -0
  31. package/generated/prisma-mongodb/index-browser.js +316 -0
  32. package/generated/prisma-mongodb/index.d.ts +22897 -0
  33. package/generated/prisma-mongodb/index.js +359 -0
  34. package/generated/prisma-mongodb/package.json +183 -0
  35. package/generated/prisma-mongodb/query-engine-debian-openssl-3.0.x +0 -0
  36. package/generated/prisma-mongodb/query-engine-rhel-openssl-3.0.x +0 -0
  37. package/generated/prisma-mongodb/runtime/binary.d.ts +1 -0
  38. package/generated/prisma-mongodb/runtime/binary.js +289 -0
  39. package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
  40. package/generated/prisma-mongodb/runtime/edge.js +34 -0
  41. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  42. package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
  43. package/generated/prisma-mongodb/runtime/library.d.ts +3977 -0
  44. package/generated/prisma-mongodb/runtime/react-native.js +83 -0
  45. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
  46. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
  47. package/generated/prisma-mongodb/schema.prisma +362 -0
  48. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
  49. package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
  50. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  51. package/generated/prisma-mongodb/wasm.js +341 -0
  52. package/generated/prisma-postgresql/client.d.ts +1 -0
  53. package/generated/prisma-postgresql/client.js +4 -0
  54. package/generated/prisma-postgresql/default.d.ts +1 -0
  55. package/generated/prisma-postgresql/default.js +4 -0
  56. package/generated/prisma-postgresql/edge.d.ts +1 -0
  57. package/generated/prisma-postgresql/edge.js +356 -0
  58. package/generated/prisma-postgresql/index-browser.js +338 -0
  59. package/generated/prisma-postgresql/index.d.ts +25071 -0
  60. package/generated/prisma-postgresql/index.js +381 -0
  61. package/generated/prisma-postgresql/package.json +183 -0
  62. package/generated/prisma-postgresql/query-engine-debian-openssl-3.0.x +0 -0
  63. package/generated/prisma-postgresql/query-engine-rhel-openssl-3.0.x +0 -0
  64. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  65. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  66. package/generated/prisma-postgresql/runtime/binary.d.ts +1 -0
  67. package/generated/prisma-postgresql/runtime/binary.js +289 -0
  68. package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
  69. package/generated/prisma-postgresql/runtime/edge.js +34 -0
  70. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  71. package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
  72. package/generated/prisma-postgresql/runtime/library.d.ts +3977 -0
  73. package/generated/prisma-postgresql/runtime/react-native.js +83 -0
  74. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
  75. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
  76. package/generated/prisma-postgresql/schema.prisma +345 -0
  77. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
  78. package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
  79. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  80. package/generated/prisma-postgresql/wasm.js +363 -0
  81. package/handlers/WEBHOOKS.md +653 -0
  82. package/handlers/backend-utils.js +118 -3
  83. package/handlers/database-migration-handler.js +227 -0
  84. package/handlers/routers/auth.js +1 -1
  85. package/handlers/routers/db-migration.handler.js +29 -0
  86. package/handlers/routers/db-migration.js +256 -0
  87. package/handlers/routers/health.js +41 -6
  88. package/handlers/routers/integration-webhook-routers.js +67 -0
  89. package/handlers/use-cases/check-integrations-health-use-case.js +22 -10
  90. package/handlers/workers/db-migration.js +352 -0
  91. package/index.js +28 -0
  92. package/integrations/WEBHOOK-QUICKSTART.md +151 -0
  93. package/integrations/integration-base.js +74 -3
  94. package/integrations/integration-router.js +60 -70
  95. package/integrations/repositories/integration-repository-interface.js +12 -0
  96. package/integrations/repositories/integration-repository-mongo.js +32 -0
  97. package/integrations/repositories/integration-repository-postgres.js +33 -0
  98. package/integrations/repositories/process-repository-postgres.js +43 -20
  99. package/integrations/tests/doubles/dummy-integration-class.js +1 -8
  100. package/integrations/tests/doubles/test-integration-repository.js +2 -2
  101. package/logs/logger.js +0 -4
  102. package/modules/entity.js +0 -1
  103. package/modules/repositories/module-repository-mongo.js +3 -12
  104. package/modules/repositories/module-repository-postgres.js +0 -11
  105. package/modules/repositories/module-repository.js +1 -12
  106. package/modules/use-cases/get-entity-options-by-id.js +1 -1
  107. package/modules/use-cases/get-module.js +1 -2
  108. package/modules/use-cases/refresh-entity-options.js +1 -1
  109. package/modules/use-cases/test-module-auth.js +1 -1
  110. package/package.json +82 -66
  111. package/prisma-mongodb/schema.prisma +21 -21
  112. package/prisma-postgresql/schema.prisma +15 -15
  113. package/queues/queuer-util.js +28 -15
  114. package/types/core/index.d.ts +2 -2
  115. package/types/module-plugin/index.d.ts +0 -2
  116. package/user/repositories/user-repository-mongo.js +53 -12
  117. package/user/repositories/user-repository-postgres.js +53 -14
  118. package/user/use-cases/authenticate-user.js +127 -0
  119. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  120. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  121. package/user/use-cases/get-user-from-x-frigg-headers.js +106 -0
  122. package/user/use-cases/login-user.js +1 -1
  123. package/user/user.js +18 -2
  124. package/websocket/repositories/websocket-connection-repository-mongo.js +11 -10
  125. package/websocket/repositories/websocket-connection-repository-postgres.js +11 -10
  126. package/websocket/repositories/websocket-connection-repository.js +11 -10
  127. package/application/commands/integration-commands.test.js +0 -123
  128. package/database/encryption/encryption-integration.test.js +0 -553
  129. package/database/encryption/encryption-schema-registry.test.js +0 -392
  130. package/database/encryption/field-encryption-service.test.js +0 -525
  131. package/database/encryption/mongo-decryption-fix-verification.test.js +0 -348
  132. package/database/encryption/postgres-decryption-fix-verification.test.js +0 -371
  133. package/database/encryption/postgres-relation-decryption.test.js +0 -245
  134. package/database/encryption/prisma-encryption-extension.test.js +0 -439
  135. package/errors/base-error.test.js +0 -32
  136. package/errors/fetch-error.test.js +0 -79
  137. package/errors/halt-error.test.js +0 -11
  138. package/errors/validation-errors.test.js +0 -120
  139. package/handlers/auth-flow.integration.test.js +0 -147
  140. package/handlers/integration-event-dispatcher.test.js +0 -141
  141. package/handlers/routers/health.test.js +0 -210
  142. package/integrations/tests/use-cases/create-integration.test.js +0 -131
  143. package/integrations/tests/use-cases/delete-integration-for-user.test.js +0 -150
  144. package/integrations/tests/use-cases/find-integration-context-by-external-entity-id.test.js +0 -92
  145. package/integrations/tests/use-cases/get-integration-for-user.test.js +0 -150
  146. package/integrations/tests/use-cases/get-integration-instance.test.js +0 -176
  147. package/integrations/tests/use-cases/get-integrations-for-user.test.js +0 -176
  148. package/integrations/tests/use-cases/get-possible-integrations.test.js +0 -188
  149. package/integrations/tests/use-cases/update-integration-messages.test.js +0 -142
  150. package/integrations/tests/use-cases/update-integration-status.test.js +0 -103
  151. package/integrations/tests/use-cases/update-integration.test.js +0 -141
  152. package/integrations/use-cases/create-process.test.js +0 -178
  153. package/integrations/use-cases/get-process.test.js +0 -190
  154. package/integrations/use-cases/load-integration-context-full.test.js +0 -329
  155. package/integrations/use-cases/load-integration-context.test.js +0 -114
  156. package/integrations/use-cases/update-process-metrics.test.js +0 -308
  157. package/integrations/use-cases/update-process-state.test.js +0 -256
  158. package/lambda/TimeoutCatcher.test.js +0 -68
  159. package/logs/logger.test.js +0 -76
  160. package/modules/module-hydration.test.js +0 -205
  161. package/modules/requester/requester.test.js +0 -28
  162. package/user/tests/use-cases/create-individual-user.test.js +0 -24
  163. package/user/tests/use-cases/create-organization-user.test.js +0 -28
  164. package/user/tests/use-cases/create-token-for-user-id.test.js +0 -19
  165. package/user/tests/use-cases/get-user-from-bearer-token.test.js +0 -64
  166. package/user/tests/use-cases/login-user.test.js +0 -140
@@ -1,6 +1,16 @@
1
1
  const { Router } = require('express');
2
- const { moduleFactory, integrationFactory } = require('./../backend-utils');
3
2
  const { createAppHandler } = require('./../app-handler-helpers');
3
+ const { loadAppDefinition } = require('./../app-definition-loader');
4
+ const { ModuleFactory } = require('../../modules/module-factory');
5
+ const {
6
+ getModulesDefinitionFromIntegrationClasses,
7
+ } = require('../../integrations/utils/map-integration-dto');
8
+ const {
9
+ createModuleRepository,
10
+ } = require('../../modules/repositories/module-repository-factory');
11
+ const {
12
+ createIntegrationRepository,
13
+ } = require('../../integrations/repositories/integration-repository-factory');
4
14
  const {
5
15
  createHealthCheckRepository,
6
16
  } = require('../../database/repositories/health-check-repository-factory');
@@ -22,6 +32,28 @@ const {
22
32
 
23
33
  const router = Router();
24
34
  const healthCheckRepository = createHealthCheckRepository();
35
+
36
+ // Load integrations and create factories just like auth router does
37
+ // This verifies the system can properly load integrations
38
+ let moduleFactory, integrationClasses;
39
+ try {
40
+ const appDef = loadAppDefinition();
41
+ integrationClasses = appDef.integrations || [];
42
+
43
+ const moduleRepository = createModuleRepository();
44
+ const moduleDefinitions = getModulesDefinitionFromIntegrationClasses(integrationClasses);
45
+
46
+ moduleFactory = new ModuleFactory({
47
+ moduleRepository,
48
+ moduleDefinitions,
49
+ });
50
+ } catch (error) {
51
+ console.error('Failed to load integrations for health check:', error.message);
52
+ // Factories will be undefined, health check will report unhealthy
53
+ moduleFactory = undefined;
54
+ integrationClasses = [];
55
+ }
56
+
25
57
  const testEncryptionUseCase = new TestEncryptionUseCase({
26
58
  healthCheckRepository,
27
59
  });
@@ -34,11 +66,11 @@ const checkEncryptionHealthUseCase = new CheckEncryptionHealthUseCase({
34
66
  const checkExternalApisHealthUseCase = new CheckExternalApisHealthUseCase();
35
67
  const checkIntegrationsHealthUseCase = new CheckIntegrationsHealthUseCase({
36
68
  moduleFactory,
37
- integrationFactory,
69
+ integrationClasses,
38
70
  });
39
71
 
40
72
  const validateApiKey = (req, res, next) => {
41
- const apiKey = req.headers['x-api-key'];
73
+ const apiKey = req.headers['x-frigg-health-api-key'];
42
74
 
43
75
  if (req.path === '/health') {
44
76
  return next();
@@ -48,7 +80,7 @@ const validateApiKey = (req, res, next) => {
48
80
  console.error('Unauthorized access attempt to health endpoint');
49
81
  return res.status(401).json({
50
82
  status: 'error',
51
- message: 'Unauthorized',
83
+ message: 'Unauthorized - x-frigg-health-api-key header required',
52
84
  });
53
85
  }
54
86
 
@@ -141,8 +173,11 @@ const detectVpcConfiguration = async () => {
141
173
  }
142
174
  }
143
175
 
144
- results.isInVpc =
145
- !results.hasInternetAccess || results.vpcEndpoints.length > 0;
176
+ // Check if Lambda is in VPC using VPC_ENABLED env var set by infrastructure
177
+ results.isInVpc = process.env.VPC_ENABLED === 'true' ||
178
+ (!results.hasInternetAccess && results.canResolvePublicDns) ||
179
+ results.vpcEndpoints.length > 0;
180
+
146
181
  results.canConnectToAws =
147
182
  results.hasInternetAccess || results.vpcEndpoints.length > 0;
148
183
  } catch (error) {
@@ -0,0 +1,67 @@
1
+ const { createAppHandler } = require('./../app-handler-helpers');
2
+ const { loadAppDefinition } = require('../app-definition-loader');
3
+ const { Router } = require('express');
4
+ const { IntegrationEventDispatcher } = require('../integration-event-dispatcher');
5
+
6
+ const handlers = {};
7
+ const { integrations: integrationClasses } = loadAppDefinition();
8
+
9
+ for (const IntegrationClass of integrationClasses) {
10
+ const webhookConfig = IntegrationClass.Definition.webhooks;
11
+
12
+ // Skip if webhooks not enabled
13
+ if (!webhookConfig || (typeof webhookConfig === 'object' && !webhookConfig.enabled)) {
14
+ continue;
15
+ }
16
+
17
+ const router = Router();
18
+ const basePath = `/api/${IntegrationClass.Definition.name}-integration/webhooks`;
19
+
20
+ console.log(`\n│ Configuring webhook routes for ${IntegrationClass.Definition.name}:`);
21
+
22
+ // General webhook route (no integration ID)
23
+ router.post(basePath, async (req, res, next) => {
24
+ try {
25
+ const integrationInstance = new IntegrationClass();
26
+ const dispatcher = new IntegrationEventDispatcher(integrationInstance);
27
+ await dispatcher.dispatchHttp({
28
+ event: 'WEBHOOK_RECEIVED',
29
+ req,
30
+ res,
31
+ next,
32
+ });
33
+ } catch (error) {
34
+ next(error);
35
+ }
36
+ });
37
+ console.log(`│ POST ${basePath}`);
38
+
39
+ // Integration-specific webhook route (with integration ID)
40
+ router.post(`${basePath}/:integrationId`, async (req, res, next) => {
41
+ try {
42
+ const integrationInstance = new IntegrationClass();
43
+ const dispatcher = new IntegrationEventDispatcher(integrationInstance);
44
+ await dispatcher.dispatchHttp({
45
+ event: 'WEBHOOK_RECEIVED',
46
+ req,
47
+ res,
48
+ next,
49
+ });
50
+ } catch (error) {
51
+ next(error);
52
+ }
53
+ });
54
+ console.log(`│ POST ${basePath}/:integrationId`);
55
+ console.log('│');
56
+
57
+ handlers[`${IntegrationClass.Definition.name}Webhook`] = {
58
+ handler: createAppHandler(
59
+ `HTTP Event: ${IntegrationClass.Definition.name} Webhook`,
60
+ router,
61
+ false // shouldUseDatabase = false
62
+ ),
63
+ };
64
+ }
65
+
66
+ module.exports = { handlers };
67
+
@@ -1,20 +1,32 @@
1
1
  class CheckIntegrationsHealthUseCase {
2
- constructor({ moduleFactory, integrationFactory }) {
2
+ constructor({ moduleFactory, integrationClasses }) {
3
3
  this.moduleFactory = moduleFactory;
4
- this.integrationFactory = integrationFactory;
4
+ this.integrationClasses = integrationClasses;
5
5
  }
6
6
 
7
7
  execute() {
8
- const moduleTypes = Array.isArray(this.moduleFactory.moduleTypes)
9
- ? this.moduleFactory.moduleTypes
8
+ const moduleDefinitions = (this.moduleFactory && this.moduleFactory.moduleDefinitions)
9
+ ? this.moduleFactory.moduleDefinitions
10
10
  : [];
11
11
 
12
- const integrationTypes = Array.isArray(
13
- this.integrationFactory.integrationTypes
14
- )
15
- ? this.integrationFactory.integrationTypes
12
+ const integrationClasses = Array.isArray(this.integrationClasses)
13
+ ? this.integrationClasses
16
14
  : [];
17
15
 
16
+ // Extract module names from definitions
17
+ const moduleTypes = Array.isArray(moduleDefinitions)
18
+ ? moduleDefinitions.map(def => def.moduleName || def.name || def.label || 'Unknown')
19
+ : [];
20
+
21
+ // Extract integration names from classes
22
+ const integrationNames = integrationClasses.map(IntegrationClass => {
23
+ try {
24
+ return IntegrationClass.Definition?.name || IntegrationClass.name || 'Unknown';
25
+ } catch {
26
+ return 'Unknown';
27
+ }
28
+ });
29
+
18
30
  return {
19
31
  status: 'healthy',
20
32
  modules: {
@@ -22,8 +34,8 @@ class CheckIntegrationsHealthUseCase {
22
34
  available: moduleTypes,
23
35
  },
24
36
  integrations: {
25
- count: integrationTypes.length,
26
- available: integrationTypes,
37
+ count: integrationNames.length,
38
+ available: integrationNames,
27
39
  },
28
40
  };
29
41
  }
@@ -0,0 +1,352 @@
1
+ /**
2
+ * Database Migration Lambda Handler
3
+ *
4
+ * Lambda function that runs Prisma database migrations from within the VPC,
5
+ * enabling CI/CD pipelines to migrate databases without requiring public access.
6
+ *
7
+ * This handler uses the prisma-runner utilities from @friggframework/core,
8
+ * ensuring consistency with the `frigg db:setup` command.
9
+ *
10
+ * Environment Variables Required:
11
+ * - DATABASE_URL: PostgreSQL connection string (automatically set from Secrets Manager)
12
+ * - DB_TYPE: Database type ('postgresql' or 'mongodb')
13
+ * - STAGE: Deployment stage (determines migration command: 'dev' or 'deploy')
14
+ *
15
+ * Invocation:
16
+ * aws lambda invoke \
17
+ * --function-name my-app-production-dbMigrate \
18
+ * --region us-east-1 \
19
+ * response.json
20
+ *
21
+ * Success Response:
22
+ * {
23
+ * "statusCode": 200,
24
+ * "body": {
25
+ * "success": true,
26
+ * "message": "Database migration completed successfully",
27
+ * "dbType": "postgresql",
28
+ * "stage": "production",
29
+ * "migrationCommand": "deploy"
30
+ * }
31
+ * }
32
+ *
33
+ * Error Response:
34
+ * {
35
+ * "statusCode": 500,
36
+ * "body": {
37
+ * "success": false,
38
+ * "error": "Migration failed: ...",
39
+ * "stack": "Error: ..."
40
+ * }
41
+ * }
42
+ */
43
+
44
+ const {
45
+ RunDatabaseMigrationUseCase,
46
+ MigrationError,
47
+ ValidationError,
48
+ } = require('../../database/use-cases/run-database-migration-use-case');
49
+ const {
50
+ CheckDatabaseStateUseCase,
51
+ } = require('../../database/use-cases/check-database-state-use-case');
52
+ const {
53
+ MigrationStatusRepositoryS3,
54
+ } = require('../../database/repositories/migration-status-repository-s3');
55
+
56
+ // Inject prisma-runner as dependency
57
+ const prismaRunner = require('../../database/utils/prisma-runner');
58
+
59
+ // Use S3 repository for migration status tracking (no User table dependency)
60
+ const bucketName = process.env.S3_BUCKET_NAME || process.env.MIGRATION_STATUS_BUCKET;
61
+ const migrationStatusRepository = new MigrationStatusRepositoryS3(bucketName);
62
+
63
+ /**
64
+ * Sanitizes error messages to prevent credential leaks
65
+ * @param {string} errorMessage - Error message that might contain credentials
66
+ * @returns {string} Sanitized error message
67
+ */
68
+ function sanitizeError(errorMessage) {
69
+ if (!errorMessage) return 'Unknown error';
70
+
71
+ return String(errorMessage)
72
+ // Remove PostgreSQL connection strings
73
+ .replace(/postgresql:\/\/[^@\s]+@[^\s/]+/gi, 'postgresql://***:***@***')
74
+ // Remove MongoDB connection strings
75
+ .replace(/mongodb(\+srv)?:\/\/[^@\s]+@[^\s/]+/gi, 'mongodb$1://***:***@***')
76
+ // Remove password parameters
77
+ .replace(/password[=:]\s*[^\s,;)]+/gi, 'password=***')
78
+ // Remove API keys
79
+ .replace(/apikey[=:]\s*[^\s,;)]+/gi, 'apikey=***')
80
+ .replace(/api[_-]?key[=:]\s*[^\s,;)]+/gi, 'api_key=***')
81
+ // Remove tokens
82
+ .replace(/token[=:]\s*[^\s,;)]+/gi, 'token=***')
83
+ .replace(/bearer\s+[^\s,;)]+/gi, 'bearer ***');
84
+ }
85
+
86
+ /**
87
+ * Sanitizes DATABASE_URL for safe logging
88
+ * @param {string} url - Database URL
89
+ * @returns {string} Sanitized URL
90
+ */
91
+ function sanitizeDatabaseUrl(url) {
92
+ if (!url) return '';
93
+
94
+ // Replace credentials in connection string
95
+ return url.replace(/(:\/\/)([^:]+):([^@]+)@/, '$1***:***@');
96
+ }
97
+
98
+ /**
99
+ * Extract migration parameters from SQS event or direct invocation
100
+ * @param {Object} event - Lambda event (SQS or direct)
101
+ * @returns {Object} Extracted parameters { migrationId, dbType, stage }
102
+ */
103
+ function extractMigrationParams(event) {
104
+ let migrationId = null;
105
+ let stage = null;
106
+
107
+ // Migration infrastructure is PostgreSQL-only, so hardcode dbType
108
+ const dbType = 'postgresql';
109
+
110
+ // Check if this is an SQS event
111
+ if (event.Records && event.Records.length > 0) {
112
+ // SQS event - extract from message body
113
+ const message = JSON.parse(event.Records[0].body);
114
+ migrationId = message.migrationId;
115
+ stage = message.stage;
116
+
117
+ console.log('SQS event detected');
118
+ console.log(` Migration ID: ${migrationId}`);
119
+ console.log(` DB Type: ${dbType} (hardcoded - PostgreSQL-only)`);
120
+ console.log(` Stage: ${stage}`);
121
+ } else {
122
+ // Direct invocation - use event properties or environment variables
123
+ migrationId = event.migrationId || null;
124
+ stage = event.stage || process.env.STAGE || 'production';
125
+
126
+ console.log('Direct invocation detected');
127
+ if (migrationId) {
128
+ console.log(` Migration ID: ${migrationId}`);
129
+ }
130
+ console.log(` DB Type: ${dbType} (hardcoded - PostgreSQL-only)`);
131
+ console.log(` Stage: ${stage}`);
132
+ }
133
+
134
+ return { migrationId, dbType, stage };
135
+ }
136
+
137
+ /**
138
+ * Lambda handler entry point
139
+ * @param {Object} event - Lambda event (SQS message or direct invocation)
140
+ * @param {Object} context - Lambda context (contains AWS request ID, timeout info)
141
+ * @returns {Promise<Object>} Response with statusCode and body
142
+ */
143
+ exports.handler = async (event, context) => {
144
+ console.log('========================================');
145
+ console.log('Database Migration Lambda Started');
146
+ console.log('========================================');
147
+ console.log('Event:', JSON.stringify(event, null, 2));
148
+ console.log('Context:', JSON.stringify({
149
+ requestId: context.requestId,
150
+ functionName: context.functionName,
151
+ remainingTimeInMillis: context.getRemainingTimeInMillis(),
152
+ }, null, 2));
153
+
154
+ // Extract migration parameters from event
155
+ const { migrationId, dbType, stage } = extractMigrationParams(event);
156
+
157
+ // Check for action parameter (direct invocation for status checks)
158
+ const action = event.action || 'migrate'; // Default to migration
159
+
160
+ // Handle checkStatus action
161
+ if (action === 'checkStatus') {
162
+ console.log(`\n========================================`);
163
+ console.log(`Action: checkStatus (dbType=${dbType}, stage=${stage})`);
164
+ console.log(`========================================`);
165
+
166
+ try {
167
+ const checkDbStateUseCase = new CheckDatabaseStateUseCase({ prismaRunner });
168
+ const status = await checkDbStateUseCase.execute(dbType, stage);
169
+
170
+ console.log('✓ Database state check completed');
171
+ console.log(` Up to date: ${status.upToDate}`);
172
+ console.log(` Pending migrations: ${status.pendingMigrations}`);
173
+
174
+ return {
175
+ statusCode: 200,
176
+ body: status,
177
+ };
178
+ } catch (error) {
179
+ console.error('❌ Database state check failed:', error.message);
180
+ return {
181
+ statusCode: 500,
182
+ body: {
183
+ success: false,
184
+ error: sanitizeError(error.message),
185
+ upToDate: false,
186
+ },
187
+ };
188
+ }
189
+ }
190
+
191
+ // Otherwise, handle migration (existing code)
192
+ console.log(`\n========================================`);
193
+ console.log(`Action: migrate (migrationId=${migrationId || 'new'})`);
194
+ console.log(`========================================`);
195
+
196
+ // Get environment variables
197
+ const databaseUrl = process.env.DATABASE_URL;
198
+
199
+ try {
200
+ // Validate DATABASE_URL is set
201
+ if (!databaseUrl) {
202
+ const error = 'DATABASE_URL environment variable is not set';
203
+ console.error('❌ Validation failed:', error);
204
+ return {
205
+ statusCode: 500,
206
+ body: JSON.stringify({
207
+ success: false,
208
+ error,
209
+ }),
210
+ };
211
+ }
212
+
213
+ console.log('✓ Environment validated');
214
+ console.log(` Database Type: ${dbType}`);
215
+ console.log(` Stage: ${stage}`);
216
+ console.log(` Database URL: ${sanitizeDatabaseUrl(databaseUrl)}`);
217
+
218
+ // Update migration status to RUNNING (if migrationId provided)
219
+ if (migrationId) {
220
+ console.log(`\n✓ Updating migration status to RUNNING: ${migrationId}`);
221
+ await migrationStatusRepository.update({
222
+ migrationId,
223
+ stage,
224
+ state: 'RUNNING',
225
+ progress: 10,
226
+ startedAt: new Date().toISOString(),
227
+ });
228
+ }
229
+
230
+ // Create use case with dependencies (Dependency Injection)
231
+ const runDatabaseMigrationUseCase = new RunDatabaseMigrationUseCase({
232
+ prismaRunner,
233
+ });
234
+
235
+ console.log('\n========================================');
236
+ console.log('Executing Database Migration');
237
+ console.log('========================================');
238
+
239
+ // Execute use case (business logic layer)
240
+ const result = await runDatabaseMigrationUseCase.execute({
241
+ dbType,
242
+ stage,
243
+ verbose: true, // Enable verbose output for Lambda CloudWatch logs
244
+ });
245
+
246
+ console.log('✓ Database migration completed successfully');
247
+ console.log('\n========================================');
248
+ console.log('Migration Summary');
249
+ console.log('========================================');
250
+ console.log(` Status: Success`);
251
+ console.log(` Database: ${result.dbType}`);
252
+ console.log(` Stage: ${result.stage}`);
253
+ console.log(` Command: ${result.command}`);
254
+ console.log('========================================');
255
+
256
+ // Update migration status to COMPLETED (if migrationId provided)
257
+ if (migrationId) {
258
+ console.log(`\n✓ Updating migration status to COMPLETED: ${migrationId}`);
259
+ await migrationStatusRepository.update({
260
+ migrationId,
261
+ stage,
262
+ state: 'COMPLETED',
263
+ progress: 100,
264
+ completedAt: new Date().toISOString(),
265
+ migrationCommand: result.command,
266
+ });
267
+ }
268
+
269
+ // Return success response (adapter layer - HTTP mapping)
270
+ const responseBody = {
271
+ success: true,
272
+ message: result.message,
273
+ dbType: result.dbType,
274
+ stage: result.stage,
275
+ migrationCommand: result.command,
276
+ timestamp: new Date().toISOString(),
277
+ };
278
+
279
+ if (migrationId) {
280
+ responseBody.migrationId = migrationId;
281
+ }
282
+
283
+ return {
284
+ statusCode: 200,
285
+ body: JSON.stringify(responseBody),
286
+ };
287
+
288
+ } catch (error) {
289
+ console.error('\n========================================');
290
+ console.error('Migration Failed');
291
+ console.error('========================================');
292
+ console.error('Error:', error.name, error.message);
293
+
294
+ // Log full stack trace to CloudWatch (only visible to developers)
295
+ if (error.stack) {
296
+ console.error('Stack:', error.stack);
297
+ }
298
+
299
+ // Log context if available (from MigrationError)
300
+ if (error.context) {
301
+ console.error('Context:', JSON.stringify(error.context, null, 2));
302
+ }
303
+
304
+ // Map domain errors to HTTP status codes (adapter layer)
305
+ let statusCode = 500;
306
+ let errorMessage = error.message || 'Unknown error occurred';
307
+
308
+ if (error instanceof ValidationError) {
309
+ statusCode = 400; // Bad Request for validation errors
310
+ } else if (error instanceof MigrationError) {
311
+ statusCode = 500; // Internal Server Error for migration failures
312
+ }
313
+
314
+ // Sanitize error message before returning
315
+ const sanitizedError = sanitizeError(errorMessage);
316
+
317
+ // Update migration status to FAILED (if migrationId provided)
318
+ if (migrationId) {
319
+ try {
320
+ console.log(`\n✓ Updating migration status to FAILED: ${migrationId}`);
321
+ await migrationStatusRepository.update({
322
+ migrationId,
323
+ stage,
324
+ state: 'FAILED',
325
+ progress: 0,
326
+ error: sanitizedError,
327
+ failedAt: new Date().toISOString(),
328
+ });
329
+ } catch (updateError) {
330
+ console.error('Failed to update migration status:', updateError.message);
331
+ // Continue - don't let status update failure block error response
332
+ }
333
+ }
334
+
335
+ const errorBody = {
336
+ success: false,
337
+ error: sanitizedError,
338
+ errorType: error.name || 'Error',
339
+ // Only include stack traces in development environments
340
+ ...(stage === 'dev' || stage === 'local' || stage === 'test' ? { stack: error.stack } : {}),
341
+ };
342
+
343
+ if (migrationId) {
344
+ errorBody.migrationId = migrationId;
345
+ }
346
+
347
+ return {
348
+ statusCode,
349
+ body: JSON.stringify(errorBody),
350
+ };
351
+ }
352
+ };
package/index.js CHANGED
@@ -33,6 +33,15 @@ const {
33
33
  UserRepositoryMongo,
34
34
  UserRepositoryPostgres,
35
35
  } = require('./user/repositories/user-repository-factory');
36
+ const {
37
+ GetUserFromXFriggHeaders,
38
+ } = require('./user/use-cases/get-user-from-x-frigg-headers');
39
+ const {
40
+ GetUserFromAdopterJwt,
41
+ } = require('./user/use-cases/get-user-from-adopter-jwt');
42
+ const {
43
+ AuthenticateUser,
44
+ } = require('./user/use-cases/authenticate-user');
36
45
 
37
46
  const {
38
47
  CredentialRepository,
@@ -43,6 +52,18 @@ const {
43
52
  const {
44
53
  IntegrationMappingRepository,
45
54
  } = require('./integrations/repositories/integration-mapping-repository');
55
+ const {
56
+ CreateProcess,
57
+ } = require('./integrations/use-cases/create-process');
58
+ const {
59
+ UpdateProcessState,
60
+ } = require('./integrations/use-cases/update-process-state');
61
+ const {
62
+ UpdateProcessMetrics,
63
+ } = require('./integrations/use-cases/update-process-metrics');
64
+ const {
65
+ GetProcess,
66
+ } = require('./integrations/use-cases/get-process');
46
67
  const { Cryptor } = require('./encrypt');
47
68
  const {
48
69
  BaseError,
@@ -111,6 +132,9 @@ module.exports = {
111
132
  createUserRepository,
112
133
  UserRepositoryMongo,
113
134
  UserRepositoryPostgres,
135
+ GetUserFromXFriggHeaders,
136
+ GetUserFromAdopterJwt,
137
+ AuthenticateUser,
114
138
  CredentialRepository,
115
139
  ModuleRepository,
116
140
  IntegrationMappingRepository,
@@ -130,6 +154,10 @@ module.exports = {
130
154
  createIntegrationRouter,
131
155
  getModulesDefinitionFromIntegrationClasses,
132
156
  LoadIntegrationContextUseCase,
157
+ CreateProcess,
158
+ UpdateProcessState,
159
+ UpdateProcessMetrics,
160
+ GetProcess,
133
161
 
134
162
  // application - Command factories for integration developers
135
163
  application,