@friggframework/core 2.0.0--canary.395.65f5f64.0 → 2.0.0--canary.398.24926ac.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/README.md +50 -931
- package/core/create-handler.js +0 -1
- package/database/models/WebsocketConnection.js +5 -0
- package/handlers/app-handler-helpers.js +3 -0
- package/handlers/backend-utils.js +44 -42
- package/handlers/routers/auth.js +14 -3
- package/handlers/routers/integration-defined-routers.js +5 -8
- package/handlers/routers/middleware/loadUser.js +15 -0
- package/handlers/routers/middleware/requireLoggedInUser.js +12 -0
- package/handlers/routers/user.js +5 -25
- package/handlers/workers/integration-defined-workers.js +3 -6
- package/index.js +16 -1
- package/integrations/create-frigg-backend.js +31 -0
- package/integrations/index.js +5 -0
- package/integrations/integration-base.js +46 -142
- package/integrations/integration-factory.js +251 -0
- package/integrations/integration-router.js +181 -303
- package/integrations/integration-user.js +144 -0
- package/integrations/options.js +1 -1
- package/integrations/test/integration-base.test.js +144 -0
- package/module-plugin/auther.js +393 -0
- package/module-plugin/entity-manager.js +70 -0
- package/{modules → module-plugin}/entity.js +0 -1
- package/{modules → module-plugin}/index.js +8 -0
- package/module-plugin/manager.js +169 -0
- package/module-plugin/module-factory.js +61 -0
- package/{modules → module-plugin}/test/mock-api/api.js +3 -8
- package/{modules → module-plugin}/test/mock-api/definition.js +8 -12
- package/package.json +5 -5
- package/syncs/sync.js +1 -0
- package/types/integrations/index.d.ts +6 -2
- package/types/module-plugin/index.d.ts +56 -4
- package/types/syncs/index.d.ts +2 -0
- package/credential/credential-repository.js +0 -56
- package/credential/use-cases/get-credential-for-user.js +0 -21
- package/credential/use-cases/update-authentication-status.js +0 -15
- package/handlers/app-definition-loader.js +0 -38
- package/integrations/integration-repository.js +0 -80
- package/integrations/tests/doubles/dummy-integration-class.js +0 -90
- package/integrations/tests/doubles/test-integration-repository.js +0 -89
- package/integrations/tests/use-cases/create-integration.test.js +0 -124
- package/integrations/tests/use-cases/delete-integration-for-user.test.js +0 -143
- package/integrations/tests/use-cases/get-integration-for-user.test.js +0 -143
- package/integrations/tests/use-cases/get-integration-instance.test.js +0 -169
- package/integrations/tests/use-cases/get-integrations-for-user.test.js +0 -169
- package/integrations/tests/use-cases/get-possible-integrations.test.js +0 -188
- package/integrations/tests/use-cases/update-integration-messages.test.js +0 -142
- package/integrations/tests/use-cases/update-integration-status.test.js +0 -103
- package/integrations/tests/use-cases/update-integration.test.js +0 -134
- package/integrations/use-cases/create-integration.js +0 -71
- package/integrations/use-cases/delete-integration-for-user.js +0 -72
- package/integrations/use-cases/get-integration-for-user.js +0 -78
- package/integrations/use-cases/get-integration-instance-by-definition.js +0 -67
- package/integrations/use-cases/get-integration-instance.js +0 -82
- package/integrations/use-cases/get-integrations-for-user.js +0 -76
- package/integrations/use-cases/get-possible-integrations.js +0 -27
- package/integrations/use-cases/index.js +0 -11
- package/integrations/use-cases/update-integration-messages.js +0 -31
- package/integrations/use-cases/update-integration-status.js +0 -28
- package/integrations/use-cases/update-integration.js +0 -91
- package/integrations/utils/map-integration-dto.js +0 -36
- package/modules/module-factory.js +0 -54
- package/modules/module-repository.js +0 -107
- package/modules/module.js +0 -218
- package/modules/tests/doubles/test-module-factory.js +0 -16
- package/modules/tests/doubles/test-module-repository.js +0 -19
- package/modules/use-cases/get-entities-for-user.js +0 -32
- package/modules/use-cases/get-entity-options-by-id.js +0 -58
- package/modules/use-cases/get-entity-options-by-type.js +0 -34
- package/modules/use-cases/get-module-instance-from-type.js +0 -31
- package/modules/use-cases/get-module.js +0 -56
- package/modules/use-cases/process-authorization-callback.js +0 -108
- package/modules/use-cases/refresh-entity-options.js +0 -58
- package/modules/use-cases/test-module-auth.js +0 -54
- package/modules/utils/map-module-dto.js +0 -18
- package/user/tests/doubles/test-user-repository.js +0 -72
- package/user/tests/use-cases/create-individual-user.test.js +0 -24
- package/user/tests/use-cases/create-organization-user.test.js +0 -28
- package/user/tests/use-cases/create-token-for-user-id.test.js +0 -19
- package/user/tests/use-cases/get-user-from-bearer-token.test.js +0 -64
- package/user/tests/use-cases/login-user.test.js +0 -140
- package/user/use-cases/create-individual-user.js +0 -61
- package/user/use-cases/create-organization-user.js +0 -47
- package/user/use-cases/create-token-for-user-id.js +0 -30
- package/user/use-cases/get-user-from-bearer-token.js +0 -77
- package/user/use-cases/login-user.js +0 -122
- package/user/user-repository.js +0 -62
- package/user/user.js +0 -77
- /package/{modules → module-plugin}/ModuleConstants.js +0 -0
- /package/{modules → module-plugin}/credential.js +0 -0
- /package/{modules → module-plugin}/requester/api-key.js +0 -0
- /package/{modules → module-plugin}/requester/basic.js +0 -0
- /package/{modules → module-plugin}/requester/oauth-2.js +0 -0
- /package/{modules → module-plugin}/requester/requester.js +0 -0
- /package/{modules → module-plugin}/requester/requester.test.js +0 -0
- /package/{modules → module-plugin}/test/auther.test.js +0 -0
- /package/{modules → module-plugin}/test/mock-api/mocks/hubspot.js +0 -0
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
const { IntegrationMapping } = require('./integration-mapping');
|
|
2
2
|
const { Options } = require('./options');
|
|
3
|
-
const { UpdateIntegrationStatus } = require('./use-cases/update-integration-status');
|
|
4
|
-
const { IntegrationRepository } = require('./integration-repository');
|
|
5
|
-
const { UpdateIntegrationMessages } = require('./use-cases/update-integration-messages');
|
|
6
|
-
|
|
7
3
|
const constantsToBeMigrated = {
|
|
8
4
|
defaultEvents: {
|
|
9
5
|
ON_CREATE: 'ON_CREATE',
|
|
@@ -23,12 +19,6 @@ const constantsToBeMigrated = {
|
|
|
23
19
|
};
|
|
24
20
|
|
|
25
21
|
class IntegrationBase {
|
|
26
|
-
|
|
27
|
-
// todo: maybe we can pass this as Dependency Injection in the sub-class constructor
|
|
28
|
-
integrationRepository = new IntegrationRepository();
|
|
29
|
-
updateIntegrationStatus = new UpdateIntegrationStatus({ integrationRepository: this.integrationRepository });
|
|
30
|
-
updateIntegrationMessages = new UpdateIntegrationMessages({ integrationRepository: this.integrationRepository });
|
|
31
|
-
|
|
32
22
|
static getOptionDetails() {
|
|
33
23
|
const options = new Options({
|
|
34
24
|
module: Object.values(this.Definition.modules)[0], // This is a placeholder until we revamp the frontend
|
|
@@ -36,7 +26,6 @@ class IntegrationBase {
|
|
|
36
26
|
});
|
|
37
27
|
return options.get();
|
|
38
28
|
}
|
|
39
|
-
|
|
40
29
|
/**
|
|
41
30
|
* CHILDREN SHOULD SPECIFY A DEFINITION FOR THE INTEGRATION
|
|
42
31
|
*/
|
|
@@ -61,7 +50,21 @@ class IntegrationBase {
|
|
|
61
50
|
static getCurrentVersion() {
|
|
62
51
|
return this.Definition.version;
|
|
63
52
|
}
|
|
64
|
-
|
|
53
|
+
loadModules() {
|
|
54
|
+
// Load all the modules defined in Definition.modules
|
|
55
|
+
const moduleNames = Object.keys(this.constructor.Definition.modules);
|
|
56
|
+
for (const moduleName of moduleNames) {
|
|
57
|
+
const { definition } =
|
|
58
|
+
this.constructor.Definition.modules[moduleName];
|
|
59
|
+
if (typeof definition.API === 'function') {
|
|
60
|
+
this[moduleName] = { api: new definition.API() };
|
|
61
|
+
} else {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Module ${moduleName} must be a function that extends IntegrationModule`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
65
68
|
registerEventHandlers() {
|
|
66
69
|
this.on = {
|
|
67
70
|
...this.defaultEvents,
|
|
@@ -69,31 +72,7 @@ class IntegrationBase {
|
|
|
69
72
|
};
|
|
70
73
|
}
|
|
71
74
|
|
|
72
|
-
constructor(params
|
|
73
|
-
// Data from database record (when instantiated by use cases)
|
|
74
|
-
this.id = params.id;
|
|
75
|
-
this.userId = params.userId || params.integrationId; // fallback for legacy
|
|
76
|
-
this.entities = params.entities;
|
|
77
|
-
this.config = params.config;
|
|
78
|
-
this.status = params.status;
|
|
79
|
-
this.version = params.version;
|
|
80
|
-
this.messages = params.messages || { errors: [], warnings: [] };
|
|
81
|
-
|
|
82
|
-
// Module instances (injected by factory)
|
|
83
|
-
this.modules = {};
|
|
84
|
-
if (params.modules) {
|
|
85
|
-
for (const mod of params.modules) {
|
|
86
|
-
const key = typeof mod.getName === 'function' ? mod.getName() : mod.name;
|
|
87
|
-
if (key) {
|
|
88
|
-
this.modules[key] = mod;
|
|
89
|
-
this[key] = mod; // Direct access (e.g., this.hubspot)
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Initialize events object (will be populated by child classes)
|
|
95
|
-
this.events = this.events || {};
|
|
96
|
-
|
|
75
|
+
constructor(params) {
|
|
97
76
|
this.defaultEvents = {
|
|
98
77
|
[constantsToBeMigrated.defaultEvents.ON_CREATE]: {
|
|
99
78
|
type: constantsToBeMigrated.types.LIFE_CYCLE_EVENT,
|
|
@@ -128,6 +107,7 @@ class IntegrationBase {
|
|
|
128
107
|
handler: this.refreshActionOptions,
|
|
129
108
|
},
|
|
130
109
|
};
|
|
110
|
+
this.loadModules();
|
|
131
111
|
}
|
|
132
112
|
|
|
133
113
|
async send(event, object) {
|
|
@@ -141,7 +121,7 @@ class IntegrationBase {
|
|
|
141
121
|
|
|
142
122
|
async validateConfig() {
|
|
143
123
|
const configOptions = await this.getConfigOptions();
|
|
144
|
-
const currentConfig = this.
|
|
124
|
+
const currentConfig = this.record.config;
|
|
145
125
|
let needsConfig = false;
|
|
146
126
|
for (const option of configOptions) {
|
|
147
127
|
if (option.required) {
|
|
@@ -153,59 +133,56 @@ class IntegrationBase {
|
|
|
153
133
|
)
|
|
154
134
|
) {
|
|
155
135
|
needsConfig = true;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
Date.now()
|
|
162
|
-
);
|
|
136
|
+
this.record.messages.warnings.push({
|
|
137
|
+
title: 'Config Validation Error',
|
|
138
|
+
message: `Missing required field of ${option.label}`,
|
|
139
|
+
timestamp: Date.now(),
|
|
140
|
+
});
|
|
163
141
|
}
|
|
164
142
|
}
|
|
165
143
|
}
|
|
166
144
|
if (needsConfig) {
|
|
167
|
-
|
|
145
|
+
this.record.status = 'NEEDS_CONFIG';
|
|
146
|
+
await this.record.save();
|
|
168
147
|
}
|
|
169
148
|
}
|
|
170
149
|
|
|
171
150
|
async testAuth() {
|
|
172
151
|
let didAuthPass = true;
|
|
173
152
|
|
|
174
|
-
for (const module of Object.keys(
|
|
153
|
+
for (const module of Object.keys(IntegrationBase.Definition.modules)) {
|
|
175
154
|
try {
|
|
176
155
|
await this[module].testAuth();
|
|
177
156
|
} catch {
|
|
178
157
|
didAuthPass = false;
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
'Authentication Error',
|
|
183
|
-
`There was an error with your ${this[
|
|
158
|
+
this.record.messages.errors.push({
|
|
159
|
+
title: 'Authentication Error',
|
|
160
|
+
message: `There was an error with your ${this[
|
|
184
161
|
module
|
|
185
162
|
].constructor.getName()} Entity.
|
|
186
163
|
Please reconnect/re-authenticate, or reach out to Support for assistance.`,
|
|
187
|
-
Date.now()
|
|
188
|
-
);
|
|
164
|
+
timestamp: Date.now(),
|
|
165
|
+
});
|
|
189
166
|
}
|
|
190
167
|
}
|
|
191
168
|
|
|
192
169
|
if (!didAuthPass) {
|
|
193
|
-
|
|
170
|
+
this.record.status = 'ERROR';
|
|
171
|
+
this.record.markModified('messages.error');
|
|
172
|
+
await this.record.save();
|
|
194
173
|
}
|
|
195
174
|
}
|
|
196
175
|
|
|
197
176
|
async getMapping(sourceId) {
|
|
198
|
-
|
|
199
|
-
return IntegrationMapping.findBy(this.id, sourceId);
|
|
177
|
+
return IntegrationMapping.findBy(this.record.id, sourceId);
|
|
200
178
|
}
|
|
201
179
|
|
|
202
180
|
async upsertMapping(sourceId, mapping) {
|
|
203
181
|
if (!sourceId) {
|
|
204
182
|
throw new Error(`sourceId must be set`);
|
|
205
183
|
}
|
|
206
|
-
// todo: this should be a use case
|
|
207
184
|
return await IntegrationMapping.upsert(
|
|
208
|
-
this.id,
|
|
185
|
+
this.record.id,
|
|
209
186
|
sourceId,
|
|
210
187
|
mapping
|
|
211
188
|
);
|
|
@@ -214,13 +191,15 @@ class IntegrationBase {
|
|
|
214
191
|
/**
|
|
215
192
|
* CHILDREN CAN OVERRIDE THESE CONFIGURATION METHODS
|
|
216
193
|
*/
|
|
217
|
-
async onCreate(
|
|
218
|
-
|
|
194
|
+
async onCreate(params) {
|
|
195
|
+
this.record.status = 'ENABLED';
|
|
196
|
+
await this.record.save();
|
|
197
|
+
return this.record;
|
|
219
198
|
}
|
|
220
199
|
|
|
221
|
-
async onUpdate(params) {
|
|
200
|
+
async onUpdate(params) {}
|
|
222
201
|
|
|
223
|
-
async onDelete(params) {
|
|
202
|
+
async onDelete(params) {}
|
|
224
203
|
|
|
225
204
|
async getConfigOptions() {
|
|
226
205
|
const options = {
|
|
@@ -257,10 +236,10 @@ class IntegrationBase {
|
|
|
257
236
|
const dynamicUserActions = await this.loadDynamicUserActions();
|
|
258
237
|
const filteredDynamicActions = actionType
|
|
259
238
|
? Object.fromEntries(
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
239
|
+
Object.entries(dynamicUserActions).filter(
|
|
240
|
+
([_, event]) => event.userActionType === actionType
|
|
241
|
+
)
|
|
242
|
+
)
|
|
264
243
|
: dynamicUserActions;
|
|
265
244
|
return { ...userActions, ...filteredDynamicActions };
|
|
266
245
|
}
|
|
@@ -280,81 +259,6 @@ class IntegrationBase {
|
|
|
280
259
|
};
|
|
281
260
|
return options;
|
|
282
261
|
}
|
|
283
|
-
|
|
284
|
-
// === Domain Methods (moved from Integration.js) ===
|
|
285
|
-
|
|
286
|
-
getConfig() {
|
|
287
|
-
return this.config;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
getModule(key) {
|
|
291
|
-
return this.modules[key];
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
setModule(key, module) {
|
|
295
|
-
this.modules[key] = module;
|
|
296
|
-
this[key] = module;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
addError(error) {
|
|
300
|
-
if (!this.messages.errors) {
|
|
301
|
-
this.messages.errors = [];
|
|
302
|
-
}
|
|
303
|
-
this.messages.errors.push(error);
|
|
304
|
-
this.status = 'ERROR';
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
addWarning(warning) {
|
|
308
|
-
if (!this.messages.warnings) {
|
|
309
|
-
this.messages.warnings = [];
|
|
310
|
-
}
|
|
311
|
-
this.messages.warnings.push(warning);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
isActive() {
|
|
315
|
-
return this.status === 'ENABLED' || this.status === 'ACTIVE';
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
needsConfiguration() {
|
|
319
|
-
return this.status === 'NEEDS_CONFIG';
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
hasErrors() {
|
|
323
|
-
return this.status === 'ERROR';
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
belongsToUser(userId) {
|
|
327
|
-
return this.userId.toString() === userId.toString();
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
async initialize() {
|
|
331
|
-
// Load dynamic user actions
|
|
332
|
-
try {
|
|
333
|
-
const additionalUserActions = await this.loadDynamicUserActions();
|
|
334
|
-
this.events = { ...this.events, ...additionalUserActions };
|
|
335
|
-
} catch (e) {
|
|
336
|
-
this.addError(e);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Register event handlers
|
|
340
|
-
await this.registerEventHandlers();
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
getOptionDetails() {
|
|
344
|
-
const options = new Options({
|
|
345
|
-
module: Object.values(this.constructor.Definition.modules)[0],
|
|
346
|
-
...this.constructor.Definition,
|
|
347
|
-
});
|
|
348
|
-
return options.get();
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// Legacy method for backward compatibility
|
|
352
|
-
async loadModules() {
|
|
353
|
-
// This method was used in the old architecture for loading modules
|
|
354
|
-
// In the new architecture, modules are injected via constructor
|
|
355
|
-
// For backward compatibility, this is a no-op
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
262
|
}
|
|
359
263
|
|
|
360
264
|
module.exports = { IntegrationBase };
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
const { ModuleFactory, Credential, Entity } = require('../module-plugin');
|
|
2
|
+
const { IntegrationModel } = require('./integration-model');
|
|
3
|
+
const _ = require('lodash');
|
|
4
|
+
|
|
5
|
+
class IntegrationFactory {
|
|
6
|
+
constructor(integrationClasses = []) {
|
|
7
|
+
this.integrationClasses = integrationClasses;
|
|
8
|
+
this.moduleFactory = new ModuleFactory(...this.getModules());
|
|
9
|
+
this.integrationTypes = this.integrationClasses.map(
|
|
10
|
+
(IntegrationClass) => IntegrationClass.getName()
|
|
11
|
+
);
|
|
12
|
+
this.getIntegrationDefinitions = this.integrationClasses.map(
|
|
13
|
+
(IntegrationClass) => IntegrationClass.Definition
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async getIntegrationOptions() {
|
|
18
|
+
const options = this.integrationClasses.map(
|
|
19
|
+
(IntegrationClass) => IntegrationClass
|
|
20
|
+
);
|
|
21
|
+
return {
|
|
22
|
+
entities: {
|
|
23
|
+
options: options.map((IntegrationClass) =>
|
|
24
|
+
IntegrationClass.getOptionDetails()
|
|
25
|
+
),
|
|
26
|
+
authorized: [],
|
|
27
|
+
},
|
|
28
|
+
integrations: [],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getModules() {
|
|
33
|
+
return [
|
|
34
|
+
...new Set(
|
|
35
|
+
this.integrationClasses
|
|
36
|
+
.map((integration) =>
|
|
37
|
+
Object.values(integration.Definition.modules).map(
|
|
38
|
+
(module) => module.definition
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
.flat()
|
|
42
|
+
),
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getIntegrationClassByType(type) {
|
|
47
|
+
const integrationClassIndex = this.integrationTypes.indexOf(type);
|
|
48
|
+
return this.integrationClasses[integrationClassIndex];
|
|
49
|
+
}
|
|
50
|
+
getModuleTypesAndKeys(integrationClass) {
|
|
51
|
+
const moduleTypesAndKeys = {};
|
|
52
|
+
const moduleTypeCount = {};
|
|
53
|
+
|
|
54
|
+
if (integrationClass && integrationClass.Definition.modules) {
|
|
55
|
+
for (const [key, moduleClass] of Object.entries(
|
|
56
|
+
integrationClass.Definition.modules
|
|
57
|
+
)) {
|
|
58
|
+
if (
|
|
59
|
+
moduleClass &&
|
|
60
|
+
typeof moduleClass.definition.getName === 'function'
|
|
61
|
+
) {
|
|
62
|
+
const moduleType = moduleClass.definition.getName();
|
|
63
|
+
|
|
64
|
+
// Check if this module type has already been seen
|
|
65
|
+
if (moduleType in moduleTypesAndKeys) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`Duplicate module type "${moduleType}" found in integration class definition.`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Well how baout now
|
|
72
|
+
|
|
73
|
+
moduleTypesAndKeys[moduleType] = key;
|
|
74
|
+
moduleTypeCount[moduleType] =
|
|
75
|
+
(moduleTypeCount[moduleType] || 0) + 1;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Check for any module types with count > 1
|
|
81
|
+
for (const [moduleType, count] of Object.entries(moduleTypeCount)) {
|
|
82
|
+
if (count > 1) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`Multiple instances of module type "${moduleType}" found in integration class definition.`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return moduleTypesAndKeys;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async getInstanceFromIntegrationId(params) {
|
|
93
|
+
const integrationRecord = await IntegrationHelper.getIntegrationById(
|
|
94
|
+
params.integrationId
|
|
95
|
+
);
|
|
96
|
+
let { userId } = params;
|
|
97
|
+
if (!integrationRecord) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`No integration found by the ID of ${params.integrationId}`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!userId) {
|
|
104
|
+
userId = integrationRecord.user._id.toString();
|
|
105
|
+
} else if (userId.toString() !== integrationRecord.user.toString()) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`Integration ${
|
|
108
|
+
params.integrationId
|
|
109
|
+
} does not belong to User ${userId}, ${integrationRecord.user.toString()}`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const integrationClass = this.getIntegrationClassByType(
|
|
114
|
+
integrationRecord.config.type
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const instance = new integrationClass({
|
|
118
|
+
userId,
|
|
119
|
+
integrationId: params.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
|
+
} else {
|
|
139
|
+
// for each entity, get the moduleinstance and load them according to their keys
|
|
140
|
+
// If it's the first entity, load the moduleinstance into primary as well
|
|
141
|
+
// If it's the second entity, load the moduleinstance into target as well
|
|
142
|
+
const moduleTypesAndKeys =
|
|
143
|
+
this.getModuleTypesAndKeys(integrationClass);
|
|
144
|
+
for (let i = 0; i < integrationRecord.entities.length; i++) {
|
|
145
|
+
const entityId = integrationRecord.entities[i];
|
|
146
|
+
const moduleInstance =
|
|
147
|
+
await this.moduleFactory.getModuleInstanceFromEntityId(
|
|
148
|
+
entityId,
|
|
149
|
+
integrationRecord.user
|
|
150
|
+
);
|
|
151
|
+
const moduleType = moduleInstance.getName();
|
|
152
|
+
const key = moduleTypesAndKeys[moduleType];
|
|
153
|
+
instance[key] = moduleInstance;
|
|
154
|
+
if (i === 0) {
|
|
155
|
+
instance.primary = moduleInstance;
|
|
156
|
+
} else if (i === 1) {
|
|
157
|
+
instance.target = moduleInstance;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
instance.record = integrationRecord;
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const additionalUserActions =
|
|
165
|
+
await instance.loadDynamicUserActions();
|
|
166
|
+
instance.events = { ...instance.events, ...additionalUserActions };
|
|
167
|
+
} catch (e) {
|
|
168
|
+
instance.record.status = 'ERROR';
|
|
169
|
+
instance.record.messages.errors.push(e);
|
|
170
|
+
await instance.record.save();
|
|
171
|
+
}
|
|
172
|
+
// Register all of the event handlers
|
|
173
|
+
|
|
174
|
+
await instance.registerEventHandlers();
|
|
175
|
+
return instance;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async createIntegration(entities, userId, config) {
|
|
179
|
+
const integrationRecord = await IntegrationModel.create({
|
|
180
|
+
entities: entities,
|
|
181
|
+
user: userId,
|
|
182
|
+
config,
|
|
183
|
+
version: '0.0.0',
|
|
184
|
+
});
|
|
185
|
+
return await this.getInstanceFromIntegrationId({
|
|
186
|
+
integrationId: integrationRecord.id,
|
|
187
|
+
userId,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const IntegrationHelper = {
|
|
193
|
+
getFormattedIntegration: async function (integrationRecord) {
|
|
194
|
+
const integrationObj = {
|
|
195
|
+
id: integrationRecord.id,
|
|
196
|
+
status: integrationRecord.status,
|
|
197
|
+
config: integrationRecord.config,
|
|
198
|
+
entities: [],
|
|
199
|
+
version: integrationRecord.version,
|
|
200
|
+
messages: integrationRecord.messages,
|
|
201
|
+
};
|
|
202
|
+
for (const entityId of integrationRecord.entities) {
|
|
203
|
+
// Only return non-internal fields. Leverages "select" and "options" to non-excepted fields and a pure object.
|
|
204
|
+
const entity = await Entity.findById(
|
|
205
|
+
entityId,
|
|
206
|
+
'-createdAt -updatedAt -user -credentials -credential -_id -__t -__v',
|
|
207
|
+
{ lean: true }
|
|
208
|
+
);
|
|
209
|
+
integrationObj.entities.push({
|
|
210
|
+
id: entityId,
|
|
211
|
+
...entity,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
return integrationObj;
|
|
215
|
+
},
|
|
216
|
+
|
|
217
|
+
getIntegrationsForUserId: async function (userId) {
|
|
218
|
+
const integrationList = await IntegrationModel.find({ user: userId });
|
|
219
|
+
return await Promise.all(
|
|
220
|
+
integrationList.map(
|
|
221
|
+
async (integrationRecord) =>
|
|
222
|
+
await IntegrationHelper.getFormattedIntegration(
|
|
223
|
+
integrationRecord
|
|
224
|
+
)
|
|
225
|
+
)
|
|
226
|
+
);
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
deleteIntegrationForUserById: async function (userId, integrationId) {
|
|
230
|
+
const integrationList = await IntegrationModel.find({
|
|
231
|
+
user: userId,
|
|
232
|
+
_id: integrationId,
|
|
233
|
+
});
|
|
234
|
+
if (integrationList.length !== 1) {
|
|
235
|
+
throw new Error(
|
|
236
|
+
`Integration with id of ${integrationId} does not exist for this user`
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
await IntegrationModel.deleteOne({ _id: integrationId });
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
getIntegrationById: async function (id) {
|
|
243
|
+
return IntegrationModel.findById(id).populate('entities');
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
listCredentials: async function (options) {
|
|
247
|
+
return Credential.find(options);
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
module.exports = { IntegrationFactory, IntegrationHelper };
|