@friggframework/core 2.0.0-next.0 → 2.0.0-next.2
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/database/index.js +10 -8
- package/database/models/WebsocketConnection.js +49 -0
- package/integrations/integration-base.js +135 -48
- package/integrations/integration-factory.js +151 -54
- package/integrations/integration-model.js +4 -0
- package/package.json +5 -5
- package/queues/index.js +4 -0
- package/queues/queuer-util.js +61 -0
package/database/index.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
const { mongoose} = require('./mongoose');
|
|
1
|
+
const { mongoose } = require('./mongoose');
|
|
2
2
|
const {
|
|
3
3
|
connectToDatabase,
|
|
4
4
|
disconnectFromDatabase,
|
|
5
5
|
createObjectId,
|
|
6
6
|
} = require('./mongo');
|
|
7
|
-
const {IndividualUser} = require('./models/IndividualUser');
|
|
8
|
-
const {OrganizationUser} = require('./models/OrganizationUser');
|
|
9
|
-
const {State} = require('./models/State');
|
|
10
|
-
const {Token} = require('./models/Token');
|
|
11
|
-
const {UserModel} = require('./models/UserModel');
|
|
7
|
+
const { IndividualUser } = require('./models/IndividualUser');
|
|
8
|
+
const { OrganizationUser } = require('./models/OrganizationUser');
|
|
9
|
+
const { State } = require('./models/State');
|
|
10
|
+
const { Token } = require('./models/Token');
|
|
11
|
+
const { UserModel } = require('./models/UserModel');
|
|
12
|
+
const { WebsocketConnection } = require('./models/WebsocketConnection');
|
|
12
13
|
|
|
13
14
|
module.exports = {
|
|
14
15
|
mongoose,
|
|
@@ -19,5 +20,6 @@ module.exports = {
|
|
|
19
20
|
OrganizationUser,
|
|
20
21
|
State,
|
|
21
22
|
Token,
|
|
22
|
-
UserModel
|
|
23
|
-
|
|
23
|
+
UserModel,
|
|
24
|
+
WebsocketConnection,
|
|
25
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const { mongoose } = require('../mongoose');
|
|
2
|
+
const AWS = require('aws-sdk');
|
|
3
|
+
|
|
4
|
+
const schema = new mongoose.Schema({
|
|
5
|
+
connectionId: { type: mongoose.Schema.Types.String },
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// Add a static method to get active connections
|
|
9
|
+
schema.statics.getActiveConnections = async function () {
|
|
10
|
+
try {
|
|
11
|
+
const connections = await this.find({}, 'connectionId');
|
|
12
|
+
return connections.map((conn) => ({
|
|
13
|
+
connectionId: conn.connectionId,
|
|
14
|
+
send: async (data) => {
|
|
15
|
+
const apigwManagementApi = new AWS.ApiGatewayManagementApi({
|
|
16
|
+
apiVersion: '2018-11-29',
|
|
17
|
+
endpoint: process.env.WEBSOCKET_API_ENDPOINT,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
await apigwManagementApi
|
|
22
|
+
.postToConnection({
|
|
23
|
+
ConnectionId: conn.connectionId,
|
|
24
|
+
Data: JSON.stringify(data),
|
|
25
|
+
})
|
|
26
|
+
.promise();
|
|
27
|
+
} catch (error) {
|
|
28
|
+
if (error.statusCode === 410) {
|
|
29
|
+
console.log(`Stale connection ${conn.connectionId}`);
|
|
30
|
+
await this.deleteOne({
|
|
31
|
+
connectionId: conn.connectionId,
|
|
32
|
+
});
|
|
33
|
+
} else {
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
}));
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('Error getting active connections:', error);
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const WebsocketConnection =
|
|
46
|
+
mongoose.models.WebsocketConnection ||
|
|
47
|
+
mongoose.model('WebsocketConnection', schema);
|
|
48
|
+
|
|
49
|
+
module.exports = { WebsocketConnection };
|
|
@@ -1,41 +1,122 @@
|
|
|
1
1
|
const { IntegrationMapping } = require('./integration-mapping');
|
|
2
|
+
const { Options } = require('./options');
|
|
3
|
+
const constantsToBeMigrated = {
|
|
4
|
+
defaultEvents: {
|
|
5
|
+
ON_CREATE: 'ON_CREATE',
|
|
6
|
+
ON_UPDATE: 'ON_UPDATE',
|
|
7
|
+
ON_DELETE: 'ON_DELETE',
|
|
8
|
+
GET_CONFIG_OPTIONS: 'GET_CONFIG_OPTIONS',
|
|
9
|
+
REFRESH_CONFIG_OPTIONS: 'REFRESH_CONFIG_OPTIONS',
|
|
10
|
+
GET_USER_ACTIONS: 'GET_USER_ACTIONS',
|
|
11
|
+
GET_USER_ACTION_OPTIONS: 'GET_USER_ACTION_OPTIONS',
|
|
12
|
+
REFRESH_USER_ACTION_OPTIONS: 'REFRESH_USER_ACTION_OPTIONS',
|
|
13
|
+
// etc...
|
|
14
|
+
},
|
|
15
|
+
types: {
|
|
16
|
+
LIFE_CYCLE_EVENT: 'LIFE_CYCLE_EVENT',
|
|
17
|
+
USER_ACTION: 'USER_ACTION',
|
|
18
|
+
},
|
|
19
|
+
};
|
|
2
20
|
|
|
3
21
|
class IntegrationBase {
|
|
22
|
+
static getOptionDetails() {
|
|
23
|
+
const options = new Options({
|
|
24
|
+
module: Object.values(this.Definition.modules)[0], // This is a placeholder until we revamp the frontend
|
|
25
|
+
...this.Definition,
|
|
26
|
+
});
|
|
27
|
+
return options.get();
|
|
28
|
+
}
|
|
4
29
|
/**
|
|
5
|
-
* CHILDREN SHOULD SPECIFY A
|
|
30
|
+
* CHILDREN SHOULD SPECIFY A DEFINITION FOR THE INTEGRATION
|
|
6
31
|
*/
|
|
7
|
-
static
|
|
32
|
+
static Definition = {
|
|
8
33
|
name: 'Integration Name',
|
|
9
34
|
version: '0.0.0', // Integration Version, used for migration and storage purposes, as well as display
|
|
10
35
|
supportedVersions: [], // Eventually usable for deprecation and future test version purposes
|
|
11
36
|
|
|
12
|
-
|
|
13
|
-
|
|
37
|
+
modules: {},
|
|
38
|
+
display: {
|
|
39
|
+
name: 'Integration Name',
|
|
40
|
+
logo: '',
|
|
41
|
+
description: '',
|
|
42
|
+
// etc...
|
|
43
|
+
},
|
|
14
44
|
};
|
|
15
45
|
|
|
16
46
|
static getName() {
|
|
17
|
-
return this.
|
|
47
|
+
return this.Definition.name;
|
|
18
48
|
}
|
|
19
49
|
|
|
20
50
|
static getCurrentVersion() {
|
|
21
|
-
return this.
|
|
51
|
+
return this.Definition.version;
|
|
52
|
+
}
|
|
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
|
+
}
|
|
68
|
+
registerEventHandlers() {
|
|
69
|
+
this.on = {
|
|
70
|
+
...this.defaultEvents,
|
|
71
|
+
...this.events,
|
|
72
|
+
};
|
|
22
73
|
}
|
|
23
74
|
|
|
24
75
|
constructor(params) {
|
|
25
|
-
this.
|
|
26
|
-
|
|
76
|
+
this.defaultEvents = {
|
|
77
|
+
[constantsToBeMigrated.defaultEvents.ON_CREATE]: {
|
|
78
|
+
type: constantsToBeMigrated.types.LIFE_CYCLE_EVENT,
|
|
79
|
+
handler: this.onCreate,
|
|
80
|
+
},
|
|
81
|
+
[constantsToBeMigrated.defaultEvents.ON_UPDATE]: {
|
|
82
|
+
type: constantsToBeMigrated.types.LIFE_CYCLE_EVENT,
|
|
83
|
+
handler: this.onUpdate,
|
|
84
|
+
},
|
|
85
|
+
[constantsToBeMigrated.defaultEvents.ON_DELETE]: {
|
|
86
|
+
type: constantsToBeMigrated.types.LIFE_CYCLE_EVENT,
|
|
87
|
+
handler: this.onDelete,
|
|
88
|
+
},
|
|
89
|
+
[constantsToBeMigrated.defaultEvents.GET_CONFIG_OPTIONS]: {
|
|
90
|
+
type: constantsToBeMigrated.types.LIFE_CYCLE_EVENT,
|
|
91
|
+
handler: this.getConfigOptions,
|
|
92
|
+
},
|
|
93
|
+
[constantsToBeMigrated.defaultEvents.REFRESH_CONFIG_OPTIONS]: {
|
|
94
|
+
type: constantsToBeMigrated.types.LIFE_CYCLE_EVENT,
|
|
95
|
+
handler: this.refreshConfigOptions,
|
|
96
|
+
},
|
|
97
|
+
[constantsToBeMigrated.defaultEvents.GET_USER_ACTIONS]: {
|
|
98
|
+
type: constantsToBeMigrated.types.LIFE_CYCLE_EVENT,
|
|
99
|
+
handler: this.loadUserActions,
|
|
100
|
+
},
|
|
101
|
+
[constantsToBeMigrated.defaultEvents.GET_USER_ACTION_OPTIONS]: {
|
|
102
|
+
type: constantsToBeMigrated.types.LIFE_CYCLE_EVENT,
|
|
103
|
+
handler: this.getActionOptions,
|
|
104
|
+
},
|
|
105
|
+
[constantsToBeMigrated.defaultEvents.REFRESH_USER_ACTION_OPTIONS]: {
|
|
106
|
+
type: constantsToBeMigrated.types.LIFE_CYCLE_EVENT,
|
|
107
|
+
handler: this.refreshActionOptions,
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
this.loadModules();
|
|
27
111
|
}
|
|
28
112
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
async notify(delegateString, object = null) {
|
|
33
|
-
if (!this.delegateTypes.includes(delegateString)) {
|
|
113
|
+
async send(event, object) {
|
|
114
|
+
if (!this.on[event]) {
|
|
34
115
|
throw new Error(
|
|
35
|
-
`
|
|
116
|
+
`Event ${event} is not defined in the Integration event object`
|
|
36
117
|
);
|
|
37
118
|
}
|
|
38
|
-
return this.
|
|
119
|
+
return this.on[event].handler.call(this, object);
|
|
39
120
|
}
|
|
40
121
|
|
|
41
122
|
async validateConfig() {
|
|
@@ -69,28 +150,20 @@ class IntegrationBase {
|
|
|
69
150
|
async testAuth() {
|
|
70
151
|
let didAuthPass = true;
|
|
71
152
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
153
|
+
for (const module of Object.keys(IntegrationBase.Definition.modules)) {
|
|
154
|
+
try {
|
|
155
|
+
await this[module].testAuth();
|
|
156
|
+
} catch {
|
|
157
|
+
didAuthPass = false;
|
|
158
|
+
this.record.messages.errors.push({
|
|
159
|
+
title: 'Authentication Error',
|
|
160
|
+
message: `There was an error with your ${this[
|
|
161
|
+
module
|
|
162
|
+
].constructor.getName()} Entity.
|
|
79
163
|
Please reconnect/re-authenticate, or reach out to Support for assistance.`,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
await this.target.testAuth();
|
|
86
|
-
} catch {
|
|
87
|
-
didAuthPass = false;
|
|
88
|
-
this.record.messages.errors.push({
|
|
89
|
-
title: 'Authentication Error',
|
|
90
|
-
message: `There was an error with your ${this.target.constructor.getName()} Entity.
|
|
91
|
-
Please reconnect/re-authenticate, or reach out to Support for assistance.`,
|
|
92
|
-
timestamp: Date.now(),
|
|
93
|
-
});
|
|
164
|
+
timestamp: Date.now(),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
94
167
|
}
|
|
95
168
|
|
|
96
169
|
if (!didAuthPass) {
|
|
@@ -115,15 +188,6 @@ class IntegrationBase {
|
|
|
115
188
|
);
|
|
116
189
|
}
|
|
117
190
|
|
|
118
|
-
async getAndSetUserActions() {
|
|
119
|
-
this.userActions = await this.getUserActions();
|
|
120
|
-
if (this.record?.config) {
|
|
121
|
-
this.record.config.userActions = this.userActions;
|
|
122
|
-
await this.record.save();
|
|
123
|
-
}
|
|
124
|
-
return this.userActions;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
191
|
/**
|
|
128
192
|
* CHILDREN CAN OVERRIDE THESE CONFIGURATION METHODS
|
|
129
193
|
*/
|
|
@@ -153,11 +217,34 @@ class IntegrationBase {
|
|
|
153
217
|
return options;
|
|
154
218
|
}
|
|
155
219
|
|
|
156
|
-
async
|
|
157
|
-
|
|
220
|
+
async loadDynamicUserActions() {
|
|
221
|
+
// Child class should override this method to load dynamic user actions.
|
|
222
|
+
// Dynamic user actions should return in the same form a valid event object
|
|
223
|
+
|
|
224
|
+
return {};
|
|
225
|
+
}
|
|
226
|
+
async loadUserActions({ actionType } = {}) {
|
|
227
|
+
console.log('loadUserActions called with actionType:', actionType);
|
|
228
|
+
const userActions = {};
|
|
229
|
+
for (const [key, event] of Object.entries(this.events)) {
|
|
230
|
+
if (event.type === constantsToBeMigrated.types.USER_ACTION) {
|
|
231
|
+
if (!actionType || event.userActionType === actionType) {
|
|
232
|
+
userActions[key] = event;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const dynamicUserActions = await this.loadDynamicUserActions();
|
|
237
|
+
const filteredDynamicActions = actionType
|
|
238
|
+
? Object.fromEntries(
|
|
239
|
+
Object.entries(dynamicUserActions).filter(
|
|
240
|
+
([_, event]) => event.userActionType === actionType
|
|
241
|
+
)
|
|
242
|
+
)
|
|
243
|
+
: dynamicUserActions;
|
|
244
|
+
return { ...userActions, ...filteredDynamicActions };
|
|
158
245
|
}
|
|
159
246
|
|
|
160
|
-
async getActionOptions() {
|
|
247
|
+
async getActionOptions(actionId, data) {
|
|
161
248
|
const options = {
|
|
162
249
|
jsonSchema: {},
|
|
163
250
|
uiSchema: {},
|
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
const { ModuleFactory, Credential, Entity } = require('../module-plugin');
|
|
2
|
-
const {IntegrationModel} = require(
|
|
2
|
+
const { IntegrationModel } = require('./integration-model');
|
|
3
3
|
const _ = require('lodash');
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
class IntegrationFactory {
|
|
7
6
|
constructor(integrationClasses = []) {
|
|
8
7
|
this.integrationClasses = integrationClasses;
|
|
9
8
|
this.moduleFactory = new ModuleFactory(...this.getModules());
|
|
10
|
-
this.integrationTypes = this.integrationClasses.map(
|
|
11
|
-
|
|
9
|
+
this.integrationTypes = this.integrationClasses.map(
|
|
10
|
+
(IntegrationClass) => IntegrationClass.getName()
|
|
11
|
+
);
|
|
12
|
+
this.getIntegrationDefinitions = this.integrationClasses.map(
|
|
13
|
+
(IntegrationClass) => IntegrationClass.Definition
|
|
14
|
+
);
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
async getIntegrationOptions() {
|
|
15
|
-
const options = this.integrationClasses.map(
|
|
18
|
+
const options = this.integrationClasses.map(
|
|
19
|
+
(IntegrationClass) => IntegrationClass
|
|
20
|
+
);
|
|
16
21
|
return {
|
|
17
22
|
entities: {
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
options: options.map((IntegrationClass) =>
|
|
24
|
+
IntegrationClass.getOptionDetails()
|
|
25
|
+
),
|
|
20
26
|
authorized: [],
|
|
21
27
|
},
|
|
22
28
|
integrations: [],
|
|
@@ -24,65 +30,148 @@ class IntegrationFactory {
|
|
|
24
30
|
}
|
|
25
31
|
|
|
26
32
|
getModules() {
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
|
|
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
|
+
];
|
|
30
44
|
}
|
|
31
45
|
|
|
32
|
-
|
|
33
|
-
function findMostFrequentElement(array) {
|
|
34
|
-
const frequencyMap = _.countBy(array);
|
|
35
|
-
return _.maxBy(_.keys(frequencyMap), (element) => frequencyMap[element]);
|
|
36
|
-
}
|
|
37
|
-
const allModulesNames = _.flatten(this.integrationClasses.map(integration =>
|
|
38
|
-
Object.values(integration.modules).map(module => module.getName())
|
|
39
|
-
));
|
|
40
|
-
return findMostFrequentElement(allModulesNames);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
getIntegrationClassDefByType(type) {
|
|
46
|
+
getIntegrationClassByType(type) {
|
|
44
47
|
const integrationClassIndex = this.integrationTypes.indexOf(type);
|
|
45
48
|
return this.integrationClasses[integrationClassIndex];
|
|
46
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
|
+
}
|
|
47
91
|
|
|
48
92
|
async getInstanceFromIntegrationId(params) {
|
|
49
|
-
const integrationRecord = await IntegrationHelper.getIntegrationById(
|
|
50
|
-
|
|
93
|
+
const integrationRecord = await IntegrationHelper.getIntegrationById(
|
|
94
|
+
params.integrationId
|
|
95
|
+
);
|
|
96
|
+
let { userId } = params;
|
|
51
97
|
if (!integrationRecord) {
|
|
52
|
-
throw new Error(
|
|
98
|
+
throw new Error(
|
|
99
|
+
`No integration found by the ID of ${params.integrationId}`
|
|
100
|
+
);
|
|
53
101
|
}
|
|
54
102
|
|
|
55
103
|
if (!userId) {
|
|
56
104
|
userId = integrationRecord.user._id.toString();
|
|
57
|
-
} else if (userId !== integrationRecord.user.
|
|
58
|
-
throw new Error(
|
|
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
|
+
);
|
|
59
111
|
}
|
|
60
112
|
|
|
61
|
-
const
|
|
62
|
-
|
|
113
|
+
const integrationClass = this.getIntegrationClassByType(
|
|
114
|
+
integrationRecord.config.type
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const instance = new integrationClass({
|
|
63
118
|
userId,
|
|
64
119
|
integrationId: params.integrationId,
|
|
65
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
|
+
}
|
|
66
161
|
instance.record = integrationRecord;
|
|
67
|
-
instance.delegateTypes.push(...integrationClassDef.Config.events);
|
|
68
|
-
instance.primary = await this.moduleFactory.getModuleInstanceFromEntityId(
|
|
69
|
-
instance.record.entities[0],
|
|
70
|
-
instance.record.user
|
|
71
|
-
);
|
|
72
|
-
instance.target = await this.moduleFactory.getModuleInstanceFromEntityId(
|
|
73
|
-
instance.record.entities[1],
|
|
74
|
-
instance.record.user
|
|
75
|
-
);
|
|
76
162
|
|
|
77
163
|
try {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
164
|
+
const additionalUserActions =
|
|
165
|
+
await instance.loadDynamicUserActions();
|
|
166
|
+
instance.events = { ...instance.events, ...additionalUserActions };
|
|
167
|
+
} catch (e) {
|
|
82
168
|
instance.record.status = 'ERROR';
|
|
83
169
|
instance.record.messages.errors.push(e);
|
|
84
170
|
await instance.record.save();
|
|
85
171
|
}
|
|
172
|
+
// Register all of the event handlers
|
|
173
|
+
|
|
174
|
+
await instance.registerEventHandlers();
|
|
86
175
|
return instance;
|
|
87
176
|
}
|
|
88
177
|
|
|
@@ -93,12 +182,15 @@ class IntegrationFactory {
|
|
|
93
182
|
config,
|
|
94
183
|
version: '0.0.0',
|
|
95
184
|
});
|
|
96
|
-
return await this.getInstanceFromIntegrationId({
|
|
185
|
+
return await this.getInstanceFromIntegrationId({
|
|
186
|
+
integrationId: integrationRecord.id,
|
|
187
|
+
userId,
|
|
188
|
+
});
|
|
97
189
|
}
|
|
98
190
|
}
|
|
99
191
|
|
|
100
192
|
const IntegrationHelper = {
|
|
101
|
-
|
|
193
|
+
getFormattedIntegration: async function (integrationRecord) {
|
|
102
194
|
const integrationObj = {
|
|
103
195
|
id: integrationRecord.id,
|
|
104
196
|
status: integrationRecord.status,
|
|
@@ -122,14 +214,19 @@ const IntegrationHelper = {
|
|
|
122
214
|
return integrationObj;
|
|
123
215
|
},
|
|
124
216
|
|
|
125
|
-
|
|
217
|
+
getIntegrationsForUserId: async function (userId) {
|
|
126
218
|
const integrationList = await IntegrationModel.find({ user: userId });
|
|
127
|
-
return await Promise.all(
|
|
128
|
-
|
|
129
|
-
|
|
219
|
+
return await Promise.all(
|
|
220
|
+
integrationList.map(
|
|
221
|
+
async (integrationRecord) =>
|
|
222
|
+
await IntegrationHelper.getFormattedIntegration(
|
|
223
|
+
integrationRecord
|
|
224
|
+
)
|
|
225
|
+
)
|
|
226
|
+
);
|
|
130
227
|
},
|
|
131
228
|
|
|
132
|
-
|
|
229
|
+
deleteIntegrationForUserById: async function (userId, integrationId) {
|
|
133
230
|
const integrationList = await IntegrationModel.find({
|
|
134
231
|
user: userId,
|
|
135
232
|
_id: integrationId,
|
|
@@ -142,13 +239,13 @@ const IntegrationHelper = {
|
|
|
142
239
|
await IntegrationModel.deleteOne({ _id: integrationId });
|
|
143
240
|
},
|
|
144
241
|
|
|
145
|
-
|
|
146
|
-
return IntegrationModel.findById(id);
|
|
242
|
+
getIntegrationById: async function (id) {
|
|
243
|
+
return IntegrationModel.findById(id).populate('entities');
|
|
147
244
|
},
|
|
148
245
|
|
|
149
|
-
|
|
246
|
+
listCredentials: async function (options) {
|
|
150
247
|
return Credential.find(options);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
248
|
+
},
|
|
249
|
+
};
|
|
153
250
|
|
|
154
251
|
module.exports = { IntegrationFactory, IntegrationHelper };
|
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-next.
|
|
4
|
+
"version": "2.0.0-next.2",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@hapi/boom": "^10.0.1",
|
|
7
7
|
"aws-sdk": "^2.1200.0",
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"node-fetch": "^2.6.7"
|
|
16
16
|
},
|
|
17
17
|
"devDependencies": {
|
|
18
|
-
"@friggframework/eslint-config": "
|
|
19
|
-
"@friggframework/prettier-config": "
|
|
20
|
-
"@friggframework/test": "
|
|
18
|
+
"@friggframework/eslint-config": "2.0.0-next.2",
|
|
19
|
+
"@friggframework/prettier-config": "2.0.0-next.2",
|
|
20
|
+
"@friggframework/test": "2.0.0-next.2",
|
|
21
21
|
"@types/lodash": "^4.14.191",
|
|
22
22
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
23
23
|
"chai": "^4.3.6",
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
},
|
|
49
49
|
"homepage": "https://github.com/friggframework/frigg#readme",
|
|
50
50
|
"description": "",
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "d2468c93806cf76851ead37495c10a4532d61489"
|
|
52
52
|
}
|
package/queues/index.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const { v4: uuid } = require('uuid');
|
|
2
|
+
const AWS = require('aws-sdk');
|
|
3
|
+
const awsConfigOptions = () => {
|
|
4
|
+
const config = {};
|
|
5
|
+
if (process.env.IS_OFFLINE) {
|
|
6
|
+
console.log('Running in offline mode');
|
|
7
|
+
}
|
|
8
|
+
if (process.env.AWS_ENDPOINT) {
|
|
9
|
+
config.endpoint = process.env.AWS_ENDPOINT;
|
|
10
|
+
}
|
|
11
|
+
return config;
|
|
12
|
+
};
|
|
13
|
+
AWS.config.update(awsConfigOptions());
|
|
14
|
+
const sqs = new AWS.SQS();
|
|
15
|
+
|
|
16
|
+
const QueuerUtil = {
|
|
17
|
+
batchSend: async (entries = [], queueUrl) => {
|
|
18
|
+
console.log(
|
|
19
|
+
`Enqueuing ${entries.length} entries on SQS to queue ${queueUrl}`
|
|
20
|
+
);
|
|
21
|
+
const buffer = [];
|
|
22
|
+
const batchSize = 10;
|
|
23
|
+
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
buffer.push({
|
|
26
|
+
Id: uuid(),
|
|
27
|
+
MessageBody: JSON.stringify(entry),
|
|
28
|
+
});
|
|
29
|
+
// Sends 10, then purges the buffer
|
|
30
|
+
if (buffer.length === batchSize) {
|
|
31
|
+
console.log('Buffer at 10, sending batch');
|
|
32
|
+
await sqs
|
|
33
|
+
.sendMessageBatch({
|
|
34
|
+
Entries: buffer,
|
|
35
|
+
QueueUrl: queueUrl,
|
|
36
|
+
})
|
|
37
|
+
.promise();
|
|
38
|
+
// Purge the buffer
|
|
39
|
+
buffer.splice(0, buffer.length);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
console.log('Buffer at end, sending final batch');
|
|
43
|
+
|
|
44
|
+
// If any remaining entries under 10 are left in the buffer, send and return
|
|
45
|
+
if (buffer.length > 0) {
|
|
46
|
+
console.log(buffer);
|
|
47
|
+
return sqs
|
|
48
|
+
.sendMessageBatch({
|
|
49
|
+
Entries: buffer,
|
|
50
|
+
QueueUrl: queueUrl,
|
|
51
|
+
})
|
|
52
|
+
.promise();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// If we're exact... just return an empty object for now
|
|
56
|
+
|
|
57
|
+
return {};
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
module.exports = { QueuerUtil };
|