@friggframework/core 2.0.0-next.40 → 2.0.0-next.42

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 (196) 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 +25 -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 +121 -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 +321 -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/migration_lock.toml +3 -0
  134. package/prisma-postgresql/schema.prisma +303 -0
  135. package/syncs/manager.js +468 -443
  136. package/syncs/repositories/sync-repository-factory.js +38 -0
  137. package/syncs/repositories/sync-repository-interface.js +109 -0
  138. package/syncs/repositories/sync-repository-mongo.js +239 -0
  139. package/syncs/repositories/sync-repository-postgres.js +319 -0
  140. package/syncs/sync.js +0 -1
  141. package/token/repositories/token-repository-factory.js +33 -0
  142. package/token/repositories/token-repository-interface.js +131 -0
  143. package/token/repositories/token-repository-mongo.js +212 -0
  144. package/token/repositories/token-repository-postgres.js +257 -0
  145. package/token/repositories/token-repository.js +219 -0
  146. package/types/integrations/index.d.ts +2 -6
  147. package/types/module-plugin/index.d.ts +5 -57
  148. package/types/syncs/index.d.ts +0 -2
  149. package/user/repositories/user-repository-factory.js +46 -0
  150. package/user/repositories/user-repository-interface.js +198 -0
  151. package/user/repositories/user-repository-mongo.js +250 -0
  152. package/user/repositories/user-repository-postgres.js +311 -0
  153. package/user/tests/doubles/test-user-repository.js +72 -0
  154. package/user/tests/use-cases/create-individual-user.test.js +24 -0
  155. package/user/tests/use-cases/create-organization-user.test.js +28 -0
  156. package/user/tests/use-cases/create-token-for-user-id.test.js +19 -0
  157. package/user/tests/use-cases/get-user-from-bearer-token.test.js +64 -0
  158. package/user/tests/use-cases/login-user.test.js +140 -0
  159. package/user/use-cases/create-individual-user.js +61 -0
  160. package/user/use-cases/create-organization-user.js +47 -0
  161. package/user/use-cases/create-token-for-user-id.js +30 -0
  162. package/user/use-cases/get-user-from-bearer-token.js +77 -0
  163. package/user/use-cases/login-user.js +122 -0
  164. package/user/user.js +77 -0
  165. package/websocket/repositories/websocket-connection-repository-factory.js +37 -0
  166. package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
  167. package/websocket/repositories/websocket-connection-repository-mongo.js +155 -0
  168. package/websocket/repositories/websocket-connection-repository-postgres.js +195 -0
  169. package/websocket/repositories/websocket-connection-repository.js +160 -0
  170. package/database/models/State.js +0 -9
  171. package/database/models/Token.js +0 -70
  172. package/database/mongo.js +0 -171
  173. package/encrypt/Cryptor.test.js +0 -32
  174. package/encrypt/encrypt.js +0 -104
  175. package/encrypt/encrypt.test.js +0 -1069
  176. package/handlers/routers/middleware/loadUser.js +0 -15
  177. package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
  178. package/integrations/create-frigg-backend.js +0 -31
  179. package/integrations/integration-factory.js +0 -251
  180. package/integrations/integration-mapping.js +0 -43
  181. package/integrations/integration-model.js +0 -46
  182. package/integrations/integration-user.js +0 -144
  183. package/integrations/test/integration-base.test.js +0 -144
  184. package/module-plugin/auther.js +0 -393
  185. package/module-plugin/credential.js +0 -22
  186. package/module-plugin/entity-manager.js +0 -70
  187. package/module-plugin/manager.js +0 -169
  188. package/module-plugin/module-factory.js +0 -61
  189. package/module-plugin/test/auther.test.js +0 -97
  190. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  191. /package/{module-plugin → modules}/requester/api-key.js +0 -0
  192. /package/{module-plugin → modules}/requester/basic.js +0 -0
  193. /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
  194. /package/{module-plugin → modules}/requester/requester.js +0 -0
  195. /package/{module-plugin → modules}/requester/requester.test.js +0 -0
  196. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -2,23 +2,188 @@ const express = require('express');
2
2
  const { get } = require('../assertions');
3
3
  const Boom = require('@hapi/boom');
4
4
  const catchAsyncError = require('express-async-handler');
