@friggframework/core 2.0.0--canary.396.accf516.0 → 2.0.0--canary.396.469364a.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 -2
- package/integrations/index.js +0 -2
- package/integrations/integration-repository.js +1 -1
- package/integrations/integration-router.js +44 -123
- 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-for-user.js +4 -4
- package/integrations/use-cases/get-integration-instance.js +73 -0
- package/integrations/use-cases/get-integrations-for-user.js +3 -3
- package/integrations/use-cases/{get-integration.js → update-integration.js} +27 -19
- package/integrations/utils/map-integration-dto.js +16 -1
- package/module-plugin/module-repository.js +21 -1
- package/package.json +5 -5
- package/integrations/integration-factory.js +0 -195
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
const { Router } = require('express');
|
|
2
2
|
const { Worker } = require('@friggframework/core');
|
|
3
|
+
const { loadAppDefinition } = require('./app-definition-loader');
|
|
4
|
+
const { IntegrationRepository } = require('../integrations/integration-repository');
|
|
5
|
+
const { ModuleService } = require('../module-plugin/module-service');
|
|
6
|
+
const { GetIntegrationInstance } = require('../integrations/use-cases/get-integration-instance');
|
|
3
7
|
|
|
4
8
|
const loadRouterFromObject = (IntegrationClass, routerObject) => {
|
|
5
9
|
const router = Router();
|
|
@@ -23,29 +27,37 @@ const loadRouterFromObject = (IntegrationClass, routerObject) => {
|
|
|
23
27
|
};
|
|
24
28
|
|
|
25
29
|
//todo: this should be in a use case class
|
|
26
|
-
const createQueueWorker = (integrationClass
|
|
30
|
+
const createQueueWorker = (integrationClass) => {
|
|
27
31
|
class QueueWorker extends Worker {
|
|
28
32
|
async _run(params, context) {
|
|
29
33
|
try {
|
|
30
|
-
let
|
|
34
|
+
let integrationInstance;
|
|
31
35
|
if (!params.integrationId) {
|
|
32
|
-
|
|
33
|
-
await
|
|
34
|
-
|
|
35
|
-
await instance.registerEventHandlers();
|
|
36
|
+
integrationInstance = new integrationClass();
|
|
37
|
+
await integrationInstance.loadModules();
|
|
38
|
+
await integrationInstance.registerEventHandlers();
|
|
36
39
|
console.log(
|
|
37
40
|
`${params.event} for ${integrationClass.Definition.name} integration with no integrationId`
|
|
38
41
|
);
|
|
39
42
|
} else {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
const { integrations: integrationClasses } = loadAppDefinition();
|
|
44
|
+
const integrationRepository = new IntegrationRepository();
|
|
45
|
+
const moduleService = new ModuleService();
|
|
46
|
+
|
|
47
|
+
const getIntegrationInstance = new GetIntegrationInstance({
|
|
48
|
+
integrationRepository,
|
|
49
|
+
integrationClasses,
|
|
50
|
+
moduleService,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// todo: are we going to have the userId available here?
|
|
54
|
+
integrationInstance = await getIntegrationInstance.execute(params.integrationId, params.userId);
|
|
44
55
|
console.log(
|
|
45
|
-
`${params.event} for ${
|
|
56
|
+
`${params.event} for ${integrationInstance.record.config.type} of integrationId: ${params.integrationId}`
|
|
46
57
|
);
|
|
47
58
|
}
|
|
48
|
-
|
|
59
|
+
|
|
60
|
+
const res = await integrationInstance.send(params.event, {
|
|
49
61
|
data: params.data,
|
|
50
62
|
context,
|
|
51
63
|
});
|
package/handlers/routers/auth.js
CHANGED
|
@@ -4,11 +4,9 @@ const {
|
|
|
4
4
|
loadAppDefinition,
|
|
5
5
|
} = require('../app-definition-loader');
|
|
6
6
|
const { UserRepository } = require('../../user/user-repository');
|
|
7
|
-
const { IntegrationFactory } = require('../../integrations/integration-factory');
|
|
8
7
|
const { GetUserFromBearerToken } = require('../../user/use-cases/get-user-from-bearer-token');
|
|
9
8
|
|
|
10
|
-
const {
|
|
11
|
-
const integrationFactory = new IntegrationFactory(integrations);
|
|
9
|
+
const { userConfig } = loadAppDefinition();
|
|
12
10
|
const userRepository = new UserRepository({ userConfig });
|
|
13
11
|
const getUserFromBearerToken = new GetUserFromBearerToken({
|
|
14
12
|
userRepository,
|
|
@@ -16,10 +14,6 @@ const getUserFromBearerToken = new GetUserFromBearerToken({
|
|
|
16
14
|
});
|
|
17
15
|
|
|
18
16
|
const router = createIntegrationRouter({
|
|
19
|
-
factory: {
|
|
20
|
-
moduleFactory: integrationFactory.moduleFactory,
|
|
21
|
-
integrationFactory,
|
|
22
|
-
},
|
|
23
17
|
getUserFromBearerToken,
|
|
24
18
|
});
|
|
25
19
|
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
const { createAppHandler } = require('./../app-handler-helpers');
|
|
2
2
|
const {
|
|
3
3
|
loadAppDefinition,
|
|
4
|
-
loadRouterFromObject,
|
|
5
4
|
} = require('../app-definition-loader');
|
|
6
5
|
const { Router } = require('express');
|
|
7
|
-
const {
|
|
6
|
+
const { loadRouterFromObject } = require('../backend-utils');
|
|
8
7
|
|
|
9
8
|
const handlers = {};
|
|
10
|
-
const { integrations } = loadAppDefinition();
|
|
11
|
-
const integrationFactory = new IntegrationFactory(integrations);
|
|
9
|
+
const { integrations: integrationClasses } = loadAppDefinition();
|
|
12
10
|
|
|
13
11
|
//todo: this should be in a use case class
|
|
14
|
-
for (const IntegrationClass of
|
|
12
|
+
for (const IntegrationClass of integrationClasses) {
|
|
15
13
|
const router = Router();
|
|
16
14
|
const basePath = `/api/${IntegrationClass.Definition.name}-integration`;
|
|
17
15
|
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
const { createHandler } = require('@friggframework/core');
|
|
2
|
-
const { loadAppDefinition
|
|
3
|
-
const {
|
|
2
|
+
const { loadAppDefinition } = require('../app-definition-loader');
|
|
3
|
+
const { createQueueWorker } = require('../backend-utils');
|
|
4
4
|
|
|
5
5
|
const handlers = {};
|
|
6
|
-
const { integrations } = loadAppDefinition();
|
|
7
|
-
const integrationFactory = new IntegrationFactory(integrations);
|
|
6
|
+
const { integrations: integrationClasses } = loadAppDefinition();
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
const defaultQueueWorker = createQueueWorker(IntegrationClass
|
|
8
|
+
integrationClasses.forEach((IntegrationClass) => {
|
|
9
|
+
const defaultQueueWorker = createQueueWorker(IntegrationClass);
|
|
11
10
|
|
|
12
11
|
handlers[`${IntegrationClass.Definition.name}`] = {
|
|
13
12
|
queueWorker: createHandler({
|
package/index.js
CHANGED
|
@@ -38,7 +38,6 @@ const {
|
|
|
38
38
|
IntegrationModel,
|
|
39
39
|
Options,
|
|
40
40
|
IntegrationMapping,
|
|
41
|
-
IntegrationFactory,
|
|
42
41
|
createIntegrationRouter,
|
|
43
42
|
checkRequiredParams,
|
|
44
43
|
} = require('./integrations/index');
|
|
@@ -105,7 +104,6 @@ module.exports = {
|
|
|
105
104
|
IntegrationModel,
|
|
106
105
|
Options,
|
|
107
106
|
IntegrationMapping,
|
|
108
|
-
IntegrationFactory,
|
|
109
107
|
checkRequiredParams,
|
|
110
108
|
createIntegrationRouter,
|
|
111
109
|
|
package/integrations/index.js
CHANGED
|
@@ -2,7 +2,6 @@ const { IntegrationBase } = require('./integration-base');
|
|
|
2
2
|
const { IntegrationModel } = require('./integration-model');
|
|
3
3
|
const { Options } = require('./options');
|
|
4
4
|
const { IntegrationMapping } = require('./integration-mapping');
|
|
5
|
-
const { IntegrationFactory } = require('./integration-factory');
|
|
6
5
|
const { createIntegrationRouter, checkRequiredParams } = require('./integration-router');
|
|
7
6
|
|
|
8
7
|
module.exports = {
|
|
@@ -10,7 +9,6 @@ module.exports = {
|
|
|
10
9
|
IntegrationModel,
|
|
11
10
|
Options,
|
|
12
11
|
IntegrationMapping,
|
|
13
|
-
IntegrationFactory,
|
|
14
12
|
createIntegrationRouter,
|
|
15
13
|
checkRequiredParams,
|
|
16
14
|
};
|
|
@@ -23,7 +23,7 @@ class IntegrationRepository {
|
|
|
23
23
|
return {
|
|
24
24
|
id: integrationRecord._id,
|
|
25
25
|
entitiesIds: integrationRecord.entities.map(e => e._id),
|
|
26
|
-
userId: integrationRecord.user,
|
|
26
|
+
userId: integrationRecord.user.toString(),
|
|
27
27
|
config: integrationRecord.config,
|
|
28
28
|
version: integrationRecord.version,
|
|
29
29
|
status: integrationRecord.status,
|
|
@@ -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,53 +11,36 @@ 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('
|
|
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
18
|
|
|
19
|
-
// todo: dont send moduleFactory and integrationFactory as a factory object, instead send them as separate params.
|
|
20
|
-
// todo: this could be a use case class
|
|
21
19
|
/**
|
|
22
20
|
* Creates an Express router with integration and entity routes configured
|
|
23
21
|
* @param {Object} params - Configuration parameters for the router
|
|
24
22
|
* @param {express.Router} [params.router] - Optional Express router instance, creates new one if not provided
|
|
25
|
-
* @param {Object} params.factory - Factory object containing moduleFactory and integrationFactory
|
|
26
|
-
* @param {Object} params.factory.moduleFactory - Factory for creating and managing API modules
|
|
27
|
-
* @param {Object} params.factory.integrationFactory - Factory for creating and managing integrations
|
|
28
23
|
* @param {import('../user/use-cases/get-user-from-bearer-token').GetUserFromBearerToken} params.getUserFromBearerToken - Use case for retrieving a user from a bearer token
|
|
29
24
|
* @returns {express.Router} Configured Express router with integration and entity routes
|
|
30
25
|
*/
|
|
31
26
|
function createIntegrationRouter(params) {
|
|
32
|
-
const { integrations } = loadAppDefinition();
|
|
27
|
+
const { integrations: integrationClasses } = loadAppDefinition();
|
|
33
28
|
const moduleRepository = new ModuleRepository();
|
|
34
29
|
const integrationRepository = new IntegrationRepository();
|
|
35
30
|
const credentialRepository = new CredentialRepository();
|
|
36
31
|
|
|
37
|
-
// todo: move this into a utils file
|
|
38
|
-
const getModules = () => {
|
|
39
|
-
return [
|
|
40
|
-
...new Set(
|
|
41
|
-
integrations
|
|
42
|
-
.map((integration) =>
|
|
43
|
-
Object.values(integration.Definition.modules).map(
|
|
44
|
-
(module) => module.definition
|
|
45
|
-
)
|
|
46
|
-
)
|
|
47
|
-
.flat()
|
|
48
|
-
),
|
|
49
|
-
];
|
|
50
|
-
};
|
|
51
32
|
const moduleService = new ModuleService({
|
|
52
33
|
moduleRepository,
|
|
53
|
-
moduleDefinitions:
|
|
34
|
+
moduleDefinitions: getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
54
35
|
});
|
|
55
36
|
const deleteIntegrationForUser = new DeleteIntegrationForUser({
|
|
56
37
|
integrationRepository,
|
|
38
|
+
integrationClasses,
|
|
57
39
|
});
|
|
58
40
|
|
|
59
41
|
const getIntegrationsForUser = new GetIntegrationsForUser({
|
|
60
42
|
integrationRepository,
|
|
61
|
-
integrationClasses
|
|
43
|
+
integrationClasses,
|
|
62
44
|
moduleService,
|
|
63
45
|
moduleRepository,
|
|
64
46
|
});
|
|
@@ -67,25 +49,38 @@ function createIntegrationRouter(params) {
|
|
|
67
49
|
});
|
|
68
50
|
const createIntegration = new CreateIntegration({
|
|
69
51
|
integrationRepository,
|
|
70
|
-
integrationClasses
|
|
52
|
+
integrationClasses,
|
|
71
53
|
moduleService,
|
|
72
54
|
});
|
|
73
55
|
|
|
74
|
-
const
|
|
56
|
+
const getEntitiesForUser = new GetEntitiesForUser({
|
|
75
57
|
moduleRepository,
|
|
76
|
-
moduleDefinitions:
|
|
58
|
+
moduleDefinitions: getModulesDefinitionFromIntegrationClasses(integrationClasses),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const getIntegrationInstance = new GetIntegrationInstance({
|
|
62
|
+
integrationRepository,
|
|
63
|
+
integrationClasses,
|
|
64
|
+
moduleService,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const updateIntegration = new UpdateIntegration({
|
|
68
|
+
integrationRepository,
|
|
69
|
+
integrationClasses,
|
|
70
|
+
moduleService,
|
|
77
71
|
});
|
|
78
72
|
|
|
79
73
|
const router = get(params, 'router', express());
|
|
80
74
|
const factory = get(params, 'factory');
|
|
81
75
|
const getUserFromBearerToken = get(params, 'getUserFromBearerToken');
|
|
82
76
|
|
|
83
|
-
|
|
84
|
-
setIntegrationRoutes(router, factory, getUserFromBearerToken, integrations, {
|
|
77
|
+
setIntegrationRoutes(router, getUserFromBearerToken, {
|
|
85
78
|
createIntegration,
|
|
86
79
|
deleteIntegrationForUser,
|
|
87
80
|
getIntegrationsForUser,
|
|
88
|
-
|
|
81
|
+
getEntitiesForUser,
|
|
82
|
+
getIntegrationInstance,
|
|
83
|
+
updateIntegration,
|
|
89
84
|
});
|
|
90
85
|
setEntityRoutes(router, factory, getUserFromBearerToken, {
|
|
91
86
|
getCredentialForUser,
|
|
@@ -118,18 +113,17 @@ function checkRequiredParams(params, requiredKeys) {
|
|
|
118
113
|
/**
|
|
119
114
|
* Sets up integration-related routes on the provided Express router
|
|
120
115
|
* @param {express.Router} router - Express router instance to add routes to
|
|
121
|
-
* @param {Object} factory - Factory object containing moduleFactory and integrationFactory
|
|
122
|
-
* @param {Object} factory.moduleFactory - Factory for creating and managing API modules
|
|
123
|
-
* @param {Object} factory.integrationFactory - Factory for creating and managing integrations
|
|
124
116
|
* @param {import('../user/use-cases/get-user-from-bearer-token').GetUserFromBearerToken} getUserFromBearerToken - Use case for retrieving a user from a bearer token
|
|
117
|
+
* @param {Object} useCases - use cases for integration management
|
|
125
118
|
*/
|
|
126
|
-
function setIntegrationRoutes(router,
|
|
127
|
-
const { integrationFactory } = factory;
|
|
119
|
+
function setIntegrationRoutes(router, getUserFromBearerToken, useCases) {
|
|
128
120
|
const {
|
|
129
121
|
createIntegration,
|
|
130
122
|
deleteIntegrationForUser,
|
|
131
123
|
getIntegrationsForUser,
|
|
132
|
-
|
|
124
|
+
getEntitiesForUser,
|
|
125
|
+
getIntegrationInstance,
|
|
126
|
+
updateIntegration,
|
|
133
127
|
} = useCases;
|
|
134
128
|
router.route('/api/integrations').get(
|
|
135
129
|
catchAsyncError(async (req, res) => {
|
|
@@ -143,7 +137,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
143
137
|
options: integrations.map((integration) =>
|
|
144
138
|
integration.options
|
|
145
139
|
),
|
|
146
|
-
authorized: await
|
|
140
|
+
authorized: await getEntitiesForUser.execute(userId),
|
|
147
141
|
},
|
|
148
142
|
integrations: integrations,
|
|
149
143
|
}
|
|
@@ -162,27 +156,16 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
162
156
|
'entities',
|
|
163
157
|
'config',
|
|
164
158
|
]);
|
|
165
|
-
|
|
159
|
+
|
|
166
160
|
get(params.config, 'type');
|
|
167
161
|
|
|
168
|
-
// create integration
|
|
169
162
|
const integration = await createIntegration.execute(
|
|
170
163
|
params.entities,
|
|
171
164
|
userId,
|
|
172
165
|
params.config
|
|
173
166
|
);
|
|
174
167
|
|
|
175
|
-
|
|
176
|
-
debug(
|
|
177
|
-
`Calling onCreate on the ${integration?.constructor?.Config?.name} Integration with no arguments`
|
|
178
|
-
);
|
|
179
|
-
await integration.send('ON_CREATE', {});
|
|
180
|
-
|
|
181
|
-
res.status(201).json(
|
|
182
|
-
await integrationFactory.getFormattedIntegration(
|
|
183
|
-
integration.record
|
|
184
|
-
)
|
|
185
|
-
);
|
|
168
|
+
res.status(201).json(integration);
|
|
186
169
|
})
|
|
187
170
|
);
|
|
188
171
|
|
|
@@ -194,23 +177,8 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
194
177
|
const userId = user.getId();
|
|
195
178
|
const params = checkRequiredParams(req.body, ['config']);
|
|
196
179
|
|
|
197
|
-
const integration =
|
|
198
|
-
|
|
199
|
-
integrationId: req.params.integrationId,
|
|
200
|
-
userId,
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
debug(
|
|
204
|
-
`Calling onUpdate on the ${integration?.constructor?.Config?.name} Integration arguments: `,
|
|
205
|
-
params
|
|
206
|
-
);
|
|
207
|
-
await integration.send('ON_UPDATE', params);
|
|
208
|
-
|
|
209
|
-
res.json(
|
|
210
|
-
await integrationFactory.getFormattedIntegration(
|
|
211
|
-
integration.record
|
|
212
|
-
)
|
|
213
|
-
);
|
|
180
|
+
const integration = await updateIntegration.execute(req.params.integrationId, userId, params.config);
|
|
181
|
+
res.json(integration);
|
|
214
182
|
})
|
|
215
183
|
);
|
|
216
184
|
|
|
@@ -220,18 +188,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
220
188
|
req.headers.authorization
|
|
221
189
|
);
|
|
222
190
|
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
223
|
-
const integration =
|
|
224
|
-
await integrationFactory.getInstanceFromIntegrationId({
|
|
225
|
-
userId: user.getId(),
|
|
226
|
-
integrationId: params.integrationId,
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
debug(
|
|
230
|
-
`Calling onUpdate on the ${integration?.constructor?.Definition?.name} Integration with no arguments`
|
|
231
|
-
);
|
|
232
|
-
await integration.send('ON_DELETE');
|
|
233
191
|
await deleteIntegrationForUser.execute(params.integrationId, user.getId());
|
|
234
|
-
|
|
235
192
|
res.status(204).json({});
|
|
236
193
|
})
|
|
237
194
|
);
|
|
@@ -242,11 +199,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
242
199
|
req.headers.authorization
|
|
243
200
|
);
|
|
244
201
|
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
245
|
-
const integration =
|
|
246
|
-
await integrationFactory.getInstanceFromIntegrationId({
|
|
247
|
-
integrationId: params.integrationId,
|
|
248
|
-
userId: user.getId(),
|
|
249
|
-
});
|
|
202
|
+
const integration = await getIntegrationInstance.execute(params.integrationId, user.getId());
|
|
250
203
|
res.json(await integration.send('GET_CONFIG_OPTIONS'));
|
|
251
204
|
})
|
|
252
205
|
);
|
|
@@ -261,13 +214,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
261
214
|
const params = checkRequiredParams(req.params, [
|
|
262
215
|
'integrationId',
|
|
263
216
|
]);
|
|
264
|
-
const integration =
|
|
265
|
-
await integrationFactory.getInstanceFromIntegrationId(
|
|
266
|
-
{
|
|
267
|
-
integrationId: params.integrationId,
|
|
268
|
-
userId: user.getId(),
|
|
269
|
-
}
|
|
270
|
-
);
|
|
217
|
+
const integration = await getIntegrationInstance.execute(params.integrationId, user.getId());
|
|
271
218
|
|
|
272
219
|
res.json(
|
|
273
220
|
await integration.send('REFRESH_CONFIG_OPTIONS', req.body)
|
|
@@ -280,11 +227,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
280
227
|
req.headers.authorization
|
|
281
228
|
);
|
|
282
229
|
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
283
|
-
const integration =
|
|
284
|
-
await integrationFactory.getInstanceFromIntegrationId({
|
|
285
|
-
integrationId: params.integrationId,
|
|
286
|
-
userId: user.getId(),
|
|
287
|
-
});
|
|
230
|
+
const integration = await getIntegrationInstance.execute(params.integrationId, user.getId());
|
|
288
231
|
res.json(await integration.send('GET_USER_ACTIONS', req.body));
|
|
289
232
|
})
|
|
290
233
|
);
|
|
@@ -300,13 +243,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
300
243
|
'integrationId',
|
|
301
244
|
'actionId',
|
|
302
245
|
]);
|
|
303
|
-
const integration =
|
|
304
|
-
await integrationFactory.getInstanceFromIntegrationId(
|
|
305
|
-
{
|
|
306
|
-
integrationId: params.integrationId,
|
|
307
|
-
userId: user.getId(),
|
|
308
|
-
}
|
|
309
|
-
);
|
|
246
|
+
const integration = await getIntegrationInstance.execute(params.integrationId, user.getId());
|
|
310
247
|
|
|
311
248
|
res.json(
|
|
312
249
|
await integration.send('GET_USER_ACTION_OPTIONS', {
|
|
@@ -330,13 +267,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
330
267
|
'integrationId',
|
|
331
268
|
'actionId',
|
|
332
269
|
]);
|
|
333
|
-
const integration =
|
|
334
|
-
await integrationFactory.getInstanceFromIntegrationId(
|
|
335
|
-
{
|
|
336
|
-
integrationId: params.integrationId,
|
|
337
|
-
userId: user.getId(),
|
|
338
|
-
}
|
|
339
|
-
);
|
|
270
|
+
const integration = await getIntegrationInstance.execute(params.integrationId, user.getId());
|
|
340
271
|
|
|
341
272
|
res.json(
|
|
342
273
|
await integration.send('REFRESH_USER_ACTION_OPTIONS', {
|
|
@@ -356,12 +287,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
356
287
|
'integrationId',
|
|
357
288
|
'actionId',
|
|
358
289
|
]);
|
|
359
|
-
const integration =
|
|
360
|
-
await integrationFactory.getInstanceFromIntegrationId({
|
|
361
|
-
integrationId: params.integrationId,
|
|
362
|
-
userId: user.getId(),
|
|
363
|
-
});
|
|
364
|
-
|
|
290
|
+
const integration = await getIntegrationInstance.execute(params.integrationId, user.getId());
|
|
365
291
|
res.json(await integration.send(params.actionId, req.body));
|
|
366
292
|
})
|
|
367
293
|
);
|
|
@@ -395,13 +321,8 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken, integrati
|
|
|
395
321
|
const user = await getUserFromBearerToken.execute(
|
|
396
322
|
req.headers.authorization
|
|
397
323
|
);
|
|
398
|
-
const userId = user.getId();
|
|
399
324
|
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
400
|
-
const instance =
|
|
401
|
-
await integrationFactory.getInstanceFromIntegrationId({
|
|
402
|
-
userId,
|
|
403
|
-
integrationId: params.integrationId,
|
|
404
|
-
});
|
|
325
|
+
const instance = await getIntegrationInstance.execute(params.integrationId, user.getId());
|
|
405
326
|
|
|
406
327
|
if (!instance) {
|
|
407
328
|
throw Boom.notFound();
|
|
@@ -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
|
}
|
|
@@ -1,28 +1,52 @@
|
|
|
1
1
|
const Boom = require('@hapi/boom');
|
|
2
2
|
|
|
3
3
|
class DeleteIntegrationForUser {
|
|
4
|
-
constructor({ integrationRepository }) {
|
|
4
|
+
constructor({ integrationRepository, integrationClasses }) {
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @type {import('../integration-repository').IntegrationRepository}
|
|
8
8
|
*/
|
|
9
9
|
this.integrationRepository = integrationRepository;
|
|
10
|
+
this.integrationClasses = integrationClasses;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
async execute(integrationId, userId) {
|
|
13
|
-
const
|
|
14
|
+
const integrationRecord = await this.integrationRepository.findIntegrationById(integrationId);
|
|
14
15
|
|
|
15
|
-
if (!
|
|
16
|
+
if (!integrationRecord) {
|
|
16
17
|
throw Boom.notFound(
|
|
17
18
|
`Integration with id of ${integrationId} does not exist`
|
|
18
19
|
);
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
const integrationClass = this.integrationClasses.find(
|
|
23
|
+
(integrationClass) => integrationClass.Definition.name === integrationRecord.config.type
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
if (integrationRecord.userId !== userId) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Integration ${integrationId} does not belong to User ${userId}`
|
|
29
|
+
);
|
|
23
30
|
}
|
|
24
31
|
|
|
32
|
+
const integrationInstance = new Integration({
|
|
33
|
+
id: integrationRecord.id,
|
|
34
|
+
userId: integrationRecord.userId,
|
|
35
|
+
entities: integrationRecord.entitiesIds,
|
|
36
|
+
config: integrationRecord.config,
|
|
37
|
+
status: integrationRecord.status,
|
|
38
|
+
version: integrationRecord.version,
|
|
39
|
+
messages: integrationRecord.messages,
|
|
40
|
+
integrationClass: integrationClass,
|
|
41
|
+
modules: [],
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// 6. Complete async initialization (load dynamic actions, register handlers)
|
|
45
|
+
await integrationInstance.initialize();
|
|
46
|
+
await integrationInstance.send('ON_DELETE');
|
|
47
|
+
|
|
25
48
|
await this.integrationRepository.deleteIntegrationById(integrationId);
|
|
49
|
+
|
|
26
50
|
}
|
|
27
51
|
}
|
|
28
52
|
|
|
@@ -34,13 +34,13 @@ class GetIntegrationForUser {
|
|
|
34
34
|
(integrationClass) => integrationClass.Definition.name === integrationRecord.config.type
|
|
35
35
|
);
|
|
36
36
|
|
|
37
|
-
const modules =
|
|
38
|
-
for (const
|
|
37
|
+
const modules = [];
|
|
38
|
+
for (const entity of entities) {
|
|
39
39
|
const moduleInstance = await this.moduleService.getModuleInstance(
|
|
40
|
-
entity.
|
|
40
|
+
entity._id,
|
|
41
41
|
integrationRecord.user
|
|
42
42
|
);
|
|
43
|
-
modules
|
|
43
|
+
modules.push(moduleInstance);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
const integrationInstance = new Integration({
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const { Integration } = require('../integration');
|
|
2
|
+
|
|
3
|
+
class GetIntegrationInstance {
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @class GetIntegrationInstance
|
|
7
|
+
* @description Use case for retrieving a single integration instance by ID and user.
|
|
8
|
+
* @param {Object} params
|
|
9
|
+
* @param {import('../integration-repository').IntegrationRepository} params.integrationRepository - Repository for integration data access
|
|
10
|
+
* @param {Array<import('../integration').Integration>} params.integrationClasses - Array of available integration classes
|
|
11
|
+
* @param {import('../module-plugin/module-service').ModuleService} params.moduleService - Service for module instantiation and management
|
|
12
|
+
*/
|
|
13
|
+
constructor({
|
|
14
|
+
integrationRepository,
|
|
15
|
+
integrationClasses,
|
|
16
|
+
moduleService,
|
|
17
|
+
}) {
|
|
18
|
+
this.integrationRepository = integrationRepository;
|
|
19
|
+
this.integrationClasses = integrationClasses;
|
|
20
|
+
this.moduleService = moduleService;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async execute(integrationId, userId) {
|
|
24
|
+
const integrationRecord = await this.integrationRepository.findIntegrationById(integrationId);
|
|
25
|
+
|
|
26
|
+
if (!integrationRecord) {
|
|
27
|
+
throw new Error(`No integration found by the ID of ${integrationId}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const integrationClass = this.integrationClasses.find(
|
|
31
|
+
(integrationClass) => integrationClass.Definition.name === integrationRecord.config.type
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
if (!integrationClass) {
|
|
35
|
+
throw new Error(`No integration class found for type: ${integrationRecord.config.type}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (integrationRecord.userId !== userId) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Integration ${integrationId} does not belong to User ${userId}`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
const modules = [];
|
|
46
|
+
for (const entityId of integrationRecord.entitiesIds) {
|
|
47
|
+
const moduleInstance = await this.moduleService.getModuleInstance(
|
|
48
|
+
entityId,
|
|
49
|
+
integrationRecord.userId
|
|
50
|
+
);
|
|
51
|
+
modules.push(moduleInstance);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const integrationInstance = new Integration({
|
|
55
|
+
id: integrationRecord.id,
|
|
56
|
+
userId: integrationRecord.userId,
|
|
57
|
+
entities: integrationRecord.entitiesIds,
|
|
58
|
+
config: integrationRecord.config,
|
|
59
|
+
status: integrationRecord.status,
|
|
60
|
+
version: integrationRecord.version,
|
|
61
|
+
messages: integrationRecord.messages,
|
|
62
|
+
integrationClass: integrationClass,
|
|
63
|
+
modules
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
await integrationInstance.initialize();
|
|
68
|
+
|
|
69
|
+
return integrationInstance;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = { GetIntegrationInstance };
|
|
@@ -29,13 +29,13 @@ class GetIntegrationsForUser {
|
|
|
29
29
|
(integrationClass) => integrationClass.Definition.name === integrationRecord.config.type
|
|
30
30
|
);
|
|
31
31
|
|
|
32
|
-
const modules =
|
|
33
|
-
for (const
|
|
32
|
+
const modules = [];
|
|
33
|
+
for (const entity of entities) {
|
|
34
34
|
const moduleInstance = await this.moduleService.getModuleInstance(
|
|
35
35
|
entity.id,
|
|
36
36
|
integrationRecord.userId
|
|
37
37
|
);
|
|
38
|
-
modules
|
|
38
|
+
modules.push(moduleInstance);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
const integrationInstance = new Integration({
|
|
@@ -2,8 +2,16 @@ const { Integration } = require('../integration');
|
|
|
2
2
|
const { mapIntegrationClassToIntegrationDTO } = require('../utils/map-integration-dto');
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
class UpdateIntegration {
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @class UpdateIntegration
|
|
9
|
+
* @description Use case for updating a single integration by ID and user.
|
|
10
|
+
* @param {Object} params
|
|
11
|
+
* @param {import('../integration-repository').IntegrationRepository} params.integrationRepository - Repository for integration data access
|
|
12
|
+
* @param {Array<import('../integration').Integration>} params.integrationClasses - Array of available integration classes
|
|
13
|
+
* @param {import('../module-plugin/module-service').ModuleService} params.moduleService - Service for module instantiation and management
|
|
14
|
+
*/
|
|
7
15
|
constructor({
|
|
8
16
|
integrationRepository,
|
|
9
17
|
integrationClasses,
|
|
@@ -14,7 +22,7 @@ class GetIntegration {
|
|
|
14
22
|
this.moduleService = moduleService;
|
|
15
23
|
}
|
|
16
24
|
|
|
17
|
-
async execute(integrationId, userId) {
|
|
25
|
+
async execute(integrationId, userId, config) {
|
|
18
26
|
// 1. Get integration record from repository
|
|
19
27
|
const integrationRecord = await this.integrationRepository.findIntegrationById(integrationId);
|
|
20
28
|
|
|
@@ -22,42 +30,41 @@ class GetIntegration {
|
|
|
22
30
|
throw new Error(`No integration found by the ID of ${integrationId}`);
|
|
23
31
|
}
|
|
24
32
|
|
|
33
|
+
// 2. Get the correct Integration class by type
|
|
34
|
+
const integrationClass = this.integrationClasses.find(
|
|
35
|
+
(integrationClass) => integrationClass.Definition.name === integrationRecord.config.type
|
|
36
|
+
);
|
|
37
|
+
|
|
25
38
|
if (!integrationClass) {
|
|
26
39
|
throw new Error(`No integration class found for type: ${integrationRecord.config.type}`);
|
|
27
40
|
}
|
|
28
41
|
|
|
29
|
-
if (
|
|
42
|
+
if (integrationRecord.userId !== userId) {
|
|
30
43
|
throw new Error(
|
|
31
44
|
`Integration ${integrationId} does not belong to User ${userId}`
|
|
32
45
|
);
|
|
33
46
|
}
|
|
34
47
|
|
|
35
|
-
// 2. Get the correct Integration class by type
|
|
36
|
-
const integrationClass = this.integrationClasses.find(
|
|
37
|
-
(integrationClass) => integrationClass.Definition.name === integrationRecord.config.type
|
|
38
|
-
);
|
|
39
|
-
|
|
40
48
|
|
|
41
49
|
// 3. Load modules based on entity references
|
|
42
|
-
const modules =
|
|
43
|
-
for (const
|
|
50
|
+
const modules = [];
|
|
51
|
+
for (const entityId of integrationRecord.entitiesIds) {
|
|
44
52
|
const moduleInstance = await this.moduleService.getModuleInstance(
|
|
45
|
-
|
|
46
|
-
integrationRecord.
|
|
53
|
+
entityId,
|
|
54
|
+
integrationRecord.userId
|
|
47
55
|
);
|
|
48
|
-
modules
|
|
56
|
+
modules.push(moduleInstance);
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
// 4. Create the Integration domain entity with modules
|
|
52
60
|
const integrationInstance = new Integration({
|
|
53
|
-
id: integrationRecord.
|
|
54
|
-
userId: integrationRecord.
|
|
55
|
-
entities: integrationRecord.
|
|
61
|
+
id: integrationRecord.id,
|
|
62
|
+
userId: integrationRecord.userId,
|
|
63
|
+
entities: integrationRecord.entitiesIds,
|
|
56
64
|
config: integrationRecord.config,
|
|
57
65
|
status: integrationRecord.status,
|
|
58
66
|
version: integrationRecord.version,
|
|
59
67
|
messages: integrationRecord.messages,
|
|
60
|
-
entityReference: integrationRecord.entityReference,
|
|
61
68
|
integrationClass: integrationClass,
|
|
62
69
|
modules
|
|
63
70
|
});
|
|
@@ -65,9 +72,10 @@ class GetIntegration {
|
|
|
65
72
|
|
|
66
73
|
// 6. Complete async initialization (load dynamic actions, register handlers)
|
|
67
74
|
await integrationInstance.initialize();
|
|
75
|
+
await integrationInstance.send('ON_UPDATE', { config });
|
|
68
76
|
|
|
69
77
|
return mapIntegrationClassToIntegrationDTO(integrationInstance);
|
|
70
78
|
}
|
|
71
79
|
}
|
|
72
80
|
|
|
73
|
-
module.exports = {
|
|
81
|
+
module.exports = { UpdateIntegration };
|
|
@@ -19,4 +19,19 @@ function mapIntegrationClassToIntegrationDTO(integration) {
|
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
const getModulesDefinitionFromIntegrationClasses = (integrationClasses) => {
|
|
24
|
+
return [
|
|
25
|
+
...new Set(
|
|
26
|
+
integrationClasses
|
|
27
|
+
.map((integration) =>
|
|
28
|
+
Object.values(integration.Definition.modules).map(
|
|
29
|
+
(module) => module.definition
|
|
30
|
+
)
|
|
31
|
+
)
|
|
32
|
+
.flat()
|
|
33
|
+
),
|
|
34
|
+
];
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
module.exports = { mapIntegrationClassToIntegrationDTO, getModulesDefinitionFromIntegrationClasses };
|
|
@@ -22,7 +22,8 @@ class ModuleRepository {
|
|
|
22
22
|
async findEntitiesByIds(entitiesIds) {
|
|
23
23
|
const entitiesRecords = await Entity.find({ _id: { $in: entitiesIds } }, '', { lean: true }).populate('credential');
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
// todo: this is a workaround needed while we create an integration with the same entity twice
|
|
26
|
+
if (entitiesRecords.length !== entitiesIds.length && entitiesIds[0] !== entitiesIds[1]) {
|
|
26
27
|
throw new Error(`Some entities not found`);
|
|
27
28
|
}
|
|
28
29
|
|
|
@@ -45,6 +46,25 @@ class ModuleRepository {
|
|
|
45
46
|
{ lean: true }
|
|
46
47
|
);
|
|
47
48
|
}
|
|
49
|
+
|
|
50
|
+
async findEntitiesByIds(entityIds) {
|
|
51
|
+
const entities = await Entity.find(
|
|
52
|
+
{ _id: { $in: entityIds } },
|
|
53
|
+
'',
|
|
54
|
+
{ lean: true }
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
return entities.map(e => ({
|
|
58
|
+
id: e._id.toString(),
|
|
59
|
+
accountId: e.accountId,
|
|
60
|
+
credentialId: e.credential.toString(),
|
|
61
|
+
userId: e.user.toString(),
|
|
62
|
+
name: e.name,
|
|
63
|
+
externalId: e.externalId,
|
|
64
|
+
type: e.__t,
|
|
65
|
+
moduleName: e.moduleName,
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
48
68
|
}
|
|
49
69
|
|
|
50
70
|
module.exports = { ModuleRepository };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@friggframework/core",
|
|
3
3
|
"prettier": "@friggframework/prettier-config",
|
|
4
|
-
"version": "2.0.0--canary.396.
|
|
4
|
+
"version": "2.0.0--canary.396.469364a.0",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@hapi/boom": "^10.0.1",
|
|
7
7
|
"aws-sdk": "^2.1200.0",
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"uuid": "^9.0.1"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@friggframework/eslint-config": "2.0.0--canary.396.
|
|
26
|
-
"@friggframework/prettier-config": "2.0.0--canary.396.
|
|
27
|
-
"@friggframework/test": "2.0.0--canary.396.
|
|
25
|
+
"@friggframework/eslint-config": "2.0.0--canary.396.469364a.0",
|
|
26
|
+
"@friggframework/prettier-config": "2.0.0--canary.396.469364a.0",
|
|
27
|
+
"@friggframework/test": "2.0.0--canary.396.469364a.0",
|
|
28
28
|
"@types/lodash": "4.17.15",
|
|
29
29
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
30
30
|
"chai": "^4.3.6",
|
|
@@ -53,5 +53,5 @@
|
|
|
53
53
|
},
|
|
54
54
|
"homepage": "https://github.com/friggframework/frigg#readme",
|
|
55
55
|
"description": "",
|
|
56
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "469364a7e870439540866a1a8f35107e9e2aef18"
|
|
57
57
|
}
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
const { ModuleFactory, Entity } = require('../module-plugin');
|
|
2
|
-
const { IntegrationModel } = require('./integration-model');
|
|
3
|
-
|
|
4
|
-
class IntegrationFactory {
|
|
5
|
-
constructor(integrationClasses = []) {
|
|
6
|
-
this.integrationClasses = integrationClasses;
|
|
7
|
-
this.moduleFactory = new ModuleFactory(...this.getModules());
|
|
8
|
-
this.integrationTypes = this.integrationClasses.map(
|
|
9
|
-
(IntegrationClass) => IntegrationClass.getName()
|
|
10
|
-
);
|
|
11
|
-
this.getIntegrationDefinitions = this.integrationClasses.map(
|
|
12
|
-
(IntegrationClass) => IntegrationClass.Definition
|
|
13
|
-
);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async getIntegrationOptions() {
|
|
17
|
-
const options = this.integrationClasses.map(
|
|
18
|
-
(IntegrationClass) => IntegrationClass
|
|
19
|
-
);
|
|
20
|
-
return {
|
|
21
|
-
entities: {
|
|
22
|
-
options: options.map((IntegrationClass) =>
|
|
23
|
-
IntegrationClass.getOptionDetails()
|
|
24
|
-
),
|
|
25
|
-
authorized: [],
|
|
26
|
-
},
|
|
27
|
-
integrations: [],
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
getModules() {
|
|
32
|
-
return [
|
|
33
|
-
...new Set(
|
|
34
|
-
this.integrationClasses
|
|
35
|
-
.map((integration) =>
|
|
36
|
-
Object.values(integration.Definition.modules).map(
|
|
37
|
-
(module) => module.definition
|
|
38
|
-
)
|
|
39
|
-
)
|
|
40
|
-
.flat()
|
|
41
|
-
),
|
|
42
|
-
];
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
getIntegrationClassByType(type) {
|
|
46
|
-
const integrationClassIndex = this.integrationTypes.indexOf(type);
|
|
47
|
-
return this.integrationClasses[integrationClassIndex];
|
|
48
|
-
}
|
|
49
|
-
getModuleTypesAndKeys(integrationClass) {
|
|
50
|
-
const moduleTypesAndKeys = {};
|
|
51
|
-
const moduleTypeCount = {};
|
|
52
|
-
|
|
53
|
-
if (integrationClass && integrationClass.Definition.modules) {
|
|
54
|
-
for (const [key, moduleClass] of Object.entries(
|
|
55
|
-
integrationClass.Definition.modules
|
|
56
|
-
)) {
|
|
57
|
-
if (
|
|
58
|
-
moduleClass &&
|
|
59
|
-
typeof moduleClass.definition.getName === 'function'
|
|
60
|
-
) {
|
|
61
|
-
const moduleType = moduleClass.definition.getName();
|
|
62
|
-
|
|
63
|
-
// Check if this module type has already been seen
|
|
64
|
-
if (moduleType in moduleTypesAndKeys) {
|
|
65
|
-
throw new Error(
|
|
66
|
-
`Duplicate module type "${moduleType}" found in integration class definition.`
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Well how baout now
|
|
71
|
-
|
|
72
|
-
moduleTypesAndKeys[moduleType] = key;
|
|
73
|
-
moduleTypeCount[moduleType] =
|
|
74
|
-
(moduleTypeCount[moduleType] || 0) + 1;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Check for any module types with count > 1
|
|
80
|
-
for (const [moduleType, count] of Object.entries(moduleTypeCount)) {
|
|
81
|
-
if (count > 1) {
|
|
82
|
-
throw new Error(
|
|
83
|
-
`Multiple instances of module type "${moduleType}" found in integration class definition.`
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return moduleTypesAndKeys;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
async getInstanceFromIntegrationId({ integrationId, userId }) {
|
|
92
|
-
const integrationRecord = await IntegrationHelper.getIntegrationById(
|
|
93
|
-
integrationId
|
|
94
|
-
);
|
|
95
|
-
if (!integrationRecord) {
|
|
96
|
-
throw new Error(
|
|
97
|
-
`No integration found by the ID of ${integrationId}`
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (!userId) {
|
|
102
|
-
userId = integrationRecord.user._id.toString();
|
|
103
|
-
} else if (userId.toString() !== integrationRecord.user.toString()) {
|
|
104
|
-
throw new Error(
|
|
105
|
-
`Integration ${integrationId
|
|
106
|
-
} does not belong to User ${userId}, ${integrationRecord.user.toString()}`
|
|
107
|
-
);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// getIntegrationClassByType is only used here
|
|
111
|
-
const integrationClass = this.getIntegrationClassByType(
|
|
112
|
-
integrationRecord.config.type
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
// here we should instantiate an Integration Domain class along with the integration record and it should happen in a use case class
|
|
116
|
-
// Actually, this integrationClass is a subclass of IntegrationBase.
|
|
117
|
-
const instance = new integrationClass({
|
|
118
|
-
userId,
|
|
119
|
-
integrationId,
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
if (
|
|
123
|
-
integrationRecord.entityReference &&
|
|
124
|
-
Object.keys(integrationRecord.entityReference) > 0
|
|
125
|
-
) {
|
|
126
|
-
// Use the specified entityReference to find the modules and load them according to their key
|
|
127
|
-
// entityReference will be a map of entityIds with their corresponding desired key
|
|
128
|
-
for (const [entityId, key] of Object.entries(
|
|
129
|
-
integrationRecord.entityReference
|
|
130
|
-
)) {
|
|
131
|
-
const moduleInstance =
|
|
132
|
-
await this.moduleFactory.getModuleInstanceFromEntityId(
|
|
133
|
-
entityId,
|
|
134
|
-
integrationRecord.user
|
|
135
|
-
);
|
|
136
|
-
instance[key] = moduleInstance;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
instance.record = integrationRecord;
|
|
141
|
-
|
|
142
|
-
try {
|
|
143
|
-
const additionalUserActions =
|
|
144
|
-
await instance.loadDynamicUserActions();
|
|
145
|
-
instance.events = { ...instance.events, ...additionalUserActions };
|
|
146
|
-
} catch (e) {
|
|
147
|
-
instance.record.status = 'ERROR';
|
|
148
|
-
instance.record.messages.errors.push(e);
|
|
149
|
-
await instance.record.save();
|
|
150
|
-
}
|
|
151
|
-
// Register all of the event handlers
|
|
152
|
-
|
|
153
|
-
await instance.registerEventHandlers();
|
|
154
|
-
return instance;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
async createIntegration(entities, userId, config) {
|
|
158
|
-
const integrationRecord = await IntegrationModel.create({
|
|
159
|
-
entities: entities,
|
|
160
|
-
user: userId,
|
|
161
|
-
config,
|
|
162
|
-
version: '0.0.0',
|
|
163
|
-
});
|
|
164
|
-
return await this.getInstanceFromIntegrationId({
|
|
165
|
-
integrationId: integrationRecord.id,
|
|
166
|
-
userId,
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
async getFormattedIntegration(integrationRecord) {
|
|
171
|
-
const integrationObj = {
|
|
172
|
-
id: integrationRecord.id,
|
|
173
|
-
status: integrationRecord.status,
|
|
174
|
-
config: integrationRecord.config,
|
|
175
|
-
entities: [],
|
|
176
|
-
version: integrationRecord.version,
|
|
177
|
-
messages: integrationRecord.messages,
|
|
178
|
-
};
|
|
179
|
-
for (const entityId of integrationRecord.entities) {
|
|
180
|
-
// Only return non-internal fields. Leverages "select" and "options" to non-excepted fields and a pure object.
|
|
181
|
-
const entity = await Entity.findById(
|
|
182
|
-
entityId,
|
|
183
|
-
'-createdAt -updatedAt -user -credentials -credential -_id -__t -__v',
|
|
184
|
-
{ lean: true }
|
|
185
|
-
);
|
|
186
|
-
integrationObj.entities.push({
|
|
187
|
-
id: entityId,
|
|
188
|
-
...entity,
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
return integrationObj;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
module.exports = { IntegrationFactory };
|