@friggframework/core 2.0.0-next.6 → 2.0.0-next.61

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 (286) hide show
  1. package/CLAUDE.md +694 -0
  2. package/README.md +959 -50
  3. package/application/commands/README.md +451 -0
  4. package/application/commands/credential-commands.js +245 -0
  5. package/application/commands/entity-commands.js +336 -0
  6. package/application/commands/integration-commands.js +210 -0
  7. package/application/commands/user-commands.js +283 -0
  8. package/application/index.js +69 -0
  9. package/core/CLAUDE.md +690 -0
  10. package/core/Worker.js +8 -21
  11. package/core/create-handler.js +14 -7
  12. package/credential/repositories/credential-repository-documentdb.js +304 -0
  13. package/credential/repositories/credential-repository-factory.js +54 -0
  14. package/credential/repositories/credential-repository-interface.js +98 -0
  15. package/credential/repositories/credential-repository-mongo.js +269 -0
  16. package/credential/repositories/credential-repository-postgres.js +291 -0
  17. package/credential/repositories/credential-repository.js +302 -0
  18. package/credential/use-cases/get-credential-for-user.js +25 -0
  19. package/credential/use-cases/update-authentication-status.js +15 -0
  20. package/database/MONGODB_TRANSACTION_FIX.md +198 -0
  21. package/database/adapters/lambda-invoker.js +97 -0
  22. package/database/config.js +154 -0
  23. package/database/documentdb-encryption-service.js +330 -0
  24. package/database/documentdb-utils.js +136 -0
  25. package/database/encryption/README.md +839 -0
  26. package/database/encryption/documentdb-encryption-service.md +3575 -0
  27. package/database/encryption/encryption-schema-registry.js +268 -0
  28. package/database/encryption/field-encryption-service.js +226 -0
  29. package/database/encryption/logger.js +79 -0
  30. package/database/encryption/prisma-encryption-extension.js +222 -0
  31. package/database/index.js +61 -21
  32. package/database/models/WebsocketConnection.js +16 -10
  33. package/database/models/readme.md +1 -0
  34. package/database/prisma.js +182 -0
  35. package/database/repositories/health-check-repository-documentdb.js +134 -0
  36. package/database/repositories/health-check-repository-factory.js +48 -0
  37. package/database/repositories/health-check-repository-interface.js +82 -0
  38. package/database/repositories/health-check-repository-mongodb.js +89 -0
  39. package/database/repositories/health-check-repository-postgres.js +82 -0
  40. package/database/repositories/health-check-repository.js +108 -0
  41. package/database/repositories/migration-status-repository-s3.js +137 -0
  42. package/database/use-cases/check-database-health-use-case.js +29 -0
  43. package/database/use-cases/check-database-state-use-case.js +81 -0
  44. package/database/use-cases/check-encryption-health-use-case.js +83 -0
  45. package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
  46. package/database/use-cases/get-migration-status-use-case.js +93 -0
  47. package/database/use-cases/run-database-migration-use-case.js +139 -0
  48. package/database/use-cases/test-encryption-use-case.js +253 -0
  49. package/database/use-cases/trigger-database-migration-use-case.js +157 -0
  50. package/database/utils/mongodb-collection-utils.js +91 -0
  51. package/database/utils/mongodb-schema-init.js +106 -0
  52. package/database/utils/prisma-runner.js +477 -0
  53. package/database/utils/prisma-schema-parser.js +182 -0
  54. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
  55. package/encrypt/Cryptor.js +34 -168
  56. package/encrypt/index.js +1 -2
  57. package/encrypt/test-encrypt.js +0 -2
  58. package/errors/client-safe-error.js +26 -0
  59. package/errors/fetch-error.js +2 -1
  60. package/errors/index.js +2 -0
  61. package/generated/prisma-mongodb/client.d.ts +1 -0
  62. package/generated/prisma-mongodb/client.js +4 -0
  63. package/generated/prisma-mongodb/default.d.ts +1 -0
  64. package/generated/prisma-mongodb/default.js +4 -0
  65. package/generated/prisma-mongodb/edge.d.ts +1 -0
  66. package/generated/prisma-mongodb/edge.js +334 -0
  67. package/generated/prisma-mongodb/index-browser.js +316 -0
  68. package/generated/prisma-mongodb/index.d.ts +22903 -0
  69. package/generated/prisma-mongodb/index.js +359 -0
  70. package/generated/prisma-mongodb/package.json +183 -0
  71. package/generated/prisma-mongodb/query-engine-debian-openssl-3.0.x +0 -0
  72. package/generated/prisma-mongodb/query-engine-rhel-openssl-3.0.x +0 -0
  73. package/generated/prisma-mongodb/runtime/binary.d.ts +1 -0
  74. package/generated/prisma-mongodb/runtime/binary.js +289 -0
  75. package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
  76. package/generated/prisma-mongodb/runtime/edge.js +34 -0
  77. package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
  78. package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
  79. package/generated/prisma-mongodb/runtime/library.d.ts +3977 -0
  80. package/generated/prisma-mongodb/runtime/react-native.js +83 -0
  81. package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
  82. package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
  83. package/generated/prisma-mongodb/schema.prisma +360 -0
  84. package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
  85. package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
  86. package/generated/prisma-mongodb/wasm.d.ts +1 -0
  87. package/generated/prisma-mongodb/wasm.js +341 -0
  88. package/generated/prisma-postgresql/client.d.ts +1 -0
  89. package/generated/prisma-postgresql/client.js +4 -0
  90. package/generated/prisma-postgresql/default.d.ts +1 -0
  91. package/generated/prisma-postgresql/default.js +4 -0
  92. package/generated/prisma-postgresql/edge.d.ts +1 -0
  93. package/generated/prisma-postgresql/edge.js +356 -0
  94. package/generated/prisma-postgresql/index-browser.js +338 -0
  95. package/generated/prisma-postgresql/index.d.ts +25077 -0
  96. package/generated/prisma-postgresql/index.js +381 -0
  97. package/generated/prisma-postgresql/package.json +183 -0
  98. package/generated/prisma-postgresql/query-engine-debian-openssl-3.0.x +0 -0
  99. package/generated/prisma-postgresql/query-engine-rhel-openssl-3.0.x +0 -0
  100. package/generated/prisma-postgresql/query_engine_bg.js +2 -0
  101. package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
  102. package/generated/prisma-postgresql/runtime/binary.d.ts +1 -0
  103. package/generated/prisma-postgresql/runtime/binary.js +289 -0
  104. package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
  105. package/generated/prisma-postgresql/runtime/edge.js +34 -0
  106. package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
  107. package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
  108. package/generated/prisma-postgresql/runtime/library.d.ts +3977 -0
  109. package/generated/prisma-postgresql/runtime/react-native.js +83 -0
  110. package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
  111. package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
  112. package/generated/prisma-postgresql/schema.prisma +343 -0
  113. package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
  114. package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
  115. package/generated/prisma-postgresql/wasm.d.ts +1 -0
  116. package/generated/prisma-postgresql/wasm.js +363 -0
  117. package/handlers/WEBHOOKS.md +653 -0
  118. package/handlers/app-definition-loader.js +38 -0
  119. package/handlers/app-handler-helpers.js +56 -0
  120. package/handlers/backend-utils.js +186 -0
  121. package/handlers/database-migration-handler.js +227 -0
  122. package/handlers/integration-event-dispatcher.js +54 -0
  123. package/handlers/routers/HEALTHCHECK.md +342 -0
  124. package/handlers/routers/auth.js +15 -0
  125. package/handlers/routers/db-migration.handler.js +29 -0
  126. package/handlers/routers/db-migration.js +326 -0
  127. package/handlers/routers/health.js +516 -0
  128. package/handlers/routers/integration-defined-routers.js +45 -0
  129. package/handlers/routers/integration-webhook-routers.js +67 -0
  130. package/handlers/routers/user.js +63 -0
  131. package/handlers/routers/websocket.js +57 -0
  132. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  133. package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
  134. package/handlers/workers/db-migration.js +352 -0
  135. package/handlers/workers/integration-defined-workers.js +27 -0
  136. package/index.js +77 -22
  137. package/integrations/WEBHOOK-QUICKSTART.md +151 -0
  138. package/integrations/index.js +12 -10
  139. package/integrations/integration-base.js +326 -55
  140. package/integrations/integration-router.js +374 -179
  141. package/integrations/options.js +1 -1
  142. package/integrations/repositories/integration-mapping-repository-documentdb.js +280 -0
  143. package/integrations/repositories/integration-mapping-repository-factory.js +57 -0
  144. package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
  145. package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
  146. package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
  147. package/integrations/repositories/integration-mapping-repository.js +156 -0
  148. package/integrations/repositories/integration-repository-documentdb.js +210 -0
  149. package/integrations/repositories/integration-repository-factory.js +51 -0
  150. package/integrations/repositories/integration-repository-interface.js +127 -0
  151. package/integrations/repositories/integration-repository-mongo.js +303 -0
  152. package/integrations/repositories/integration-repository-postgres.js +352 -0
  153. package/integrations/repositories/process-repository-documentdb.js +243 -0
  154. package/integrations/repositories/process-repository-factory.js +53 -0
  155. package/integrations/repositories/process-repository-interface.js +90 -0
  156. package/integrations/repositories/process-repository-mongo.js +190 -0
  157. package/integrations/repositories/process-repository-postgres.js +217 -0
  158. package/integrations/tests/doubles/config-capturing-integration.js +81 -0
  159. package/integrations/tests/doubles/dummy-integration-class.js +105 -0
  160. package/integrations/tests/doubles/test-integration-repository.js +99 -0
  161. package/integrations/use-cases/create-integration.js +83 -0
  162. package/integrations/use-cases/create-process.js +128 -0
  163. package/integrations/use-cases/delete-integration-for-user.js +101 -0
  164. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +72 -0
  165. package/integrations/use-cases/get-integration-for-user.js +78 -0
  166. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  167. package/integrations/use-cases/get-integration-instance.js +83 -0
  168. package/integrations/use-cases/get-integrations-for-user.js +88 -0
  169. package/integrations/use-cases/get-possible-integrations.js +27 -0
  170. package/integrations/use-cases/get-process.js +87 -0
  171. package/integrations/use-cases/index.js +19 -0
  172. package/integrations/use-cases/load-integration-context.js +71 -0
  173. package/integrations/use-cases/update-integration-messages.js +44 -0
  174. package/integrations/use-cases/update-integration-status.js +32 -0
  175. package/integrations/use-cases/update-integration.js +92 -0
  176. package/integrations/use-cases/update-process-metrics.js +201 -0
  177. package/integrations/use-cases/update-process-state.js +119 -0
  178. package/integrations/utils/map-integration-dto.js +37 -0
  179. package/jest-global-setup-noop.js +3 -0
  180. package/jest-global-teardown-noop.js +3 -0
  181. package/logs/logger.js +0 -4
  182. package/{module-plugin → modules}/entity.js +1 -1
  183. package/{module-plugin → modules}/index.js +0 -8
  184. package/modules/module-factory.js +56 -0
  185. package/modules/module.js +221 -0
  186. package/modules/repositories/module-repository-documentdb.js +307 -0
  187. package/modules/repositories/module-repository-factory.js +40 -0
  188. package/modules/repositories/module-repository-interface.js +129 -0
  189. package/modules/repositories/module-repository-mongo.js +377 -0
  190. package/modules/repositories/module-repository-postgres.js +426 -0
  191. package/modules/repositories/module-repository.js +316 -0
  192. package/modules/requester/api-key.js +52 -0
  193. package/{module-plugin → modules}/requester/requester.js +1 -0
  194. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  195. package/{module-plugin → modules}/test/mock-api/definition.js +12 -8
  196. package/modules/tests/doubles/test-module-factory.js +16 -0
  197. package/modules/tests/doubles/test-module-repository.js +39 -0
  198. package/modules/use-cases/get-entities-for-user.js +32 -0
  199. package/modules/use-cases/get-entity-options-by-id.js +71 -0
  200. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  201. package/modules/use-cases/get-module-instance-from-type.js +31 -0
  202. package/modules/use-cases/get-module.js +74 -0
  203. package/modules/use-cases/process-authorization-callback.js +133 -0
  204. package/modules/use-cases/refresh-entity-options.js +72 -0
  205. package/modules/use-cases/test-module-auth.js +72 -0
  206. package/modules/utils/map-module-dto.js +18 -0
  207. package/package.json +82 -50
  208. package/prisma-mongodb/schema.prisma +360 -0
  209. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  210. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  211. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  212. package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
  213. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  214. package/prisma-postgresql/schema.prisma +343 -0
  215. package/queues/queuer-util.js +27 -22
  216. package/syncs/manager.js +468 -443
  217. package/syncs/repositories/sync-repository-documentdb.js +240 -0
  218. package/syncs/repositories/sync-repository-factory.js +43 -0
  219. package/syncs/repositories/sync-repository-interface.js +109 -0
  220. package/syncs/repositories/sync-repository-mongo.js +239 -0
  221. package/syncs/repositories/sync-repository-postgres.js +319 -0
  222. package/syncs/sync.js +0 -1
  223. package/token/repositories/token-repository-documentdb.js +137 -0
  224. package/token/repositories/token-repository-factory.js +40 -0
  225. package/token/repositories/token-repository-interface.js +131 -0
  226. package/token/repositories/token-repository-mongo.js +219 -0
  227. package/token/repositories/token-repository-postgres.js +264 -0
  228. package/token/repositories/token-repository.js +219 -0
  229. package/types/core/index.d.ts +2 -2
  230. package/types/integrations/index.d.ts +2 -6
  231. package/types/module-plugin/index.d.ts +5 -59
  232. package/types/syncs/index.d.ts +0 -2
  233. package/user/repositories/user-repository-documentdb.js +441 -0
  234. package/user/repositories/user-repository-factory.js +52 -0
  235. package/user/repositories/user-repository-interface.js +201 -0
  236. package/user/repositories/user-repository-mongo.js +308 -0
  237. package/user/repositories/user-repository-postgres.js +360 -0
  238. package/user/tests/doubles/test-user-repository.js +72 -0
  239. package/user/use-cases/authenticate-user.js +127 -0
  240. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  241. package/user/use-cases/create-individual-user.js +61 -0
  242. package/user/use-cases/create-organization-user.js +47 -0
  243. package/user/use-cases/create-token-for-user-id.js +30 -0
  244. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  245. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  246. package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
  247. package/user/use-cases/login-user.js +122 -0
  248. package/user/user.js +125 -0
  249. package/utils/backend-path.js +38 -0
  250. package/utils/index.js +6 -0
  251. package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
  252. package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
  253. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  254. package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
  255. package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
  256. package/websocket/repositories/websocket-connection-repository.js +161 -0
  257. package/database/models/State.js +0 -9
  258. package/database/models/Token.js +0 -70
  259. package/database/mongo.js +0 -45
  260. package/encrypt/Cryptor.test.js +0 -32
  261. package/encrypt/encrypt.js +0 -132
  262. package/encrypt/encrypt.test.js +0 -1069
  263. package/errors/base-error.test.js +0 -32
  264. package/errors/fetch-error.test.js +0 -79
  265. package/errors/halt-error.test.js +0 -11
  266. package/errors/validation-errors.test.js +0 -120
  267. package/integrations/create-frigg-backend.js +0 -31
  268. package/integrations/integration-factory.js +0 -251
  269. package/integrations/integration-mapping.js +0 -43
  270. package/integrations/integration-model.js +0 -46
  271. package/integrations/integration-user.js +0 -144
  272. package/integrations/test/integration-base.test.js +0 -144
  273. package/lambda/TimeoutCatcher.test.js +0 -68
  274. package/logs/logger.test.js +0 -76
  275. package/module-plugin/auther.js +0 -393
  276. package/module-plugin/credential.js +0 -22
  277. package/module-plugin/entity-manager.js +0 -70
  278. package/module-plugin/manager.js +0 -169
  279. package/module-plugin/module-factory.js +0 -61
  280. package/module-plugin/requester/api-key.js +0 -36
  281. package/module-plugin/requester/requester.test.js +0 -28
  282. package/module-plugin/test/auther.test.js +0 -97
  283. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  284. /package/{module-plugin → modules}/requester/basic.js +0 -0
  285. /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
  286. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -0,0 +1,90 @@
