@friggframework/core 2.0.0--canary.397.fe6d7a2.0 → 2.0.0--canary.405.91dae19.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 (100) hide show
  1. package/README.md +50 -931
  2. package/core/create-handler.js +0 -1
  3. package/encrypt/encrypt.js +4 -27
  4. package/handlers/app-handler-helpers.js +3 -0
  5. package/handlers/backend-utils.js +44 -42
  6. package/handlers/routers/HEALTHCHECK.md +240 -0
  7. package/handlers/routers/auth.js +14 -3
  8. package/handlers/routers/health.js +451 -0
  9. package/handlers/routers/health.test.js +203 -0
  10. package/handlers/routers/integration-defined-routers.js +5 -8
  11. package/handlers/routers/middleware/loadUser.js +15 -0
  12. package/handlers/routers/middleware/requireLoggedInUser.js +12 -0
  13. package/handlers/routers/user.js +5 -25
  14. package/handlers/workers/integration-defined-workers.js +3 -6
  15. package/index.js +16 -1
  16. package/integrations/create-frigg-backend.js +31 -0
  17. package/integrations/index.js +5 -0
  18. package/integrations/integration-base.js +46 -142
  19. package/integrations/integration-factory.js +251 -0
  20. package/integrations/integration-router.js +181 -303
  21. package/integrations/integration-user.js +144 -0
  22. package/integrations/options.js +1 -1
  23. package/integrations/test/integration-base.test.js +144 -0
  24. package/module-plugin/auther.js +393 -0
  25. package/module-plugin/entity-manager.js +70 -0
  26. package/{modules → module-plugin}/entity.js +0 -1
  27. package/{modules → module-plugin}/index.js +8 -0
  28. package/module-plugin/manager.js +169 -0
  29. package/module-plugin/module-factory.js +61 -0
  30. package/{modules → module-plugin}/test/mock-api/api.js +3 -8
  31. package/{modules → module-plugin}/test/mock-api/definition.js +8 -12
  32. package/package.json +5 -5
  33. package/syncs/sync.js +1 -0
  34. package/types/integrations/index.d.ts +6 -2
  35. package/types/module-plugin/index.d.ts +56 -4
  36. package/types/syncs/index.d.ts +2 -0
  37. package/credential/credential-repository.js +0 -56
  38. package/credential/use-cases/get-credential-for-user.js +0 -21
  39. package/credential/use-cases/update-authentication-status.js +0 -15
  40. package/handlers/app-definition-loader.js +0 -38
  41. package/integrations/integration-repository.js +0 -80
  42. package/integrations/tests/doubles/dummy-integration-class.js +0 -90
  43. package/integrations/tests/doubles/test-integration-repository.js +0 -89
  44. package/integrations/tests/use-cases/create-integration.test.js +0 -124
  45. package/integrations/tests/use-cases/delete-integration-for-user.test.js +0 -143
  46. package/integrations/tests/use-cases/get-integration-for-user.test.js +0 -143
  47. package/integrations/tests/use-cases/get-integration-instance.test.js +0 -169
  48. package/integrations/tests/use-cases/get-integrations-for-user.test.js +0 -169
  49. package/integrations/tests/use-cases/get-possible-integrations.test.js +0 -188
  50. package/integrations/tests/use-cases/update-integration-messages.test.js +0 -142
  51. package/integrations/tests/use-cases/update-integration-status.test.js +0 -103
  52. package/integrations/tests/use-cases/update-integration.test.js +0 -134
  53. package/integrations/use-cases/create-integration.js +0 -71
  54. package/integrations/use-cases/delete-integration-for-user.js +0 -72
  55. package/integrations/use-cases/get-integration-for-user.js +0 -78
  56. package/integrations/use-cases/get-integration-instance-by-definition.js +0 -67
  57. package/integrations/use-cases/get-integration-instance.js +0 -82
  58. package/integrations/use-cases/get-integrations-for-user.js +0 -76
  59. package/integrations/use-cases/get-possible-integrations.js +0 -27
  60. package/integrations/use-cases/index.js +0 -11
  61. package/integrations/use-cases/update-integration-messages.js +0 -31
  62. package/integrations/use-cases/update-integration-status.js +0 -28
  63. package/integrations/use-cases/update-integration.js +0 -91
  64. package/integrations/utils/map-integration-dto.js +0 -36
  65. package/modules/module-factory.js +0 -54
  66. package/modules/module-repository.js +0 -107
  67. package/modules/module.js +0 -218
  68. package/modules/tests/doubles/test-module-factory.js +0 -16
  69. package/modules/tests/doubles/test-module-repository.js +0 -19
  70. package/modules/use-cases/get-entities-for-user.js +0 -32
  71. package/modules/use-cases/get-entity-options-by-id.js +0 -58
  72. package/modules/use-cases/get-entity-options-by-type.js +0 -34
  73. package/modules/use-cases/get-module-instance-from-type.js +0 -31
  74. package/modules/use-cases/get-module.js +0 -56
  75. package/modules/use-cases/process-authorization-callback.js +0 -108
  76. package/modules/use-cases/refresh-entity-options.js +0 -58
  77. package/modules/use-cases/test-module-auth.js +0 -54
  78. package/modules/utils/map-module-dto.js +0 -18
  79. package/user/tests/doubles/test-user-repository.js +0 -72
  80. package/user/tests/use-cases/create-individual-user.test.js +0 -24
  81. package/user/tests/use-cases/create-organization-user.test.js +0 -28
  82. package/user/tests/use-cases/create-token-for-user-id.test.js +0 -19
  83. package/user/tests/use-cases/get-user-from-bearer-token.test.js +0 -64
  84. package/user/tests/use-cases/login-user.test.js +0 -140
  85. package/user/use-cases/create-individual-user.js +0 -61
  86. package/user/use-cases/create-organization-user.js +0 -47
  87. package/user/use-cases/create-token-for-user-id.js +0 -30
  88. package/user/use-cases/get-user-from-bearer-token.js +0 -77
  89. package/user/use-cases/login-user.js +0 -122
  90. package/user/user-repository.js +0 -62
  91. package/user/user.js +0 -77
  92. /package/{modules → module-plugin}/ModuleConstants.js +0 -0
  93. /package/{modules → module-plugin}/credential.js +0 -0
  94. /package/{modules → module-plugin}/requester/api-key.js +0 -0
  95. /package/{modules → module-plugin}/requester/basic.js +0 -0
  96. /package/{modules → module-plugin}/requester/oauth-2.js +0 -0
  97. /package/{modules → module-plugin}/requester/requester.js +0 -0
  98. /package/{modules → module-plugin}/requester/requester.test.js +0 -0
  99. /package/{modules → module-plugin}/test/auther.test.js +0 -0
  100. /package/{modules → module-plugin}/test/mock-api/mocks/hubspot.js +0 -0
