@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,336 @@
1
+ const {
2
+ createModuleRepository,
3
+ } = require('../../modules/repositories/module-repository-factory');
4
+
5
+ const ERROR_CODE_MAP = {
6
+ ENTITY_NOT_FOUND: 404,
7
+ INVALID_ENTITY_DATA: 400,
8
+ };
9
+
10
+ function mapErrorToResponse(error) {
11
+ const status = ERROR_CODE_MAP[error?.code] || 500;
12
+ return {
13
+ error: status,
14
+ reason: error?.message,
15
+ code: error?.code,
16
+ };
17
+ }
18
+
19
+ /**
20
+ * Create entity command factory
21
+ *
22
+ * NOTE: This is an internal API. Integration developers should use createFriggCommands() instead.
23
+ *
24
+ * @returns {Object} Entity command object with CRUD operations
25
+ */
26
+ function createEntityCommands() {
27
+ const moduleRepo = createModuleRepository();
28
+
29
+ return {
30
+ /**
31
+ * Create a new entity
32
+ * @param {Object} params
33
+ * @param {string} params.userId - User ID who owns this entity
34
+ * @param {string} params.externalId - External identifier from the API module
35
+ * @param {string} params.name - Entity name
36
+ * @param {string} params.moduleName - Module name (e.g., 'husbpot', 'frontify')
37
+ * @param {string} [params.credentialId] - Associated credential ID
38
+ * @returns {Promise<Object>} Created entity object
39
+ */
40
+ async createEntity({
41
+ userId,
42
+ externalId,
43
+ name,
44
+ moduleName,
45
+ credentialId,
46
+ } = {}) {
47
+ try {
48
+ if (!userId || !externalId || !moduleName) {
49
+ const error = new Error(
50
+ 'userId, externalId, and moduleName are required'
51
+ );
52
+ error.code = 'INVALID_ENTITY_DATA';
53
+ throw error;
54
+ }
55
+
56
+ const entityData = {
57
+ user: userId,
58
+ externalId,
59
+ name,
60
+ moduleName,
61
+ };
62
+
63
+ if (credentialId) {
64
+ entityData.credential = credentialId;
65
+ }
66
+
67
+ const entity = await moduleRepo.createEntity(entityData);
68
+
69
+ return {
70
+ id: entity.id,
71
+ userId: entity.userId,
72
+ externalId: entity.externalId,
73
+ name: entity.name,
74
+ moduleName: entity.moduleName,
75
+ credentialId: entity.credential?._id
76
+ ? entity.credential._id.toString()
77
+ : entity.credential,
78
+ };
79
+ } catch (error) {
80
+ return mapErrorToResponse(error);
81
+ }
82
+ },
83
+
84
+ /**
85
+ * Find an entity by filter criteria
86
+ * @param {Object} filter
87
+ * @param {string} [filter.externalId] - External ID to search for
88
+ * @param {string} [filter.userId] - User ID to search for
89
+ * @param {string} [filter.moduleName] - Module name to search for
90
+ * @returns {Promise<Object|null>} Entity object or null if not found
91
+ */
92
+ async findEntity(filter = {}) {
93
+ try {
94
+ if (
95
+ !filter.externalId &&
96
+ !filter.userId &&
97
+ !filter.moduleName
98
+ ) {
99
+ const error = new Error(
100
+ 'At least one filter criterion is required'
101
+ );
102
+ error.code = 'INVALID_ENTITY_DATA';
103
+ throw error;
104
+ }
105
+
106
+ const entity = await moduleRepo.findEntity(filter);
107
+
108
+ if (!entity) {
109
+ return null;
110
+ }
111
+
112
+ return {
113
+ id: entity.id,
114
+ userId: entity.userId,
115
+ externalId: entity.externalId,
116
+ name: entity.name,
117
+ moduleName: entity.moduleName,
118
+ credentialId: entity.credential?._id
119
+ ? entity.credential._id.toString()
120
+ : entity.credential,
121
+ };
122
+ } catch (error) {
123
+ return mapErrorToResponse(error);
124
+ }
125
+ },
126
+
127
+ /**
128
+ * Find all entities for a user
129
+ * @param {string} userId - User ID to search for
130
+ * @returns {Promise<Array>} Array of entity objects
131
+ */
132
+ async findEntitiesByUserId(userId) {
133
+ try {
134
+ if (!userId) {
135
+ const error = new Error('userId is required');
136
+ error.code = 'INVALID_ENTITY_DATA';
137
+ throw error;
138
+ }
139
+
140
+ const entities = await moduleRepo.findEntitiesByUserId(userId);
141
+
142
+ return entities.map((entity) => ({
143
+ id: entity.id,
144
+ userId: entity.userId,
145
+ externalId: entity.externalId,
146
+ name: entity.name,
147
+ moduleName: entity.moduleName,
148
+ credentialId: entity.credential?._id
149
+ ? entity.credential._id.toString()
150
+ : entity.credential,
151
+ }));
152
+ } catch (error) {
153
+ if (error.code) {
154
+ return mapErrorToResponse(error);
155
+ }
156
+ // For find operations, return empty array on error instead of error object
157
+ return [];
158
+ }
159
+ },
160
+
161
+ /**
162
+ * Find entities by user ID and module name
163
+ * @param {string} userId - User ID to search for
164
+ * @param {string} moduleName - Module name to filter by
165
+ * @returns {Promise<Array>} Array of entity objects
166
+ */
167
+ async findEntitiesByUserIdAndModuleName(userId, moduleName) {
168
+ try {
169
+ if (!userId || !moduleName) {
170
+ const error = new Error(
171
+ 'userId and moduleName are required'
172
+ );
173
+ error.code = 'INVALID_ENTITY_DATA';
174
+ throw error;
175
+ }
176
+
177
+ const entities =
178
+ await moduleRepo.findEntitiesByUserIdAndModuleName(
179
+ userId,
180
+ moduleName
181
+ );
182
+
183
+ return entities.map((entity) => ({
184
+ id: entity.id,
185
+ userId: entity.userId,
186
+ externalId: entity.externalId,
187
+ name: entity.name,
188
+ moduleName: entity.moduleName,
189
+ credentialId: entity.credential?._id
190
+ ? entity.credential._id.toString()
191
+ : entity.credential,
192
+ }));
193
+ } catch (error) {
194
+ if (error.code) {
195
+ return mapErrorToResponse(error);
196
+ }
197
+ return [];
198
+ }
199
+ },
200
+
201
+ /**
202
+ * Find an entity by ID
203
+ * @param {string} entityId - Entity ID to search for
204
+ * @returns {Promise<Object>} Entity object
205
+ */
206
+ async findEntityById(entityId) {
207
+ try {
208
+ if (!entityId) {
209
+ const error = new Error('entityId is required');
210
+ error.code = 'INVALID_ENTITY_DATA';
211
+ throw error;
212
+ }
213
+
214
+ const entity = await moduleRepo.findEntityById(entityId);
215
+
216
+ return {
217
+ id: entity.id,
218
+ userId: entity.userId,
219
+ externalId: entity.externalId,
220
+ name: entity.name,
221
+ moduleName: entity.moduleName,
222
+ credentialId: entity.credential?._id
223
+ ? entity.credential._id.toString()
224
+ : entity.credential,
225
+ };
226
+ } catch (error) {
227
+ return mapErrorToResponse(error);
228
+ }
229
+ },
230
+
231
+ /**
232
+ * Update an entity
233
+ * @param {string} entityId - Entity ID to update
234
+ * @param {Object} updates - Fields to update
235
+ * @returns {Promise<Object>} Updated entity object
236
+ */
237
+ async updateEntity(entityId, updates) {
238
+ try {
239
+ if (!entityId) {
240
+ const error = new Error('entityId is required');
241
+ error.code = 'INVALID_ENTITY_DATA';
242
+ throw error;
243
+ }
244
+
245
+ const entity = await moduleRepo.updateEntity(entityId, updates);
246
+
247
+ if (!entity) {
248
+ const error = new Error(`Entity ${entityId} not found`);
249
+ error.code = 'ENTITY_NOT_FOUND';
250
+ throw error;
251
+ }
252
+
253
+ return {
254
+ id: entity.id,
255
+ userId: entity.userId,
256
+ externalId: entity.externalId,
257
+ name: entity.name,
258
+ moduleName: entity.moduleName,
259
+ credentialId: entity.credential?._id
260
+ ? entity.credential._id.toString()
261
+ : entity.credential,
262
+ };
263
+ } catch (error) {
264
+ return mapErrorToResponse(error);
265
+ }
266
+ },
267
+
268
+ /**
269
+ * Delete an entity
270
+ * @param {string} entityId - Entity ID to delete
271
+ * @returns {Promise<Object>} Result object with success flag
272
+ */
273
+ async deleteEntity(entityId) {
274
+ try {
275
+ if (!entityId) {
276
+ const error = new Error('entityId is required');
277
+ error.code = 'INVALID_ENTITY_DATA';
278
+ throw error;
279
+ }
280
+
281
+ await moduleRepo.deleteEntity(entityId);
282
+
283
+ return { success: true };
284
+ } catch (error) {
285
+ return mapErrorToResponse(error);
286
+ }
287
+ },
288
+
289
+ /**
290
+ * Delete an entity by ID (alias for deleteEntity)
291
+ * @param {string} entityId - Entity ID to delete
292
+ * @returns {Promise<Object>} Result object with success flag
293
+ */
294
+ async deleteEntityById(entityId) {
295
+ try {
296
+ if (!entityId) {
297
+ const error = new Error('entityId is required');
298
+ error.code = 'INVALID_ENTITY_DATA';
299
+ throw error;
300
+ }
301
+
302
+ await moduleRepo.deleteEntity(entityId);
303
+
304
+ return { success: true };
305
+ } catch (error) {
306
+ return mapErrorToResponse(error);
307
+ }
308
+ },
309
+
310
+ /**
311
+ * Remove credential reference from an entity
312
+ * @param {string} entityId - Entity ID to update
313
+ * @returns {Promise<Object>} Result object with success flag
314
+ */
315
+ async unsetCredential(entityId) {
316
+ try {
317
+ if (!entityId) {
318
+ const error = new Error('entityId is required');
319
+ error.code = 'INVALID_ENTITY_DATA';
320
+ throw error;
321
+ }
322
+
323
+ const acknowledged = await moduleRepo.unsetCredential(entityId);
324
+
325
+ return { success: acknowledged };
326
+ } catch (error) {
327
+ return mapErrorToResponse(error);
328
+ }
329
+ },
330
+ };
331
+ }
332
+
333
+ module.exports = {
334
+ createEntityCommands,
335
+ ERROR_CODE_MAP,
336
+ };
@@ -0,0 +1,210 @@
1
+ const {
2
+ createIntegrationRepository,
3
+ } = require('../../integrations/repositories/integration-repository-factory');
4
+ const {
5
+ createModuleRepository,
6
+ } = require('../../modules/repositories/module-repository-factory');
7
+ const { ModuleFactory } = require('../../modules/module-factory');
8
+ const {
9
+ LoadIntegrationContextUseCase,
10
+ } = require('../../integrations/use-cases/load-integration-context');
11
+ const {
12
+ FindIntegrationContextByExternalEntityIdUseCase,
13
+ } = require('../../integrations/use-cases/find-integration-context-by-external-entity-id');
14
+ const {
15
+ GetIntegrationsForUser,
16
+ } = require('../../integrations/use-cases/get-integrations-for-user');
17
+ const {
18
+ CreateIntegration,
19
+ } = require('../../integrations/use-cases/create-integration');
20
+ const {
21
+ getModulesDefinitionFromIntegrationClasses,
22
+ } = require('../../integrations/utils/map-integration-dto');
23
+
24
+ const ERROR_CODE_MAP = {
25
+ ENTITY_NOT_FOUND: 401,
26
+ ENTITY_USER_NOT_FOUND: 401,
27
+ INTEGRATION_NOT_FOUND: 404,
28
+ EXTERNAL_ENTITY_ID_REQUIRED: 400,
29
+ INTEGRATION_RECORD_NOT_FOUND: 404,
30
+ };
31
+
32
+ function mapErrorToResponse(error) {
33
+ const status = ERROR_CODE_MAP[error?.code] || 500;
34
+ return {
35
+ error: status,
36
+ reason: error?.message,
37
+ code: error?.code,
38
+ };
39
+ }
40
+
41
+ function createIntegrationCommands({ integrationClass }) {
42
+ if (!integrationClass) {
43
+ throw new Error('integrationClass is required');
44
+ }
45
+
46
+ // Always use Frigg's default repositories and use cases
47
+ const integrationRepository = createIntegrationRepository();
48
+ const moduleRepository = createModuleRepository();
49
+
50
+ const moduleDefinitions = getModulesDefinitionFromIntegrationClasses([
51
+ integrationClass,
52
+ ]);
53
+
54
+ const moduleFactory = new ModuleFactory({
55
+ moduleRepository,
56
+ moduleDefinitions,
57
+ });
58
+
59
+ const loadIntegrationContextUseCase = new LoadIntegrationContextUseCase({
60
+ integrationRepository,
61
+ moduleRepository,
62
+ moduleFactory,
63
+ });
64
+
65
+ const findByExternalEntityIdUseCase =
66
+ new FindIntegrationContextByExternalEntityIdUseCase({
67
+ integrationRepository,
68
+ moduleRepository,
69
+ loadIntegrationContextUseCase: loadIntegrationContextUseCase,
70
+ });
71
+
72
+ const getIntegrationsForUserUseCase = new GetIntegrationsForUser({
73
+ integrationRepository,
74
+ integrationClasses: [integrationClass],
75
+ moduleFactory,
76
+ moduleRepository,
77
+ });
78
+
79
+ const createIntegrationUseCase = new CreateIntegration({
80
+ integrationRepository,
81
+ integrationClasses: [integrationClass],
82
+ moduleFactory,
83
+ });
84
+
85
+ return {
86
+ async findIntegrationContextByExternalEntityId(externalEntityId) {
87
+ try {
88
+ const { context } = await findByExternalEntityIdUseCase.execute(
89
+ {
90
+ externalEntityId,
91
+ }
92
+ );
93
+ return { context };
94
+ } catch (error) {
95
+ return mapErrorToResponse(error);
96
+ }
97
+ },
98
+
99
+ async loadIntegrationContextById(integrationId) {
100
+ try {
101
+ const context = await loadIntegrationContextUseCase.execute({
102
+ integrationId,
103
+ });
104
+ return { context };
105
+ } catch (error) {
106
+ return mapErrorToResponse(error);
107
+ }
108
+ },
109
+
110
+ /**
111
+ * Find all integrations for a user
112
+ * @param {string} userId - User ID to search for
113
+ * @returns {Promise<Array>} Array of integration records
114
+ */
115
+ async findIntegrationsByUserId(userId) {
116
+ try {
117
+ const integrations =
118
+ await getIntegrationsForUserUseCase.execute(userId);
119
+ return integrations;
120
+ } catch (error) {
121
+ return mapErrorToResponse(error);
122
+ }
123
+ },
124
+
125
+ /**
126
+ * Create a new integration
127
+ * @param {Object} params
128
+ * @param {Array<string>} params.entityIds - Array of entity IDs
129
+ * @param {string} params.userId - User ID
130
+ * @param {Object} params.config - Integration configuration (must include type)
131
+ * @returns {Promise<Object>} Created integration object
132
+ */
133
+ async createIntegration({ entityIds, userId, config }) {
134
+ try {
135
+ const integration = await createIntegrationUseCase.execute(
136
+ entityIds,
137
+ userId,
138
+ config
139
+ );
140
+ return integration;
141
+ } catch (error) {
142
+ return mapErrorToResponse(error);
143
+ }
144
+ },
145
+
146
+ /**
147
+ * Update integration configuration
148
+ * @param {Object} params
149
+ * @param {string} params.integrationId - Integration ID
150
+ * @param {Object} params.config - Updated config object
151
+ * @returns {Promise<Object>} Updated integration
152
+ */
153
+ async updateIntegrationConfig({ integrationId, config }) {
154
+ try {
155
+ const integration = await integrationRepository.updateIntegrationConfig(
156
+ integrationId,
157
+ config
158
+ );
159
+ return integration;
160
+ } catch (error) {
161
+ return mapErrorToResponse(error);
162
+ }
163
+ },
164
+
165
+ /**
166
+ * Delete an integration by ID
167
+ * @param {string} integrationId - Integration ID to delete
168
+ * @returns {Promise<Object>} Deletion result
169
+ */
170
+ async deleteIntegrationById(integrationId) {
171
+ try {
172
+ if (!integrationId) {
173
+ const error = new Error('integrationId is required');
174
+ error.code = 'INVALID_INTEGRATION_DATA';
175
+ throw error;
176
+ }
177
+
178
+ const deleted = await integrationRepository.deleteIntegrationById(integrationId);
179
+
180
+ if (!deleted) {
181
+ const error = new Error(`Integration ${integrationId} not found`);
182
+ error.code = 'INTEGRATION_NOT_FOUND';
183
+ return mapErrorToResponse(error);
184
+ }
185
+
186
+ return {
187
+ success: true,
188
+ integrationId,
189
+ message: 'Integration deleted successfully',
190
+ };
191
+ } catch (error) {
192
+ return mapErrorToResponse(error);
193
+ }
194
+ },
195
+ };
196
+ }
197
+
198
+ async function findIntegrationContextByExternalEntityId({
199
+ integrationClass,
200
+ externalEntityId,
201
+ } = {}) {
202
+ const commands = createIntegrationCommands({ integrationClass });
203
+
204
+ return commands.findIntegrationContextByExternalEntityId(externalEntityId);
205
+ }
206
+
207
+ module.exports = {
208
+ createIntegrationCommands,
209
+ findIntegrationContextByExternalEntityId,
210
+ };