5
- const { debug } = require('../logs');
6
- function createIntegrationRouter(params) {
7
- const router = get(params, 'router', express());
8
- const factory = get(params, 'factory');
9
- const getUserId = get(params, 'getUserId', (req) => null);
10
- const requireLoggedInUser = get(
11
- params,
12
- 'requireLoggedInUser',
13
- (req, res, next) => next()
14
- );
15
-
16
- router.all('/api/entities*', requireLoggedInUser);
17
- router.all('/api/authorize', requireLoggedInUser);
18
- router.all('/api/integrations*', requireLoggedInUser);
19
-
20
- setIntegrationRoutes(router, factory, getUserId);
21
- setEntityRoutes(router, factory, getUserId);
5
+ const {
6
+ createIntegrationRepository,
7
+ } = require('./repositories/integration-repository-factory');
8
+ const {
9
+ DeleteIntegrationForUser,
10
+ } = require('./use-cases/delete-integration-for-user');
11
+ const {
12
+ GetIntegrationsForUser,
13
+ } = require('./use-cases/get-integrations-for-user');
14
+ const {
15
+ createCredentialRepository,
16
+ } = require('../credential/repositories/credential-repository-factory');
17
+ const {
18
+ GetCredentialForUser,
19
+ } = require('../credential/use-cases/get-credential-for-user');
20
+ const { CreateIntegration } = require('./use-cases/create-integration');
21
+ const { ModuleFactory } = require('../modules/module-factory');
22
+ const {
23
+ createModuleRepository,
24
+ } = require('../modules/repositories/module-repository-factory');
25
+ const {
26
+ GetEntitiesForUser,
27
+ } = require('../modules/use-cases/get-entities-for-user');
28
+ const { loadAppDefinition } = require('../handlers/app-definition-loader');
29
+ const {
30
+ GetIntegrationInstance,
31
+ } = require('./use-cases/get-integration-instance');
32
+ const { UpdateIntegration } = require('./use-cases/update-integration');
33
+ const {
34
+ getModulesDefinitionFromIntegrationClasses,
35
+ } = require('./utils/map-integration-dto');
36
+ const {
37
+ GetModuleInstanceFromType,
38
+ } = require('../modules/use-cases/get-module-instance-from-type');
39
+ const {
40
+ GetEntityOptionsByType,
41
+ } = require('../modules/use-cases/get-entity-options-by-type');
42
+ const { TestModuleAuth } = require('../modules/use-cases/test-module-auth');
43
+ const { GetModule } = require('../modules/use-cases/get-module');
44
+ const {
45
+ GetEntityOptionsById,
46
+ } = require('../modules/use-cases/get-entity-options-by-id');
47
+ const {
48
+ RefreshEntityOptions,
49
+ } = require('../modules/use-cases/refresh-entity-options');
50
+ const {
51
+ GetPossibleIntegrations,
52
+ } = require('./use-cases/get-possible-integrations');
53
+ const {
54
+ createUserRepository,
55
+ } = require('../user/repositories/user-repository-factory');
56
+ const {
57
+ GetUserFromBearerToken,
58
+ } = require('../user/use-cases/get-user-from-bearer-token');
59
+ const {
60
+ ProcessAuthorizationCallback,
61
+ } = require('../modules/use-cases/process-authorization-callback');
62
+
63
+ function createIntegrationRouter() {
64
+ const { integrations: integrationClasses, userConfig } =
65
+ loadAppDefinition();
66
+ const moduleRepository = createModuleRepository();
67
+ const integrationRepository = createIntegrationRepository();
68
+ const credentialRepository = createCredentialRepository();
69
+ const userRepository = createUserRepository();
70
+
71
+ const getUserFromBearerToken = new GetUserFromBearerToken({
72
+ userRepository,
73
+ userConfig,
74
+ });
75
+
76
+ const moduleFactory = new ModuleFactory({
77
+ moduleRepository,
78
+ moduleDefinitions:
79
+ getModulesDefinitionFromIntegrationClasses(integrationClasses),
80
+ });
81
+ const deleteIntegrationForUser = new DeleteIntegrationForUser({
82
+ integrationRepository,
83
+ integrationClasses,
84
+ });
85
+
86
+ const getIntegrationsForUser = new GetIntegrationsForUser({
87
+ integrationRepository,
88
+ integrationClasses,
89
+ moduleFactory,
90
+ moduleRepository,
91
+ });
92
+
93
+ const getCredentialForUser = new GetCredentialForUser({
94
+ credentialRepository,
95
+ });
96
+
97
+ const createIntegration = new CreateIntegration({
98
+ integrationRepository,
99
+ integrationClasses,
100
+ moduleFactory,
101
+ });
102
+
103
+ const getEntitiesForUser = new GetEntitiesForUser({
104
+ moduleRepository,
105
+ moduleDefinitions:
106
+ getModulesDefinitionFromIntegrationClasses(integrationClasses),
107
+ });
108
+
109
+ const getIntegrationInstance = new GetIntegrationInstance({
110
+ integrationRepository,
111
+ integrationClasses,
112
+ moduleFactory,
113
+ });
114
+
115
+ const updateIntegration = new UpdateIntegration({
116
+ integrationRepository,
117
+ integrationClasses,
118
+ moduleFactory,
119
+ });
120
+
121
+ const getModuleInstanceFromType = new GetModuleInstanceFromType({
122
+ moduleDefinitions:
123
+ getModulesDefinitionFromIntegrationClasses(integrationClasses),
124
+ });
125
+
126
+ const getEntityOptionsByType = new GetEntityOptionsByType({
127
+ moduleDefinitions:
128
+ getModulesDefinitionFromIntegrationClasses(integrationClasses),
129
+ });
130
+
131
+ const testModuleAuth = new TestModuleAuth({
132
+ moduleRepository,
133
+ moduleDefinitions:
134
+ getModulesDefinitionFromIntegrationClasses(integrationClasses),
135
+ });
136
+
137
+ const getModule = new GetModule({
138
+ moduleRepository,
139
+ moduleDefinitions:
140
+ getModulesDefinitionFromIntegrationClasses(integrationClasses),
141
+ });
142
+
143
+ const getEntityOptionsById = new GetEntityOptionsById({
144
+ moduleRepository,
145
+ moduleDefinitions:
146
+ getModulesDefinitionFromIntegrationClasses(integrationClasses),
147
+ });
148
+
149
+ const refreshEntityOptions = new RefreshEntityOptions({
150
+ moduleRepository,
151
+ moduleDefinitions:
152
+ getModulesDefinitionFromIntegrationClasses(integrationClasses),
153
+ });
154
+
155
+ const getPossibleIntegrations = new GetPossibleIntegrations({
156
+ integrationClasses,
157
+ });
158
+
159
+ const processAuthorizationCallback = new ProcessAuthorizationCallback({
160
+ moduleRepository,
161
+ credentialRepository,
162
+ moduleDefinitions:
163
+ getModulesDefinitionFromIntegrationClasses(integrationClasses),
164
+ });
165
+
166
+ const router = express();
167
+
168
+ setIntegrationRoutes(router, getUserFromBearerToken, {
169
+ createIntegration,
170
+ deleteIntegrationForUser,
171
+ getIntegrationsForUser,
172
+ getEntitiesForUser,
173
+ getIntegrationInstance,
174
+ updateIntegration,
175
+ getPossibleIntegrations,
176
+ });
177
+ setEntityRoutes(router, getUserFromBearerToken, {
178
+ getCredentialForUser,
179
+ getModuleInstanceFromType,
180
+ getEntityOptionsByType,
181
+ testModuleAuth,
182
+ getModule,
183
+ getEntityOptionsById,
184
+ refreshEntityOptions,
185
+ processAuthorizationCallback,
186
+ });
22
187
  return router;
23
188
  }
24
189
 
@@ -46,111 +211,105 @@ function checkRequiredParams(params, requiredKeys) {
46
211
  return returnDict;
47
212
  }
48
213
 
49
- function setIntegrationRoutes(router, factory, getUserId) {
50
- const { moduleFactory, integrationFactory, IntegrationHelper } = factory;
214
+ /**
215
+ * Sets up integration-related routes on the provided Express router
216
+ * @param {express.Router} router - Express router instance to add routes to
217
+ * @param {import('../user/use-cases/get-user-from-bearer-token').GetUserFromBearerToken} getUserFromBearerToken - Use case for retrieving a user from a bearer token
218
+ * @param {Object} useCases - use cases for integration management
219
+ */
220
+ function setIntegrationRoutes(router, getUserFromBearerToken, useCases) {
221
+ const {
222
+ createIntegration,
223
+ deleteIntegrationForUser,
224
+ getIntegrationsForUser,
225
+ getEntitiesForUser,
226
+ getIntegrationInstance,
227
+ updateIntegration,
228
+ getPossibleIntegrations,
229
+ } = useCases;
51
230
  router.route('/api/integrations').get(
52
231
  catchAsyncError(async (req, res) => {
53
- const results = await integrationFactory.getIntegrationOptions();
54
- results.entities.authorized =
55
- await moduleFactory.getEntitiesForUser(getUserId(req));
56
- results.integrations =
57
- await IntegrationHelper.getIntegrationsForUserId(
58
- getUserId(req)
59
- );
232
+ const user = await getUserFromBearerToken.execute(
233
+ req.headers.authorization
234
+ );
235
+ const userId = user.getId();
236
+ const integrations = await getIntegrationsForUser.execute(userId);
237
+ const results = {
238
+ entities: {
239
+ options: await getPossibleIntegrations.execute(),
240
+ authorized: await getEntitiesForUser.execute(userId),
241
+ },
242
+ integrations: integrations,
243
+ };
60
244
 
61
- for (const integrationRecord of results.integrations) {
62
- const integration =
63
- await integrationFactory.getInstanceFromIntegrationId({
64
- integrationId: integrationRecord.id,
65
- userId: getUserId(req),
66
- });
67
- integrationRecord.userActions = integration.userActions;
68
- }
69
245
  res.json(results);
70
246
  })
71
247
  );
72
248
 
73
249
  router.route('/api/integrations').post(
74
250
  catchAsyncError(async (req, res) => {
251
+ const user = await getUserFromBearerToken.execute(
252
+ req.headers.authorization
253
+ );
254
+ const userId = user.getId();
75
255
  const params = checkRequiredParams(req.body, [
76
256
  'entities',
77
257
  'config',
78
258
  ]);
79
- // throw if not value
259
+
80
260
  get(params.config, 'type');
81
261
 
82
- // create integration
83
- const integration = await integrationFactory.createIntegration(
262
+ const integration = await createIntegration.execute(
84
263
  params.entities,
85
- getUserId(req),
264
+ userId,
86
265
  params.config
87
266
  );
88
267
 
89
- // post integration initialization
90
- debug(
91
- `Calling onCreate on the ${integration?.constructor?.Config?.name} Integration with no arguments`
92
- );
93
- await integration.send('ON_CREATE', {});
94
-
95
- res.status(201).json(
96
- await IntegrationHelper.getFormattedIntegration(
97
- integration.record
98
- )
99
- );
268
+ res.status(201).json(integration);
100
269
  })
101
270
  );
102
271
 
103
272
  router.route('/api/integrations/:integrationId').patch(
104
273
  catchAsyncError(async (req, res) => {
105
- const params = checkRequiredParams(req.body, ['config']);
106
-
107
- const integration =
108
- await integrationFactory.getInstanceFromIntegrationId({
109
- integrationId: req.params.integrationId,
110
- userId: getUserId(req),
111
- });
112
-
113
- debug(
114
- `Calling onUpdate on the ${integration?.constructor?.Config?.name} Integration arguments: `,
115
- params
274
+ const user = await getUserFromBearerToken.execute(
275
+ req.headers.authorization
116
276
  );
117
- await integration.send('ON_UPDATE', params);
277
+ const userId = user.getId();
278
+ const params = checkRequiredParams(req.body, ['config']);
118
279
 
119
- res.json(
120
- await IntegrationHelper.getFormattedIntegration(
121
- integration.record
122
- )
280
+ const integration = await updateIntegration.execute(
281
+ req.params.integrationId,
282
+ userId,
283
+ params.config
123
284
  );
285
+ res.json(integration);
124
286
  })
125
287
  );
126
288
 
127
289
  router.route('/api/integrations/:integrationId').delete(
128
290
  catchAsyncError(async (req, res) => {
129
- const params = checkRequiredParams(req.params, ['integrationId']);
130
- const integration =
131
- await integrationFactory.getInstanceFromIntegrationId({
132
- userId: getUserId(req),
133
- integrationId: params.integrationId,
134
- });
135
-
136
- debug(
137
- `Calling onUpdate on the ${integration?.constructor?.Definition?.name} Integration with no arguments`
291
+ const user = await getUserFromBearerToken.execute(
292
+ req.headers.authorization
138
293
  );
139
- await integration.send('ON_DELETE');
140
- await IntegrationHelper.deleteIntegrationForUserById(
141
- getUserId(req),
142
- params.integrationId
294
+ const params = checkRequiredParams(req.params, ['integrationId']);
295
+ await deleteIntegrationForUser.execute(
296
+ params.integrationId,
297
+ user.getId()
143
298
  );
144
-
145
- res.status(201).json({});
299
+ res.status(204).json({});
146
300
  })
147
301
  );
148
302
 
149
303
  router.route('/api/integrations/:integrationId/config/options').get(
150
304
  catchAsyncError(async (req, res) => {
305
+ const user = await getUserFromBearerToken.execute(
306
+ req.headers.authorization
307
+ );
151
308
  const params = checkRequiredParams(req.params, ['integrationId']);
152
- const integration =
153
- await integrationFactory.getInstanceFromIntegrationId(params);
309
+ const integration = await getIntegrationInstance.execute(
310
+ params.integrationId,
311
+ user.getId()
312
+ );
154
313
  res.json(await integration.send('GET_CONFIG_OPTIONS'));
155
314
  })
156
315
  );
@@ -159,13 +318,16 @@ function setIntegrationRoutes(router, factory, getUserId) {
159
318
  .route('/api/integrations/:integrationId/config/options/refresh')
160
319
  .post(
161
320
  catchAsyncError(async (req, res) => {
321
+ const user = await getUserFromBearerToken.execute(
322
+ req.headers.authorization
323
+ );
162
324
  const params = checkRequiredParams(req.params, [
163
325
  'integrationId',
164
326
  ]);
165
- const integration =
166
- await integrationFactory.getInstanceFromIntegrationId(
167
- params
168
- );
327
+ const integration = await getIntegrationInstance.execute(
328
+ params.integrationId,
329
+ user.getId()
330
+ );
169
331
 
170
332
  res.json(
171
333
  await integration.send('REFRESH_CONFIG_OPTIONS', req.body)
@@ -174,9 +336,14 @@ function setIntegrationRoutes(router, factory, getUserId) {
174
336
  );
175
337
  router.route('/api/integrations/:integrationId/actions').all(
176
338
  catchAsyncError(async (req, res) => {
339
+ const user = await getUserFromBearerToken.execute(
340
+ req.headers.authorization
341
+ );
177
342
  const params = checkRequiredParams(req.params, ['integrationId']);
178
- const integration =
179
- await integrationFactory.getInstanceFromIntegrationId(params);
343
+ const integration = await getIntegrationInstance.execute(
344
+ params.integrationId,
345
+ user.getId()
346
+ );
180
347
  res.json(await integration.send('GET_USER_ACTIONS', req.body));
181
348
  })
182
349
  );
@@ -185,14 +352,17 @@ function setIntegrationRoutes(router, factory, getUserId) {
185
352
  .route('/api/integrations/:integrationId/actions/:actionId/options')
186
353
  .all(
187
354
  catchAsyncError(async (req, res) => {
355
+ const user = await getUserFromBearerToken.execute(
356
+ req.headers.authorization
357
+ );
188
358
  const params = checkRequiredParams(req.params, [
189
359
  'integrationId',
190
360
  'actionId',
191
361
  ]);
192
- const integration =
193
- await integrationFactory.getInstanceFromIntegrationId(
194
- params
195
- );
362
+ const integration = await getIntegrationInstance.execute(
363
+ params.integrationId,
364
+ user.getId()
365
+ );
196
366
 
197
367
  res.json(
198
368
  await integration.send('GET_USER_ACTION_OPTIONS', {
@@ -209,14 +379,17 @@ function setIntegrationRoutes(router, factory, getUserId) {
209
379
  )
210
380
  .post(
211
381
  catchAsyncError(async (req, res) => {
382
+ const user = await getUserFromBearerToken.execute(
383
+ req.headers.authorization
384
+ );
212
385
  const params = checkRequiredParams(req.params, [
213
386
  'integrationId',
214
387
  'actionId',
215
388
  ]);
216
- const integration =
217
- await integrationFactory.getInstanceFromIntegrationId(
218
- params
219
- );
389
+ const integration = await getIntegrationInstance.execute(
390
+ params.integrationId,
391
+ user.getId()
392
+ );
220
393
 
221
394
  res.json(
222
395
  await integration.send('REFRESH_USER_ACTION_OPTIONS', {
@@ -229,23 +402,37 @@ function setIntegrationRoutes(router, factory, getUserId) {
229
402
 
230
403
  router.route('/api/integrations/:integrationId/actions/:actionId').post(
231
404
  catchAsyncError(async (req, res) => {
405
+ const user = await getUserFromBearerToken.execute(
406
+ req.headers.authorization
407
+ );
232
408
  const params = checkRequiredParams(req.params, [
233
409
  'integrationId',
234
410
  'actionId',
235
411
  ]);
236
- const integration =
237
- await integrationFactory.getInstanceFromIntegrationId(params);
238
-
412
+ const integration = await getIntegrationInstance.execute(
413
+ params.integrationId,
414
+ user.getId()
415
+ );
239
416
  res.json(await integration.send(params.actionId, req.body));
240
417
  })
241
418
  );
242
419
 
243
420
  router.route('/api/integrations/:integrationId').get(
244
421
  catchAsyncError(async (req, res) => {
422
+ const user = await getUserFromBearerToken.execute(
423
+ req.headers.authorization
424
+ );
425
+
426
+ if (!user) {
427
+ throw Boom.forbidden('User not found');
428
+ }
429
+
245
430
  const params = checkRequiredParams(req.params, ['integrationId']);
246
- const integration = await IntegrationHelper.getIntegrationById(
247
- params.integrationId
431
+ const integration = await getIntegrationInstance.execute(
432
+ params.integrationId,
433
+ user.getId()
248
434
  );
435
+
249
436
  // We could perhaps augment router with dynamic options? Haven't decided yet, but here may be the place
250
437
 
251
438
  res.json({
@@ -259,12 +446,14 @@ function setIntegrationRoutes(router, factory, getUserId) {
259
446
 
260
447
  router.route('/api/integrations/:integrationId/test-auth').get(
261
448
  catchAsyncError(async (req, res) => {
449
+ const user = await getUserFromBearerToken.execute(
450
+ req.headers.authorization
451
+ );
262
452
  const params = checkRequiredParams(req.params, ['integrationId']);
263
- const instance =
264
- await integrationFactory.getInstanceFromIntegrationId({
265
- userId: getUserId(req),
266
- integrationId: params.integrationId,
267
- });
453
+ const instance = await getIntegrationInstance.execute(
454
+ params.integrationId,
455
+ user.getId()
456
+ );
268
457
 
269
458
  if (!instance) {
270
459
  throw Boom.notFound();
@@ -286,57 +475,73 @@ function setIntegrationRoutes(router, factory, getUserId) {
286
475
  );
287
476
  }
288
477
 
289
- function setEntityRoutes(router, factory, getUserId) {
290
- const { moduleFactory, IntegrationHelper } = factory;
291
- const getModuleInstance = async (req, entityType) => {
292
- if (!moduleFactory.checkIsValidType(entityType)) {
293
- throw Boom.badRequest(
294
- `Error: Invalid entity type of ${entityType}, options are ${moduleFactory.moduleTypes.join(
295
- ', '
296
- )}`
297
- );
298
- }
299
- return await moduleFactory.getInstanceFromTypeName(
300
- entityType,
301
- getUserId(req)
302
- );
303
- };
478
+ /**
479
+ * Sets up entity-related routes for the integration router
480
+ * @param {Object} router - Express router instance
481
+ * @param {import('../user/use-cases/get-user-from-bearer-token').GetUserFromBearerToken} getUserFromBearerToken - Use case for retrieving a user from a bearer token
482
+ */
483
+ function setEntityRoutes(router, getUserFromBearerToken, useCases) {
484
+ const {
485
+ getCredentialForUser,
486
+ getModuleInstanceFromType,
487
+ getEntityOptionsByType,
488
+ testModuleAuth,
489
+ getModule,
490
+ getEntityOptionsById,
491
+ refreshEntityOptions,
492
+ processAuthorizationCallback,
493
+ } = useCases;
304
494
 
305
495
  router.route('/api/authorize').get(
306
496
  catchAsyncError(async (req, res) => {
497
+ const user = await getUserFromBearerToken.execute(
498
+ req.headers.authorization
499
+ );
500
+ const userId = user.getId();
307
501
  const params = checkRequiredParams(req.query, ['entityType']);
308
- const module = await getModuleInstance(req, params.entityType);
502
+ const module = await getModuleInstanceFromType.execute(
503
+ userId,
504
+ params.entityType
505
+ );
309
506
  const areRequirementsValid =
310
507
  module.validateAuthorizationRequirements();
311
508
  if (!areRequirementsValid) {
312
509
  throw new Error(
313
- `Error: EntityManager of type ${params.entityType} requires a valid url`
510
+ `Error: Entity of type ${params.entityType} requires a valid url`
314
511
  );
315
512
  }
316
513
 
317
- res.json(await module.getAuthorizationRequirements());
514
+ res.json(module.getAuthorizationRequirements());
318
515
  })
319
516
  );
320
517
 
321
518
  router.route('/api/authorize').post(
322
519
  catchAsyncError(async (req, res) => {
520
+ const user = await getUserFromBearerToken.execute(
521
+ req.headers.authorization
522
+ );
523
+ const userId = user.getId();
323
524
  const params = checkRequiredParams(req.body, [
324
525
  'entityType',
325
526
  'data',
326
527
  ]);
327
- const module = await getModuleInstance(req, params.entityType);
328
528
 
329
- res.json(
330
- await module.processAuthorizationCallback({
331
- userId: getUserId(req),
332
- data: params.data,
333
- })
529
+ const entityDetails = await processAuthorizationCallback.execute(
530
+ userId,
531
+ params.entityType,
532
+ params.data
334
533
  );
534
+
535
+ res.json(entityDetails);
335
536
  })
336
537
  );
337
538
 
338
539
  router.route('/api/entity').post(
339
540
  catchAsyncError(async (req, res) => {
541
+ const user = await getUserFromBearerToken.execute(
542
+ req.headers.authorization
543
+ );
544
+ const userId = user.getId();
340
545
  const params = checkRequiredParams(req.body, [
341
546
  'entityType',
342
547
  'data',
@@ -344,20 +549,24 @@ function setEntityRoutes(router, factory, getUserId) {
344
549
  checkRequiredParams(req.body.data, ['credential_id']);
345
550
 
346
551
  // May want to pass along the user ID as well so credential ID's can't be fished???
347
- const credential = await IntegrationHelper.getCredentialById(
348
- params.data.credential_id
552
+ const credential = await getCredentialForUser.execute(
553
+ params.data.credential_id,
554
+ userId
349
555
  );
350
556
 
351
557
  if (!credential) {
352
558
  throw Boom.badRequest('Invalid credential ID');
353
559
  }
354
560
 
355
- const module = await getModuleInstance(req, params.entityType);
561
+ const module = await getModuleInstanceFromType.execute(
562
+ userId,
563
+ params.entityType
564
+ );
356
565
  const entityDetails = await module.getEntityDetails(
357
566
  module.api,
358
567
  null,
359
568
  null,
360
- getUserId(req)
569
+ userId
361
570
  );
362
571
 
363
572
  res.json(await module.findOrCreateEntity(entityDetails));
@@ -366,36 +575,42 @@ function setEntityRoutes(router, factory, getUserId) {
366
575
 
367
576
  router.route('/api/entity/options/:credentialId').get(
368
577
  catchAsyncError(async (req, res) => {
578
+ const user = await getUserFromBearerToken.execute(
579
+ req.headers.authorization
580
+ );
581
+ const userId = user.getId();
369
582
  // TODO May want to pass along the user ID as well so credential ID's can't be fished???
370
583
  // TODO **flagging this for review** -MW
371
- const credential = await IntegrationHelper.getCredentialById(
372
- req.params.credentialId
584
+ const credential = await getCredentialForUser.execute(
585
+ req.params.credentialId,
586
+ userId
373
587
  );
374
- if (credential.user._id.toString() !== getUserId(req)) {
588
+ if (credential.user._id.toString() !== userId) {
375
589
  throw Boom.forbidden('Credential does not belong to user');
376
590
  }
377
591
 
378
592
  const params = checkRequiredParams(req.query, ['entityType']);
379
- const module = await getModuleInstance(req, params.entityType);
593
+ const entityOptions = await getEntityOptionsByType.execute(
594
+ userId,
595
+ params.entityType
596
+ );
380
597
 
381
- res.json(await module.getEntityOptions());
598
+ res.json(entityOptions);
382
599
  })
383
600
  );
384
601
 
385
602
  router.route('/api/entities/:entityId/test-auth').get(
386
603
  catchAsyncError(async (req, res) => {
604
+ const user = await getUserFromBearerToken.execute(
605
+ req.headers.authorization
606
+ );
607
+ const userId = user.getId();
387
608
  const params = checkRequiredParams(req.params, ['entityId']);
388
- const module = await moduleFactory.getModuleInstanceFromEntityId(
609
+ const testAuthResponse = await testModuleAuth.execute(
389
610
  params.entityId,
390
- getUserId(req)
611
+ userId
391
612
  );
392
613
 
393
- if (!module) {
394
- throw Boom.notFound();
395
- }
396
-
397
- const testAuthResponse = await module.testAuth();
398
-
399
614
  if (!testAuthResponse) {
400
615
  res.status(400);
401
616
  res.json({
@@ -415,55 +630,48 @@ function setEntityRoutes(router, factory, getUserId) {
415
630
 
416
631
  router.route('/api/entities/:entityId').get(
417
632
  catchAsyncError(async (req, res) => {
418
- const params = checkRequiredParams(req.params, ['entityId']);
419
- const module = await moduleFactory.getModuleInstanceFromEntityId(
420
- params.entityId,
421
- getUserId(req)
633
+ const user = await getUserFromBearerToken.execute(
634
+ req.headers.authorization
422
635
  );
636
+ const userId = user.getId();
637
+ const params = checkRequiredParams(req.params, ['entityId']);
638
+ const module = await getModule.execute(params.entityId, userId);
423
639
 
424
- if (!module) {
425
- throw Boom.notFound();
426
- }
427
-
428
- res.json(module.entity);
640
+ res.json(module);
429
641
  })
430
642
  );
431
643
 
432
644
  router.route('/api/entities/:entityId/options').post(
433
645
  catchAsyncError(async (req, res) => {
434
- const params = checkRequiredParams(req.params, [
435
- 'entityId',
436
- getUserId(req),
437
- ]);
438
- const module = await moduleFactory.getModuleInstanceFromEntityId(
439
- params.entityId,
440
- getUserId(req)
646
+ const user = await getUserFromBearerToken.execute(
647
+ req.headers.authorization
441
648
  );
649
+ const userId = user.getId();
650
+ const params = checkRequiredParams(req.params, ['entityId']);
442
651
 
443
- if (!module) {
444
- throw Boom.notFound();
445
- }
652
+ const entityOptions = await getEntityOptionsById.execute(
653
+ params.entityId,
654
+ userId
655
+ );
446
656
 
447
- res.json(await module.getEntityOptions());
657
+ res.json(entityOptions);
448
658
  })
449
659
  );
450
660
 
451
661
  router.route('/api/entities/:entityId/options/refresh').post(
452
662
  catchAsyncError(async (req, res) => {
453
- const params = checkRequiredParams(req.params, [
454
- 'entityId',
455
- getUserId(req),
456
- ]);
457
- const module = await moduleFactory.getModuleInstanceFromEntityId(
663
+ const user = await getUserFromBearerToken.execute(
664
+ req.headers.authorization
665
+ );
666
+ const userId = user.getId();
667
+ const params = checkRequiredParams(req.params, ['entityId']);
668
+ const updatedOptions = await refreshEntityOptions.execute(
458
669
  params.entityId,
459
- getUserId(req)
670
+ userId,
671
+ req.body
460
672
  );
461
673
 
462
- if (!module) {
463
- throw Boom.notFound();
464
- }
465
-
466
- res.json(await module.refreshEntityOptions(req.body));
674
+ res.json(updatedOptions);
467
675
  })
468
676
  );
469
677
  }