@friggframework/core 2.0.0--canary.396.0dd37aa.0 → 2.0.0--canary.397.216d54b.0
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.
- package/handlers/backend-utils.js +24 -12
- package/handlers/routers/auth.js +1 -7
- package/handlers/routers/integration-defined-routers.js +3 -5
- package/handlers/workers/integration-defined-workers.js +5 -6
- package/index.js +0 -4
- package/integrations/index.js +0 -2
- package/integrations/integration-router.js +105 -176
- package/integrations/integration.js +51 -4
- package/integrations/use-cases/create-integration.js +20 -10
- package/integrations/use-cases/delete-integration-for-user.js +29 -5
- package/integrations/use-cases/get-integration-instance.js +73 -0
- package/integrations/use-cases/get-integrations-for-user.js +1 -1
- package/integrations/use-cases/{get-integration.js → update-integration.js} +12 -13
- package/integrations/utils/map-integration-dto.js +16 -1
- package/module-plugin/index.js +0 -2
- package/module-plugin/module-repository.js +13 -2
- package/module-plugin/module.js +10 -18
- package/module-plugin/use-cases/get-entity-options-by-id.js +58 -0
- package/module-plugin/use-cases/get-entity-options-by-type.js +34 -0
- package/module-plugin/use-cases/get-module-instance-from-type.js +31 -0
- package/module-plugin/use-cases/get-module.js +56 -0
- package/module-plugin/use-cases/refresh-entity-options.js +58 -0
- package/module-plugin/use-cases/test-module-auth.js +54 -0
- package/package.json +5 -5
- package/integrations/integration-factory.js +0 -195
- package/module-plugin/module-factory.js +0 -42
|
@@ -2,7 +2,6 @@ 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
5
|
const { IntegrationRepository } = require('./integration-repository');
|
|
7
6
|
const { DeleteIntegrationForUser } = require('./use-cases/delete-integration-for-user');
|
|
8
7
|
const { GetIntegrationsForUser } = require('./use-cases/get-integrations-for-user');
|
|
@@ -12,54 +11,42 @@ const { CreateIntegration } = require('./use-cases/create-integration');
|
|
|
12
11
|
const { ModuleService } = require('../module-plugin/module-service');
|
|
13
12
|
const { ModuleRepository } = require('../module-plugin/module-repository');
|
|
14
13
|
const { GetEntitiesForUser } = require('../module-plugin/use-cases/get-entities-for-user');
|
|
15
|
-
const {
|
|
16
|
-
|
|
17
|
-
} = require('
|
|
18
|
-
const {
|
|
14
|
+
const { loadAppDefinition } = require('../handlers/app-definition-loader');
|
|
15
|
+
const { GetIntegrationInstance } = require('./use-cases/get-integration-instance');
|
|
16
|
+
const { UpdateIntegration } = require('./use-cases/update-integration');
|
|
17
|
+
const { getModulesDefinitionFromIntegrationClasses } = require('./utils/map-integration-dto');
|
|
18
|
+
const { GetModuleInstanceFromType } = require('../module-plugin/use-cases/get-module-instance-from-type');
|
|
19
|
+
const { GetEntityOptionsByType } = require('../module-plugin/use-cases/get-entity-options-by-type');
|
|
20
|
+
const { TestModuleAuth } = require('../module-plugin/use-cases/test-module-auth');
|
|
21
|
+
const { GetModule } = require('../module-plugin/use-cases/get-module');
|
|
22
|
+
const { GetEntityOptionsById } = require('../module-plugin/use-cases/get-entity-options-by-id');
|
|
23
|
+
const { RefreshEntityOptions } = require('../module-plugin/use-cases/refresh-entity-options');
|
|
19
24
|
|
|
20
|
-
// todo: dont send moduleFactory and integrationFactory as a factory object, instead send them as separate params.
|
|
21
|
-
// todo: this could be a use case class
|
|
22
25
|
/**
|
|
23
26
|
* Creates an Express router with integration and entity routes configured
|
|
24
27
|
* @param {Object} params - Configuration parameters for the router
|
|
25
28
|
* @param {express.Router} [params.router] - Optional Express router instance, creates new one if not provided
|
|
26
|
-
* @param {Object} params.factory - Factory object containing moduleFactory and integrationFactory
|
|
27
|
-
* @param {Object} params.factory.moduleFactory - Factory for creating and managing API modules
|
|
28
|
-
* @param {Object} params.factory.integrationFactory - Factory for creating and managing integrations
|
|
29
29
|
* @param {import('../user/use-cases/get-user-from-bearer-token').GetUserFromBearerToken} params.getUserFromBearerToken - Use case for retrieving a user from a bearer token
|
|
30
30
|
* @returns {express.Router} Configured Express router with integration and entity routes
|
|
31
31
|
*/
|
|
32
32
|
function createIntegrationRouter(params) {
|
|
33
|
-
const { integrations } = loadAppDefinition();
|
|
33
|
+
const { integrations: integrationClasses } = loadAppDefinition();
|
|
34
34
|
const moduleRepository = new ModuleRepository();
|
|
35
35
|
const integrationRepository = new IntegrationRepository();
|
|
36
36
|
const credentialRepository = new CredentialRepository();
|
|
37
37
|
|
|
38
|
-
// todo: move this into a utils file
|
|
39
|
-
const getModules = () => {
|
|
40
|
-
return [
|
|
41
|
-
...new Set(
|
|
42
|
-
integrations
|
|
43
|
-
.map((integration) =>
|
|
44
|
-
Object.values(integration.Definition.modules).map(
|
|
45
|
-
(module) => module.definition
|
|
46
|
-
)
|
|
47
|
-
)
|
|
48
|
-
.flat()
|
|
49
|
-
),
|
|
50
|
-
];
|
|
51
|
-
};
|
|
52
38
|
const moduleService = new ModuleService({
|
|
53
39
|
moduleRepository,
|
|
54
|
-
moduleDefinitions:
|
|
40
|
+
moduleDefinitions: getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
55
41
|
});
|
|
56
42
|
const deleteIntegrationForUser = new DeleteIntegrationForUser({
|
|
57
43
|
integrationRepository,
|
|
44
|
+
integrationClasses,
|
|
58
45
|
});
|
|
59
46
|
|
|
60
47
|
const getIntegrationsForUser = new GetIntegrationsForUser({
|
|
61
48
|
integrationRepository,
|
|
62
|
-
integrationClasses
|
|
49
|
+
integrationClasses,
|
|
63
50
|
moduleService,
|
|
64
51
|
moduleRepository,
|
|
65
52
|
});
|
|
@@ -68,35 +55,74 @@ function createIntegrationRouter(params) {
|
|
|
68
55
|
});
|
|
69
56
|
const createIntegration = new CreateIntegration({
|
|
70
57
|
integrationRepository,
|
|
71
|
-
integrationClasses
|
|
58
|
+
integrationClasses,
|
|
72
59
|
moduleService,
|
|
73
60
|
});
|
|
74
61
|
|
|
75
|
-
const
|
|
62
|
+
const getEntitiesForUser = new GetEntitiesForUser({
|
|
76
63
|
moduleRepository,
|
|
77
|
-
moduleDefinitions:
|
|
64
|
+
moduleDefinitions: getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
78
65
|
});
|
|
79
66
|
|
|
80
|
-
const
|
|
67
|
+
const getIntegrationInstance = new GetIntegrationInstance({
|
|
81
68
|
integrationRepository,
|
|
82
|
-
integrationClasses
|
|
69
|
+
integrationClasses,
|
|
83
70
|
moduleService,
|
|
84
71
|
});
|
|
85
72
|
|
|
73
|
+
const updateIntegration = new UpdateIntegration({
|
|
74
|
+
integrationRepository,
|
|
75
|
+
integrationClasses,
|
|
76
|
+
moduleService,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const getModuleInstanceFromType = new GetModuleInstanceFromType({
|
|
80
|
+
moduleDefinitions: getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const getEntityOptionsByType = new GetEntityOptionsByType({
|
|
84
|
+
moduleDefinitions: getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const testModuleAuth = new TestModuleAuth({
|
|
88
|
+
moduleRepository,
|
|
89
|
+
moduleDefinitions: getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const getModule = new GetModule({
|
|
93
|
+
moduleRepository,
|
|
94
|
+
moduleDefinitions: getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const getEntityOptionsById = new GetEntityOptionsById({
|
|
98
|
+
moduleRepository,
|
|
99
|
+
moduleDefinitions: getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const refreshEntityOptions = new RefreshEntityOptions({
|
|
103
|
+
moduleRepository,
|
|
104
|
+
moduleDefinitions: getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
105
|
+
});
|
|
106
|
+
|
|
86
107
|
const router = get(params, 'router', express());
|
|
87
|
-
const factory = get(params, 'factory');
|
|
88
108
|
const getUserFromBearerToken = get(params, 'getUserFromBearerToken');
|
|
89
109
|
|
|
90
|
-
|
|
91
|
-
setIntegrationRoutes(router, factory, getUserFromBearerToken, integrations, {
|
|
110
|
+
setIntegrationRoutes(router, getUserFromBearerToken, {
|
|
92
111
|
createIntegration,
|
|
93
112
|
deleteIntegrationForUser,
|
|
94
113
|
getIntegrationsForUser,
|
|
95
|
-
|
|
96
|
-
|
|
114
|
+
getEntitiesForUser,
|
|
115
|
+
getIntegrationInstance,
|
|
116
|
+
updateIntegration,
|
|
97
117
|
});
|
|
98
|
-
setEntityRoutes(router,
|
|
118
|
+
setEntityRoutes(router, getUserFromBearerToken, {
|
|
99
119
|
getCredentialForUser,
|
|
120
|
+
getModuleInstanceFromType,
|
|
121
|
+
getEntityOptionsByType,
|
|
122
|
+
testModuleAuth,
|
|
123
|
+
getModule,
|
|
124
|
+
getEntityOptionsById,
|
|
125
|
+
refreshEntityOptions,
|
|
100
126
|
});
|
|
101
127
|
return router;
|
|
102
128
|
}
|
|
@@ -126,19 +152,17 @@ function checkRequiredParams(params, requiredKeys) {
|
|
|
126
152
|
/**
|
|
127
153
|
* Sets up integration-related routes on the provided Express router
|
|
128
154
|
* @param {express.Router} router - Express router instance to add routes to
|
|
129
|
-
* @param {Object} factory - Factory object containing moduleFactory and integrationFactory
|
|
130
|
-
* @param {Object} factory.moduleFactory - Factory for creating and managing API modules
|
|
131
|
-
* @param {Object} factory.integrationFactory - Factory for creating and managing integrations
|
|
132
155
|
* @param {import('../user/use-cases/get-user-from-bearer-token').GetUserFromBearerToken} getUserFromBearerToken - Use case for retrieving a user from a bearer token
|
|
156
|
+
* @param {Object} useCases - use cases for integration management
|
|
133
157
|
*/
|
|
134
|
-
function setIntegrationRoutes(router,
|
|
135
|
-
const { integrationFactory } = factory;
|
|
158
|
+
function setIntegrationRoutes(router, getUserFromBearerToken, useCases) {
|
|
136
159
|
const {
|
|
137
160
|
createIntegration,
|
|
138
161
|
deleteIntegrationForUser,
|
|
139
162
|
getIntegrationsForUser,
|
|
140
|
-
|
|
141
|
-
|
|
163
|
+
getEntitiesForUser,
|
|
164
|
+
getIntegrationInstance,
|
|
165
|
+
updateIntegration,
|
|
142
166
|
} = useCases;
|
|
143
167
|
router.route('/api/integrations').get(
|
|
144
168
|
catchAsyncError(async (req, res) => {
|
|
@@ -152,7 +176,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
152
176
|
options: integrations.map((integration) =>
|
|
153
177
|
integration.options
|
|
154
178
|
),
|
|
155
|
-
authorized: await
|
|
179
|
+
authorized: await getEntitiesForUser.execute(userId),
|
|
156
180
|
},
|
|
157
181
|
integrations: integrations,
|
|
158
182
|
}
|
|
@@ -171,27 +195,16 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
171
195
|
'entities',
|
|
172
196
|
'config',
|
|
173
197
|
]);
|
|
174
|
-
|
|
198
|
+
|
|
175
199
|
get(params.config, 'type');
|
|
176
200
|
|
|
177
|
-
// create integration
|
|
178
201
|
const integration = await createIntegration.execute(
|
|
179
202
|
params.entities,
|
|
180
203
|
userId,
|
|
181
204
|
params.config
|
|
182
205
|
);
|
|
183
206
|
|
|
184
|
-
|
|
185
|
-
debug(
|
|
186
|
-
`Calling onCreate on the ${integration?.constructor?.Config?.name} Integration with no arguments`
|
|
187
|
-
);
|
|
188
|
-
await integration.send('ON_CREATE', {});
|
|
189
|
-
|
|
190
|
-
res.status(201).json(
|
|
191
|
-
await integrationFactory.getFormattedIntegration(
|
|
192
|
-
integration.record
|
|
193
|
-
)
|
|
194
|
-
);
|
|
207
|
+
res.status(201).json(integration);
|
|
195
208
|
})
|
|
196
209
|
);
|
|
197
210
|
|
|
@@ -203,23 +216,8 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
203
216
|
const userId = user.getId();
|
|
204
217
|
const params = checkRequiredParams(req.body, ['config']);
|
|
205
218
|
|
|
206
|
-
const integration =
|
|
207
|
-
|
|
208
|
-
integrationId: req.params.integrationId,
|
|
209
|
-
userId,
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
debug(
|
|
213
|
-
`Calling onUpdate on the ${integration?.constructor?.Config?.name} Integration arguments: `,
|
|
214
|
-
params
|
|
215
|
-
);
|
|
216
|
-
await integration.send('ON_UPDATE', params);
|
|
217
|
-
|
|
218
|
-
res.json(
|
|
219
|
-
await integrationFactory.getFormattedIntegration(
|
|
220
|
-
integration.record
|
|
221
|
-
)
|
|
222
|
-
);
|
|
219
|
+
const integration = await updateIntegration.execute(req.params.integrationId, userId, params.config);
|
|
220
|
+
res.json(integration);
|
|
223
221
|
})
|
|
224
222
|
);
|
|
225
223
|
|
|
@@ -229,18 +227,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
229
227
|
req.headers.authorization
|
|
230
228
|
);
|
|
231
229
|
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
232
|
-
const integration =
|
|
233
|
-
await integrationFactory.getInstanceFromIntegrationId({
|
|
234
|
-
userId: user.getId(),
|
|
235
|
-
integrationId: params.integrationId,
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
debug(
|
|
239
|
-
`Calling onUpdate on the ${integration?.constructor?.Definition?.name} Integration with no arguments`
|
|
240
|
-
);
|
|
241
|
-
await integration.send('ON_DELETE');
|
|
242
230
|
await deleteIntegrationForUser.execute(params.integrationId, user.getId());
|
|
243
|
-
|
|
244
231
|
res.status(204).json({});
|
|
245
232
|
})
|
|
246
233
|
);
|
|
@@ -251,11 +238,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
251
238
|
req.headers.authorization
|
|
252
239
|
);
|
|
253
240
|
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
254
|
-
const integration =
|
|
255
|
-
await integrationFactory.getInstanceFromIntegrationId({
|
|
256
|
-
integrationId: params.integrationId,
|
|
257
|
-
userId: user.getId(),
|
|
258
|
-
});
|
|
241
|
+
const integration = await getIntegrationInstance.execute(params.integrationId, user.getId());
|
|
259
242
|
res.json(await integration.send('GET_CONFIG_OPTIONS'));
|
|
260
243
|
})
|
|
261
244
|
);
|
|
@@ -270,13 +253,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
270
253
|
const params = checkRequiredParams(req.params, [
|
|
271
254
|
'integrationId',
|
|
272
255
|
]);
|
|
273
|
-
const integration =
|
|
274
|
-
await integrationFactory.getInstanceFromIntegrationId(
|
|
275
|
-
{
|
|
276
|
-
integrationId: params.integrationId,
|
|
277
|
-
userId: user.getId(),
|
|
278
|
-
}
|
|
279
|
-
);
|
|
256
|
+
const integration = await getIntegrationInstance.execute(params.integrationId, user.getId());
|
|
280
257
|
|
|
281
258
|
res.json(
|
|
282
259
|
await integration.send('REFRESH_CONFIG_OPTIONS', req.body)
|
|
@@ -289,7 +266,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
289
266
|
req.headers.authorization
|
|
290
267
|
);
|
|
291
268
|
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
292
|
-
const integration = await
|
|
269
|
+
const integration = await getIntegrationInstance.execute(params.integrationId, user.getId());
|
|
293
270
|
res.json(await integration.send('GET_USER_ACTIONS', req.body));
|
|
294
271
|
})
|
|
295
272
|
);
|
|
@@ -305,13 +282,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
305
282
|
'integrationId',
|
|
306
283
|
'actionId',
|
|
307
284
|
]);
|
|
308
|
-
const integration =
|
|
309
|
-
await integrationFactory.getInstanceFromIntegrationId(
|
|
310
|
-
{
|
|
311
|
-
integrationId: params.integrationId,
|
|
312
|
-
userId: user.getId(),
|
|
313
|
-
}
|
|
314
|
-
);
|
|
285
|
+
const integration = await getIntegrationInstance.execute(params.integrationId, user.getId());
|
|
315
286
|
|
|
316
287
|
res.json(
|
|
317
288
|
await integration.send('GET_USER_ACTION_OPTIONS', {
|
|
@@ -335,13 +306,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
335
306
|
'integrationId',
|
|
336
307
|
'actionId',
|
|
337
308
|
]);
|
|
338
|
-
const integration =
|
|
339
|
-
await integrationFactory.getInstanceFromIntegrationId(
|
|
340
|
-
{
|
|
341
|
-
integrationId: params.integrationId,
|
|
342
|
-
userId: user.getId(),
|
|
343
|
-
}
|
|
344
|
-
);
|
|
309
|
+
const integration = await getIntegrationInstance.execute(params.integrationId, user.getId());
|
|
345
310
|
|
|
346
311
|
res.json(
|
|
347
312
|
await integration.send('REFRESH_USER_ACTION_OPTIONS', {
|
|
@@ -361,12 +326,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
361
326
|
'integrationId',
|
|
362
327
|
'actionId',
|
|
363
328
|
]);
|
|
364
|
-
const integration =
|
|
365
|
-
await integrationFactory.getInstanceFromIntegrationId({
|
|
366
|
-
integrationId: params.integrationId,
|
|
367
|
-
userId: user.getId(),
|
|
368
|
-
});
|
|
369
|
-
|
|
329
|
+
const integration = await getIntegrationInstance.execute(params.integrationId, user.getId());
|
|
370
330
|
res.json(await integration.send(params.actionId, req.body));
|
|
371
331
|
})
|
|
372
332
|
);
|
|
@@ -400,13 +360,8 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
400
360
|
const user = await getUserFromBearerToken.execute(
|
|
401
361
|
req.headers.authorization
|
|
402
362
|
);
|
|
403
|
-
const userId = user.getId();
|
|
404
363
|
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
405
|
-
const instance =
|
|
406
|
-
await integrationFactory.getInstanceFromIntegrationId({
|
|
407
|
-
userId,
|
|
408
|
-
integrationId: params.integrationId,
|
|
409
|
-
});
|
|
364
|
+
const instance = await getIntegrationInstance.execute(params.integrationId, user.getId());
|
|
410
365
|
|
|
411
366
|
if (!instance) {
|
|
412
367
|
throw Boom.notFound();
|
|
@@ -432,25 +387,18 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
432
387
|
/**
|
|
433
388
|
* Sets up entity-related routes for the integration router
|
|
434
389
|
* @param {Object} router - Express router instance
|
|
435
|
-
* @param {Object} factory - Factory object containing moduleFactory
|
|
436
390
|
* @param {import('../user/use-cases/get-user-from-bearer-token').GetUserFromBearerToken} getUserFromBearerToken - Use case for retrieving a user from a bearer token
|
|
437
391
|
*/
|
|
438
|
-
function setEntityRoutes(router,
|
|
439
|
-
const {
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
}
|
|
449
|
-
return await moduleFactory.getInstanceFromTypeName(
|
|
450
|
-
entityType,
|
|
451
|
-
userId
|
|
452
|
-
);
|
|
453
|
-
};
|
|
392
|
+
function setEntityRoutes(router, getUserFromBearerToken, useCases) {
|
|
393
|
+
const {
|
|
394
|
+
getCredentialForUser,
|
|
395
|
+
getModuleInstanceFromType,
|
|
396
|
+
getEntityOptionsByType,
|
|
397
|
+
testModuleAuth,
|
|
398
|
+
getModule,
|
|
399
|
+
getEntityOptionsById,
|
|
400
|
+
refreshEntityOptions,
|
|
401
|
+
} = useCases;
|
|
454
402
|
|
|
455
403
|
router.route('/api/authorize').get(
|
|
456
404
|
catchAsyncError(async (req, res) => {
|
|
@@ -459,7 +407,7 @@ function setEntityRoutes(router, factory, getUserFromBearerToken, useCases) {
|
|
|
459
407
|
);
|
|
460
408
|
const userId = user.getId();
|
|
461
409
|
const params = checkRequiredParams(req.query, ['entityType']);
|
|
462
|
-
const module = await
|
|
410
|
+
const module = await getModuleInstanceFromType.execute(userId, params.entityType);
|
|
463
411
|
const areRequirementsValid =
|
|
464
412
|
module.validateAuthorizationRequirements();
|
|
465
413
|
if (!areRequirementsValid) {
|
|
@@ -482,7 +430,7 @@ function setEntityRoutes(router, factory, getUserFromBearerToken, useCases) {
|
|
|
482
430
|
'entityType',
|
|
483
431
|
'data',
|
|
484
432
|
]);
|
|
485
|
-
const module = await
|
|
433
|
+
const module = await getModuleInstanceFromType.execute(userId, params.entityType);
|
|
486
434
|
|
|
487
435
|
res.json(
|
|
488
436
|
await module.processAuthorizationCallback({
|
|
@@ -515,7 +463,7 @@ function setEntityRoutes(router, factory, getUserFromBearerToken, useCases) {
|
|
|
515
463
|
throw Boom.badRequest('Invalid credential ID');
|
|
516
464
|
}
|
|
517
465
|
|
|
518
|
-
const module = await
|
|
466
|
+
const module = await getModuleInstanceFromType.execute(userId, params.entityType);
|
|
519
467
|
const entityDetails = await module.getEntityDetails(
|
|
520
468
|
module.api,
|
|
521
469
|
null,
|
|
@@ -544,9 +492,9 @@ function setEntityRoutes(router, factory, getUserFromBearerToken, useCases) {
|
|
|
544
492
|
}
|
|
545
493
|
|
|
546
494
|
const params = checkRequiredParams(req.query, ['entityType']);
|
|
547
|
-
const
|
|
495
|
+
const entityOptions = await getEntityOptionsByType.execute(userId, params.entityType);
|
|
548
496
|
|
|
549
|
-
res.json(
|
|
497
|
+
res.json(entityOptions);
|
|
550
498
|
})
|
|
551
499
|
);
|
|
552
500
|
|
|
@@ -557,17 +505,11 @@ function setEntityRoutes(router, factory, getUserFromBearerToken, useCases) {
|
|
|
557
505
|
);
|
|
558
506
|
const userId = user.getId();
|
|
559
507
|
const params = checkRequiredParams(req.params, ['entityId']);
|
|
560
|
-
const
|
|
508
|
+
const testAuthResponse = await testModuleAuth.execute(
|
|
561
509
|
params.entityId,
|
|
562
510
|
userId
|
|
563
511
|
);
|
|
564
512
|
|
|
565
|
-
if (!module) {
|
|
566
|
-
throw Boom.notFound();
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
const testAuthResponse = await module.testAuth();
|
|
570
|
-
|
|
571
513
|
if (!testAuthResponse) {
|
|
572
514
|
res.status(400);
|
|
573
515
|
res.json({
|
|
@@ -592,16 +534,12 @@ function setEntityRoutes(router, factory, getUserFromBearerToken, useCases) {
|
|
|
592
534
|
);
|
|
593
535
|
const userId = user.getId();
|
|
594
536
|
const params = checkRequiredParams(req.params, ['entityId']);
|
|
595
|
-
const module = await
|
|
537
|
+
const module = await getModule.execute(
|
|
596
538
|
params.entityId,
|
|
597
539
|
userId
|
|
598
540
|
);
|
|
599
541
|
|
|
600
|
-
|
|
601
|
-
throw Boom.notFound();
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
res.json(module.entity);
|
|
542
|
+
res.json(module);
|
|
605
543
|
})
|
|
606
544
|
);
|
|
607
545
|
|
|
@@ -614,16 +552,10 @@ function setEntityRoutes(router, factory, getUserFromBearerToken, useCases) {
|
|
|
614
552
|
const params = checkRequiredParams(req.params, [
|
|
615
553
|
'entityId',
|
|
616
554
|
]);
|
|
617
|
-
const module = await moduleFactory.getModuleInstanceFromEntityId(
|
|
618
|
-
params.entityId,
|
|
619
|
-
userId
|
|
620
|
-
);
|
|
621
555
|
|
|
622
|
-
|
|
623
|
-
throw Boom.notFound();
|
|
624
|
-
}
|
|
556
|
+
const entityOptions = await getEntityOptionsById.execute(params.entityId, userId);
|
|
625
557
|
|
|
626
|
-
res.json(
|
|
558
|
+
res.json(entityOptions);
|
|
627
559
|
})
|
|
628
560
|
);
|
|
629
561
|
|
|
@@ -636,16 +568,13 @@ function setEntityRoutes(router, factory, getUserFromBearerToken, useCases) {
|
|
|
636
568
|
const params = checkRequiredParams(req.params, [
|
|
637
569
|
'entityId',
|
|
638
570
|
]);
|
|
639
|
-
const
|
|
571
|
+
const updatedOptions = await refreshEntityOptions.execute(
|
|
640
572
|
params.entityId,
|
|
641
|
-
userId
|
|
573
|
+
userId,
|
|
574
|
+
req.body
|
|
642
575
|
);
|
|
643
576
|
|
|
644
|
-
|
|
645
|
-
throw Boom.notFound();
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
res.json(await module.refreshEntityOptions(req.body));
|
|
577
|
+
res.json(updatedOptions);
|
|
649
578
|
})
|
|
650
579
|
);
|
|
651
580
|
}
|
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
const { Options } = require('./options');
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Integration (Domain Aggregate-Root)
|
|
5
|
+
* ----------------------------------
|
|
6
|
+
* This class represents a *configured* integration instance at runtime. It is
|
|
7
|
+
* deliberately split into **two layers**:
|
|
8
|
+
* 1. A *data snapshot* of the persisted record (id, userId, config, etc.).
|
|
9
|
+
* 2. A *behaviour* object: a concrete class supplied by the app developer
|
|
10
|
+
* that extends `IntegrationBase` and implements event handlers, user
|
|
11
|
+
* actions, custom routes, etc.
|
|
12
|
+
*
|
|
13
|
+
* The two layers are glued together via a **JavaScript `Proxy`**. When a
|
|
14
|
+
* property is requested on an `Integration` instance we:
|
|
15
|
+
* • Check if the property exists on the wrapper itself (data-layer).
|
|
16
|
+
* • Fallback to the behaviour instance (logic-layer).
|
|
17
|
+
* • If the value is a function we `.bind(this)` so that the function's
|
|
18
|
+
* `this` always points to the *wrapper* – giving it access to both data
|
|
19
|
+
* and behaviour transparently.
|
|
20
|
+
*
|
|
21
|
+
* This means you can treat a hydrated Integration as if it *were* the custom
|
|
22
|
+
* class:
|
|
23
|
+
*
|
|
24
|
+
* ```js
|
|
25
|
+
* const integration = await getIntegration.execute(id, userId);
|
|
26
|
+
* // `send` actually lives on IntegrationBase but is accessible here
|
|
27
|
+
* const actions = await integration.send('GET_USER_ACTIONS');
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* A corollary benefit is that **circular references stay internal**: the heavy
|
|
31
|
+
* `Module → Api → delegate` graph is never exposed when we later serialise the
|
|
32
|
+
* object to JSON – we map it to a DTO first.
|
|
33
|
+
*/
|
|
34
|
+
|
|
3
35
|
/**
|
|
4
36
|
* Integration Domain Entity
|
|
5
37
|
* Represents a configured integration with its data and behavior
|
|
@@ -15,7 +47,6 @@ class Integration {
|
|
|
15
47
|
status,
|
|
16
48
|
version,
|
|
17
49
|
messages,
|
|
18
|
-
entityReference,
|
|
19
50
|
integrationClass,
|
|
20
51
|
modules = {}
|
|
21
52
|
}) {
|
|
@@ -27,7 +58,6 @@ class Integration {
|
|
|
27
58
|
this.status = status;
|
|
28
59
|
this.version = version;
|
|
29
60
|
this.messages = messages;
|
|
30
|
-
this.entityReference = entityReference;
|
|
31
61
|
|
|
32
62
|
// Integration behavior (strategy pattern)
|
|
33
63
|
this.integrationClass = integrationClass;
|
|
@@ -38,7 +68,10 @@ class Integration {
|
|
|
38
68
|
// Initialize basic behavior (sync parts only)
|
|
39
69
|
this._initializeBasicBehavior();
|
|
40
70
|
|
|
41
|
-
//
|
|
71
|
+
// --- Behaviour delegation via Proxy --------------------------------
|
|
72
|
+
// The Proxy merges the *data layer* (this wrapper) with the *behaviour
|
|
73
|
+
// layer* (custom IntegrationBase subclass). Consumers don't have to
|
|
74
|
+
// know (or care) where a method/property is defined.
|
|
42
75
|
return new Proxy(this, {
|
|
43
76
|
get(target, prop) {
|
|
44
77
|
// First, check if property exists on Integration entity
|
|
@@ -76,6 +109,21 @@ class Integration {
|
|
|
76
109
|
// Copy events
|
|
77
110
|
this.events = this.behavior.events || {};
|
|
78
111
|
this.defaultEvents = this.behavior.defaultEvents || {};
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
// Expose behaviour instance methods directly on the wrapper so that
|
|
115
|
+
// early-bound handlers (created before behaviour existed) can still
|
|
116
|
+
// access them without falling back through the Proxy. This prevents
|
|
117
|
+
// `undefined` errors for methods like `loadDynamicUserActions` that
|
|
118
|
+
// may be invoked inside default event-handlers.
|
|
119
|
+
const proto = Object.getPrototypeOf(this.behavior);
|
|
120
|
+
for (const key of Object.getOwnPropertyNames(proto)) {
|
|
121
|
+
if (key === 'constructor') continue;
|
|
122
|
+
if (typeof proto[key] === 'function' && this[key] === undefined) {
|
|
123
|
+
// Bind to behaviour so internal `this` remains correct.
|
|
124
|
+
this[key] = proto[key].bind(this.behavior);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
79
127
|
}
|
|
80
128
|
}
|
|
81
129
|
|
|
@@ -176,7 +224,6 @@ class Integration {
|
|
|
176
224
|
status: this.status,
|
|
177
225
|
version: this.version,
|
|
178
226
|
messages: this.messages,
|
|
179
|
-
entityReference: this.entityReference,
|
|
180
227
|
// Expose userActions if they were loaded/attached elsewhere
|
|
181
228
|
userActions: this.userActions,
|
|
182
229
|
};
|
|
@@ -17,29 +17,39 @@ class CreateIntegration {
|
|
|
17
17
|
async execute(entities, userId, config) {
|
|
18
18
|
const integrationRecord = await this.integrationRepository.createIntegration(entities, userId, config);
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
|
|
21
|
+
// 2. Get the correct Integration class by type
|
|
22
|
+
const integrationClass = this.integrationClasses.find(
|
|
23
|
+
(integrationClass) => integrationClass.Definition.name === integrationRecord.config.type
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
if (!integrationClass) {
|
|
27
|
+
throw new Error(`No integration class found for type: ${integrationRecord.config.type}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const modules = [];
|
|
31
|
+
for (const entityId of integrationRecord.entitiesIds) {
|
|
22
32
|
const moduleInstance = await this.moduleService.getModuleInstance(
|
|
23
|
-
|
|
24
|
-
integrationRecord.
|
|
33
|
+
entityId,
|
|
34
|
+
integrationRecord.userId
|
|
25
35
|
);
|
|
26
|
-
modules
|
|
36
|
+
modules.push(moduleInstance);
|
|
27
37
|
}
|
|
28
38
|
|
|
29
39
|
const integrationInstance = new Integration({
|
|
30
|
-
id: integrationRecord.
|
|
31
|
-
userId: integrationRecord.
|
|
32
|
-
entities: integrationRecord.
|
|
40
|
+
id: integrationRecord.id,
|
|
41
|
+
userId: integrationRecord.userId,
|
|
42
|
+
entities: integrationRecord.entitiesIds,
|
|
33
43
|
config: integrationRecord.config,
|
|
34
44
|
status: integrationRecord.status,
|
|
35
45
|
version: integrationRecord.version,
|
|
36
46
|
messages: integrationRecord.messages,
|
|
37
|
-
|
|
38
|
-
integrationClass: this.integrationClasses[integrationRecord.config.type], // todo: check if this is correct
|
|
47
|
+
integrationClass: integrationClass,
|
|
39
48
|
modules
|
|
40
49
|
});
|
|
41
50
|
|
|
42
51
|
await integrationInstance.initialize();
|
|
52
|
+
await integrationInstance.send('ON_CREATE', {});
|
|
43
53
|
|
|
44
54
|
return mapIntegrationClassToIntegrationDTO(integrationInstance);
|
|
45
55
|
}
|