@friggframework/core 2.0.0-next.41 → 2.0.0-next.43

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 (197) hide show
  1. package/CLAUDE.md +693 -0
  2. package/README.md +931 -50
  3. package/application/commands/README.md +421 -0
  4. package/application/commands/credential-commands.js +224 -0
  5. package/application/commands/entity-commands.js +315 -0
  6. package/application/commands/integration-commands.js +160 -0
  7. package/application/commands/integration-commands.test.js +123 -0
  8. package/application/commands/user-commands.js +213 -0
  9. package/application/index.js +69 -0
  10. package/core/CLAUDE.md +690 -0
  11. package/core/create-handler.js +0 -6
  12. package/credential/repositories/credential-repository-factory.js +47 -0
  13. package/credential/repositories/credential-repository-interface.js +98 -0
  14. package/credential/repositories/credential-repository-mongo.js +301 -0
  15. package/credential/repositories/credential-repository-postgres.js +307 -0
  16. package/credential/repositories/credential-repository.js +307 -0
  17. package/credential/use-cases/get-credential-for-user.js +21 -0
  18. package/credential/use-cases/update-authentication-status.js +15 -0
  19. package/database/config.js +117 -0
  20. package/database/encryption/README.md +683 -0
  21. package/database/encryption/encryption-integration.test.js +553 -0
  22. package/database/encryption/encryption-schema-registry.js +141 -0
  23. package/database/encryption/encryption-schema-registry.test.js +392 -0
  24. package/database/encryption/field-encryption-service.js +226 -0
  25. package/database/encryption/field-encryption-service.test.js +525 -0
  26. package/database/encryption/logger.js +79 -0
  27. package/database/encryption/mongo-decryption-fix-verification.test.js +348 -0
  28. package/database/encryption/postgres-decryption-fix-verification.test.js +371 -0
  29. package/database/encryption/postgres-relation-decryption.test.js +245 -0
  30. package/database/encryption/prisma-encryption-extension.js +222 -0
  31. package/database/encryption/prisma-encryption-extension.test.js +439 -0
  32. package/database/index.js +25 -12
  33. package/database/models/readme.md +1 -0
  34. package/database/prisma.js +162 -0
  35. package/database/repositories/health-check-repository-factory.js +38 -0
  36. package/database/repositories/health-check-repository-interface.js +86 -0
  37. package/database/repositories/health-check-repository-mongodb.js +72 -0
  38. package/database/repositories/health-check-repository-postgres.js +75 -0
  39. package/database/repositories/health-check-repository.js +108 -0
  40. package/database/use-cases/check-database-health-use-case.js +34 -0
  41. package/database/use-cases/check-encryption-health-use-case.js +82 -0
  42. package/database/use-cases/test-encryption-use-case.js +252 -0
  43. package/encrypt/Cryptor.js +20 -152
  44. package/encrypt/index.js +1 -2
  45. package/encrypt/test-encrypt.js +0 -2
  46. package/handlers/app-definition-loader.js +38 -0
  47. package/handlers/app-handler-helpers.js +0 -3
  48. package/handlers/auth-flow.integration.test.js +147 -0
  49. package/handlers/backend-utils.js +25 -45
  50. package/handlers/integration-event-dispatcher.js +54 -0
  51. package/handlers/integration-event-dispatcher.test.js +141 -0
  52. package/handlers/routers/HEALTHCHECK.md +103 -1
  53. package/handlers/routers/auth.js +3 -14
  54. package/handlers/routers/health.js +63 -424
  55. package/handlers/routers/health.test.js +7 -0
  56. package/handlers/routers/integration-defined-routers.js +8 -5
  57. package/handlers/routers/user.js +27 -5
  58. package/handlers/routers/websocket.js +5 -3
  59. package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
  60. package/handlers/use-cases/check-integrations-health-use-case.js +32 -0
  61. package/handlers/workers/integration-defined-workers.js +6 -3
  62. package/index.js +45 -22
  63. package/integrations/index.js +12 -10
  64. package/integrations/integration-base.js +224 -53
  65. package/integrations/integration-router.js +386 -178
  66. package/integrations/options.js +1 -1
  67. package/integrations/repositories/integration-mapping-repository-factory.js +50 -0
  68. package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
  69. package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
  70. package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
  71. package/integrations/repositories/integration-mapping-repository.js +156 -0
  72. package/integrations/repositories/integration-repository-factory.js +44 -0
  73. package/integrations/repositories/integration-repository-interface.js +115 -0
  74. package/integrations/repositories/integration-repository-mongo.js +271 -0
  75. package/integrations/repositories/integration-repository-postgres.js +319 -0
  76. package/integrations/tests/doubles/dummy-integration-class.js +90 -0
  77. package/integrations/tests/doubles/test-integration-repository.js +99 -0
  78. package/integrations/tests/use-cases/create-integration.test.js +131 -0
  79. package/integrations/tests/use-cases/delete-integration-for-user.test.js +150 -0
  80. package/integrations/tests/use-cases/find-integration-context-by-external-entity-id.test.js +92 -0
  81. package/integrations/tests/use-cases/get-integration-for-user.test.js +150 -0
  82. package/integrations/tests/use-cases/get-integration-instance.test.js +176 -0
  83. package/integrations/tests/use-cases/get-integrations-for-user.test.js +176 -0
  84. package/integrations/tests/use-cases/get-possible-integrations.test.js +188 -0
  85. package/integrations/tests/use-cases/update-integration-messages.test.js +142 -0
  86. package/integrations/tests/use-cases/update-integration-status.test.js +103 -0
  87. package/integrations/tests/use-cases/update-integration.test.js +141 -0
  88. package/integrations/use-cases/create-integration.js +83 -0
  89. package/integrations/use-cases/delete-integration-for-user.js +73 -0
  90. package/integrations/use-cases/find-integration-context-by-external-entity-id.js +72 -0
  91. package/integrations/use-cases/get-integration-for-user.js +78 -0
  92. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  93. package/integrations/use-cases/get-integration-instance.js +83 -0
  94. package/integrations/use-cases/get-integrations-for-user.js +87 -0
  95. package/integrations/use-cases/get-possible-integrations.js +27 -0
  96. package/integrations/use-cases/index.js +11 -0
  97. package/integrations/use-cases/load-integration-context-full.test.js +329 -0
  98. package/integrations/use-cases/load-integration-context.js +71 -0
  99. package/integrations/use-cases/load-integration-context.test.js +114 -0
  100. package/integrations/use-cases/update-integration-messages.js +44 -0
  101. package/integrations/use-cases/update-integration-status.js +32 -0
  102. package/integrations/use-cases/update-integration.js +93 -0
  103. package/integrations/utils/map-integration-dto.js +36 -0
  104. package/jest-global-setup-noop.js +3 -0
  105. package/jest-global-teardown-noop.js +3 -0
  106. package/{module-plugin → modules}/entity.js +1 -0
  107. package/{module-plugin → modules}/index.js +0 -8
  108. package/modules/module-factory.js +56 -0
  109. package/modules/module-hydration.test.js +205 -0
  110. package/modules/module.js +221 -0
  111. package/modules/repositories/module-repository-factory.js +33 -0
  112. package/modules/repositories/module-repository-interface.js +129 -0
  113. package/modules/repositories/module-repository-mongo.js +386 -0
  114. package/modules/repositories/module-repository-postgres.js +437 -0
  115. package/modules/repositories/module-repository.js +327 -0
  116. package/{module-plugin → modules}/test/mock-api/api.js +8 -3
  117. package/{module-plugin → modules}/test/mock-api/definition.js +12 -8
  118. package/modules/tests/doubles/test-module-factory.js +16 -0
  119. package/modules/tests/doubles/test-module-repository.js +39 -0
  120. package/modules/use-cases/get-entities-for-user.js +32 -0
  121. package/modules/use-cases/get-entity-options-by-id.js +59 -0
  122. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  123. package/modules/use-cases/get-module-instance-from-type.js +31 -0
  124. package/modules/use-cases/get-module.js +56 -0
  125. package/modules/use-cases/process-authorization-callback.js +122 -0
  126. package/modules/use-cases/refresh-entity-options.js +59 -0
  127. package/modules/use-cases/test-module-auth.js +55 -0
  128. package/modules/utils/map-module-dto.js +18 -0
  129. package/package.json +14 -6
  130. package/prisma-mongodb/schema.prisma +318 -0
  131. package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
  132. package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
  133. package/prisma-postgresql/migrations/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
  134. package/prisma-postgresql/migrations/migration_lock.toml +3 -0
  135. package/prisma-postgresql/schema.prisma +300 -0
  136. package/syncs/manager.js +468 -443
  137. package/syncs/repositories/sync-repository-factory.js +38 -0
  138. package/syncs/repositories/sync-repository-interface.js +109 -0
  139. package/syncs/repositories/sync-repository-mongo.js +239 -0
  140. package/syncs/repositories/sync-repository-postgres.js +319 -0
  141. package/syncs/sync.js +0 -1
  142. package/token/repositories/token-repository-factory.js +33 -0
  143. package/token/repositories/token-repository-interface.js +131 -0
  144. package/token/repositories/token-repository-mongo.js +212 -0
  145. package/token/repositories/token-repository-postgres.js +257 -0
  146. package/token/repositories/token-repository.js +219 -0
  147. package/types/integrations/index.d.ts +2 -6
  148. package/types/module-plugin/index.d.ts +5 -57
  149. package/types/syncs/index.d.ts +0 -2
  150. package/user/repositories/user-repository-factory.js +46 -0
  151. package/user/repositories/user-repository-interface.js +198 -0
  152. package/user/repositories/user-repository-mongo.js +250 -0
  153. package/user/repositories/user-repository-postgres.js +311 -0
  154. package/user/tests/doubles/test-user-repository.js +72 -0
  155. package/user/tests/use-cases/create-individual-user.test.js +24 -0
  156. package/user/tests/use-cases/create-organization-user.test.js +28 -0
  157. package/user/tests/use-cases/create-token-for-user-id.test.js +19 -0
  158. package/user/tests/use-cases/get-user-from-bearer-token.test.js +64 -0
  159. package/user/tests/use-cases/login-user.test.js +140 -0
  160. package/user/use-cases/create-individual-user.js +61 -0
  161. package/user/use-cases/create-organization-user.js +47 -0
  162. package/user/use-cases/create-token-for-user-id.js +30 -0
  163. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  164. package/user/use-cases/login-user.js +122 -0
  165. package/user/user.js +77 -0
  166. package/websocket/repositories/websocket-connection-repository-factory.js +37 -0
  167. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  168. package/websocket/repositories/websocket-connection-repository-mongo.js +155 -0
  169. package/websocket/repositories/websocket-connection-repository-postgres.js +195 -0
  170. package/websocket/repositories/websocket-connection-repository.js +160 -0
  171. package/database/models/State.js +0 -9
  172. package/database/models/Token.js +0 -70
  173. package/database/mongo.js +0 -171
  174. package/encrypt/Cryptor.test.js +0 -32
  175. package/encrypt/encrypt.js +0 -104
  176. package/encrypt/encrypt.test.js +0 -1069
  177. package/handlers/routers/middleware/loadUser.js +0 -15
  178. package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
  179. package/integrations/create-frigg-backend.js +0 -31
  180. package/integrations/integration-factory.js +0 -251
  181. package/integrations/integration-mapping.js +0 -43
  182. package/integrations/integration-model.js +0 -46
  183. package/integrations/integration-user.js +0 -144
  184. package/integrations/test/integration-base.test.js +0 -144
  185. package/module-plugin/auther.js +0 -393
  186. package/module-plugin/credential.js +0 -22
  187. package/module-plugin/entity-manager.js +0 -70
  188. package/module-plugin/manager.js +0 -169
  189. package/module-plugin/module-factory.js +0 -61
  190. package/module-plugin/test/auther.test.js +0 -97
  191. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  192. /package/{module-plugin → modules}/requester/api-key.js +0 -0
  193. /package/{module-plugin → modules}/requester/basic.js +0 -0
  194. /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
  195. /package/{module-plugin → modules}/requester/requester.js +0 -0
  196. /package/{module-plugin → modules}/requester/requester.test.js +0 -0
  197. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -0,0 +1,327 @@
