@friggframework/core 2.0.0--canary.396.6862738.0 → 2.0.0--canary.397.c07f148.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 +931 -50
- package/core/create-handler.js +1 -0
- package/credential/credential-repository.js +48 -1
- package/credential/use-cases/update-authentication-status.js +15 -0
- package/handlers/backend-utils.js +34 -31
- package/handlers/routers/auth.js +1 -15
- package/index.js +1 -5
- package/integrations/integration-base.js +133 -40
- package/integrations/integration-repository.js +39 -3
- package/integrations/integration-router.js +109 -85
- package/integrations/tests/doubles/dummy-integration-class.js +90 -0
- package/integrations/tests/doubles/test-integration-repository.js +89 -0
- package/integrations/tests/use-cases/create-integration.test.js +124 -0
- package/integrations/tests/use-cases/delete-integration-for-user.test.js +143 -0
- package/integrations/tests/use-cases/get-integration-for-user.test.js +143 -0
- package/integrations/tests/use-cases/get-integration-instance.test.js +169 -0
- package/integrations/tests/use-cases/get-integrations-for-user.test.js +169 -0
- package/integrations/tests/use-cases/get-possible-integrations.test.js +188 -0
- package/integrations/tests/use-cases/update-integration-messages.test.js +142 -0
- package/integrations/tests/use-cases/update-integration-status.test.js +103 -0
- package/integrations/tests/use-cases/update-integration.test.js +134 -0
- package/integrations/use-cases/create-integration.js +25 -12
- package/integrations/use-cases/delete-integration-for-user.js +21 -2
- package/integrations/use-cases/get-integration-for-user.js +28 -13
- package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
- package/integrations/use-cases/get-integration-instance.js +20 -11
- package/integrations/use-cases/get-integrations-for-user.js +22 -10
- package/integrations/use-cases/get-possible-integrations.js +27 -0
- package/integrations/use-cases/update-integration-messages.js +31 -0
- package/integrations/use-cases/update-integration-status.js +28 -0
- package/integrations/use-cases/update-integration.js +23 -13
- package/integrations/utils/map-integration-dto.js +0 -1
- package/{module-plugin → modules}/entity.js +1 -0
- package/{module-plugin → modules}/index.js +0 -4
- package/{module-plugin/module-service.js → modules/module-factory.js} +9 -5
- package/modules/module-repository.js +107 -0
- package/modules/module.js +218 -0
- package/modules/tests/doubles/test-module-factory.js +16 -0
- package/modules/tests/doubles/test-module-repository.js +19 -0
- package/{module-plugin → modules}/use-cases/get-entities-for-user.js +1 -1
- package/modules/use-cases/get-entity-options-by-id.js +58 -0
- package/modules/use-cases/get-entity-options-by-type.js +34 -0
- package/modules/use-cases/get-module-instance-from-type.js +31 -0
- package/modules/use-cases/get-module.js +56 -0
- package/modules/use-cases/process-authorization-callback.js +108 -0
- package/modules/use-cases/refresh-entity-options.js +58 -0
- package/modules/use-cases/test-module-auth.js +54 -0
- package/{module-plugin → modules}/utils/map-module-dto.js +1 -1
- package/package.json +5 -5
- package/syncs/sync.js +0 -1
- package/types/module-plugin/index.d.ts +0 -35
- package/types/syncs/index.d.ts +0 -2
- package/integrations/integration.js +0 -233
- package/integrations/test/integration-base.test.js +0 -144
- package/module-plugin/manager.js +0 -169
- package/module-plugin/module-factory.js +0 -42
- package/module-plugin/module-repository.js +0 -70
- package/module-plugin/module.js +0 -329
- /package/{module-plugin → modules}/ModuleConstants.js +0 -0
- /package/{module-plugin → modules}/credential.js +0 -0
- /package/{module-plugin → modules}/requester/api-key.js +0 -0
- /package/{module-plugin → modules}/requester/basic.js +0 -0
- /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
- /package/{module-plugin → modules}/requester/requester.js +0 -0
- /package/{module-plugin → modules}/requester/requester.test.js +0 -0
- /package/{module-plugin → modules}/test/auther.test.js +0 -0
- /package/{module-plugin → modules}/test/mock-api/api.js +0 -0
- /package/{module-plugin → modules}/test/mock-api/definition.js +0 -0
- /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
package/core/create-handler.js
CHANGED
|
@@ -34,6 +34,7 @@ const createHandler = (optionByName = {}) => {
|
|
|
34
34
|
// Helps mongoose reuse the connection. Lowers response times.
|
|
35
35
|
context.callbackWaitsForEmptyEventLoop = false;
|
|
36
36
|
|
|
37
|
+
// todo: this should not be necessary anymore
|
|
37
38
|
if (shouldUseDatabase) {
|
|
38
39
|
await connectToDatabase();
|
|
39
40
|
}
|
|
@@ -1,9 +1,56 @@
|
|
|
1
|
-
const { Credential } = require('../
|
|
1
|
+
const { Credential } = require('../modules');
|
|
2
2
|
|
|
3
3
|
class CredentialRepository {
|
|
4
4
|
async findCredentialById(id) {
|
|
5
5
|
return Credential.findById(id);
|
|
6
6
|
}
|
|
7
|
+
|
|
8
|
+
async updateAuthenticationStatus(credentialId, authIsValid) {
|
|
9
|
+
return Credential.updateOne({ _id: credentialId }, { $set: { auth_is_valid: authIsValid } });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Permanently remove a credential document.
|
|
14
|
+
* @param {string} credentialId
|
|
15
|
+
* @returns {Promise<import('mongoose').DeleteResult>}
|
|
16
|
+
*/
|
|
17
|
+
async deleteCredentialById(credentialId) {
|
|
18
|
+
return Credential.deleteOne({ _id: credentialId });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create a new credential or update an existing one matching the identifiers.
|
|
23
|
+
* `credentialDetails` format: { identifiers: { ... }, details: { ... } }
|
|
24
|
+
* Identifiers are used in the query filter; details are merged into the document.
|
|
25
|
+
* @param {{identifiers: Object, details: Object}} credentialDetails
|
|
26
|
+
* @returns {Promise<Object>} The persisted credential (lean object)
|
|
27
|
+
*/
|
|
28
|
+
async upsertCredential(credentialDetails) {
|
|
29
|
+
const { identifiers, details } = credentialDetails;
|
|
30
|
+
if (!identifiers) throw new Error('identifiers required to upsert credential');
|
|
31
|
+
|
|
32
|
+
const query = { ...identifiers };
|
|
33
|
+
|
|
34
|
+
const update = { $set: { ...details } };
|
|
35
|
+
|
|
36
|
+
const options = {
|
|
37
|
+
upsert: true,
|
|
38
|
+
new: true,
|
|
39
|
+
setDefaultsOnInsert: true,
|
|
40
|
+
lean: true,
|
|
41
|
+
strict: false,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const credential = await Credential.findOneAndUpdate(query, update, options);
|
|
45
|
+
return {
|
|
46
|
+
id: credential._id.toString(),
|
|
47
|
+
externalId: credential.externalId,
|
|
48
|
+
userId: credential.user.toString(),
|
|
49
|
+
auth_is_valid: credential.auth_is_valid,
|
|
50
|
+
access_token: credential.access_token,
|
|
51
|
+
refresh_token: credential.refresh_token,
|
|
52
|
+
}
|
|
53
|
+
}
|
|
7
54
|
}
|
|
8
55
|
|
|
9
56
|
module.exports = { CredentialRepository };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class UpdateAuthenticationStatus {
|
|
2
|
+
constructor({ credentialRepository }) {
|
|
3
|
+
this.credentialRepository = credentialRepository;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {string} credentialId
|
|
8
|
+
* @param {boolean} authIsValid
|
|
9
|
+
*/
|
|
10
|
+
async execute(credentialId, authIsValid) {
|
|
11
|
+
await this.credentialRepository.updateAuthenticationStatus(credentialId, authIsValid);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = { UpdateAuthenticationStatus };
|
|
@@ -1,21 +1,34 @@
|
|
|
1
1
|
const { Router } = require('express');
|
|
2
2
|
const { Worker } = require('@friggframework/core');
|
|
3
|
-
const { loadAppDefinition } = require('./app-definition-loader');
|
|
4
3
|
const { IntegrationRepository } = require('../integrations/integration-repository');
|
|
5
|
-
const {
|
|
6
|
-
const {
|
|
4
|
+
const { ModuleFactory } = require('../modules/module-factory');
|
|
5
|
+
const { getModulesDefinitionFromIntegrationClasses } = require('../integrations/utils/map-integration-dto');
|
|
6
|
+
const { ModuleRepository } = require('../modules/module-repository');
|
|
7
|
+
const { GetIntegrationInstanceByDefinition } = require('../integrations/use-cases/get-integration-instance-by-definition');
|
|
7
8
|
|
|
8
9
|
const loadRouterFromObject = (IntegrationClass, routerObject) => {
|
|
10
|
+
|
|
11
|
+
const integrationRepository = new IntegrationRepository();
|
|
12
|
+
const moduleRepository = new ModuleRepository();
|
|
13
|
+
const moduleFactory = new ModuleFactory({
|
|
14
|
+
moduleRepository,
|
|
15
|
+
moduleDefinitions: getModulesDefinitionFromIntegrationClasses([IntegrationClass]),
|
|
16
|
+
});
|
|
9
17
|
const router = Router();
|
|
10
18
|
const { path, method, event } = routerObject;
|
|
19
|
+
|
|
11
20
|
console.log(
|
|
12
21
|
`Registering ${method} ${path} for ${IntegrationClass.Definition.name}`
|
|
13
22
|
);
|
|
23
|
+
|
|
14
24
|
router[method.toLowerCase()](path, async (req, res, next) => {
|
|
15
25
|
try {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
26
|
+
const getIntegrationInstanceByDefinition = new GetIntegrationInstanceByDefinition({
|
|
27
|
+
integrationRepository,
|
|
28
|
+
moduleFactory,
|
|
29
|
+
moduleRepository,
|
|
30
|
+
});
|
|
31
|
+
const integration = await getIntegrationInstanceByDefinition.execute(IntegrationClass);
|
|
19
32
|
const result = await integration.send(event, { req, res, next });
|
|
20
33
|
res.json(result);
|
|
21
34
|
} catch (error) {
|
|
@@ -29,35 +42,25 @@ const loadRouterFromObject = (IntegrationClass, routerObject) => {
|
|
|
29
42
|
//todo: this should be in a use case class
|
|
30
43
|
const createQueueWorker = (integrationClass) => {
|
|
31
44
|
class QueueWorker extends Worker {
|
|
45
|
+
|
|
46
|
+
integrationRepository = new IntegrationRepository();
|
|
47
|
+
moduleRepository = new ModuleRepository();
|
|
48
|
+
moduleFactory = new ModuleFactory({
|
|
49
|
+
moduleRepository: this.moduleRepository,
|
|
50
|
+
moduleDefinitions: getModulesDefinitionFromIntegrationClasses([integrationClass]),
|
|
51
|
+
});
|
|
52
|
+
|
|
32
53
|
async _run(params, context) {
|
|
33
54
|
try {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
console.log(
|
|
40
|
-
`${params.event} for ${integrationClass.Definition.name} integration with no integrationId`
|
|
41
|
-
);
|
|
42
|
-
} else {
|
|
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
|
-
});
|
|
55
|
+
const getIntegrationInstanceByDefinition = new GetIntegrationInstanceByDefinition({
|
|
56
|
+
integrationRepository: this.integrationRepository,
|
|
57
|
+
moduleFactory: this.moduleFactory,
|
|
58
|
+
moduleRepository: this.moduleRepository,
|
|
59
|
+
});
|
|
52
60
|
|
|
53
|
-
|
|
54
|
-
integrationInstance = await getIntegrationInstance.execute(params.integrationId, params.userId);
|
|
55
|
-
console.log(
|
|
56
|
-
`${params.event} for ${integrationInstance.record.config.type} of integrationId: ${params.integrationId}`
|
|
57
|
-
);
|
|
58
|
-
}
|
|
61
|
+
const integration = await getIntegrationInstanceByDefinition.execute(integrationClass);
|
|
59
62
|
|
|
60
|
-
const res = await
|
|
63
|
+
const res = await integration.send(params.event, {
|
|
61
64
|
data: params.data,
|
|
62
65
|
context,
|
|
63
66
|
});
|
package/handlers/routers/auth.js
CHANGED
|
@@ -1,21 +1,7 @@
|
|
|
1
1
|
const { createIntegrationRouter } = require('@friggframework/core');
|
|
2
2
|
const { createAppHandler } = require('./../app-handler-helpers');
|
|
3
|
-
const {
|
|
4
|
-
loadAppDefinition,
|
|
5
|
-
} = require('../app-definition-loader');
|
|
6
|
-
const { UserRepository } = require('../../user/user-repository');
|
|
7
|
-
const { GetUserFromBearerToken } = require('../../user/use-cases/get-user-from-bearer-token');
|
|
8
3
|
|
|
9
|
-
const
|
|
10
|
-
const userRepository = new UserRepository({ userConfig });
|
|
11
|
-
const getUserFromBearerToken = new GetUserFromBearerToken({
|
|
12
|
-
userRepository,
|
|
13
|
-
userConfig,
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
const router = createIntegrationRouter({
|
|
17
|
-
getUserFromBearerToken,
|
|
18
|
-
});
|
|
4
|
+
const router = createIntegrationRouter();
|
|
19
5
|
|
|
20
6
|
router.route('/redirect/:appId').get((req, res) => {
|
|
21
7
|
res.redirect(
|
package/index.js
CHANGED
|
@@ -46,14 +46,12 @@ const { debug, initDebugLog, flushDebugLog } = require('./logs/index');
|
|
|
46
46
|
const {
|
|
47
47
|
Credential,
|
|
48
48
|
Entity,
|
|
49
|
-
ModuleManager,
|
|
50
49
|
ApiKeyRequester,
|
|
51
50
|
BasicAuthRequester,
|
|
52
51
|
OAuth2Requester,
|
|
53
52
|
Requester,
|
|
54
53
|
ModuleConstants,
|
|
55
|
-
|
|
56
|
-
} = require('./module-plugin/index');
|
|
54
|
+
} = require('./modules/index');
|
|
57
55
|
const utils = require('./utils');
|
|
58
56
|
|
|
59
57
|
// const {Sync } = require('./syncs/model');
|
|
@@ -118,13 +116,11 @@ module.exports = {
|
|
|
118
116
|
// module plugin
|
|
119
117
|
Credential,
|
|
120
118
|
Entity,
|
|
121
|
-
ModuleManager,
|
|
122
119
|
ApiKeyRequester,
|
|
123
120
|
BasicAuthRequester,
|
|
124
121
|
OAuth2Requester,
|
|
125
122
|
Requester,
|
|
126
123
|
ModuleConstants,
|
|
127
|
-
ModuleFactory,
|
|
128
124
|
// queues
|
|
129
125
|
QueuerUtil,
|
|
130
126
|
|
|
@@ -1,5 +1,8 @@
|
|
|
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');
|
|
3
6
|
|
|
4
7
|
const constantsToBeMigrated = {
|
|
5
8
|
defaultEvents: {
|
|
@@ -21,6 +24,11 @@ const constantsToBeMigrated = {
|
|
|
21
24
|
|
|
22
25
|
class IntegrationBase {
|
|
23
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
|
+
|
|
24
32
|
static getOptionDetails() {
|
|
25
33
|
const options = new Options({
|
|
26
34
|
module: Object.values(this.Definition.modules)[0], // This is a placeholder until we revamp the frontend
|
|
@@ -53,21 +61,7 @@ class IntegrationBase {
|
|
|
53
61
|
static getCurrentVersion() {
|
|
54
62
|
return this.Definition.version;
|
|
55
63
|
}
|
|
56
|
-
|
|
57
|
-
// Load all the modules defined in Definition.modules
|
|
58
|
-
const moduleNames = Object.keys(this.constructor.Definition.modules);
|
|
59
|
-
for (const moduleName of moduleNames) {
|
|
60
|
-
const { definition } =
|
|
61
|
-
this.constructor.Definition.modules[moduleName];
|
|
62
|
-
if (typeof definition.API === 'function') {
|
|
63
|
-
this[moduleName] = { api: new definition.API({}) };
|
|
64
|
-
} else {
|
|
65
|
-
throw new Error(
|
|
66
|
-
`Module ${moduleName} must be a function that extends IntegrationModule`
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
64
|
+
|
|
71
65
|
registerEventHandlers() {
|
|
72
66
|
this.on = {
|
|
73
67
|
...this.defaultEvents,
|
|
@@ -75,7 +69,31 @@ class IntegrationBase {
|
|
|
75
69
|
};
|
|
76
70
|
}
|
|
77
71
|
|
|
78
|
-
constructor(params) {
|
|
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
|
+
|
|
79
97
|
this.defaultEvents = {
|
|
80
98
|
[constantsToBeMigrated.defaultEvents.ON_CREATE]: {
|
|
81
99
|
type: constantsToBeMigrated.types.LIFE_CYCLE_EVENT,
|
|
@@ -110,7 +128,6 @@ class IntegrationBase {
|
|
|
110
128
|
handler: this.refreshActionOptions,
|
|
111
129
|
},
|
|
112
130
|
};
|
|
113
|
-
this.loadModules();
|
|
114
131
|
}
|
|
115
132
|
|
|
116
133
|
async send(event, object) {
|
|
@@ -124,7 +141,7 @@ class IntegrationBase {
|
|
|
124
141
|
|
|
125
142
|
async validateConfig() {
|
|
126
143
|
const configOptions = await this.getConfigOptions();
|
|
127
|
-
const currentConfig = this.
|
|
144
|
+
const currentConfig = this.getConfig();
|
|
128
145
|
let needsConfig = false;
|
|
129
146
|
for (const option of configOptions) {
|
|
130
147
|
if (option.required) {
|
|
@@ -136,56 +153,59 @@ class IntegrationBase {
|
|
|
136
153
|
)
|
|
137
154
|
) {
|
|
138
155
|
needsConfig = true;
|
|
139
|
-
this.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
156
|
+
await this.updateIntegrationMessages.execute(
|
|
157
|
+
this.id,
|
|
158
|
+
'warnings',
|
|
159
|
+
'Config Validation Error',
|
|
160
|
+
`Missing required field of ${option.label}`,
|
|
161
|
+
Date.now()
|
|
162
|
+
);
|
|
144
163
|
}
|
|
145
164
|
}
|
|
146
165
|
}
|
|
147
166
|
if (needsConfig) {
|
|
148
|
-
this.
|
|
149
|
-
await this.record.save();
|
|
167
|
+
await this.updateIntegrationStatus.execute(this.id, 'NEEDS_CONFIG');
|
|
150
168
|
}
|
|
151
169
|
}
|
|
152
170
|
|
|
153
171
|
async testAuth() {
|
|
154
172
|
let didAuthPass = true;
|
|
155
173
|
|
|
156
|
-
for (const module of Object.keys(
|
|
174
|
+
for (const module of Object.keys(this.constructor.Definition.modules)) {
|
|
157
175
|
try {
|
|
158
176
|
await this[module].testAuth();
|
|
159
177
|
} catch {
|
|
160
178
|
didAuthPass = false;
|
|
161
|
-
this.
|
|
162
|
-
|
|
163
|
-
|
|
179
|
+
await this.updateIntegrationMessages.execute(
|
|
180
|
+
this.id,
|
|
181
|
+
'errors',
|
|
182
|
+
'Authentication Error',
|
|
183
|
+
`There was an error with your ${this[
|
|
164
184
|
module
|
|
165
185
|
].constructor.getName()} Entity.
|
|
166
186
|
Please reconnect/re-authenticate, or reach out to Support for assistance.`,
|
|
167
|
-
|
|
168
|
-
|
|
187
|
+
Date.now()
|
|
188
|
+
);
|
|
169
189
|
}
|
|
170
190
|
}
|
|
171
191
|
|
|
172
192
|
if (!didAuthPass) {
|
|
173
|
-
this.
|
|
174
|
-
this.record.markModified('messages.error');
|
|
175
|
-
await this.record.save();
|
|
193
|
+
await this.updateIntegrationStatus.execute(this.id, 'ERROR');
|
|
176
194
|
}
|
|
177
195
|
}
|
|
178
196
|
|
|
179
197
|
async getMapping(sourceId) {
|
|
180
|
-
|
|
198
|
+
// todo: this should be a use case
|
|
199
|
+
return IntegrationMapping.findBy(this.id, sourceId);
|
|
181
200
|
}
|
|
182
201
|
|
|
183
202
|
async upsertMapping(sourceId, mapping) {
|
|
184
203
|
if (!sourceId) {
|
|
185
204
|
throw new Error(`sourceId must be set`);
|
|
186
205
|
}
|
|
206
|
+
// todo: this should be a use case
|
|
187
207
|
return await IntegrationMapping.upsert(
|
|
188
|
-
this.
|
|
208
|
+
this.id,
|
|
189
209
|
sourceId,
|
|
190
210
|
mapping
|
|
191
211
|
);
|
|
@@ -194,10 +214,8 @@ class IntegrationBase {
|
|
|
194
214
|
/**
|
|
195
215
|
* CHILDREN CAN OVERRIDE THESE CONFIGURATION METHODS
|
|
196
216
|
*/
|
|
197
|
-
async onCreate(
|
|
198
|
-
this.
|
|
199
|
-
await this.record.save();
|
|
200
|
-
return this.record;
|
|
217
|
+
async onCreate({ integrationId }) {
|
|
218
|
+
await this.updateIntegrationStatus.execute(integrationId, 'ENABLED');
|
|
201
219
|
}
|
|
202
220
|
|
|
203
221
|
async onUpdate(params) { }
|
|
@@ -262,6 +280,81 @@ class IntegrationBase {
|
|
|
262
280
|
};
|
|
263
281
|
return options;
|
|
264
282
|
}
|
|
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
|
+
}
|
|
265
358
|
}
|
|
266
359
|
|
|
267
360
|
module.exports = { IntegrationBase };
|
|
@@ -4,7 +4,7 @@ class IntegrationRepository {
|
|
|
4
4
|
async findIntegrationsByUserId(userId) {
|
|
5
5
|
const integrationRecords = await IntegrationModel.find({ user: userId }, '', { lean: true }).populate('entities');
|
|
6
6
|
return integrationRecords.map(integrationRecord => ({
|
|
7
|
-
id: integrationRecord._id,
|
|
7
|
+
id: integrationRecord._id.toString(),
|
|
8
8
|
entitiesIds: integrationRecord.entities.map(e => e._id),
|
|
9
9
|
userId: integrationRecord.user.toString(),
|
|
10
10
|
config: integrationRecord.config,
|
|
@@ -18,10 +18,23 @@ class IntegrationRepository {
|
|
|
18
18
|
return IntegrationModel.deleteOne({ _id: integrationId });
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
async findIntegrationByName(name) {
|
|
22
|
+
const integrationRecord = await IntegrationModel.findOne({ 'config.type': name }, '', { lean: true }).populate('entities');
|
|
23
|
+
return {
|
|
24
|
+
id: integrationRecord._id.toString(),
|
|
25
|
+
entitiesIds: integrationRecord.entities.map(e => e._id),
|
|
26
|
+
userId: integrationRecord.user.toString(),
|
|
27
|
+
config: integrationRecord.config,
|
|
28
|
+
version: integrationRecord.version,
|
|
29
|
+
status: integrationRecord.status,
|
|
30
|
+
messages: integrationRecord.messages,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
21
34
|
async findIntegrationById(id) {
|
|
22
35
|
const integrationRecord = await IntegrationModel.findById(id, '', { lean: true }).populate('entities');
|
|
23
36
|
return {
|
|
24
|
-
id: integrationRecord._id,
|
|
37
|
+
id: integrationRecord._id.toString(),
|
|
25
38
|
entitiesIds: integrationRecord.entities.map(e => e._id),
|
|
26
39
|
userId: integrationRecord.user.toString(),
|
|
27
40
|
config: integrationRecord.config,
|
|
@@ -31,13 +44,36 @@ class IntegrationRepository {
|
|
|
31
44
|
}
|
|
32
45
|
}
|
|
33
46
|
|
|
47
|
+
async updateIntegrationStatus(integrationId, status) {
|
|
48
|
+
const integrationRecord = await IntegrationModel.updateOne({ _id: integrationId }, { status });
|
|
49
|
+
return integrationRecord.acknowledged;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async updateIntegrationMessages(integrationId, messageType, messageTitle, messageBody, messageTimestamp) {
|
|
53
|
+
const integrationRecord = await IntegrationModel.updateOne(
|
|
54
|
+
{ _id: integrationId },
|
|
55
|
+
{ $push: { [`messages.${messageType}`]: { title: messageTitle, message: messageBody, timestamp: messageTimestamp } } }
|
|
56
|
+
);
|
|
57
|
+
return integrationRecord.acknowledged;
|
|
58
|
+
}
|
|
59
|
+
|
|
34
60
|
async createIntegration(entities, userId, config) {
|
|
35
|
-
|
|
61
|
+
const integrationRecord = await IntegrationModel.create({
|
|
36
62
|
entities: entities,
|
|
37
63
|
user: userId,
|
|
38
64
|
config,
|
|
39
65
|
version: '0.0.0',
|
|
40
66
|
});
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
id: integrationRecord._id.toString(),
|
|
70
|
+
entitiesIds: integrationRecord.entities.map(e => e._id),
|
|
71
|
+
userId: integrationRecord.user.toString(),
|
|
72
|
+
config: integrationRecord.config,
|
|
73
|
+
version: integrationRecord.version,
|
|
74
|
+
status: integrationRecord.status,
|
|
75
|
+
messages: integrationRecord.messages,
|
|
76
|
+
};
|
|
41
77
|
}
|
|
42
78
|
}
|
|
43
79
|
|