@@ -1,29 +1,10 @@
1
1
  const express = require('express');
2
2
  const { createAppHandler } = require('../app-handler-helpers');
3
3
  const { checkRequiredParams } = require('@friggframework/core');
4
- const { UserRepository } = require('../../user/user-repository');
5
- const {
6
- CreateIndividualUser,
7
- } = require('../../user/use-cases/create-individual-user');
8
- const { LoginUser } = require('../../user/use-cases/login-user');
9
- const {
10
- CreateTokenForUserId,
11
- } = require('../../user/use-cases/create-token-for-user-id');
4
+ const { User } = require('../backend-utils');
12
5
  const catchAsyncError = require('express-async-handler');
13
- const { loadAppDefinition } = require('../app-definition-loader');
14
6
 
15
7
  const router = express();
16
- const { userConfig } = loadAppDefinition();
17
- const userRepository = new UserRepository({ userConfig });
18
- const createIndividualUser = new CreateIndividualUser({
19
- userRepository,
20
- userConfig,
21
- });
22
- const loginUser = new LoginUser({
23
- userRepository,
24
- userConfig,
25
- });
26
- const createTokenForUserId = new CreateTokenForUserId({ userRepository });
27
8
 
28
9
  // define the login endpoint
29
10
  router.route('/user/login').post(
@@ -32,8 +13,8 @@ router.route('/user/login').post(
32
13
  'username',
33
14
  'password',
34
15
  ]);
35
- const user = await loginUser.execute({ username, password });
36
- const token = await createTokenForUserId.execute(user.getId(), 120);
16
+ const user = await User.loginUser({ username, password });
17
+ const token = await user.createUserToken(120);
37
18
  res.status(201);
38
19
  res.json({ token });
39
20
  })
@@ -45,12 +26,11 @@ router.route('/user/create').post(
45
26
  'username',
46
27
  'password',
47
28
  ]);
48
-
49
- const user = await createIndividualUser.execute({
29
+ const user = await User.createIndividualUser({
50
30
  username,
51
31
  password,
52
32
  });
53
- const token = await createTokenForUserId.execute(user.getId(), 120);
33
+ const token = await user.createUserToken(120);
54
34
  res.status(201);
55
35
  res.json({ token });
56
36
  })
@@ -1,12 +1,9 @@
1
1
  const { createHandler } = require('@friggframework/core');
2
- const { loadAppDefinition } = require('../app-definition-loader');
3
- const { createQueueWorker } = require('../backend-utils');
2
+ const { integrationFactory, createQueueWorker } = require('../backend-utils');
4
3
 
5
4
  const handlers = {};