1
+ const { prisma } = require('../../database/prisma');
2
+ const { ModuleRepositoryInterface } = require('./module-repository-interface');
3
+
4
+ /**
5
+ * Prisma-based Module Repository
6
+ * Handles Entity model operations for external service entities
7
+ *
8
+ * Works identically for both MongoDB and PostgreSQL:
9
+ * - MongoDB: String IDs with @db.ObjectId
10
+ * - PostgreSQL: Integer IDs with auto-increment
11
+ * - Both use same query patterns (no many-to-many differences)
12
+ *
13
+ * Migration from Mongoose:
14
+ * - Constructor injection of Prisma client
15
+ * - populate('credential') → include: { credential: true }
16
+ * - entity.__t (discriminator) → entity.subType
17
+ * - _id → id conversion automatic in Prisma
18
+ */
19
+ class ModuleRepository extends ModuleRepositoryInterface {
20
+ constructor(prismaClient = prisma) {
21
+ super();
22
+ this.prisma = prismaClient; // Allow injection for testing
23
+ }
24
+
25
+ /**
26
+ * Find entity by ID with credential
27
+ * Replaces: Entity.findById(entityId).populate('credential')
28
+ *
29
+ * @param {string} entityId - Entity ID
30
+ * @returns {Promise<Object>} Entity object
31
+ * @throws {Error} If entity not found
32
+ */
33
+ async findEntityById(entityId) {
34
+ const entity = await this.prisma.entity.findUnique({
35
+ where: { id: entityId },
36
+ include: { credential: true },
37
+ });
38
+
39
+ if (!entity) {
40
+ throw new Error(`Entity ${entityId} not found`);
41
+ }
42
+
43
+ return {
44
+ id: entity.id,
45
+ accountId: entity.accountId,
46
+ credential: entity.credential,
47
+ userId: entity.userId,
48
+ name: entity.name,
49
+ externalId: entity.externalId,
50
+ type: entity.subType,
51
+ moduleName: entity.moduleName,
52
+ };
53
+ }
54
+
55
+ /**
56
+ * Find all entities for a user
57
+ * Replaces: Entity.find({ user: userId }).populate('credential')
58
+ *
59
+ * @param {string} userId - User ID
60
+ * @returns {Promise<Array>} Array of entity objects
61
+ */
62
+ async findEntitiesByUserId(userId) {
63
+ const entities = await this.prisma.entity.findMany({
64
+ where: { userId },
65
+ include: { credential: true },
66
+ });
67
+
68
+ return entities.map((e) => ({
69
+ id: e.id,
70
+ accountId: e.accountId,
71
+ credential: e.credential,
72
+ userId: e.userId,
73
+ name: e.name,
74
+ externalId: e.externalId,
75
+ type: e.subType,
76
+ moduleName: e.moduleName,
77
+ }));
78
+ }
79
+
80
+ /**
81
+ * Find entities by array of IDs
82
+ * Replaces: Entity.find({ _id: { $in: entitiesIds } }).populate('credential')
83
+ *
84
+ * @param {Array<string>} entitiesIds - Array of entity IDs
85
+ * @returns {Promise<Array>} Array of entity objects
86
+ */
87
+ async findEntitiesByIds(entitiesIds) {
88
+ const entities = await this.prisma.entity.findMany({
89
+ where: { id: { in: entitiesIds } },
90
+ include: { credential: true },
91
+ });
92
+
93
+ return entities.map((e) => ({
94
+ id: e.id,
95
+ accountId: e.accountId,
96
+ credential: e.credential,
97
+ userId: e.userId,
98
+ name: e.name,
99
+ externalId: e.externalId,
100
+ type: e.subType,
101
+ moduleName: e.moduleName,
102
+ }));
103
+ }
104
+
105
+ /**
106
+ * Find entities by user ID and module name
107
+ * Replaces: Entity.find({ user: userId, moduleName: moduleName }).populate('credential')
108
+ *
109
+ * @param {string} userId - User ID
110
+ * @param {string} moduleName - Module name
111
+ * @returns {Promise<Array>} Array of entity objects
112
+ */
113
+ async findEntitiesByUserIdAndModuleName(userId, moduleName) {
114
+ const entities = await this.prisma.entity.findMany({
115
+ where: {
116
+ userId,
117
+ moduleName,
118
+ },
119
+ include: { credential: true },
120
+ });
121
+
122
+ return entities.map((e) => ({
123
+ id: e.id,
124
+ accountId: e.accountId,
125
+ credential: e.credential,
126
+ userId: e.userId,
127
+ name: e.name,
128
+ externalId: e.externalId,
129
+ type: e.subType,
130
+ moduleName: e.moduleName,
131
+ }));
132
+ }
133
+
134
+ /**
135
+ * Remove credential reference from entity
136
+ * Replaces: Entity.updateOne({ _id: entityId }, { $unset: { credential: "" } })
137
+ *
138
+ * @param {string} entityId - Entity ID
139
+ * @returns {Promise<boolean>} Success indicator
140
+ */
141
+ async unsetCredential(entityId) {
142
+ await this.prisma.entity.update({
143
+ where: { id: entityId },
144
+ data: { credentialId: null },
145
+ });
146
+
147
+ return true;
148
+ }
149
+
150
+ /**
151
+ * Find entity by filter criteria
152
+ * Replaces: Entity.findOne(filter).populate('credential')
153
+ *
154
+ * @param {Object} filter - Filter criteria
155
+ * @returns {Promise<Object|null>} Entity object or null
156
+ */
157
+ async findEntity(filter) {
158
+ const where = this._convertFilterToWhere(filter);
159
+ const entity = await this.prisma.entity.findFirst({
160
+ where,
161
+ include: { credential: true },
162
+ });
163
+
164
+ if (!entity) {
165
+ return null;
166
+ }
167
+
168
+ return {
169
+ id: entity.id,
170
+ accountId: entity.accountId,
171
+ credential: entity.credential,
172
+ userId: entity.userId,
173
+ name: entity.name,
174
+ externalId: entity.externalId,
175
+ type: entity.subType,
176
+ moduleName: entity.moduleName,
177
+ };
178
+ }
179
+
180
+ /**
181
+ * Create a new entity
182
+ * Replaces: Entity.create(entityData)
183
+ *
184
+ * @param {Object} entityData - Entity data
185
+ * @returns {Promise<Object>} Created entity object
186
+ */
187
+ async createEntity(entityData) {
188
+ // Convert Mongoose-style fields to Prisma
189
+ const data = {
190
+ userId: entityData.user || entityData.userId,
191
+ credentialId: entityData.credential || entityData.credentialId,
192
+ subType: entityData.type || entityData.subType,
193
+ name: entityData.name,
194
+ moduleName: entityData.moduleName,
195
+ externalId: entityData.externalId,
196
+ accountId: entityData.accountId,
197
+ };
198
+
199
+ const entity = await this.prisma.entity.create({
200
+ data,
201
+ include: { credential: true },
202
+ });
203
+
204
+ return {
205
+ id: entity.id,
206
+ accountId: entity.accountId,
207
+ credential: entity.credential,
208
+ userId: entity.userId,
209
+ name: entity.name,
210
+ externalId: entity.externalId,
211
+ type: entity.subType,
212
+ moduleName: entity.moduleName,
213
+ };
214
+ }
215
+
216
+ /**
217
+ * Update an entity by ID
218
+ * Replaces: Entity.findByIdAndUpdate(entityId, updates, { new: true })
219
+ *
220
+ * @param {string} entityId - Entity ID to update
221
+ * @param {Object} updates - Fields to update
222
+ * @returns {Promise<Object|null>} Updated entity object or null if not found
223
+ */
224
+ async updateEntity(entityId, updates) {
225
+ // Convert Mongoose-style fields to Prisma
226
+ const data = {};
227
+ if (updates.user !== undefined) data.userId = updates.user;
228
+ if (updates.userId !== undefined) data.userId = updates.userId;
229
+ if (updates.credential !== undefined)
230
+ data.credentialId = updates.credential;
231
+ if (updates.credentialId !== undefined)
232
+ data.credentialId = updates.credentialId;
233
+ if (updates.type !== undefined) data.subType = updates.type;
234
+ if (updates.subType !== undefined) data.subType = updates.subType;
235
+ if (updates.name !== undefined) data.name = updates.name;
236
+ if (updates.moduleName !== undefined)
237
+ data.moduleName = updates.moduleName;
238
+ if (updates.externalId !== undefined)
239
+ data.externalId = updates.externalId;
240
+ if (updates.accountId !== undefined) data.accountId = updates.accountId;
241
+
242
+ try {
243
+ const entity = await this.prisma.entity.update({
244
+ where: { id: entityId },
245
+ data,
246
+ include: { credential: true },
247
+ });
248
+
249
+ return {
250
+ id: entity.id,
251
+ accountId: entity.accountId,
252
+ credential: entity.credential,
253
+ userId: entity.userId,
254
+ name: entity.name,
255
+ externalId: entity.externalId,
256
+ type: entity.subType,
257
+ moduleName: entity.moduleName,
258
+ };
259
+ } catch (error) {
260
+ if (error.code === 'P2025') {
261
+ // Record not found
262
+ return null;
263
+ }
264
+ throw error;
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Delete an entity by ID
270
+ * Replaces: Entity.deleteOne({ _id: entityId })
271
+ *
272
+ * @param {string} entityId - Entity ID to delete
273
+ * @returns {Promise<boolean>} True if deleted successfully
274
+ */
275
+ async deleteEntity(entityId) {
276
+ try {
277
+ await this.prisma.entity.delete({
278
+ where: { id: entityId },
279
+ });
280
+ return true;
281
+ } catch (error) {
282
+ if (error.code === 'P2025') {
283
+ // Record not found
284
+ return false;
285
+ }
286
+ throw error;
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Convert Mongoose-style filter to Prisma where clause
292
+ * @private
293
+ * @param {Object} filter - Mongoose filter
294
+ * @returns {Object} Prisma where clause
295
+ */
296
+ _convertFilterToWhere(filter) {
297
+ const where = {};
298
+
299
+ // Handle _id field (Mongoose uses _id, Prisma uses id)
300
+ if (filter._id) {
301
+ where.id = filter._id;
302
+ }
303
+
304
+ // Handle user field (Mongoose uses user, Prisma uses userId)
305
+ if (filter.user) {
306
+ where.userId = filter.user;
307
+ }
308
+
309
+ // Handle credential field (Mongoose uses credential, Prisma uses credentialId)
310
+ if (filter.credential) {
311
+ where.credentialId = filter.credential;
312
+ }
313
+
314
+ // Copy other fields directly
315
+ if (filter.id) where.id = filter.id;
316
+ if (filter.userId) where.userId = filter.userId;
317
+ if (filter.credentialId) where.credentialId = filter.credentialId;
318
+ if (filter.name) where.name = filter.name;
319
+ if (filter.moduleName) where.moduleName = filter.moduleName;
320
+ if (filter.externalId) where.externalId = filter.externalId;
321
+ if (filter.subType) where.subType = filter.subType;
322
+
323
+ return where;
324
+ }
325
+ }
326
+
327
+ module.exports = { ModuleRepository };
@@ -1,5 +1,5 @@
1
- const { get } = require('../../assertions');
2
- const { OAuth2Requester } = require('../../module-plugin');
1
+ const { get } = require('../../../assertions');
2
+ const { OAuth2Requester } = require('../..');
3
3
 
4
4
  class Api extends OAuth2Requester {
5
5
  constructor(params) {
@@ -23,7 +23,12 @@ class Api extends OAuth2Requester {
23
23
  return this.authorizationUri;
24
24
  }
25
25
 
26
-
26
+ getAuthorizationRequirements() {
27
+ return {
28
+ url: this.getAuthUri(),
29
+ type: 'oauth2',
30
+ };
31
+ }
27
32
  }
28
33
 
29
34
  module.exports = { Api };
@@ -1,19 +1,23 @@
1
1
  require('dotenv').config();
2
- const {Api} = require('./api');
3
- const {get} = require('../../assertions');
4
- const config = {name: 'anapi'}
2
+ const { Api } = require('./api');
3
+ const { get } = require('../../../assertions');
4
+ const config = { name: 'anapi' }
5
5
 
6
6
  const Definition = {
7
7
  API: Api,
8
- getName: function() {return config.name},
8
+ getAuthorizationRequirements: () => ({
9
+ url: 'http://localhost:3000/redirect/anapi',
10
+ type: 'oauth2',
11
+ }),
12
+ getName: function () { return config.name },
9
13
  moduleName: config.name,
10
14
  modelName: 'AnApi',
11
15
  requiredAuthMethods: {
12
- getToken: async function(api, params){
16
+ getToken: async function (api, params) {
13
17
  const code = get(params.data, 'code');
14
18
  return api.getTokenFromCode(code);
15
19
  },
16
- getEntityDetails: async function(api, callbackParams, tokenResponse, userId) {
20
+ getEntityDetails: async function (api, callbackParams, tokenResponse, userId) {
17
21
  const userDetails = await api.getUserDetails();
18
22
  return {
19
23
  identifiers: { externalId: userDetails.portalId, user: userId },
@@ -26,14 +30,14 @@ const Definition = {
26
30
  ],
27
31
  entity: [],
28
32
  },
29
- getCredentialDetails: async function(api, userId) {
33
+ getCredentialDetails: async function (api, userId) {
30
34
  const userDetails = await api.getUserDetails();
31
35
  return {
32
36
  identifiers: { externalId: userDetails.portalId, user: userId },
33
37
  details: {}
34
38
  };
35
39
  },
36
- testAuthRequest: async function(api){
40
+ testAuthRequest: async function (api) {
37
41
  return api.getUserDetails()
38
42
  },
39
43
  },
@@ -0,0 +1,16 @@
1
+ class TestModuleFactory {
2
+ constructor() { }
3
+
4
+ async getModuleInstance(entityId, userId) {
5
+ // return minimal stub module with getName and api property
6
+ return {
7
+ getName() { return 'stubModule'; },
8
+ api: {},
9
+ entityId,
10
+ userId,
11
+ testAuth: async () => true,
12
+ };
13
+ }
14
+ }
15
+
16
+ module.exports = { TestModuleFactory };
@@ -0,0 +1,39 @@
1
+ class TestModuleRepository {
2
+ constructor() {
3
+ this.entities = new Map();
4
+ }
5
+
6
+ addEntity(entity) {
7
+ this.entities.set(entity.id, entity);
8
+ }
9
+
10
+ async findEntityById(id) {
11
+ return this.entities.get(id);
12
+ }
13
+
14
+ async findEntitiesByIds(ids) {
15
+ return ids.map((id) => this.entities.get(id));
16
+ }
17
+
18
+ async findEntity(filter) {
19
+ if (!filter || typeof filter !== 'object') {
20
+ return null;
21
+ }
22
+
23
+ if (filter.id && this.entities.has(filter.id)) {
24
+ return this.entities.get(filter.id);
25
+ }
26
+
27
+ if (filter.externalId) {
28
+ for (const entity of this.entities.values()) {
29
+ if (entity.externalId === filter.externalId) {
30
+ return entity;
31
+ }
32
+ }
33
+ }
34
+
35
+ return null;
36
+ }
37
+ }
38
+
39
+ module.exports = { TestModuleRepository };
@@ -0,0 +1,32 @@
1
+ const { Module } = require('../module');
2
+ const { mapModuleClassToModuleDTO } = require('../utils/map-module-dto');
3
+
4
+ class GetEntitiesForUser {
5
+ constructor({ moduleRepository, moduleDefinitions }) {
6
+ this.moduleRepository = moduleRepository;
7
+
8
+ this.definitionMap = new Map();
9
+ for (const definition of moduleDefinitions) {
10
+ this.definitionMap.set(definition.moduleName, definition);
11
+ }
12
+ }
13
+
14
+ async execute(userId) {
15
+ const entities = await this.moduleRepository.findEntitiesByUserId(
16
+ userId
17
+ );
18
+
19
+ return entities.map((entity) => {
20
+ const definition = this.definitionMap.get(entity.moduleName);
21
+
22
+ const moduleInstance = new Module({
23
+ userId,
24
+ definition: definition,
25
+ entity: entity,
26
+ });
27
+ return mapModuleClassToModuleDTO(moduleInstance);
28
+ });
29
+ }
30
+ }
31
+
32
+ module.exports = { GetEntitiesForUser };
@@ -0,0 +1,59 @@
1
+ const { Module } = require('../module');
2
+
3
+ class GetEntityOptionsById {
4
+ /**
5
+ * @param {Object} params
6
+ * @param {import('../repositories/module-repository-interface').ModuleRepositoryInterface} params.moduleRepository
7
+ * @param {} params.moduleDefinitions
8
+ */
9
+ constructor({ moduleRepository, moduleDefinitions }) {
10
+ this.moduleRepository = moduleRepository;
11
+ this.moduleDefinitions = moduleDefinitions;
12
+ }
13
+
14
+ /**
15
+ * Retrieve a Module instance for a given user and entity/module type.
16
+ * @param {string} userId
17
+ * @param {string} entityId
18
+ */
19
+ async execute(entityId, userId) {
20
+ const entity = await this.moduleRepository.findEntityById(
21
+ entityId,
22
+ userId
23
+ );
24
+
25
+ if (!entity) {
26
+ throw new Error(`Entity ${entityId} not found`);
27
+ }
28
+
29
+ if (entity.userId !== userId) {
30
+ throw new Error(
31
+ `Entity ${entityId} does not belong to user ${userId}`
32
+ );
33
+ }
34
+
35
+ const entityType = entity.type;
36
+ const moduleDefinition = this.moduleDefinitions.find((def) => {
37
+ const modelName =
38
+ Module.getEntityModelFromDefinition(def).modelName;
39
+ return entityType === modelName;
40
+ });
41
+
42
+ if (!moduleDefinition) {
43
+ throw new Error(
44
+ `Module definition not found for entity type: ${entityType}`
45
+ );
46
+ }
47
+
48
+ const module = new Module({
49
+ userId,
50
+ entity,
51
+ definition: moduleDefinition,
52
+ });
53
+
54
+ const entityOptions = await module.getEntityOptions();
55
+ return entityOptions;
56
+ }
57
+ }
58
+
59
+ module.exports = { GetEntityOptionsById };
@@ -0,0 +1,34 @@
1
+ const { Module } = require('../module');
2
+
3
+ class GetEntityOptionsByType {
4
+ /**
5
+ * @param {Object} params
6
+ * @param {} params.moduleDefinitions
7
+ */
8
+ constructor({ moduleDefinitions }) {
9
+ this.moduleDefinitions = moduleDefinitions;
10
+ }
11
+
12
+ /**
13
+ * Retrieve a Module instance for a given user and entity/module type.
14
+ * @param {string} userId
15
+ * @param {string} type – human-readable module/entity type (e.g. "Hubspot")
16
+ */
17
+ async execute(userId, type) {
18
+ const moduleDefinition = this.moduleDefinitions.find(
19
+ (def) => def.getName() === type
20
+ );
21
+ if (!moduleDefinition) {
22
+ throw new Error(`Module definition not found for type: ${type}`);
23
+ }
24
+ const moduleInstance = new Module({
25
+ userId,
26
+ definition: moduleDefinition,
27
+ });
28
+
29
+ const entityOptions = await moduleInstance.getEntityOptions();
30
+ return entityOptions;
31
+ }
32
+ }
33
+
34
+ module.exports = { GetEntityOptionsByType };
@@ -0,0 +1,31 @@
1
+ const { Module } = require('../module');
2
+
3
+ class GetModuleInstanceFromType {
4
+ /**
5
+ * @param {Object} params
6
+ * @param {} params.moduleDefinitions
7
+ */
8
+ constructor({ moduleDefinitions }) {
9
+ this.moduleDefinitions = moduleDefinitions;
10
+ }
11
+
12
+ /**
13
+ * Retrieve a Module instance for a given user and entity/module type.
14
+ * @param {string} userId
15
+ * @param {string} type – human-readable module/entity type (e.g. "Hubspot")
16
+ */
17
+ async execute(userId, type) {
18
+ const moduleDefinition = this.moduleDefinitions.find(
19
+ (def) => def.getName() === type
20
+ );
21
+ if (!moduleDefinition) {
22
+ throw new Error(`Module definition not found for type: ${type}`);
23
+ }
24
+ return new Module({
25
+ userId,
26
+ definition: moduleDefinition,
27
+ });
28
+ }
29
+ }
30
+
31
+ module.exports = { GetModuleInstanceFromType };
@@ -0,0 +1,56 @@
1
+ const { Module } = require('../module');
2
+
3
+ class GetModule {
4
+ constructor({ moduleRepository, moduleDefinitions }) {
5
+ this.moduleRepository = moduleRepository;
6
+ this.moduleDefinitions = moduleDefinitions;
7
+ }
8
+
9
+ async execute(entityId, userId) {
10
+ const entity = await this.moduleRepository.findEntityById(
11
+ entityId,
12
+ userId
13
+ );
14
+
15
+ if (!entity) {
16
+ throw new Error(`Entity ${entityId} not found`);
17
+ }
18
+
19
+ if (entity.userId !== userId) {
20
+ throw new Error(
21
+ `Entity ${entityId} does not belong to user ${userId}`
22
+ );
23
+ }
24
+
25
+ const entityType = entity.type;
26
+ const moduleDefinition = this.moduleDefinitions.find((def) => {
27
+ const modelName = Module.getEntityModelFromDefinition(def).modelName;
28
+ return entityType === modelName;
29
+ });
30
+
31
+ if (!moduleDefinition) {
32
+ throw new Error(
33
+ `Module definition not found for entity type: ${entityType}`
34
+ );
35
+ }
36
+
37
+ const module = new Module({
38
+ userId,
39
+ entity,
40
+ definition: moduleDefinition,
41
+ });
42
+
43
+ // todo: this properties should be methods in the Module class
44
+ return {
45
+ id: module.entity.id,
46
+ name: module.entity.name,
47
+ type: module.entity.moduleName,
48
+ moduleName: module.entity.moduleName,
49
+ credential: module.credential,
50
+ externalId: module.entity.externalId,
51
+ userId: module.entity.user.toString(),
52
+ }
53
+ }
54
+ }
55
+
56
+ module.exports = { GetModule };