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

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 (285) 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/dummy-integration-class.js +83 -0
  159. package/integrations/tests/doubles/test-integration-repository.js +99 -0
  160. package/integrations/use-cases/create-integration.js +83 -0
  161. package/integrations/use-cases/create-process.js +128 -0
  162. package/integrations/use-cases/delete-integration-for-user.js +101 -0
  163. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +72 -0
  164. package/integrations/use-cases/get-integration-for-user.js +78 -0
  165. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  166. package/integrations/use-cases/get-integration-instance.js +83 -0
  167. package/integrations/use-cases/get-integrations-for-user.js +88 -0
  168. package/integrations/use-cases/get-possible-integrations.js +27 -0
  169. package/integrations/use-cases/get-process.js +87 -0
  170. package/integrations/use-cases/index.js +19 -0
  171. package/integrations/use-cases/load-integration-context.js +71 -0
  172. package/integrations/use-cases/update-integration-messages.js +44 -0
  173. package/integrations/use-cases/update-integration-status.js +32 -0
  174. package/integrations/use-cases/update-integration.js +93 -0
  175. package/integrations/use-cases/update-process-metrics.js +201 -0
  176. package/integrations/use-cases/update-process-state.js +119 -0
  177. package/integrations/utils/map-integration-dto.js +37 -0
  178. package/jest-global-setup-noop.js +3 -0
  179. package/jest-global-teardown-noop.js +3 -0
  180. package/logs/logger.js +0 -4
  181. package/{module-plugin → modules}/entity.js +1 -1
  182. package/{module-plugin → modules}/index.js +0 -8
  183. package/modules/module-factory.js +56 -0
  184. package/modules/module.js +221 -0
  185. package/modules/repositories/module-repository-documentdb.js +307 -0
  186. package/modules/repositories/module-repository-factory.js +40 -0
  187. package/modules/repositories/module-repository-interface.js +129 -0
  188. package/modules/repositories/module-repository-mongo.js +377 -0
  189. package/modules/repositories/module-repository-postgres.js +426 -0
  190. package/modules/repositories/module-repository.js +316 -0
  191. package/modules/requester/api-key.js +52 -0
  192. package/{module-plugin → modules}/requester/requester.js +1 -0
  193. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  194. package/{module-plugin → modules}/test/mock-api/definition.js +12 -8
  195. package/modules/tests/doubles/test-module-factory.js +16 -0
  196. package/modules/tests/doubles/test-module-repository.js +39 -0
  197. package/modules/use-cases/get-entities-for-user.js +32 -0
  198. package/modules/use-cases/get-entity-options-by-id.js +71 -0
  199. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  200. package/modules/use-cases/get-module-instance-from-type.js +31 -0
  201. package/modules/use-cases/get-module.js +74 -0
  202. package/modules/use-cases/process-authorization-callback.js +133 -0
  203. package/modules/use-cases/refresh-entity-options.js +72 -0
  204. package/modules/use-cases/test-module-auth.js +72 -0
  205. package/modules/utils/map-module-dto.js +18 -0
  206. package/package.json +82 -50
  207. package/prisma-mongodb/schema.prisma +360 -0
  208. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  209. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  210. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  211. package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
  212. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  213. package/prisma-postgresql/schema.prisma +343 -0
  214. package/queues/queuer-util.js +27 -22
  215. package/syncs/manager.js +468 -443
  216. package/syncs/repositories/sync-repository-documentdb.js +240 -0
  217. package/syncs/repositories/sync-repository-factory.js +43 -0
  218. package/syncs/repositories/sync-repository-interface.js +109 -0
  219. package/syncs/repositories/sync-repository-mongo.js +239 -0
  220. package/syncs/repositories/sync-repository-postgres.js +319 -0
  221. package/syncs/sync.js +0 -1
  222. package/token/repositories/token-repository-documentdb.js +137 -0
  223. package/token/repositories/token-repository-factory.js +40 -0
  224. package/token/repositories/token-repository-interface.js +131 -0
  225. package/token/repositories/token-repository-mongo.js +219 -0
  226. package/token/repositories/token-repository-postgres.js +264 -0
  227. package/token/repositories/token-repository.js +219 -0
  228. package/types/core/index.d.ts +2 -2
  229. package/types/integrations/index.d.ts +2 -6
  230. package/types/module-plugin/index.d.ts +5 -59
  231. package/types/syncs/index.d.ts +0 -2
  232. package/user/repositories/user-repository-documentdb.js +441 -0
  233. package/user/repositories/user-repository-factory.js +52 -0
  234. package/user/repositories/user-repository-interface.js +201 -0
  235. package/user/repositories/user-repository-mongo.js +308 -0
  236. package/user/repositories/user-repository-postgres.js +360 -0
  237. package/user/tests/doubles/test-user-repository.js +72 -0
  238. package/user/use-cases/authenticate-user.js +127 -0
  239. package/user/use-cases/authenticate-with-shared-secret.js +48 -0
  240. package/user/use-cases/create-individual-user.js +61 -0
  241. package/user/use-cases/create-organization-user.js +47 -0
  242. package/user/use-cases/create-token-for-user-id.js +30 -0
  243. package/user/use-cases/get-user-from-adopter-jwt.js +149 -0
  244. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  245. package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
  246. package/user/use-cases/login-user.js +122 -0
  247. package/user/user.js +125 -0
  248. package/utils/backend-path.js +38 -0
  249. package/utils/index.js +6 -0
  250. package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
  251. package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
  252. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  253. package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
  254. package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
  255. package/websocket/repositories/websocket-connection-repository.js +161 -0
  256. package/database/models/State.js +0 -9
  257. package/database/models/Token.js +0 -70
  258. package/database/mongo.js +0 -45
  259. package/encrypt/Cryptor.test.js +0 -32
  260. package/encrypt/encrypt.js +0 -132
  261. package/encrypt/encrypt.test.js +0 -1069
  262. package/errors/base-error.test.js +0 -32
  263. package/errors/fetch-error.test.js +0 -79
  264. package/errors/halt-error.test.js +0 -11
  265. package/errors/validation-errors.test.js +0 -120
  266. package/integrations/create-frigg-backend.js +0 -31
  267. package/integrations/integration-factory.js +0 -251
  268. package/integrations/integration-mapping.js +0 -43
  269. package/integrations/integration-model.js +0 -46
  270. package/integrations/integration-user.js +0 -144
  271. package/integrations/test/integration-base.test.js +0 -144
  272. package/lambda/TimeoutCatcher.test.js +0 -68
  273. package/logs/logger.test.js +0 -76
  274. package/module-plugin/auther.js +0 -393
  275. package/module-plugin/credential.js +0 -22
  276. package/module-plugin/entity-manager.js +0 -70
  277. package/module-plugin/manager.js +0 -169
  278. package/module-plugin/module-factory.js +0 -61
  279. package/module-plugin/requester/api-key.js +0 -36
  280. package/module-plugin/requester/requester.test.js +0 -28
  281. package/module-plugin/test/auther.test.js +0 -97
  282. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  283. /package/{module-plugin → modules}/requester/basic.js +0 -0
  284. /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
  285. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -0,0 +1,352 @@