6
- const { integrations: integrationClasses } = loadAppDefinition();
7
-
8
- integrationClasses.forEach((IntegrationClass) => {
9
- const defaultQueueWorker = createQueueWorker(IntegrationClass);
5
+ integrationFactory.integrationClasses.forEach((IntegrationClass) => {
6
+ const defaultQueueWorker = createQueueWorker(IntegrationClass, integrationFactory);
10
7
 
11
8
  handlers[`${IntegrationClass.Definition.name}`] = {
12
9
  queueWorker: createHandler({
package/index.js CHANGED
@@ -38,20 +38,27 @@ const {
38
38
  IntegrationModel,
39
39
  Options,
40
40
  IntegrationMapping,
41
+ IntegrationFactory,
42
+ IntegrationHelper,
41
43
  createIntegrationRouter,
42
44
  checkRequiredParams,
45
+ createFriggBackend,
43
46
  } = require('./integrations/index');
44
47
  const { TimeoutCatcher } = require('./lambda/index');
45
48
  const { debug, initDebugLog, flushDebugLog } = require('./logs/index');
46
49
  const {
47
50
  Credential,
51
+ EntityManager,
48
52
  Entity,
53
+ ModuleManager,
49
54
  ApiKeyRequester,
50
55
  BasicAuthRequester,
51
56
  OAuth2Requester,
52
57
  Requester,
53
58
  ModuleConstants,
54
- } = require('./modules/index');
59
+ ModuleFactory,
60
+ Auther,
61
+ } = require('./module-plugin/index');
55
62
  const utils = require('./utils');
56
63
 
57
64
  // const {Sync } = require('./syncs/model');
@@ -102,8 +109,11 @@ module.exports = {
102
109
  IntegrationModel,
103
110
  Options,
104
111
  IntegrationMapping,
112
+ IntegrationFactory,
113
+ IntegrationHelper,
105
114
  checkRequiredParams,
106
115
  createIntegrationRouter,
116
+ createFriggBackend,
107
117
 
108
118
  // lambda
109
119
  TimeoutCatcher,
@@ -115,12 +125,17 @@ module.exports = {
115
125
 
116
126
  // module plugin
117
127
  Credential,
128
+ EntityManager,
118
129
  Entity,
130
+ ModuleManager,
119
131
  ApiKeyRequester,
120
132
  BasicAuthRequester,
121
133
  OAuth2Requester,
122
134
  Requester,
123
135
  ModuleConstants,
136
+ ModuleFactory,
137
+ Auther,
138
+
124
139
  // queues
125
140
  QueuerUtil,
126
141
 
@@ -0,0 +1,31 @@
1
+ const {IntegrationFactory, IntegrationHelper} = require('./integration-factory');
2
+ const User = require('./integration-user');
3
+
4
+ function createFriggBackend(appDefinition) {
5
+ const {integrations = [], user=null} = appDefinition
6
+ const integrationFactory = new IntegrationFactory(integrations);
7
+ if (user) {
8
+ if (user.usePassword) {
9
+ User.usePassword = true;
10
+ }
11
+ if (user.primary === 'organization') {
12
+ User.primary = User.OrganizationUser
13
+ }
14
+ if (user.individualUserRequired !== undefined) {
15
+ User.individualUserRequired = user.individualUserRequired
16
+ }
17
+ if (user.organizationUserRequired !== undefined) {
18
+ User.organizationUserRequired = user.organizationUserRequired
19
+ }
20
+
21
+ }
22
+ const backend = {
23
+ integrationFactory,
24
+ moduleFactory: integrationFactory.moduleFactory,
25
+ IntegrationHelper,
26
+ User: User
27
+ }
28
+ return backend
29
+ }
30
+
31
+ module.exports = { createFriggBackend }
@@ -2,13 +2,18 @@ const { IntegrationBase } = require('./integration-base');
2
2
  const { IntegrationModel } = require('./integration-model');
3
3
  const { Options } = require('./options');
4
4
  const { IntegrationMapping } = require('./integration-mapping');
5
+ const { IntegrationFactory, IntegrationHelper } = require('./integration-factory');
5
6
  const { createIntegrationRouter, checkRequiredParams } = require('./integration-router');
7
+ const { createFriggBackend } = require('./create-frigg-backend');
6
8
 
7
9
  module.exports = {
8
10
  IntegrationBase,
9
11
  IntegrationModel,
10
12
  Options,
11
13
  IntegrationMapping,
14
+ IntegrationFactory,
15
+ IntegrationHelper,
12
16
  createIntegrationRouter,
13
17
  checkRequiredParams,
18
+ createFriggBackend
14
19
  };
@@ -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.getConfig();
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
- await this.updateIntegrationMessages.execute(
157
- this.id,
158
- 'warnings',
159
- 'Config Validation Error',
160
- `Missing required field of ${option.label}`,
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
- await this.updateIntegrationStatus.execute(this.id, 'NEEDS_CONFIG');
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(this.constructor.Definition.modules)) {
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
- await this.updateIntegrationMessages.execute(
180
- this.id,
181
- 'errors',
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
- await this.updateIntegrationStatus.execute(this.id, 'ERROR');
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
- // todo: this should be a use case
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({ integrationId }) {
218
- await this.updateIntegrationStatus.execute(integrationId, 'ENABLED');
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
- Object.entries(dynamicUserActions).filter(
261
- ([_, event]) => event.userActionType === actionType
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 };