1
+ /**
2
+ * ProcessRepository Interface
3
+ *
4
+ * Defines the contract for Process data access operations.
5
+ * Implementations must provide concrete methods for all operations.
6
+ *
7
+ * This interface supports the Hexagonal Architecture pattern by:
8
+ * - Defining clear boundaries between domain logic and data access
9
+ * - Allowing multiple implementations (MongoDB, PostgreSQL, in-memory)
10
+ * - Enabling dependency injection and testability
11
+ */
12
+ class ProcessRepositoryInterface {
13
+ /**
14
+ * Create a new process record
15
+ * @param {Object} processData - Process data to create
16
+ * @param {string} processData.userId - User ID
17
+ * @param {string} processData.integrationId - Integration ID
18
+ * @param {string} processData.name - Process name
19
+ * @param {string} processData.type - Process type
20
+ * @param {string} processData.state - Initial state
21
+ * @param {Object} [processData.context] - Process context
22
+ * @param {Object} [processData.results] - Process results
23
+ * @param {string[]} [processData.childProcesses] - Child process IDs
24
+ * @param {string} [processData.parentProcessId] - Parent process ID
25
+ * @returns {Promise<Object>} Created process record
26
+ */
27
+ async create(processData) {
28
+ throw new Error('Method create() must be implemented');
29
+ }
30
+
31
+ /**
32
+ * Find a process by ID
33
+ * @param {string} processId - Process ID to find
34
+ * @returns {Promise<Object|null>} Process record or null if not found
35
+ */
36
+ async findById(processId) {
37
+ throw new Error('Method findById() must be implemented');
38
+ }
39
+
40
+ /**
41
+ * Update a process record
42
+ * @param {string} processId - Process ID to update
43
+ * @param {Object} updates - Fields to update
44
+ * @returns {Promise<Object>} Updated process record
45
+ */
46
+ async update(processId, updates) {
47
+ throw new Error('Method update() must be implemented');
48
+ }
49
+
50
+ /**
51
+ * Find processes by integration and type
52
+ * @param {string} integrationId - Integration ID
53
+ * @param {string} type - Process type
54
+ * @returns {Promise<Array>} Array of process records
55
+ */
56
+ async findByIntegrationAndType(integrationId, type) {
57
+ throw new Error('Method findByIntegrationAndType() must be implemented');
58
+ }
59
+
60
+ /**
61
+ * Find active processes (not in excluded states)
62
+ * @param {string} integrationId - Integration ID
63
+ * @param {string[]} [excludeStates=['COMPLETED', 'ERROR']] - States to exclude
64
+ * @returns {Promise<Array>} Array of active process records
65
+ */
66
+ async findActiveProcesses(integrationId, excludeStates = ['COMPLETED', 'ERROR']) {
67
+ throw new Error('Method findActiveProcesses() must be implemented');
68
+ }
69
+
70
+ /**
71
+ * Find a process by name (most recent)
72
+ * @param {string} name - Process name
73
+ * @returns {Promise<Object|null>} Most recent process with given name, or null
74
+ */
75
+ async findByName(name) {
76
+ throw new Error('Method findByName() must be implemented');
77
+ }
78
+
79
+ /**
80
+ * Delete a process by ID
81
+ * @param {string} processId - Process ID to delete
82
+ * @returns {Promise<void>}
83
+ */
84
+ async deleteById(processId) {
85
+ throw new Error('Method deleteById() must be implemented');
86
+ }
87
+ }
88
+
89
+ module.exports = { ProcessRepositoryInterface };
90
+
@@ -0,0 +1,190 @@
1
+ const { prisma } = require('../../database/prisma');
2
+ const { ProcessRepositoryInterface } = require('./process-repository-interface');
3
+
4
+ /**
5
+ * MongoDB Process Repository Adapter
6
+ * Handles process persistence using Prisma with MongoDB
7
+ *
8
+ * MongoDB-specific characteristics:
9
+ * - Uses scalar fields for relations (userId, integrationId)
10
+ * - IDs are strings with @db.ObjectId
11
+ * - JSON fields for flexible context and results storage
12
+ * - Array field for childProcesses references
13
+ *
14
+ * Design Philosophy:
15
+ * - Generic Process model supports any type of long-running operation
16
+ * - Context and results stored as JSON for maximum flexibility
17
+ * - Integration-specific logic lives in use cases and services
18
+ */
19
+ class ProcessRepositoryMongo extends ProcessRepositoryInterface {
20
+ constructor() {
21
+ super();
22
+ this.prisma = prisma;
23
+ }
24
+
25
+ /**
26
+ * Create a new process record
27
+ * @param {Object} processData - Process data to create
28
+ * @returns {Promise<Object>} Created process record
29
+ */
30
+ async create(processData) {
31
+ const process = await this.prisma.process.create({
32
+ data: {
33
+ userId: processData.userId,
34
+ integrationId: processData.integrationId,
35
+ name: processData.name,
36
+ type: processData.type,
37
+ state: processData.state || 'INITIALIZING',
38
+ context: processData.context || {},
39
+ results: processData.results || {},
40
+ childProcesses: processData.childProcesses || [],
41
+ parentProcessId: processData.parentProcessId || null,
42
+ },
43
+ });
44
+
45
+ return this._toPlainObject(process);
46
+ }
47
+
48
+ /**
49
+ * Find a process by ID
50
+ * @param {string} processId - Process ID to find
51
+ * @returns {Promise<Object|null>} Process record or null if not found
52
+ */
53
+ async findById(processId) {
54
+ const process = await this.prisma.process.findUnique({
55
+ where: { id: processId },
56
+ });
57
+
58
+ return process ? this._toPlainObject(process) : null;
59
+ }
60
+
61
+ /**
62
+ * Update a process record
63
+ * @param {string} processId - Process ID to update
64
+ * @param {Object} updates - Fields to update
65
+ * @returns {Promise<Object>} Updated process record
66
+ */
67
+ async update(processId, updates) {
68
+ // Prepare update data, excluding undefined values
69
+ const updateData = {};
70
+
71
+ if (updates.state !== undefined) {
72
+ updateData.state = updates.state;
73
+ }
74
+ if (updates.context !== undefined) {
75
+ updateData.context = updates.context;
76
+ }
77
+ if (updates.results !== undefined) {
78
+ updateData.results = updates.results;
79
+ }
80
+ if (updates.childProcesses !== undefined) {
81
+ updateData.childProcesses = updates.childProcesses;
82
+ }
83
+ if (updates.parentProcessId !== undefined) {
84
+ updateData.parentProcessId = updates.parentProcessId;
85
+ }
86
+
87
+ const process = await this.prisma.process.update({
88
+ where: { id: processId },
89
+ data: updateData,
90
+ });
91
+
92
+ return this._toPlainObject(process);
93
+ }
94
+
95
+ /**
96
+ * Find processes by integration and type
97
+ * @param {string} integrationId - Integration ID
98
+ * @param {string} type - Process type
99
+ * @returns {Promise<Array>} Array of process records
100
+ */
101
+ async findByIntegrationAndType(integrationId, type) {
102
+ const processes = await this.prisma.process.findMany({
103
+ where: {
104
+ integrationId,
105
+ type,
106
+ },
107
+ orderBy: {
108
+ createdAt: 'desc',
109
+ },
110
+ });
111
+
112
+ return processes.map((p) => this._toPlainObject(p));
113
+ }
114
+
115
+ /**
116
+ * Find active processes (not in excluded states)
117
+ * @param {string} integrationId - Integration ID
118
+ * @param {string[]} [excludeStates=['COMPLETED', 'ERROR']] - States to exclude
119
+ * @returns {Promise<Array>} Array of active process records
120
+ */
121
+ async findActiveProcesses(integrationId, excludeStates = ['COMPLETED', 'ERROR']) {
122
+ const processes = await this.prisma.process.findMany({
123
+ where: {
124
+ integrationId,
125
+ state: {
126
+ notIn: excludeStates,
127
+ },
128
+ },
129
+ orderBy: {
130
+ createdAt: 'desc',
131
+ },
132
+ });
133
+
134
+ return processes.map((p) => this._toPlainObject(p));
135
+ }
136
+
137
+ /**
138
+ * Find a process by name (most recent)
139
+ * @param {string} name - Process name
140
+ * @returns {Promise<Object|null>} Most recent process with given name, or null
141
+ */
142
+ async findByName(name) {
143
+ const process = await this.prisma.process.findFirst({
144
+ where: { name },
145
+ orderBy: {
146
+ createdAt: 'desc',
147
+ },
148
+ });
149
+
150
+ return process ? this._toPlainObject(process) : null;
151
+ }
152
+
153
+ /**
154
+ * Delete a process by ID
155
+ * @param {string} processId - Process ID to delete
156
+ * @returns {Promise<void>}
157
+ */
158
+ async deleteById(processId) {
159
+ await this.prisma.process.delete({
160
+ where: { id: processId },
161
+ });
162
+ }
163
+
164
+ /**
165
+ * Convert Prisma model to plain JavaScript object
166
+ * Ensures consistent API across repository implementations
167
+ * @private
168
+ * @param {Object} process - Prisma process model
169
+ * @returns {Object} Plain process object
170
+ */
171
+ _toPlainObject(process) {
172
+ return {
173
+ id: process.id,
174
+ userId: process.userId,
175
+ integrationId: process.integrationId,
176
+ name: process.name,
177
+ type: process.type,
178
+ state: process.state,
179
+ context: process.context,
180
+ results: process.results,
181
+ childProcesses: process.childProcesses,
182
+ parentProcessId: process.parentProcessId,
183
+ createdAt: process.createdAt,
184
+ updatedAt: process.updatedAt,
185
+ };
186
+ }
187
+ }
188
+
189
+ module.exports = { ProcessRepositoryMongo };
190
+
@@ -0,0 +1,217 @@
1
+ const { prisma } = require('../../database/prisma');
2
+ const {
3
+ ProcessRepositoryInterface,
4
+ } = require('./process-repository-interface');
5
+
6
+ /**
7
+ * PostgreSQL Process Repository Adapter
8
+ * Handles process persistence using Prisma with PostgreSQL
9
+ *
10
+ * PostgreSQL-specific characteristics:
11
+ * - Uses foreign key constraints for relations
12
+ * - JSONB type for context and results (efficient querying)
13
+ * - Array type for childProcesses references
14
+ * - Transactional support available if needed
15
+ *
16
+ * Design Philosophy:
17
+ * - Same interface as MongoDB repository
18
+ * - Prisma abstracts away most database-specific details
19
+ * - Minor differences in JSON handling internally managed by Prisma
20
+ */
21
+ class ProcessRepositoryPostgres extends ProcessRepositoryInterface {
22
+ constructor() {
23
+ super();
24
+ this.prisma = prisma;
25
+ }
26
+
27
+ /**
28
+ * Convert string ID to integer for PostgreSQL queries
29
+ * @private
30
+ * @param {string|number|null|undefined} id - ID to convert
31
+ * @returns {number|null|undefined} Integer ID or null/undefined
32
+ * @throws {Error} If ID cannot be converted to integer
33
+ */
34
+ _convertId(id) {
35
+ if (id === null || id === undefined) return id;
36
+ const parsed = parseInt(id, 10);
37
+ if (isNaN(parsed)) {
38
+ throw new Error(`Invalid ID: ${id} cannot be converted to integer`);
39
+ }
40
+ return parsed;
41
+ }
42
+
43
+ /**
44
+ * Create a new process record
45
+ * @param {Object} processData - Process data to create
46
+ * @returns {Promise<Object>} Created process record
47
+ */
48
+ async create(processData) {
49
+ const process = await this.prisma.process.create({
50
+ data: {
51
+ userId: this._convertId(processData.userId),
52
+ integrationId: this._convertId(processData.integrationId),
53
+ name: processData.name,
54
+ type: processData.type,
55
+ state: processData.state || 'INITIALIZING',
56
+ context: processData.context || {},
57
+ results: processData.results || {},
58
+ parentProcessId: this._convertId(processData.parentProcessId),
59
+ },
60
+ });
61
+
62
+ return this._toPlainObject(process);
63
+ }
64
+
65
+ /**
66
+ * Find a process by ID
67
+ * @param {string} processId - Process ID to find
68
+ * @returns {Promise<Object|null>} Process record or null if not found
69
+ */
70
+ async findById(processId) {
71
+ const process = await this.prisma.process.findUnique({
72
+ where: { id: this._convertId(processId) },
73
+ });
74
+
75
+ return process ? this._toPlainObject(process) : null;
76
+ }
77
+
78
+ /**
79
+ * Update a process record
80
+ * @param {string} processId - Process ID to update
81
+ * @param {Object} updates - Fields to update
82
+ * @returns {Promise<Object>} Updated process record
83
+ */
84
+ async update(processId, updates) {
85
+ // Prepare update data, excluding undefined values
86
+ const updateData = {};
87
+
88
+ if (updates.state !== undefined) {
89
+ updateData.state = updates.state;
90
+ }
91
+ if (updates.context !== undefined) {
92
+ updateData.context = updates.context;
93
+ }
94
+ if (updates.results !== undefined) {
95
+ updateData.results = updates.results;
96
+ }
97
+ if (updates.parentProcessId !== undefined) {
98
+ updateData.parentProcessId = this._convertId(
99
+ updates.parentProcessId
100
+ );
101
+ }
102
+
103
+ const process = await this.prisma.process.update({
104
+ where: { id: this._convertId(processId) },
105
+ data: updateData,
106
+ });
107
+
108
+ return this._toPlainObject(process);
109
+ }
110
+
111
+ /**
112
+ * Find processes by integration and type
113
+ * @param {string} integrationId - Integration ID
114
+ * @param {string} type - Process type
115
+ * @returns {Promise<Array>} Array of process records
116
+ */
117
+ async findByIntegrationAndType(integrationId, type) {
118
+ const processes = await this.prisma.process.findMany({
119
+ where: {
120
+ integrationId: this._convertId(integrationId),
121
+ type,
122
+ },
123
+ orderBy: {
124
+ createdAt: 'desc',
125
+ },
126
+ });
127
+
128
+ return processes.map((p) => this._toPlainObject(p));
129
+ }
130
+
131
+ /**
132
+ * Find active processes (not in excluded states)
133
+ * @param {string} integrationId - Integration ID
134
+ * @param {string[]} [excludeStates=['COMPLETED', 'ERROR']] - States to exclude
135
+ * @returns {Promise<Array>} Array of active process records
136
+ */
137
+ async findActiveProcesses(
138
+ integrationId,
139
+ excludeStates = ['COMPLETED', 'ERROR']
140
+ ) {
141
+ const processes = await this.prisma.process.findMany({
142
+ where: {
143
+ integrationId: this._convertId(integrationId),
144
+ state: {
145
+ notIn: excludeStates,
146
+ },
147
+ },
148
+ orderBy: {
149
+ createdAt: 'desc',
150
+ },
151
+ });
152
+
153
+ return processes.map((p) => this._toPlainObject(p));
154
+ }
155
+
156
+ /**
157
+ * Find a process by name (most recent)
158
+ * @param {string} name - Process name
159
+ * @returns {Promise<Object|null>} Most recent process with given name, or null
160
+ */
161
+ async findByName(name) {
162
+ const process = await this.prisma.process.findFirst({
163
+ where: { name },
164
+ orderBy: {
165
+ createdAt: 'desc',
166
+ },
167
+ });
168
+
169
+ return process ? this._toPlainObject(process) : null;
170
+ }
171
+
172
+ /**
173
+ * Delete a process by ID
174
+ * @param {string} processId - Process ID to delete
175
+ * @returns {Promise<void>}
176
+ */
177
+ async deleteById(processId) {
178
+ await this.prisma.process.delete({
179
+ where: { id: this._convertId(processId) },
180
+ });
181
+ }
182
+
183
+ /**
184
+ * Convert Prisma model to plain JavaScript object
185
+ * Ensures consistent API across repository implementations
186
+ * @private
187
+ * @param {Object} process - Prisma process model
188
+ * @returns {Object} Plain process object
189
+ */
190
+ _toPlainObject(process) {
191
+ return {
192
+ id: String(process.id),
193
+ userId: String(process.userId),
194
+ integrationId: String(process.integrationId),
195
+ name: process.name,
196
+ type: process.type,
197
+ state: process.state,
198
+ context: process.context,
199
+ results: process.results,
200
+ childProcesses: Array.isArray(process.childProcesses)
201
+ ? process.childProcesses.length > 0 &&
202
+ typeof process.childProcesses[0] === 'object' &&
203
+ process.childProcesses[0] !== null
204
+ ? process.childProcesses.map((child) => String(child.id))
205
+ : process.childProcesses
206
+ : [],
207
+ parentProcessId:
208
+ process.parentProcessId !== null
209
+ ? String(process.parentProcessId)
210
+ : null,
211
+ createdAt: process.createdAt,
212
+ updatedAt: process.updatedAt,
213
+ };
214
+ }
215
+ }
216
+
217
+ module.exports = { ProcessRepositoryPostgres };
@@ -0,0 +1,81 @@
1
+ const { IntegrationBase } = require('../../integration-base');
2
+
3
+ class ConfigCapturingModule {
4
+ static definition = {
5
+ getName: () => 'config-capturing-module'
6
+ };
7
+ }
8
+
9
+ class ConfigCapturingIntegration extends IntegrationBase {
10
+ static Definition = {
11
+ name: 'config-capturing',
12
+ version: '1.0.0',
13
+ modules: {
14
+ primary: ConfigCapturingModule
15
+ },
16
+ display: {
17
+ label: 'Config Capturing Integration',
18
+ description: 'Test double for capturing config state during updates',
19
+ detailsUrl: 'https://example.com',
20
+ icon: 'test-icon'
21
+ }
22
+ };
23
+
24
+ static _capturedOnUpdateState = null;
25
+
26
+ static resetCaptures() {
27
+ this._capturedOnUpdateState = null;
28
+ }
29
+
30
+ static getCapturedOnUpdateState() {
31
+ return this._capturedOnUpdateState;
32
+ }
33
+
34
+ constructor(params) {
35
+ super(params);
36
+ this.integrationRepository = {
37
+ updateIntegrationById: jest.fn().mockResolvedValue({}),
38
+ findIntegrationById: jest.fn().mockResolvedValue({}),
39
+ };
40
+ this.updateIntegrationStatus = {
41
+ execute: jest.fn().mockResolvedValue({})
42
+ };
43
+ this.updateIntegrationMessages = {
44
+ execute: jest.fn().mockResolvedValue({})
45
+ };
46
+ }
47
+
48
+ async initialize() {
49
+ this.registerEventHandlers();
50
+ }
51
+
52
+ async onUpdate(params) {
53
+ ConfigCapturingIntegration._capturedOnUpdateState = {
54
+ thisConfig: JSON.parse(JSON.stringify(this.config)),
55
+ paramsConfig: params.config
56
+ };
57
+
58
+ this.config = this._deepMerge(this.config, params.config);
59
+ }
60
+
61
+ _deepMerge(target, source) {
62
+ const result = { ...target };
63
+ for (const key of Object.keys(source)) {
64
+ if (
65
+ source[key] !== null &&
66
+ typeof source[key] === 'object' &&
67
+ !Array.isArray(source[key]) &&
68
+ target[key] !== null &&
69
+ typeof target[key] === 'object' &&
70
+ !Array.isArray(target[key])
71
+ ) {
72
+ result[key] = this._deepMerge(target[key], source[key]);
73
+ } else {
74
+ result[key] = source[key];
75
+ }
76
+ }
77
+ return result;
78
+ }
79
+ }
80
+
81
+ module.exports = { ConfigCapturingIntegration };
@@ -0,0 +1,105 @@
1
+ const { IntegrationBase } = require('../../integration-base');
2
+
3
+ class DummyModule {
4
+ static definition = {
5
+ getName: () => 'dummy'
6
+ };
7
+ }
8
+
9
+ class DummyIntegration extends IntegrationBase {
10
+ static Definition = {
11
+ name: 'dummy',
12
+ version: '1.0.0',
13
+ modules: {
14
+ dummy: DummyModule
15
+ },
16
+ display: {
17
+ label: 'Dummy Integration',
18
+ description: 'A dummy integration for testing',
19
+ detailsUrl: 'https://example.com',
20
+ icon: 'dummy-icon'
21
+ }
22
+ };
23
+
24
+ static getOptionDetails() {
25
+ return {
26
+ name: this.Definition.name,
27
+ version: this.Definition.version,
28
+ display: this.Definition.display
29
+ };
30
+ }
31
+
32
+ constructor(params) {
33
+ super(params);
34
+ this.sendSpy = jest.fn();
35
+ this.eventCallHistory = [];
36
+ this.events = {};
37
+
38
+ this.integrationRepository = {
39
+ updateIntegrationById: jest.fn().mockResolvedValue({}),
40
+ findIntegrationById: jest.fn().mockResolvedValue({}),
41
+ };
42
+
43
+ this.updateIntegrationStatus = {
44
+ execute: jest.fn().mockResolvedValue({})
45
+ };
46
+
47
+ this.updateIntegrationMessages = {
48
+ execute: jest.fn().mockResolvedValue({})
49
+ };
50
+ }
51
+
52
+ async loadDynamicUserActions() {
53
+ return {};
54
+ }
55
+
56
+ async send(event, data) {
57
+ this.sendSpy(event, data);
58
+ this.eventCallHistory.push({ event, data, timestamp: Date.now() });
59
+ if (event === 'ON_UPDATE') {
60
+ await this.onUpdate(data);
61
+ }
62
+ return { event, data };
63
+ }
64
+
65
+ async initialize() {
66
+ return;
67
+ }
68
+
69
+ async onCreate({ integrationId }) {
70
+ return;
71
+ }
72
+
73
+ async onUpdate(params) {
74
+ this.config = this._deepMerge(this.config, params.config);
75
+ }
76
+
77
+ _deepMerge(target, source) {
78
+ const result = { ...target };
79
+ for (const key of Object.keys(source)) {
80
+ if (
81
+ source[key] !== null &&
82
+ typeof source[key] === 'object' &&
83
+ !Array.isArray(source[key]) &&
84
+ target[key] !== null &&
85
+ typeof target[key] === 'object' &&
86
+ !Array.isArray(target[key])
87
+ ) {
88
+ result[key] = this._deepMerge(target[key], source[key]);
89
+ } else {
90
+ result[key] = source[key];
91
+ }
92
+ }
93
+ return result;
94
+ }
95
+
96
+ async onDelete(params) {
97
+ return;
98
+ }
99
+
100
+ getConfig() {
101
+ return this.config || {};
102
+ }
103
+ }
104
+
105
+ module.exports = { DummyIntegration };