@friggframework/core 2.0.0--canary.395.af305ea.0 → 2.0.0--canary.396.d14a555.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/credential/credential-repository.js +9 -0
- package/credential/use-cases/get-credential-for-user.js +21 -0
- package/handlers/backend-utils.js +2 -2
- package/handlers/routers/auth.js +1 -2
- package/index.js +0 -5
- package/integrations/integration-base.js +9 -6
- package/integrations/integration-factory.js +9 -64
- package/integrations/integration-repository.js +26 -0
- package/integrations/integration-router.js +68 -31
- package/integrations/integration.js +156 -0
- package/integrations/options.js +1 -1
- package/integrations/use-cases/create-integration.js +49 -0
- package/integrations/use-cases/delete-integration-for-user.js +29 -0
- package/integrations/use-cases/get-integration-for-user.js +30 -0
- package/integrations/use-cases/get-integration-instance.js +77 -0
- package/integrations/use-cases/get-integrations-for-user.js +19 -0
- package/integrations/use-cases/index.js +15 -0
- package/module-plugin/index.js +0 -2
- package/module-plugin/module-factory.js +14 -20
- package/module-plugin/module-repository.js +14 -0
- package/module-plugin/module-service.js +50 -0
- package/module-plugin/{auther.js → module.js} +93 -159
- package/package.json +5 -5
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class GetCredentialForUser {
|
|
2
|
+
constructor({ credentialRepository }) {
|
|
3
|
+
this.credentialRepository = credentialRepository;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
async execute(credentialId, userId) {
|
|
7
|
+
const credential = await this.credentialRepository.findCredentialById(credentialId);
|
|
8
|
+
|
|
9
|
+
if (!credential) {
|
|
10
|
+
throw new Error(`Credential with id ${credentialId} not found`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (credential.user.toString() !== userId.toString()) {
|
|
14
|
+
throw new Error(`Credential ${credentialId} does not belong to user ${userId}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return credential;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = { GetCredentialForUser };
|
|
@@ -44,7 +44,7 @@ const loadRouterFromObject = (IntegrationClass, routerObject) => {
|
|
|
44
44
|
);
|
|
45
45
|
router[method.toLowerCase()](path, async (req, res, next) => {
|
|
46
46
|
try {
|
|
47
|
-
const integration = new IntegrationClass(
|
|
47
|
+
const integration = new IntegrationClass();
|
|
48
48
|
await integration.loadModules();
|
|
49
49
|
await integration.registerEventHandlers();
|
|
50
50
|
const result = await integration.send(event, { req, res, next });
|
|
@@ -64,7 +64,7 @@ const createQueueWorker = (integrationClass, integrationFactory) => {
|
|
|
64
64
|
try {
|
|
65
65
|
let instance;
|
|
66
66
|
if (!params.integrationId) {
|
|
67
|
-
instance = new integrationClass(
|
|
67
|
+
instance = new integrationClass();
|
|
68
68
|
await instance.loadModules();
|
|
69
69
|
// await instance.loadUserActions();
|
|
70
70
|
await instance.registerEventHandlers();
|
package/handlers/routers/auth.js
CHANGED
|
@@ -4,7 +4,7 @@ const {
|
|
|
4
4
|
loadAppDefinition,
|
|
5
5
|
} = require('./../backend-utils');
|
|
6
6
|
const { UserRepository } = require('../../user/user-repository');
|
|
7
|
-
const { IntegrationFactory
|
|
7
|
+
const { IntegrationFactory } = require('../../integrations/integration-factory');
|
|
8
8
|
const { GetUserFromBearerToken } = require('../../user/use-cases/get-user-from-bearer-token');
|
|
9
9
|
|
|
10
10
|
const { integrations, userConfig } = loadAppDefinition();
|
|
@@ -19,7 +19,6 @@ const router = createIntegrationRouter({
|
|
|
19
19
|
factory: {
|
|
20
20
|
moduleFactory: integrationFactory.moduleFactory,
|
|
21
21
|
integrationFactory,
|
|
22
|
-
IntegrationHelper
|
|
23
22
|
},
|
|
24
23
|
getUserFromBearerToken,
|
|
25
24
|
});
|
package/index.js
CHANGED
|
@@ -39,7 +39,6 @@ const {
|
|
|
39
39
|
Options,
|
|
40
40
|
IntegrationMapping,
|
|
41
41
|
IntegrationFactory,
|
|
42
|
-
IntegrationHelper,
|
|
43
42
|
createIntegrationRouter,
|
|
44
43
|
checkRequiredParams,
|
|
45
44
|
} = require('./integrations/index');
|
|
@@ -56,7 +55,6 @@ const {
|
|
|
56
55
|
Requester,
|
|
57
56
|
ModuleConstants,
|
|
58
57
|
ModuleFactory,
|
|
59
|
-
Auther,
|
|
60
58
|
} = require('./module-plugin/index');
|
|
61
59
|
const utils = require('./utils');
|
|
62
60
|
|
|
@@ -109,7 +107,6 @@ module.exports = {
|
|
|
109
107
|
Options,
|
|
110
108
|
IntegrationMapping,
|
|
111
109
|
IntegrationFactory,
|
|
112
|
-
IntegrationHelper,
|
|
113
110
|
checkRequiredParams,
|
|
114
111
|
createIntegrationRouter,
|
|
115
112
|
|
|
@@ -132,8 +129,6 @@ module.exports = {
|
|
|
132
129
|
Requester,
|
|
133
130
|
ModuleConstants,
|
|
134
131
|
ModuleFactory,
|
|
135
|
-
Auther,
|
|
136
|
-
|
|
137
132
|
// queues
|
|
138
133
|
QueuerUtil,
|
|
139
134
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { IntegrationMapping } = require('./integration-mapping');
|
|
2
2
|
const { Options } = require('./options');
|
|
3
|
+
|
|
3
4
|
const constantsToBeMigrated = {
|
|
4
5
|
defaultEvents: {
|
|
5
6
|
ON_CREATE: 'ON_CREATE',
|
|
@@ -19,6 +20,7 @@ const constantsToBeMigrated = {
|
|
|
19
20
|
};
|
|
20
21
|
|
|
21
22
|
class IntegrationBase {
|
|
23
|
+
|
|
22
24
|
static getOptionDetails() {
|
|
23
25
|
const options = new Options({
|
|
24
26
|
module: Object.values(this.Definition.modules)[0], // This is a placeholder until we revamp the frontend
|
|
@@ -26,6 +28,7 @@ class IntegrationBase {
|
|
|
26
28
|
});
|
|
27
29
|
return options.get();
|
|
28
30
|
}
|
|
31
|
+
|
|
29
32
|
/**
|
|
30
33
|
* CHILDREN SHOULD SPECIFY A DEFINITION FOR THE INTEGRATION
|
|
31
34
|
*/
|
|
@@ -197,9 +200,9 @@ class IntegrationBase {
|
|
|
197
200
|
return this.record;
|
|
198
201
|
}
|
|
199
202
|
|
|
200
|
-
async onUpdate(params) {}
|
|
203
|
+
async onUpdate(params) { }
|
|
201
204
|
|
|
202
|
-
async onDelete(params) {}
|
|
205
|
+
async onDelete(params) { }
|
|
203
206
|
|
|
204
207
|
async getConfigOptions() {
|
|
205
208
|
const options = {
|
|
@@ -236,10 +239,10 @@ class IntegrationBase {
|
|
|
236
239
|
const dynamicUserActions = await this.loadDynamicUserActions();
|
|
237
240
|
const filteredDynamicActions = actionType
|
|
238
241
|
? Object.fromEntries(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
242
|
+
Object.entries(dynamicUserActions).filter(
|
|
243
|
+
([_, event]) => event.userActionType === actionType
|
|
244
|
+
)
|
|
245
|
+
)
|
|
243
246
|
: dynamicUserActions;
|
|
244
247
|
return { ...userActions, ...filteredDynamicActions };
|
|
245
248
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
const { ModuleFactory,
|
|
1
|
+
const { ModuleFactory, Entity } = require('../module-plugin');
|
|
2
2
|
const { IntegrationModel } = require('./integration-model');
|
|
3
|
-
const _ = require('lodash');
|
|
4
3
|
|
|
5
4
|
class IntegrationFactory {
|
|
6
5
|
constructor(integrationClasses = []) {
|
|
@@ -108,10 +107,13 @@ class IntegrationFactory {
|
|
|
108
107
|
);
|
|
109
108
|
}
|
|
110
109
|
|
|
110
|
+
// getIntegrationClassByType is only used here
|
|
111
111
|
const integrationClass = this.getIntegrationClassByType(
|
|
112
112
|
integrationRecord.config.type
|
|
113
113
|
);
|
|
114
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.
|
|
115
117
|
const instance = new integrationClass({
|
|
116
118
|
userId,
|
|
117
119
|
integrationId,
|
|
@@ -133,29 +135,8 @@ class IntegrationFactory {
|
|
|
133
135
|
);
|
|
134
136
|
instance[key] = moduleInstance;
|
|
135
137
|
}
|
|
136
|
-
} else {
|
|
137
|
-
// for each entity, get the moduleinstance and load them according to their keys
|
|
138
|
-
// If it's the first entity, load the moduleinstance into primary as well
|
|
139
|
-
// If it's the second entity, load the moduleinstance into target as well
|
|
140
|
-
const moduleTypesAndKeys =
|
|
141
|
-
this.getModuleTypesAndKeys(integrationClass);
|
|
142
|
-
for (let i = 0; i < integrationRecord.entities.length; i++) {
|
|
143
|
-
const entityId = integrationRecord.entities[i];
|
|
144
|
-
const moduleInstance =
|
|
145
|
-
await this.moduleFactory.getModuleInstanceFromEntityId(
|
|
146
|
-
entityId,
|
|
147
|
-
integrationRecord.user
|
|
148
|
-
);
|
|
149
|
-
const moduleType = moduleInstance.getName();
|
|
150
|
-
const key = moduleTypesAndKeys[moduleType];
|
|
151
|
-
instance[key] = moduleInstance;
|
|
152
|
-
if (i === 0) {
|
|
153
|
-
instance.primary = moduleInstance;
|
|
154
|
-
} else if (i === 1) {
|
|
155
|
-
instance.target = moduleInstance;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
138
|
}
|
|
139
|
+
|
|
159
140
|
instance.record = integrationRecord;
|
|
160
141
|
|
|
161
142
|
try {
|
|
@@ -185,11 +166,8 @@ class IntegrationFactory {
|
|
|
185
166
|
userId,
|
|
186
167
|
});
|
|
187
168
|
}
|
|
188
|
-
}
|
|
189
169
|
|
|
190
|
-
|
|
191
|
-
const IntegrationHelper = {
|
|
192
|
-
getFormattedIntegration: async function (integrationRecord) {
|
|
170
|
+
async getFormattedIntegration(integrationRecord) {
|
|
193
171
|
const integrationObj = {
|
|
194
172
|
id: integrationRecord.id,
|
|
195
173
|
status: integrationRecord.status,
|
|
@@ -211,40 +189,7 @@ const IntegrationHelper = {
|
|
|
211
189
|
});
|
|
212
190
|
}
|
|
213
191
|
return integrationObj;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
getIntegrationsForUserId: async function (userId) {
|
|
217
|
-
const integrationList = await IntegrationModel.find({ user: userId });
|
|
218
|
-
return await Promise.all(
|
|
219
|
-
integrationList.map(
|
|
220
|
-
async (integrationRecord) =>
|
|
221
|
-
await IntegrationHelper.getFormattedIntegration(
|
|
222
|
-
integrationRecord
|
|
223
|
-
)
|
|
224
|
-
)
|
|
225
|
-
);
|
|
226
|
-
},
|
|
227
|
-
|
|
228
|
-
deleteIntegrationForUserById: async function (userId, integrationId) {
|
|
229
|
-
const integrationList = await IntegrationModel.find({
|
|
230
|
-
user: userId,
|
|
231
|
-
_id: integrationId,
|
|
232
|
-
});
|
|
233
|
-
if (integrationList.length !== 1) {
|
|
234
|
-
throw new Error(
|
|
235
|
-
`Integration with id of ${integrationId} does not exist for this user`
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
await IntegrationModel.deleteOne({ _id: integrationId });
|
|
239
|
-
},
|
|
240
|
-
|
|
241
|
-
getIntegrationById: async function (id) {
|
|
242
|
-
return IntegrationModel.findById(id).populate('entities');
|
|
243
|
-
},
|
|
244
|
-
|
|
245
|
-
listCredentials: async function (options) {
|
|
246
|
-
return Credential.find(options);
|
|
247
|
-
},
|
|
248
|
-
};
|
|
192
|
+
}
|
|
193
|
+
}
|
|
249
194
|
|
|
250
|
-
module.exports = { IntegrationFactory
|
|
195
|
+
module.exports = { IntegrationFactory };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const { IntegrationModel } = require('./integration-model');
|
|
2
|
+
|
|
3
|
+
class IntegrationRepository {
|
|
4
|
+
async findIntegrationsByUserId(userId) {
|
|
5
|
+
return IntegrationModel.find({ user: userId });
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async deleteIntegrationById(integrationId) {
|
|
9
|
+
return IntegrationModel.deleteOne({ _id: integrationId });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async findIntegrationById(id) {
|
|
13
|
+
return IntegrationModel.findById(id).populate('entities');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async createIntegration(entities, userId, config) {
|
|
17
|
+
return IntegrationModel.create({
|
|
18
|
+
entities: entities,
|
|
19
|
+
user: userId,
|
|
20
|
+
config,
|
|
21
|
+
version: '0.0.0',
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = { IntegrationRepository };
|
|
@@ -3,19 +3,57 @@ const { get } = require('../assertions');
|
|
|
3
3
|
const Boom = require('@hapi/boom');
|
|
4
4
|
const catchAsyncError = require('express-async-handler');
|
|
5
5
|
const { debug } = require('../logs');
|
|
6
|
+
const { IntegrationRepository } = require('./integration-repository');
|
|
7
|
+
const { DeleteIntegrationForUser } = require('./use-cases/delete-integration-for-user');
|
|
8
|
+
const { GetIntegrationForUser } = require('./use-cases/get-integration-for-user');
|
|
9
|
+
const { GetIntegrationsForUser } = require('./use-cases/get-integrations-for-user');
|
|
10
|
+
const { CredentialRepository } = require('../credential/credential-repository');
|
|
11
|
+
const { GetCredentialForUser } = require('../credential/use-cases/get-credential-for-user');
|
|
12
|
+
const { CreateIntegration } = require('./use-cases/create-integration');
|
|
13
|
+
const { ModuleService } = require('../module-plugin/module-service');
|
|
14
|
+
const { ModuleRepository } = require('../module-plugin/module-repository');
|
|
15
|
+
const { loadAppDefinition } = require('../backend-utils');
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
const { integrations } = loadAppDefinition();
|
|
19
|
+
const moduleRepository = new ModuleRepository();
|
|
20
|
+
const integrationRepository = new IntegrationRepository();
|
|
21
|
+
const credentialRepository = new CredentialRepository();
|
|
22
|
+
const moduleService = new ModuleService({ moduleRepository, moduleDefinitions: { ...getModules() } });
|
|
23
|
+
const deleteIntegrationForUser = new DeleteIntegrationForUser({ integrationRepository });
|
|
24
|
+
const getIntegrationForUser = new GetIntegrationForUser({ integrationRepository });
|
|
25
|
+
const getIntegrationsForUser = new GetIntegrationsForUser({ integrationRepository });
|
|
26
|
+
const getCredentialForUser = new GetCredentialForUser({ credentialRepository });
|
|
27
|
+
const createIntegration = new CreateIntegration({
|
|
28
|
+
integrationRepository,
|
|
29
|
+
integrationClasses: integrations,
|
|
30
|
+
moduleService,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// todo: move this into a utils file
|
|
34
|
+
const getModules = () => {
|
|
35
|
+
return [
|
|
36
|
+
...new Set(
|
|
37
|
+
integrations
|
|
38
|
+
.map((integration) =>
|
|
39
|
+
Object.values(integration.Definition.modules).map(
|
|
40
|
+
(module) => module.definition
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
.flat()
|
|
44
|
+
),
|
|
45
|
+
];
|
|
46
|
+
}
|
|
6
47
|
|
|
7
|
-
|
|
8
|
-
// todo: dont send moduleFactory, integrationFactory, and IntegrationHelper as a factory object, instead send them as separate
|
|
9
|
-
// params and import IntegrationHelper where needed.
|
|
48
|
+
// todo: dont send moduleFactory and integrationFactory as a factory object, instead send them as separate params.
|
|
10
49
|
// todo: this could be a use case class
|
|
11
50
|
/**
|
|
12
51
|
* Creates an Express router with integration and entity routes configured
|
|
13
52
|
* @param {Object} params - Configuration parameters for the router
|
|
14
53
|
* @param {express.Router} [params.router] - Optional Express router instance, creates new one if not provided
|
|
15
|
-
* @param {Object} params.factory - Factory object containing moduleFactory
|
|
54
|
+
* @param {Object} params.factory - Factory object containing moduleFactory and integrationFactory
|
|
16
55
|
* @param {Object} params.factory.moduleFactory - Factory for creating and managing API modules
|
|
17
56
|
* @param {Object} params.factory.integrationFactory - Factory for creating and managing integrations
|
|
18
|
-
* @param {Object} params.factory.IntegrationHelper - Helper utilities for integration operations
|
|
19
57
|
* @param {import('../user/use-cases/get-user-from-bearer-token').GetUserFromBearerToken} params.getUserFromBearerToken - Use case for retrieving a user from a bearer token
|
|
20
58
|
* @returns {express.Router} Configured Express router with integration and entity routes
|
|
21
59
|
*/
|
|
@@ -54,14 +92,14 @@ function checkRequiredParams(params, requiredKeys) {
|
|
|
54
92
|
/**
|
|
55
93
|
* Sets up integration-related routes on the provided Express router
|
|
56
94
|
* @param {express.Router} router - Express router instance to add routes to
|
|
57
|
-
* @param {Object} factory - Factory object containing moduleFactory
|
|
95
|
+
* @param {Object} factory - Factory object containing moduleFactory and integrationFactory
|
|
58
96
|
* @param {Object} factory.moduleFactory - Factory for creating and managing API modules
|
|
59
97
|
* @param {Object} factory.integrationFactory - Factory for creating and managing integrations
|
|
60
|
-
* @param {Object} factory.IntegrationHelper - Helper utilities for integration operations
|
|
61
98
|
* @param {import('../user/use-cases/get-user-from-bearer-token').GetUserFromBearerToken} getUserFromBearerToken - Use case for retrieving a user from a bearer token
|
|
62
99
|
*/
|
|
63
100
|
function setIntegrationRoutes(router, factory, getUserFromBearerToken) {
|
|
64
|
-
const { moduleFactory, integrationFactory
|
|
101
|
+
const { moduleFactory, integrationFactory } = factory;
|
|
102
|
+
|
|
65
103
|
router.route('/api/integrations').get(
|
|
66
104
|
catchAsyncError(async (req, res) => {
|
|
67
105
|
const user = await getUserFromBearerToken.execute(
|
|
@@ -72,7 +110,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken) {
|
|
|
72
110
|
results.entities.authorized =
|
|
73
111
|
await moduleFactory.getEntitiesForUser(userId);
|
|
74
112
|
results.integrations =
|
|
75
|
-
await
|
|
113
|
+
await getIntegrationsForUser.execute(userId);
|
|
76
114
|
|
|
77
115
|
for (const integrationRecord of results.integrations) {
|
|
78
116
|
const integration =
|
|
@@ -100,7 +138,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken) {
|
|
|
100
138
|
get(params.config, 'type');
|
|
101
139
|
|
|
102
140
|
// create integration
|
|
103
|
-
const integration = await
|
|
141
|
+
const integration = await createIntegration.execute(
|
|
104
142
|
params.entities,
|
|
105
143
|
userId,
|
|
106
144
|
params.config
|
|
@@ -113,7 +151,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken) {
|
|
|
113
151
|
await integration.send('ON_CREATE', {});
|
|
114
152
|
|
|
115
153
|
res.status(201).json(
|
|
116
|
-
await
|
|
154
|
+
await integrationFactory.getFormattedIntegration(
|
|
117
155
|
integration.record
|
|
118
156
|
)
|
|
119
157
|
);
|
|
@@ -141,7 +179,7 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken) {
|
|
|
141
179
|
await integration.send('ON_UPDATE', params);
|
|
142
180
|
|
|
143
181
|
res.json(
|
|
144
|
-
await
|
|
182
|
+
await integrationFactory.getFormattedIntegration(
|
|
145
183
|
integration.record
|
|
146
184
|
)
|
|
147
185
|
);
|
|
@@ -153,11 +191,10 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken) {
|
|
|
153
191
|
const user = await getUserFromBearerToken.execute(
|
|
154
192
|
req.headers.authorization
|
|
155
193
|
);
|
|
156
|
-
const userId = user.getId();
|
|
157
194
|
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
158
195
|
const integration =
|
|
159
196
|
await integrationFactory.getInstanceFromIntegrationId({
|
|
160
|
-
userId,
|
|
197
|
+
userId: user.getId(),
|
|
161
198
|
integrationId: params.integrationId,
|
|
162
199
|
});
|
|
163
200
|
|
|
@@ -165,12 +202,9 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken) {
|
|
|
165
202
|
`Calling onUpdate on the ${integration?.constructor?.Definition?.name} Integration with no arguments`
|
|
166
203
|
);
|
|
167
204
|
await integration.send('ON_DELETE');
|
|
168
|
-
await
|
|
169
|
-
userId,
|
|
170
|
-
params.integrationId
|
|
171
|
-
);
|
|
205
|
+
await deleteIntegrationForUser.execute(params.integrationId, user.getId());
|
|
172
206
|
|
|
173
|
-
res.status(
|
|
207
|
+
res.status(204).json({});
|
|
174
208
|
})
|
|
175
209
|
);
|
|
176
210
|
|
|
@@ -309,13 +343,14 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken) {
|
|
|
309
343
|
const user = await getUserFromBearerToken.execute(
|
|
310
344
|
req.headers.authorization
|
|
311
345
|
);
|
|
346
|
+
|
|
347
|
+
if (!user) {
|
|
348
|
+
throw Boom.forbidden('User not found');
|
|
349
|
+
}
|
|
350
|
+
|
|
312
351
|
const params = checkRequiredParams(req.params, ['integrationId']);
|
|
313
|
-
const integration = await
|
|
314
|
-
|
|
315
|
-
integrationId: params.integrationId,
|
|
316
|
-
userId: user.getId(),
|
|
317
|
-
}
|
|
318
|
-
);
|
|
352
|
+
const integration = await getIntegrationForUser.execute(params.integrationId, user.getId());
|
|
353
|
+
|
|
319
354
|
// We could perhaps augment router with dynamic options? Haven't decided yet, but here may be the place
|
|
320
355
|
|
|
321
356
|
res.json({
|
|
@@ -364,11 +399,11 @@ function setIntegrationRoutes(router, factory, getUserFromBearerToken) {
|
|
|
364
399
|
/**
|
|
365
400
|
* Sets up entity-related routes for the integration router
|
|
366
401
|
* @param {Object} router - Express router instance
|
|
367
|
-
* @param {Object} factory - Factory object containing moduleFactory
|
|
402
|
+
* @param {Object} factory - Factory object containing moduleFactory
|
|
368
403
|
* @param {import('../user/use-cases/get-user-from-bearer-token').GetUserFromBearerToken} getUserFromBearerToken - Use case for retrieving a user from a bearer token
|
|
369
404
|
*/
|
|
370
405
|
function setEntityRoutes(router, factory, getUserFromBearerToken) {
|
|
371
|
-
const { moduleFactory
|
|
406
|
+
const { moduleFactory } = factory;
|
|
372
407
|
const getModuleInstance = async (userId, entityType) => {
|
|
373
408
|
if (!moduleFactory.checkIsValidType(entityType)) {
|
|
374
409
|
throw Boom.badRequest(
|
|
@@ -437,8 +472,9 @@ function setEntityRoutes(router, factory, getUserFromBearerToken) {
|
|
|
437
472
|
checkRequiredParams(req.body.data, ['credential_id']);
|
|
438
473
|
|
|
439
474
|
// May want to pass along the user ID as well so credential ID's can't be fished???
|
|
440
|
-
const credential = await
|
|
441
|
-
params.data.credential_id
|
|
475
|
+
const credential = await getCredentialForUser.execute(
|
|
476
|
+
params.data.credential_id,
|
|
477
|
+
userId
|
|
442
478
|
);
|
|
443
479
|
|
|
444
480
|
if (!credential) {
|
|
@@ -465,8 +501,9 @@ function setEntityRoutes(router, factory, getUserFromBearerToken) {
|
|
|
465
501
|
const userId = user.getId();
|
|
466
502
|
// TODO May want to pass along the user ID as well so credential ID's can't be fished???
|
|
467
503
|
// TODO **flagging this for review** -MW
|
|
468
|
-
const credential = await
|
|
469
|
-
req.params.credentialId
|
|
504
|
+
const credential = await getCredentialForUser.execute(
|
|
505
|
+
req.params.credentialId,
|
|
506
|
+
userId
|
|
470
507
|
);
|
|
471
508
|
if (credential.user._id.toString() !== userId) {
|
|
472
509
|
throw Boom.forbidden('Credential does not belong to user');
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration Domain Entity
|
|
3
|
+
* Represents a configured integration with its data and behavior
|
|
4
|
+
* Uses the strategy pattern to delegate behavior to the integration class
|
|
5
|
+
* This is the main class that is used to interact with integrations
|
|
6
|
+
*/
|
|
7
|
+
class Integration {
|
|
8
|
+
constructor({
|
|
9
|
+
id,
|
|
10
|
+
userId,
|
|
11
|
+
entities,
|
|
12
|
+
config,
|
|
13
|
+
status,
|
|
14
|
+
version,
|
|
15
|
+
messages,
|
|
16
|
+
entityReference,
|
|
17
|
+
integrationClass,
|
|
18
|
+
modules = {}
|
|
19
|
+
}) {
|
|
20
|
+
// Data from record
|
|
21
|
+
this.id = id;
|
|
22
|
+
this.userId = userId;
|
|
23
|
+
this.entities = entities;
|
|
24
|
+
this.config = config;
|
|
25
|
+
this.status = status;
|
|
26
|
+
this.version = version;
|
|
27
|
+
this.messages = messages;
|
|
28
|
+
this.entityReference = entityReference;
|
|
29
|
+
|
|
30
|
+
// Integration behavior (strategy pattern)
|
|
31
|
+
this.integrationClass = integrationClass;
|
|
32
|
+
|
|
33
|
+
// Loaded modules
|
|
34
|
+
this.modules = modules;
|
|
35
|
+
|
|
36
|
+
// Initialize basic behavior (sync parts only)
|
|
37
|
+
this._initializeBasicBehavior();
|
|
38
|
+
|
|
39
|
+
// Return a Proxy to handle dynamic method delegation
|
|
40
|
+
return new Proxy(this, {
|
|
41
|
+
get(target, prop) {
|
|
42
|
+
// First, check if property exists on Integration entity
|
|
43
|
+
if (prop in target) {
|
|
44
|
+
return target[prop];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Then, check if it exists on the behavior instance
|
|
48
|
+
if (target.behavior && prop in target.behavior) {
|
|
49
|
+
const value = target.behavior[prop];
|
|
50
|
+
|
|
51
|
+
// If it's a function, bind the context to the Integration entity
|
|
52
|
+
if (typeof value === 'function') {
|
|
53
|
+
return value.bind(target);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Return undefined for non-existent properties
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
_initializeBasicBehavior() {
|
|
66
|
+
// Initialize basic behavior (sync parts only)
|
|
67
|
+
if (this.integrationClass) {
|
|
68
|
+
// Create instance for behavior delegation
|
|
69
|
+
this.behavior = new this.integrationClass({
|
|
70
|
+
userId: this.userId,
|
|
71
|
+
integrationId: this.id
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Copy events
|
|
75
|
+
this.events = this.behavior.events || {};
|
|
76
|
+
this.defaultEvents = this.behavior.defaultEvents || {};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async initialize() {
|
|
81
|
+
// Complete async initialization
|
|
82
|
+
if (this.behavior) {
|
|
83
|
+
// Load dynamic user actions
|
|
84
|
+
try {
|
|
85
|
+
const additionalUserActions = await this.loadDynamicUserActions();
|
|
86
|
+
this.events = { ...this.events, ...additionalUserActions };
|
|
87
|
+
} catch (e) {
|
|
88
|
+
this.addError(e);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Register event handlers
|
|
92
|
+
await this.registerEventHandlers();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Core methods that should always be on Integration entity
|
|
97
|
+
// These override any behavior methods with the same name
|
|
98
|
+
|
|
99
|
+
// Module access helpers
|
|
100
|
+
getModule(key) {
|
|
101
|
+
return this.modules[key];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
setModule(key, module) {
|
|
105
|
+
this.modules[key] = module;
|
|
106
|
+
// Also set on behavior for backward compatibility
|
|
107
|
+
if (this.behavior) {
|
|
108
|
+
this.behavior[key] = module;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// State management
|
|
113
|
+
addError(error) {
|
|
114
|
+
if (!this.messages.errors) {
|
|
115
|
+
this.messages.errors = [];
|
|
116
|
+
}
|
|
117
|
+
this.messages.errors.push(error);
|
|
118
|
+
this.status = 'ERROR';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
addWarning(warning) {
|
|
122
|
+
if (!this.messages.warnings) {
|
|
123
|
+
this.messages.warnings = [];
|
|
124
|
+
}
|
|
125
|
+
this.messages.warnings.push(warning);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Domain methods
|
|
129
|
+
isActive() {
|
|
130
|
+
return this.status === 'ENABLED' || this.status === 'ACTIVE';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
needsConfiguration() {
|
|
134
|
+
return this.status === 'NEEDS_CONFIG';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
hasErrors() {
|
|
138
|
+
return this.status === 'ERROR';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
belongsToUser(userId) {
|
|
142
|
+
return this.userId.toString() === userId.toString();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Get the underlying behavior instance (useful for debugging or special cases)
|
|
146
|
+
getBehavior() {
|
|
147
|
+
return this.behavior;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Check if a method exists (either on entity or behavior)
|
|
151
|
+
hasMethod(methodName) {
|
|
152
|
+
return methodName in this || (this.behavior && methodName in this.behavior);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
module.exports = { Integration };
|
package/integrations/options.js
CHANGED