1
+ const { prisma } = require('../../database/prisma');
2
+ const {
3
+ IntegrationRepositoryInterface,
4
+ } = require('./integration-repository-interface');
5
+
6
+ /**
7
+ * PostgreSQL Integration Repository Adapter
8
+ * Handles integration persistence using Prisma with PostgreSQL
9
+ *
10
+ * PostgreSQL-specific characteristics:
11
+ * - Uses nested relations for foreign keys (user, entities)
12
+ * - Uses Int IDs with autoincrement
13
+ * - Requires ID conversion: String (app layer) ↔ Int (database)
14
+ * - All returned IDs are converted to strings for application layer consistency
15
+ * - Implicit join tables for many-to-many relationships (_EntityToIntegration)
16
+ * - Uses connect/disconnect syntax for relations
17
+ */
18
+ class IntegrationRepositoryPostgres extends IntegrationRepositoryInterface {
19
+ constructor() {
20
+ super();
21
+ this.prisma = prisma;
22
+ }
23
+
24
+ /**
25
+ * Convert string ID to integer for PostgreSQL queries
26
+ * @private
27
+ * @param {string|number|null|undefined} id - ID to convert
28
+ * @returns {number|null|undefined} Integer ID or null/undefined
29
+ * @throws {Error} If ID cannot be converted to integer
30
+ */
31
+ _convertId(id) {
32
+ if (id === null || id === undefined) return id;
33
+ const parsed = parseInt(id, 10);
34
+ if (isNaN(parsed)) {
35
+ throw new Error(`Invalid ID: ${id} cannot be converted to integer`);
36
+ }
37
+ return parsed;
38
+ }
39
+
40
+ /**
41
+ * Convert integration object IDs to strings
42
+ * @private
43
+ * @param {Object|null} integration - Integration object from database
44
+ * @returns {Object|null} Integration with string IDs
45
+ */
46
+ _convertIntegrationIds(integration) {
47
+ if (!integration) return integration;
48
+ return {
49
+ ...integration,
50
+ id: integration.id?.toString(),
51
+ userId: integration.userId?.toString(),
52
+ entities: integration.entities?.map(e => ({
53
+ ...e,
54
+ id: e.id?.toString(),
55
+ userId: e.userId?.toString(),
56
+ credentialId: e.credentialId?.toString()
57
+ }))
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Find all integrations for a user
63
+ *
64
+ * @param {string} userId - User ID (string from application layer)
65
+ * @returns {Promise<Array>} Array of integration objects with string IDs
66
+ */
67
+ async findIntegrationsByUserId(userId) {
68
+ const intUserId = this._convertId(userId);
69
+ const integrations = await this.prisma.integration.findMany({
70
+ where: { userId: intUserId },
71
+ include: {
72
+ entities: true,
73
+ },
74
+ });
75
+
76
+ // Map to domain objects with string IDs
77
+ return integrations.map((integration) => {
78
+ const converted = this._convertIntegrationIds(integration);
79
+ return {
80
+ id: converted.id,
81
+ entitiesIds: converted.entities.map((e) => e.id),
82
+ userId: converted.userId,
83
+ config: converted.config,
84
+ version: converted.version,
85
+ status: converted.status,
86
+ messages: converted.messages,
87
+ };
88
+ });
89
+ }
90
+
91
+ /**
92
+ * Delete integration by ID
93
+ *
94
+ * @param {string} integrationId - Integration ID (string from application layer)
95
+ * @returns {Promise<Object>} Deletion result
96
+ */
97
+ async deleteIntegrationById(integrationId) {
98
+ const intId = this._convertId(integrationId);
99
+ await this.prisma.integration.delete({
100
+ where: { id: intId },
101
+ });
102
+
103
+ // Return Mongoose-compatible result
104
+ return { acknowledged: true, deletedCount: 1 };
105
+ }
106
+
107
+ /**
108
+ * Find integration by name
109
+ *
110
+ * @param {string} name - Integration type name
111
+ * @returns {Promise<Object>} Integration object with string IDs
112
+ */
113
+ async findIntegrationByName(name) {
114
+ const integration = await this.prisma.integration.findFirst({
115
+ where: {
116
+ config: {
117
+ path: ['type'],
118
+ equals: name,
119
+ },
120
+ },
121
+ include: {
122
+ entities: true,
123
+ },
124
+ });
125
+
126
+ if (!integration) {
127
+ throw new Error(`Integration with name ${name} not found`);
128
+ }
129
+
130
+ const converted = this._convertIntegrationIds(integration);
131
+ return {
132
+ id: converted.id,
133
+ entitiesIds: converted.entities.map((e) => e.id),
134
+ userId: converted.userId,
135
+ config: converted.config,
136
+ version: converted.version,
137
+ status: converted.status,
138
+ messages: converted.messages,
139
+ };
140
+ }
141
+
142
+ /**
143
+ * Find integration by ID
144
+ *
145
+ * @param {string} id - Integration ID (string from application layer)
146
+ * @returns {Promise<Object>} Integration object with string IDs
147
+ */
148
+ async findIntegrationById(id) {
149
+ const intId = this._convertId(id);
150
+ const integration = await this.prisma.integration.findUnique({
151
+ where: { id: intId },
152
+ include: {
153
+ entities: true,
154
+ },
155
+ });
156
+
157
+ if (!integration) {
158
+ throw new Error(`Integration with id ${id} not found`);
159
+ }
160
+
161
+ const converted = this._convertIntegrationIds(integration);
162
+ return {
163
+ id: converted.id,
164
+ entitiesIds: converted.entities.map((e) => e.id),
165
+ userId: converted.userId,
166
+ config: converted.config,
167
+ version: converted.version,
168
+ status: converted.status,
169
+ messages: converted.messages,
170
+ };
171
+ }
172
+
173
+ /**
174
+ * Update integration status
175
+ *
176
+ * @param {string} integrationId - Integration ID (string from application layer)
177
+ * @param {string} status - New status
178
+ * @returns {Promise<boolean>} Success indicator
179
+ */
180
+ async updateIntegrationStatus(integrationId, status) {
181
+ const intId = this._convertId(integrationId);
182
+ await this.prisma.integration.update({
183
+ where: { id: intId },
184
+ data: { status },
185
+ });
186
+
187
+ return true; // Mongoose compatibility
188
+ }
189
+
190
+ /**
191
+ * Update integration messages
192
+ *
193
+ * @param {string} integrationId - Integration ID (string from application layer)
194
+ * @param {string} messageType - Type of message (errors, warnings, info, logs)
195
+ * @param {string} messageTitle - Message title
196
+ * @param {string} messageBody - Message body
197
+ * @param {Date} messageTimestamp - Message timestamp
198
+ * @returns {Promise<boolean>} Success indicator
199
+ */
200
+ async updateIntegrationMessages(
201
+ integrationId,
202
+ messageType,
203
+ messageTitle,
204
+ messageBody,
205
+ messageTimestamp
206
+ ) {
207
+ const intId = this._convertId(integrationId);
208
+
209
+ // Get current integration
210
+ const integration = await this.prisma.integration.findUnique({
211
+ where: { id: intId },
212
+ });
213
+
214
+ if (!integration) {
215
+ throw new Error(`Integration ${integrationId} not found`);
216
+ }
217
+
218
+ // Parse existing messages (JSON field)
219
+ const messages = integration.messages || {};
220
+ const messageArray = Array.isArray(messages[messageType])
221
+ ? messages[messageType]
222
+ : [];
223
+
224
+ // Add new message
225
+ messageArray.push({
226
+ title: messageTitle,
227
+ message: messageBody,
228
+ timestamp: messageTimestamp,
229
+ });
230
+
231
+ // Update messages
232
+ await this.prisma.integration.update({
233
+ where: { id: intId },
234
+ data: {
235
+ [messageType]: messageArray,
236
+ },
237
+ });
238
+
239
+ return true; // Mongoose compatibility
240
+ }
241
+
242
+ /**
243
+ * Create a new integration
244
+ *
245
+ * PostgreSQL-specific: Uses nested relations with connect syntax
246
+ *
247
+ * @param {Array<string>} entities - Array of entity IDs (strings from application layer)
248
+ * @param {string} userId - User ID (string from application layer)
249
+ * @param {Object} config - Integration configuration
250
+ * @returns {Promise<Object>} Created integration object with string IDs
251
+ */
252
+ async createIntegration(entities, userId, config) {
253
+ const data = {
254
+ config,
255
+ version: '0.0.0',
256
+ };
257
+
258
+ // PostgreSQL: use nested relations with ID conversion
259
+ if (userId) {
260
+ data.user = { connect: { id: this._convertId(userId) } };
261
+ }
262
+ if (entities && entities.length > 0) {
263
+ data.entities = {
264
+ connect: entities.map((id) => ({ id: this._convertId(id) })),
265
+ };
266
+ }
267
+
268
+ const integration = await this.prisma.integration.create({
269
+ data,
270
+ include: {
271
+ entities: true,
272
+ },
273
+ });
274
+
275
+ const converted = this._convertIntegrationIds(integration);
276
+ return {
277
+ id: converted.id,
278
+ entitiesIds: converted.entities.map((e) => e.id),
279
+ userId: converted.userId,
280
+ config: converted.config,
281
+ version: converted.version,
282
+ status: converted.status,
283
+ messages: converted.messages,
284
+ };
285
+ }
286
+
287
+ /**
288
+ * Find integration by user ID (returns single integration)
289
+ *
290
+ * @param {string} userId - User ID (string from application layer)
291
+ * @returns {Promise<Object|null>} Integration object with string IDs or null
292
+ */
293
+ async findIntegrationByUserId(userId) {
294
+ const intUserId = this._convertId(userId);
295
+ const integration = await this.prisma.integration.findFirst({
296
+ where: { userId: intUserId },
297
+ include: {
298
+ entities: true,
299
+ },
300
+ });
301
+
302
+ if (!integration) {
303
+ return null;
304
+ }
305
+
306
+ const converted = this._convertIntegrationIds(integration);
307
+ return {
308
+ id: converted.id,
309
+ entitiesIds: converted.entities.map((e) => e.id),
310
+ userId: converted.userId,
311
+ config: converted.config,
312
+ version: converted.version,
313
+ status: converted.status,
314
+ messages: converted.messages,
315
+ };
316
+ }
317
+
318
+ /**
319
+ * Update integration configuration
320
+ *
321
+ * @param {string} integrationId - Integration ID (string from application layer)
322
+ * @param {Object} config - Updated configuration object
323
+ * @returns {Promise<Object>} Updated integration object with string IDs
324
+ */
325
+ async updateIntegrationConfig(integrationId, config) {
326
+ if (config === null || config === undefined) {
327
+ throw new Error('Config parameter is required');
328
+ }
329
+
330
+ const intId = this._convertId(integrationId);
331
+ const integration = await this.prisma.integration.update({
332
+ where: { id: intId },
333
+ data: { config },
334
+ include: {
335
+ entities: true,
336
+ },
337
+ });
338
+
339
+ const converted = this._convertIntegrationIds(integration);
340
+ return {
341
+ id: converted.id,
342
+ entitiesIds: converted.entities.map((e) => e.id),
343
+ userId: converted.userId,
344
+ config: converted.config,
345
+ version: converted.version,
346
+ status: converted.status,
347
+ messages: converted.messages,
348
+ };
349
+ }
350
+ }
351
+
352
+ module.exports = { IntegrationRepositoryPostgres };
@@ -0,0 +1,243 @@
1
+ const { prisma } = require('../../database/prisma');
2
+ const {
3
+ toObjectId,
4
+ fromObjectId,
5
+ findMany,
6
+ findOne,
7
+ insertOne,
8
+ updateOne,
9
+ deleteOne,
10
+ } = require('../../database/documentdb-utils');
11
+ const {
12
+ ProcessRepositoryInterface,
13
+ } = require('./process-repository-interface');
14
+ const {
15
+ DocumentDBEncryptionService,
16
+ } = require('../../database/documentdb-encryption-service');
17
+
18
+ class ProcessRepositoryDocumentDB extends ProcessRepositoryInterface {
19
+ constructor() {
20
+ super();
21
+ this.prisma = prisma;
22
+ this.encryptionService = new DocumentDBEncryptionService();
23
+ }
24
+
25
+ async create(processData) {
26
+ const now = new Date();
27
+ const plainDocument = {
28
+ userId: toObjectId(processData.userId),
29
+ integrationId: toObjectId(processData.integrationId),
30
+ name: processData.name,
31
+ type: processData.type,
32
+ state: processData.state || 'INITIALIZING',
33
+ context: processData.context || {},
34
+ results: processData.results || {},
35
+ childProcesses: (processData.childProcesses || [])
36
+ .map((id) => toObjectId(id))
37
+ .filter(Boolean),
38
+ parentProcessId: processData.parentProcessId
39
+ ? toObjectId(processData.parentProcessId)
40
+ : null,
41
+ createdAt: now,
42
+ updatedAt: now,
43
+ };
44
+
45
+ const encryptedDocument = await this.encryptionService.encryptFields(
46
+ 'Process',
47
+ plainDocument
48
+ );
49
+
50
+ const insertedId = await insertOne(
51
+ this.prisma,
52
+ 'Process',
53
+ encryptedDocument
54
+ );
55
+
56
+ const created = await findOne(this.prisma, 'Process', {
57
+ _id: insertedId,
58
+ });
59
+ if (!created) {
60
+ console.error(
61
+ '[ProcessRepositoryDocumentDB] Process not found after insert',
62
+ {
63
+ insertedId: fromObjectId(insertedId),
64
+ processData: {
65
+ userId: processData.userId,
66
+ integrationId: processData.integrationId,
67
+ name: processData.name,
68
+ type: processData.type,
69
+ },
70
+ }
71
+ );
72
+ throw new Error(
73
+ 'Failed to create process: Document not found after insert. ' +
74
+ 'This indicates a database consistency issue.'
75
+ );
76
+ }
77
+ const decryptedProcess = await this.encryptionService.decryptFields(
78
+ 'Process',
79
+ created
80
+ );
81
+ return this._mapProcess(decryptedProcess);
82
+ }
83
+
84
+ async findById(processId) {
85
+ const objectId = toObjectId(processId);
86
+ if (!objectId) return null;
87
+ const doc = await findOne(this.prisma, 'Process', { _id: objectId });
88
+ if (!doc) return null;
89
+
90
+ const decryptedProcess = await this.encryptionService.decryptFields(
91
+ 'Process',
92
+ doc
93
+ );
94
+ return this._mapProcess(decryptedProcess);
95
+ }
96
+
97
+ async update(processId, updates) {
98
+ const objectId = toObjectId(processId);
99
+ if (!objectId) return null;
100
+
101
+ const existing = await findOne(this.prisma, 'Process', {
102
+ _id: objectId,
103
+ });
104
+ if (!existing) return null;
105
+
106
+ const updatePayload = {};
107
+ if (updates.state !== undefined) updatePayload.state = updates.state;
108
+ if (updates.context !== undefined)
109
+ updatePayload.context = updates.context;
110
+ if (updates.results !== undefined)
111
+ updatePayload.results = updates.results;
112
+ if (updates.childProcesses !== undefined) {
113
+ updatePayload.childProcesses = (updates.childProcesses || [])
114
+ .map((id) => toObjectId(id))
115
+ .filter(Boolean);
116
+ }
117
+ if (updates.parentProcessId !== undefined) {
118
+ updatePayload.parentProcessId = updates.parentProcessId
119
+ ? toObjectId(updates.parentProcessId)
120
+ : null;
121
+ }
122
+ updatePayload.updatedAt = new Date();
123
+
124
+ const encryptedUpdate = await this.encryptionService.encryptFields(
125
+ 'Process',
126
+ updatePayload
127
+ );
128
+
129
+ await updateOne(
130
+ this.prisma,
131
+ 'Process',
132
+ { _id: objectId },
133
+ { $set: encryptedUpdate }
134
+ );
135
+
136
+ const updated = await findOne(this.prisma, 'Process', {
137
+ _id: objectId,
138
+ });
139
+ if (!updated) {
140
+ console.error(
141
+ '[ProcessRepositoryDocumentDB] Process not found after update',
142
+ {
143
+ processId: fromObjectId(objectId),
144
+ }
145
+ );
146
+ throw new Error(
147
+ 'Failed to update process: Document not found after update. ' +
148
+ 'This indicates a database consistency issue.'
149
+ );
150
+ }
151
+ const decryptedProcess = await this.encryptionService.decryptFields(
152
+ 'Process',
153
+ updated
154
+ );
155
+ return this._mapProcess(decryptedProcess);
156
+ }
157
+
158
+ async findByIntegrationAndType(integrationId, type) {
159
+ const integrationObjectId = toObjectId(integrationId);
160
+ const filter = {
161
+ integrationId: integrationObjectId,
162
+ type,
163
+ };
164
+ const docs = await findMany(this.prisma, 'Process', filter, {
165
+ sort: { createdAt: -1 },
166
+ });
167
+
168
+ const decryptedDocs = await Promise.all(
169
+ docs.map((doc) =>
170
+ this.encryptionService.decryptFields('Process', doc)
171
+ )
172
+ );
173
+
174
+ return decryptedDocs.map((doc) => this._mapProcess(doc));
175
+ }
176
+
177
+ async findActiveProcesses(
178
+ integrationId,
179
+ excludeStates = ['COMPLETED', 'ERROR']
180
+ ) {
181
+ const integrationObjectId = toObjectId(integrationId);
182
+ const filter = {
183
+ integrationId: integrationObjectId,
184
+ state: { $nin: excludeStates },
185
+ };
186
+ const docs = await findMany(this.prisma, 'Process', filter, {
187
+ sort: { createdAt: -1 },
188
+ });
189
+
190
+ const decryptedDocs = await Promise.all(
191
+ docs.map((doc) =>
192
+ this.encryptionService.decryptFields('Process', doc)
193
+ )
194
+ );
195
+
196
+ return decryptedDocs.map((doc) => this._mapProcess(doc));
197
+ }
198
+
199
+ async findByName(name) {
200
+ const doc = await findOne(
201
+ this.prisma,
202
+ 'Process',
203
+ { name },
204
+ { sort: { createdAt: -1 } }
205
+ );
206
+ if (!doc) return null;
207
+
208
+ const decryptedProcess = await this.encryptionService.decryptFields(
209
+ 'Process',
210
+ doc
211
+ );
212
+ return this._mapProcess(decryptedProcess);
213
+ }
214
+
215
+ async deleteById(processId) {
216
+ const objectId = toObjectId(processId);
217
+ if (!objectId) return;
218
+ await deleteOne(this.prisma, 'Process', { _id: objectId });
219
+ }
220
+
221
+ _mapProcess(doc) {
222
+ return {
223
+ id: fromObjectId(doc?._id),
224
+ userId: fromObjectId(doc?.userId),
225
+ integrationId: fromObjectId(doc?.integrationId),
226
+ name: doc?.name ?? null,
227
+ type: doc?.type ?? null,
228
+ state: doc?.state ?? null,
229
+ context: doc?.context ?? {},
230
+ results: doc?.results ?? {},
231
+ childProcesses: (doc?.childProcesses || []).map((id) =>
232
+ fromObjectId(id)
233
+ ),
234
+ parentProcessId: doc?.parentProcessId
235
+ ? fromObjectId(doc.parentProcessId)
236
+ : null,
237
+ createdAt: doc?.createdAt ? new Date(doc.createdAt) : null,
238
+ updatedAt: doc?.updatedAt ? new Date(doc.updatedAt) : null,
239
+ };
240
+ }
241
+ }
242
+
243
+ module.exports = { ProcessRepositoryDocumentDB };
@@ -0,0 +1,53 @@
1
+ const { ProcessRepositoryMongo } = require('./process-repository-mongo');
2
+ const { ProcessRepositoryPostgres } = require('./process-repository-postgres');
3
+ const {
4
+ ProcessRepositoryDocumentDB,
5
+ } = require('./process-repository-documentdb');
6
+ const config = require('../../database/config');
7
+
8
+ /**
9
+ * Process Repository Factory
10
+ * Creates the appropriate repository adapter based on database type
11
+ *
12
+ * This implements the Factory pattern for Hexagonal Architecture:
13
+ * - Reads database type from app definition (backend/index.js)
14
+ * - Returns correct adapter (MongoDB or PostgreSQL)
15
+ * - Provides clear error for unsupported databases
16
+ *
17
+ * Usage:
18
+ * ```javascript
19
+ * const repository = createProcessRepository();
20
+ * await repository.create({ userId, integrationId, name, type, state });
21
+ * ```
22
+ *
23
+ * @returns {ProcessRepositoryInterface} Configured repository adapter
24
+ * @throws {Error} If database type is not supported
25
+ */
26
+ function createProcessRepository() {
27
+ const dbType = config.DB_TYPE;
28
+
29
+ switch (dbType) {
30
+ case 'mongodb':
31
+ return new ProcessRepositoryMongo();
32
+
33
+ case 'postgresql':
34
+ return new ProcessRepositoryPostgres();
35
+
36
+ case 'documentdb':
37
+ return new ProcessRepositoryDocumentDB();
38
+
39
+ default:
40
+ throw new Error(
41
+ `Unsupported database type: ${dbType}. Supported values: 'mongodb', 'documentdb', 'postgresql'`
42
+ );
43
+ }
44
+ }
45
+
46
+ module.exports = {
47
+ createProcessRepository,
48
+ // Export adapters for direct testing
49
+ ProcessRepositoryMongo,
50
+ ProcessRepositoryPostgres,
51
+ ProcessRepositoryDocumentDB,
52
+ };
53
+