@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.
Files changed (69) hide show
  1. package/README.md +931 -50
  2. package/core/create-handler.js +1 -0
  3. package/credential/credential-repository.js +48 -1
  4. package/credential/use-cases/update-authentication-status.js +15 -0
  5. package/handlers/backend-utils.js +34 -31
  6. package/handlers/routers/auth.js +1 -15
  7. package/index.js +1 -5
  8. package/integrations/integration-base.js +133 -40
  9. package/integrations/integration-repository.js +39 -3
  10. package/integrations/integration-router.js +109 -85
  11. package/integrations/tests/doubles/dummy-integration-class.js +90 -0
  12. package/integrations/tests/doubles/test-integration-repository.js +89 -0
  13. package/integrations/tests/use-cases/create-integration.test.js +124 -0
  14. package/integrations/tests/use-cases/delete-integration-for-user.test.js +143 -0
  15. package/integrations/tests/use-cases/get-integration-for-user.test.js +143 -0
  16. package/integrations/tests/use-cases/get-integration-instance.test.js +169 -0
  17. package/integrations/tests/use-cases/get-integrations-for-user.test.js +169 -0
  18. package/integrations/tests/use-cases/get-possible-integrations.test.js +188 -0
  19. package/integrations/tests/use-cases/update-integration-messages.test.js +142 -0
  20. package/integrations/tests/use-cases/update-integration-status.test.js +103 -0
  21. package/integrations/tests/use-cases/update-integration.test.js +134 -0
  22. package/integrations/use-cases/create-integration.js +25 -12
  23. package/integrations/use-cases/delete-integration-for-user.js +21 -2
  24. package/integrations/use-cases/get-integration-for-user.js +28 -13
  25. package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
  26. package/integrations/use-cases/get-integration-instance.js +20 -11
  27. package/integrations/use-cases/get-integrations-for-user.js +22 -10
  28. package/integrations/use-cases/get-possible-integrations.js +27 -0
  29. package/integrations/use-cases/update-integration-messages.js +31 -0
  30. package/integrations/use-cases/update-integration-status.js +28 -0
  31. package/integrations/use-cases/update-integration.js +23 -13
  32. package/integrations/utils/map-integration-dto.js +0 -1
  33. package/{module-plugin → modules}/entity.js +1 -0
  34. package/{module-plugin → modules}/index.js +0 -4
  35. package/{module-plugin/module-service.js → modules/module-factory.js} +9 -5
  36. package/modules/module-repository.js +107 -0
  37. package/modules/module.js +218 -0
  38. package/modules/tests/doubles/test-module-factory.js +16 -0
  39. package/modules/tests/doubles/test-module-repository.js +19 -0
  40. package/{module-plugin → modules}/use-cases/get-entities-for-user.js +1 -1
  41. package/modules/use-cases/get-entity-options-by-id.js +58 -0
  42. package/modules/use-cases/get-entity-options-by-type.js +34 -0
  43. package/modules/use-cases/get-module-instance-from-type.js +31 -0
  44. package/modules/use-cases/get-module.js +56 -0
  45. package/modules/use-cases/process-authorization-callback.js +108 -0
  46. package/modules/use-cases/refresh-entity-options.js +58 -0
  47. package/modules/use-cases/test-module-auth.js +54 -0
  48. package/{module-plugin → modules}/utils/map-module-dto.js +1 -1
  49. package/package.json +5 -5
  50. package/syncs/sync.js +0 -1
  51. package/types/module-plugin/index.d.ts +0 -35
  52. package/types/syncs/index.d.ts +0 -2
  53. package/integrations/integration.js +0 -233
  54. package/integrations/test/integration-base.test.js +0 -144
  55. package/module-plugin/manager.js +0 -169
  56. package/module-plugin/module-factory.js +0 -42
  57. package/module-plugin/module-repository.js +0 -70
  58. package/module-plugin/module.js +0 -329
  59. /package/{module-plugin → modules}/ModuleConstants.js +0 -0
  60. /package/{module-plugin → modules}/credential.js +0 -0
  61. /package/{module-plugin → modules}/requester/api-key.js +0 -0
  62. /package/{module-plugin → modules}/requester/basic.js +0 -0
  63. /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
  64. /package/{module-plugin → modules}/requester/requester.js +0 -0
  65. /package/{module-plugin → modules}/requester/requester.test.js +0 -0
  66. /package/{module-plugin → modules}/test/auther.test.js +0 -0
  67. /package/{module-plugin → modules}/test/mock-api/api.js +0 -0
  68. /package/{module-plugin → modules}/test/mock-api/definition.js +0 -0
  69. /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
@@ -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('../module-plugin');
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 { ModuleService } = require('../module-plugin/module-service');
6
- const { GetIntegrationInstance } = require('../integrations/use-cases/get-integration-instance');
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 integration = new IntegrationClass();
17
- await integration.loadModules();
18
- await integration.registerEventHandlers();
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
- let integrationInstance;
35
- if (!params.integrationId) {
36
- integrationInstance = new integrationClass();
37
- await integrationInstance.loadModules();
38
- await integrationInstance.registerEventHandlers();
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
- // todo: are we going to have the userId available here?
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 integrationInstance.send(params.event, {
63
+ const res = await integration.send(params.event, {
61
64
  data: params.data,
62
65
  context,
63
66
  });
@@ -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 { userConfig } = loadAppDefinition();
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
- ModuleFactory,
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
- loadModules() {
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.record.config;
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.record.messages.warnings.push({
140
- title: 'Config Validation Error',
141
- message: `Missing required field of ${option.label}`,
142
- timestamp: Date.now(),
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.record.status = 'NEEDS_CONFIG';
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(IntegrationBase.Definition.modules)) {
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.record.messages.errors.push({
162
- title: 'Authentication Error',
163
- message: `There was an error with your ${this[
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
- timestamp: Date.now(),
168
- });
187
+ Date.now()
188
+ );
169
189
  }
170
190
  }
171
191
 
172
192
  if (!didAuthPass) {
173
- this.record.status = 'ERROR';
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
- return IntegrationMapping.findBy(this.record.id, sourceId);
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.record.id,
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(params) {
198
- this.record.status = 'ENABLED';
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
- return IntegrationModel.create({
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