@friggframework/core 2.0.0--canary.548.c8ae0ca.0 → 2.0.0--canary.545.c40eca4.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 (237) hide show
  1. package/CHANGELOG.md +66 -67
  2. package/CLAUDE.md +148 -124
  3. package/README.md +165 -130
  4. package/admin-scripts/index.js +52 -0
  5. package/admin-scripts/repositories/admin-api-key-repository-documentdb.js +21 -0
  6. package/admin-scripts/repositories/admin-api-key-repository-factory.js +51 -0
  7. package/admin-scripts/repositories/admin-api-key-repository-interface.js +121 -0
  8. package/admin-scripts/repositories/admin-api-key-repository-mongo.js +155 -0
  9. package/admin-scripts/repositories/admin-api-key-repository-postgres.js +189 -0
  10. package/admin-scripts/repositories/admin-process-repository-documentdb.js +21 -0
  11. package/admin-scripts/repositories/admin-process-repository-factory.js +49 -0
  12. package/admin-scripts/repositories/admin-process-repository-interface.js +150 -0
  13. package/admin-scripts/repositories/admin-process-repository-mongo.js +213 -0
  14. package/admin-scripts/repositories/admin-process-repository-postgres.js +251 -0
  15. package/admin-scripts/repositories/script-execution-repository-documentdb.js +21 -0
  16. package/admin-scripts/repositories/script-execution-repository-factory.js +51 -0
  17. package/admin-scripts/repositories/script-execution-repository-interface.js +193 -0
  18. package/admin-scripts/repositories/script-execution-repository-mongo.js +278 -0
  19. package/admin-scripts/repositories/script-execution-repository-postgres.js +320 -0
  20. package/admin-scripts/repositories/script-schedule-repository-documentdb.js +21 -0
  21. package/admin-scripts/repositories/script-schedule-repository-factory.js +49 -0
  22. package/admin-scripts/repositories/script-schedule-repository-interface.js +108 -0
  23. package/admin-scripts/repositories/script-schedule-repository-mongo.js +179 -0
  24. package/admin-scripts/repositories/script-schedule-repository-postgres.js +210 -0
  25. package/application/commands/admin-script-commands.js +348 -0
  26. package/application/commands/integration-commands.js +12 -6
  27. package/application/index.js +5 -3
  28. package/assertions/get.js +17 -7
  29. package/associations/association.js +59 -57
  30. package/core/CLAUDE.md +222 -156
  31. package/core/Worker.js +33 -12
  32. package/credential/credential-router.js +262 -0
  33. package/credential/repositories/credential-repository-documentdb.js +71 -1
  34. package/credential/repositories/credential-repository-factory.js +4 -6
  35. package/credential/repositories/credential-repository-interface.js +8 -2
  36. package/credential/repositories/credential-repository-mongo.js +45 -4
  37. package/credential/repositories/credential-repository-postgres.js +44 -3
  38. package/credential/repositories/credential-repository.js +3 -1
  39. package/credential/use-cases/delete-credential-for-user.js +50 -0
  40. package/credential/use-cases/get-credential-for-user.js +21 -3
  41. package/credential/use-cases/list-credentials-for-user.js +31 -0
  42. package/credential/use-cases/reauthorize-credential.js +103 -0
  43. package/credential/use-cases/update-authentication-status.js +5 -2
  44. package/database/MONGODB_TRANSACTION_FIX.md +72 -52
  45. package/database/config.js +13 -59
  46. package/database/documentdb-encryption-service.js +29 -13
  47. package/database/documentdb-utils.js +21 -12
  48. package/database/encryption/README.md +53 -36
  49. package/database/encryption/encryption-schema-registry.js +29 -23
  50. package/database/encryption/field-encryption-service.js +15 -4
  51. package/database/encryption/logger.js +3 -2
  52. package/database/encryption/prisma-encryption-extension.js +5 -8
  53. package/database/prisma.js +22 -14
  54. package/database/repositories/health-check-repository-factory.js +7 -5
  55. package/database/repositories/health-check-repository-interface.js +15 -5
  56. package/database/repositories/health-check-repository-postgres.js +1 -1
  57. package/database/use-cases/check-database-health-use-case.js +3 -2
  58. package/database/use-cases/check-database-state-use-case.js +8 -7
  59. package/database/use-cases/get-database-state-via-worker-use-case.js +15 -12
  60. package/database/use-cases/get-migration-status-use-case.js +4 -2
  61. package/database/use-cases/run-database-migration-use-case.js +41 -9
  62. package/database/use-cases/test-encryption-use-case.js +4 -5
  63. package/database/use-cases/trigger-database-migration-use-case.js +9 -8
  64. package/database/utils/mongodb-collection-utils.js +11 -3
  65. package/database/utils/mongodb-schema-init.js +10 -4
  66. package/database/utils/prisma-runner.js +144 -74
  67. package/database/utils/prisma-schema-parser.js +2 -2
  68. package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +135 -80
  69. package/encrypt/Cryptor.js +54 -49
  70. package/encrypt/aes-encryption-key-provider.js +82 -0
  71. package/encrypt/encryption-key-provider-interface.js +50 -0
  72. package/errors/fetch-error.js +1 -1
  73. package/generated/prisma-mongodb/edge.js +59 -4
  74. package/generated/prisma-mongodb/index-browser.js +49 -1
  75. package/generated/prisma-mongodb/index.d.ts +4023 -61
  76. package/generated/prisma-mongodb/index.js +59 -4
  77. package/generated/prisma-mongodb/package.json +1 -1
  78. package/generated/prisma-mongodb/schema.prisma +87 -0
  79. package/generated/prisma-mongodb/wasm.js +59 -4
  80. package/generated/prisma-postgresql/edge.js +58 -4
  81. package/generated/prisma-postgresql/index-browser.js +48 -1
  82. package/generated/prisma-postgresql/index.d.ts +4738 -54
  83. package/generated/prisma-postgresql/index.js +58 -4
  84. package/generated/prisma-postgresql/package.json +1 -1
  85. package/generated/prisma-postgresql/schema.prisma +85 -0
  86. package/generated/prisma-postgresql/wasm.js +58 -4
  87. package/handlers/WEBHOOKS.md +104 -81
  88. package/handlers/app-definition-loader.js +53 -10
  89. package/handlers/app-handler-helpers.js +14 -3
  90. package/handlers/backend-utils.js +0 -1
  91. package/handlers/database-migration-handler.js +64 -45
  92. package/handlers/middleware/admin-auth.js +53 -0
  93. package/handlers/routers/HEALTHCHECK.md +170 -135
  94. package/handlers/routers/admin.js +474 -0
  95. package/handlers/routers/auth.js +51 -9
  96. package/handlers/routers/db-migration.handler.js +2 -3
  97. package/handlers/routers/db-migration.js +102 -66
  98. package/handlers/routers/docs.js +180 -0
  99. package/handlers/routers/health.js +84 -283
  100. package/handlers/routers/integration-defined-routers.js +44 -33
  101. package/handlers/routers/integration-webhook-routers.js +73 -52
  102. package/handlers/routers/middleware/loadUser.js +39 -0
  103. package/handlers/routers/middleware/requireAdmin.js +43 -0
  104. package/handlers/routers/middleware/requireLoggedInUser.js +19 -0
  105. package/handlers/routers/user.js +30 -14
  106. package/handlers/routers/websocket.js +18 -4
  107. package/handlers/use-cases/check-integrations-health-use-case.js +13 -6
  108. package/handlers/workers/db-migration.js +74 -35
  109. package/handlers/workers/integration-defined-workers.js +31 -20
  110. package/index.js +50 -16
  111. package/infrastructure/scheduler/index.js +15 -11
  112. package/infrastructure/scheduler/netlify-scheduler-adapter.js +185 -0
  113. package/infrastructure/scheduler/scheduler-service-factory.js +38 -31
  114. package/integrations/WEBHOOK-QUICKSTART.md +21 -14
  115. package/integrations/index.js +4 -0
  116. package/integrations/integration-base.js +9 -6
  117. package/integrations/integration-router.js +1828 -157
  118. package/integrations/options.js +47 -20
  119. package/integrations/repositories/integration-mapping-repository-factory.js +4 -8
  120. package/integrations/repositories/integration-repository-documentdb.js +53 -24
  121. package/integrations/repositories/integration-repository-factory.js +6 -6
  122. package/integrations/repositories/integration-repository-interface.js +27 -9
  123. package/integrations/repositories/integration-repository-postgres.js +3 -3
  124. package/integrations/repositories/process-repository-factory.js +4 -7
  125. package/integrations/repositories/process-repository-interface.js +9 -5
  126. package/integrations/repositories/process-repository-mongo.js +7 -3
  127. package/integrations/repositories/process-repository-postgres.js +2 -2
  128. package/integrations/tests/doubles/config-capturing-integration.js +9 -8
  129. package/integrations/tests/doubles/dummy-integration-class.js +81 -8
  130. package/integrations/tests/doubles/test-integration-repository.js +51 -11
  131. package/integrations/use-cases/create-integration.js +36 -32
  132. package/integrations/use-cases/create-process.js +20 -9
  133. package/integrations/use-cases/delete-integration-for-user.js +5 -4
  134. package/integrations/use-cases/execute-proxy-request.js +564 -0
  135. package/integrations/use-cases/get-integration-for-user.js +25 -10
  136. package/integrations/use-cases/get-integration-instance-by-definition.js +13 -7
  137. package/integrations/use-cases/get-integrations-for-user.js +4 -1
  138. package/integrations/use-cases/get-possible-integrations.js +1 -1
  139. package/integrations/use-cases/get-process.js +5 -6
  140. package/integrations/use-cases/index.js +3 -1
  141. package/integrations/use-cases/load-integration-context.js +1 -5
  142. package/integrations/use-cases/update-process-state.js +10 -8
  143. package/integrations/utils/map-integration-dto.js +9 -3
  144. package/jest-setup.js +1 -1
  145. package/jest-teardown.js +1 -1
  146. package/lambda/TimeoutCatcher.js +32 -30
  147. package/lambda/index.js +1 -1
  148. package/logs/index.js +3 -3
  149. package/modules/domain/entities/AuthorizationSession.js +131 -0
  150. package/modules/domain/entities/index.js +10 -0
  151. package/modules/module.js +4 -9
  152. package/modules/repositories/authorization-session-repository-factory.js +53 -0
  153. package/modules/repositories/authorization-session-repository-interface.js +75 -0
  154. package/modules/repositories/authorization-session-repository-mongo.js +169 -0
  155. package/modules/repositories/authorization-session-repository-postgres.js +170 -0
  156. package/modules/repositories/module-repository-documentdb.js +89 -41
  157. package/modules/repositories/module-repository-factory.js +4 -6
  158. package/modules/repositories/module-repository-interface.js +13 -0
  159. package/modules/repositories/module-repository-mongo.js +40 -5
  160. package/modules/repositories/module-repository-postgres.js +42 -5
  161. package/modules/repositories/module-repository.js +28 -0
  162. package/modules/requester/api-key.js +0 -2
  163. package/modules/requester/basic.js +0 -1
  164. package/modules/requester/oauth-2.js +3 -14
  165. package/modules/test/mock-api/definition.js +15 -10
  166. package/modules/test/mock-api/mocks/hubspot.js +36 -36
  167. package/modules/tests/doubles/test-module-factory.js +10 -4
  168. package/modules/tests/doubles/test-module-repository.js +1 -1
  169. package/modules/use-cases/delete-module-entity.js +23 -0
  170. package/modules/use-cases/get-authorization-requirements.js +110 -0
  171. package/modules/use-cases/get-entities-for-user.js +1 -1
  172. package/modules/use-cases/get-entity-options-by-id.js +8 -6
  173. package/modules/use-cases/get-module-entity-by-id.js +16 -0
  174. package/modules/use-cases/get-module.js +12 -9
  175. package/modules/use-cases/process-authorization-callback.js +12 -6
  176. package/modules/use-cases/process-authorization-step.js +160 -0
  177. package/modules/use-cases/refresh-entity-options.js +8 -6
  178. package/modules/use-cases/start-authorization-session.js +86 -0
  179. package/modules/use-cases/test-module-auth.js +8 -6
  180. package/modules/use-cases/update-module-entity.js +27 -0
  181. package/modules/utils/map-module-dto.js +2 -2
  182. package/openapi/openapi-spec-generator.js +370 -0
  183. package/openapi/openapi-v1.yaml +1360 -0
  184. package/openapi/openapi-v2.yaml +1535 -0
  185. package/package.json +7 -9
  186. package/prisma-mongodb/schema.prisma +87 -0
  187. package/prisma-postgresql/migrations/20260222000000_add_admin_process_and_script_schedule/migration.sql +55 -0
  188. package/prisma-postgresql/schema.prisma +85 -0
  189. package/providers/index.js +13 -0
  190. package/providers/resolve-provider.js +93 -0
  191. package/queues/index.js +19 -0
  192. package/queues/providers/index.js +7 -0
  193. package/queues/providers/netlify-background-provider.js +76 -0
  194. package/queues/providers/qstash-queue-provider.js +122 -0
  195. package/queues/queue-client-interface.js +55 -0
  196. package/queues/queue-provider-factory.js +93 -0
  197. package/queues/queue-provider.js +46 -0
  198. package/queues/queuer-util.js +33 -25
  199. package/syncs/repositories/sync-repository-documentdb.js +39 -19
  200. package/syncs/repositories/sync-repository-factory.js +4 -4
  201. package/syncs/repositories/sync-repository-interface.js +6 -2
  202. package/syncs/repositories/sync-repository-postgres.js +19 -13
  203. package/syncs/sync.js +104 -100
  204. package/token/repositories/token-repository-factory.js +4 -6
  205. package/types/assertions/index.d.ts +72 -72
  206. package/types/core/index.d.ts +98 -53
  207. package/types/errors/index.d.ts +61 -54
  208. package/types/eslint-config/index.d.ts +40 -40
  209. package/types/lambda/index.d.ts +26 -26
  210. package/types/logs/index.d.ts +4 -4
  211. package/types/prettier-config/index.d.ts +5 -5
  212. package/types/test-environment/index.d.ts +14 -14
  213. package/types/tsconfig.json +95 -93
  214. package/user/repositories/user-repository-factory.js +4 -4
  215. package/user/tests/doubles/test-user-repository.js +5 -2
  216. package/user/use-cases/authenticate-user.js +2 -4
  217. package/user/use-cases/authenticate-with-shared-secret.js +1 -1
  218. package/user/use-cases/create-individual-user.js +8 -7
  219. package/user/use-cases/create-organization-user.js +1 -1
  220. package/user/use-cases/create-token-for-user-id.js +1 -1
  221. package/user/use-cases/delete-user.js +47 -0
  222. package/user/use-cases/get-user-from-adopter-jwt.js +0 -2
  223. package/user/use-cases/get-user-from-bearer-token.js +7 -3
  224. package/user/use-cases/get-user-from-x-frigg-headers.js +14 -11
  225. package/user/use-cases/login-user.js +8 -8
  226. package/user/user.js +18 -4
  227. package/utils/backend-path.js +2 -2
  228. package/utils/index.js +4 -1
  229. package/websocket/repositories/websocket-connection-repository-documentdb.js +46 -21
  230. package/websocket/repositories/websocket-connection-repository-factory.js +4 -8
  231. package/websocket/repositories/websocket-connection-repository-mongo.js +26 -16
  232. package/websocket/repositories/websocket-connection-repository-postgres.js +26 -15
  233. package/websocket/repositories/websocket-connection-repository.js +26 -20
  234. package/websocket/websocket-message-sender-interface.js +38 -0
  235. package/database/adapters/lambda-invoker.js +0 -97
  236. package/database/repositories/migration-status-repository-s3.js +0 -137
  237. package/infrastructure/scheduler/eventbridge-scheduler-adapter.js +0 -184
@@ -4,11 +4,8 @@ const { get } = require('../assertions');
4
4
  class Options {
5
5
  constructor(params) {
6
6
  this.module = get(params, 'module');
7
- this.isMany = Boolean(get(params, 'isMany', false));
7
+ this.modules = params.modules || {}; // Store modules for requiredEntities extraction
8
8
  this.hasUserConfig = Boolean(get(params, 'hasUserConfig', false));
9
- this.requiresNewEntity = Boolean(
10
- get(params, 'requiresNewEntity', false)
11
- );
12
9
  if (!params.display) {
13
10
  throw new RequiredPropertyError({
14
11
  parent: this,
@@ -17,37 +14,67 @@ class Options {
17
14
  }
18
15
 
19
16
  this.display = {};
17
+ // Required fields
20
18
  this.display.name = get(params.display, 'label');
21
19
  this.display.description = get(params.display, 'description');
22
- this.display.detailsUrl = get(params.display, 'detailsUrl');
23
- this.display.icon = get(params.display, 'icon');
20
+ // Optional fields - use defaults if not provided
21
+ this.display.detailsUrl = params.display.detailsUrl || null;
22
+ this.display.icon = params.display.icon || null;
24
23
  }
25
24
 
26
25
  get() {
26
+ // Extract module names from the modules object to determine required entities
27
+ const requiredEntities = this.modules ? Object.keys(this.modules) : [];
28
+
29
+ // Get module type name - handle both getName() method and moduleName property
30
+ const moduleType = this._getModuleTypeName();
31
+
27
32
  return {
28
- type: this.module.definition.getName(),
33
+ type: moduleType,
29
34
 
30
35
  // Flag for if the User can configure any settings
31
36
  hasUserConfig: this.hasUserConfig,
32
37
 
33
- // if this integration can be used multiple times with the same integration pair. For example I want to
34
- // connect two different Etsy shops to the same Freshbooks account.
35
- isMany: this.isMany,
36
-
37
- // if this is true it means we need to create a new entity for every integration pair and not use an
38
- // existing one. This would be true for scenarios where the client wishes to have individual control over
39
- // the integerations it has connected to its app. They would want this to let their users only delete
40
- // single integrations without notifying our server.
41
- requiresNewEntity: this.requiresNewEntity,
38
+ // Array of module/entity type names required for this integration (e.g., ['nagaris', 'creditorwatch'])
39
+ // UI uses this to check if user has connected the necessary accounts before creating integration
40
+ requiredEntities: requiredEntities,
42
41
 
43
42
  // this is information required for the display side of things on the front end
44
43
  display: this.display,
45
-
46
- // this is information for post-authentication config, using jsonSchema and uiSchema for display on the frontend
47
- // Maybe include but probably not, I like making someone make a follow-on request
48
- // configOptions: this.configOptions,
49
44
  };
50
45
  }
46
+
47
+ /**
48
+ * Get the module type name from the module definition.
49
+ * Supports both:
50
+ * - getName() method (standard Frigg API modules)
51
+ * - moduleName property (custom API modules)
52
+ * @returns {string} The module type name
53
+ * @private
54
+ */
55
+ _getModuleTypeName() {
56
+ const definition = this.module?.definition;
57
+ if (!definition) {
58
+ return 'unknown';
59
+ }
60
+
61
+ // Try getName() method first (standard pattern)
62
+ if (typeof definition.getName === 'function') {
63
+ return definition.getName();
64
+ }
65
+
66
+ // Fall back to moduleName property
67
+ if (definition.moduleName) {
68
+ return definition.moduleName;
69
+ }
70
+
71
+ // Last resort - try name property
72
+ if (definition.name) {
73
+ return definition.name;
74
+ }
75
+
76
+ return 'unknown';
77
+ }
51
78
  }
52
79
 
53
80
  module.exports = { Options };
@@ -1,12 +1,6 @@
1
- const {
2
- IntegrationMappingRepositoryMongo,
3
- } = require('./integration-mapping-repository-mongo');
4
1
  const {
5
2
  IntegrationMappingRepositoryPostgres,
6
3
  } = require('./integration-mapping-repository-postgres');
7
- const {
8
- IntegrationMappingRepositoryDocumentDB,
9
- } = require('./integration-mapping-repository-documentdb');
10
4
  const config = require('../../database/config');
11
5
 
12
6
  /**
@@ -33,12 +27,14 @@ function createIntegrationMappingRepository() {
33
27
 
34
28
  switch (dbType) {
35
29
  case 'mongodb':
30
+ const { IntegrationMappingRepositoryMongo } = require('./integration-mapping-repository-mongo');
36
31
  return new IntegrationMappingRepositoryMongo();
37
32
 
38
33
  case 'postgresql':
39
34
  return new IntegrationMappingRepositoryPostgres();
40
35
 
41
36
  case 'documentdb':
37
+ const { IntegrationMappingRepositoryDocumentDB } = require('./integration-mapping-repository-documentdb');
42
38
  return new IntegrationMappingRepositoryDocumentDB();
43
39
 
44
40
  default:
@@ -51,7 +47,7 @@ function createIntegrationMappingRepository() {
51
47
  module.exports = {
52
48
  createIntegrationMappingRepository,
53
49
  // Export adapters for direct testing
54
- IntegrationMappingRepositoryMongo,
50
+ get IntegrationMappingRepositoryMongo() { return require('./integration-mapping-repository-mongo').IntegrationMappingRepositoryMongo; },
55
51
  IntegrationMappingRepositoryPostgres,
56
- IntegrationMappingRepositoryDocumentDB,
52
+ get IntegrationMappingRepositoryDocumentDB() { return require('./integration-mapping-repository-documentdb').IntegrationMappingRepositoryDocumentDB; },
57
53
  };
@@ -29,13 +29,17 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
29
29
  async deleteIntegrationById(integrationId) {
30
30
  const objectId = toObjectId(integrationId);
31
31
  if (!objectId) return { acknowledged: true, deletedCount: 0 };
32
- const result = await deleteOne(this.prisma, 'Integration', { _id: objectId });
32
+ const result = await deleteOne(this.prisma, 'Integration', {
33
+ _id: objectId,
34
+ });
33
35
  const deleted = result?.n ?? 0;
34
36
  return { acknowledged: true, deletedCount: deleted };
35
37
  }
36
38
 
37
39
  async findIntegrationByName(name) {
38
- const doc = await findOne(this.prisma, 'Integration', { 'config.type': name });
40
+ const doc = await findOne(this.prisma, 'Integration', {
41
+ 'config.type': name,
42
+ });
39
43
  if (!doc) {
40
44
  throw new Error(`Integration with name ${name} not found`);
41
45
  }
@@ -47,7 +51,9 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
47
51
  if (!objectId) {
48
52
  throw new Error(`Integration with id ${id} not found`);
49
53
  }
50
- const doc = await findOne(this.prisma, 'Integration', { _id: objectId });
54
+ const doc = await findOne(this.prisma, 'Integration', {
55
+ _id: objectId,
56
+ });
51
57
  if (!doc) {
52
58
  throw new Error(`Integration with id ${id} not found`);
53
59
  }
@@ -79,12 +85,16 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
79
85
  if (!objectId) {
80
86
  throw new Error(`Integration ${integrationId} not found`);
81
87
  }
82
- const existing = await findOne(this.prisma, 'Integration', { _id: objectId });
88
+ const existing = await findOne(this.prisma, 'Integration', {
89
+ _id: objectId,
90
+ });
83
91
  if (!existing) {
84
92
  throw new Error(`Integration ${integrationId} not found`);
85
93
  }
86
94
  const messages = this._extractMessages(existing);
87
- const list = Array.isArray(messages[messageType]) ? [...messages[messageType]] : [];
95
+ const list = Array.isArray(messages[messageType])
96
+ ? [...messages[messageType]]
97
+ : [];
88
98
  list.push({
89
99
  title: messageTitle ?? null,
90
100
  message: messageBody,
@@ -125,17 +135,26 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
125
135
  createdAt: now,
126
136
  updatedAt: now,
127
137
  };
128
- const insertedId = await insertOne(this.prisma, 'Integration', document);
129
- const created = await findOne(this.prisma, 'Integration', { _id: insertedId });
138
+ const insertedId = await insertOne(
139
+ this.prisma,
140
+ 'Integration',
141
+ document
142
+ );
143
+ const created = await findOne(this.prisma, 'Integration', {
144
+ _id: insertedId,
145
+ });
130
146
  if (!created) {
131
- console.error('[IntegrationRepositoryDocumentDB] Integration not found after insert', {
132
- insertedId: fromObjectId(insertedId),
133
- userId,
134
- config,
135
- });
147
+ console.error(
148
+ '[IntegrationRepositoryDocumentDB] Integration not found after insert',
149
+ {
150
+ insertedId: fromObjectId(insertedId),
151
+ userId,
152
+ config,
153
+ }
154
+ );
136
155
  throw new Error(
137
156
  'Failed to create integration: Document not found after insert. ' +
138
- 'This indicates a database consistency issue.'
157
+ 'This indicates a database consistency issue.'
139
158
  );
140
159
  }
141
160
  return this._mapIntegration(created);
@@ -144,7 +163,9 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
144
163
  async findIntegrationByUserId(userId) {
145
164
  const objectId = toObjectId(userId);
146
165
  if (!objectId) return null;
147
- const doc = await findOne(this.prisma, 'Integration', { userId: objectId });
166
+ const doc = await findOne(this.prisma, 'Integration', {
167
+ userId: objectId,
168
+ });
148
169
  return doc ? this._mapIntegration(doc) : null;
149
170
  }
150
171
 
@@ -167,15 +188,20 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
167
188
  },
168
189
  }
169
190
  );
170
- const updated = await findOne(this.prisma, 'Integration', { _id: objectId });
191
+ const updated = await findOne(this.prisma, 'Integration', {
192
+ _id: objectId,
193
+ });
171
194
  if (!updated) {
172
- console.error('[IntegrationRepositoryDocumentDB] Integration not found after update', {
173
- integrationId: fromObjectId(objectId),
174
- config,
175
- });
195
+ console.error(
196
+ '[IntegrationRepositoryDocumentDB] Integration not found after update',
197
+ {
198
+ integrationId: fromObjectId(objectId),
199
+ config,
200
+ }
201
+ );
176
202
  throw new Error(
177
203
  'Failed to update integration: Document not found after update. ' +
178
- 'This indicates a database consistency issue.'
204
+ 'This indicates a database consistency issue.'
179
205
  );
180
206
  }
181
207
  return this._mapIntegration(updated);
@@ -185,7 +211,9 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
185
211
  const messages = this._extractMessages(doc);
186
212
  return {
187
213
  id: fromObjectId(doc?._id),
188
- entitiesIds: (doc?.entityIds || []).map((value) => fromObjectId(value)),
214
+ entitiesIds: (doc?.entityIds || []).map((value) =>
215
+ fromObjectId(value)
216
+ ),
189
217
  userId: fromObjectId(doc?.userId),
190
218
  config: doc?.config ?? null,
191
219
  version: doc?.version ?? null,
@@ -195,7 +223,10 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
195
223
  }
196
224
 
197
225
  _extractMessages(doc) {
198
- const base = doc?.messages && typeof doc.messages === 'object' ? doc.messages : {};
226
+ const base =
227
+ doc?.messages && typeof doc.messages === 'object'
228
+ ? doc.messages
229
+ : {};
199
230
  return {
200
231
  errors: base.errors ?? doc?.errors ?? [],
201
232
  warnings: base.warnings ?? doc?.warnings ?? [],
@@ -206,5 +237,3 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
206
237
  }
207
238
 
208
239
  module.exports = { IntegrationRepositoryDocumentDB };
209
-
210
-
@@ -1,8 +1,6 @@
1
- const { IntegrationRepositoryMongo } = require('./integration-repository-mongo');
2
- const { IntegrationRepositoryPostgres } = require('./integration-repository-postgres');
3
1
  const {
4
- IntegrationRepositoryDocumentDB,
5
- } = require('./integration-repository-documentdb');
2
+ IntegrationRepositoryPostgres,
3
+ } = require('./integration-repository-postgres');
6
4
  const config = require('../../database/config');
7
5
 
8
6
  /**
@@ -27,12 +25,14 @@ function createIntegrationRepository() {
27
25
 
28
26
  switch (dbType) {
29
27
  case 'mongodb':
28
+ const { IntegrationRepositoryMongo } = require('./integration-repository-mongo');
30
29
  return new IntegrationRepositoryMongo();
31
30
 
32
31
  case 'postgresql':
33
32
  return new IntegrationRepositoryPostgres();
34
33
 
35
34
  case 'documentdb':
35
+ const { IntegrationRepositoryDocumentDB } = require('./integration-repository-documentdb');
36
36
  return new IntegrationRepositoryDocumentDB();
37
37
 
38
38
  default:
@@ -45,7 +45,7 @@ function createIntegrationRepository() {
45
45
  module.exports = {
46
46
  createIntegrationRepository,
47
47
  // Export adapters for direct testing
48
- IntegrationRepositoryMongo,
48
+ get IntegrationRepositoryMongo() { return require('./integration-repository-mongo').IntegrationRepositoryMongo; },
49
49
  IntegrationRepositoryPostgres,
50
- IntegrationRepositoryDocumentDB,
50
+ get IntegrationRepositoryDocumentDB() { return require('./integration-repository-documentdb').IntegrationRepositoryDocumentDB; },
51
51
  };
@@ -18,7 +18,9 @@ class IntegrationRepositoryInterface {
18
18
  * @abstract
19
19
  */
20
20
  async findIntegrationsByUserId(userId) {
21
- throw new Error('Method findIntegrationsByUserId must be implemented by subclass');
21
+ throw new Error(
22
+ 'Method findIntegrationsByUserId must be implemented by subclass'
23
+ );
22
24
  }
23
25
 
24
26
  /**
@@ -29,7 +31,9 @@ class IntegrationRepositoryInterface {
29
31
  * @abstract
30
32
  */
31
33
  async deleteIntegrationById(integrationId) {
32
- throw new Error('Method deleteIntegrationById must be implemented by subclass');
34
+ throw new Error(
35
+ 'Method deleteIntegrationById must be implemented by subclass'
36
+ );
33
37
  }
34
38
 
35
39
  /**
@@ -40,7 +44,9 @@ class IntegrationRepositoryInterface {
40
44
  * @abstract
41
45
  */
42
46
  async findIntegrationByName(name) {
43
- throw new Error('Method findIntegrationByName must be implemented by subclass');
47
+ throw new Error(
48
+ 'Method findIntegrationByName must be implemented by subclass'
49
+ );
44
50
  }
45
51
 
46
52
  /**
@@ -51,7 +57,9 @@ class IntegrationRepositoryInterface {
51
57
  * @abstract
52
58
  */
53
59
  async findIntegrationById(id) {
54
- throw new Error('Method findIntegrationById must be implemented by subclass');
60
+ throw new Error(
61
+ 'Method findIntegrationById must be implemented by subclass'
62
+ );
55
63
  }
56
64
 
57
65
  /**
@@ -63,7 +71,9 @@ class IntegrationRepositoryInterface {
63
71
  * @abstract
64
72
  */
65
73
  async updateIntegrationStatus(integrationId, status) {
66
- throw new Error('Method updateIntegrationStatus must be implemented by subclass');
74
+ throw new Error(
75
+ 'Method updateIntegrationStatus must be implemented by subclass'
76
+ );
67
77
  }
68
78
 
69
79
  /**
@@ -84,7 +94,9 @@ class IntegrationRepositoryInterface {
84
94
  messageBody,
85
95
  messageTimestamp
86
96
  ) {
87
- throw new Error('Method updateIntegrationMessages must be implemented by subclass');
97
+ throw new Error(
98
+ 'Method updateIntegrationMessages must be implemented by subclass'
99
+ );
88
100
  }
89
101
 
90
102
  /**
@@ -97,7 +109,9 @@ class IntegrationRepositoryInterface {
97
109
  * @abstract
98
110
  */
99
111
  async createIntegration(entities, userId, config) {
100
- throw new Error('Method createIntegration must be implemented by subclass');
112
+ throw new Error(
113
+ 'Method createIntegration must be implemented by subclass'
114
+ );
101
115
  }
102
116
 
103
117
  /**
@@ -108,7 +122,9 @@ class IntegrationRepositoryInterface {
108
122
  * @abstract
109
123
  */
110
124
  async findIntegrationByUserId(userId) {
111
- throw new Error('Method findIntegrationByUserId must be implemented by subclass');
125
+ throw new Error(
126
+ 'Method findIntegrationByUserId must be implemented by subclass'
127
+ );
112
128
  }
113
129
 
114
130
  /**
@@ -120,7 +136,9 @@ class IntegrationRepositoryInterface {
120
136
  * @abstract
121
137
  */
122
138
  async updateIntegrationConfig(integrationId, config) {
123
- throw new Error('Method updateIntegrationConfig must be implemented by subclass');
139
+ throw new Error(
140
+ 'Method updateIntegrationConfig must be implemented by subclass'
141
+ );
124
142
  }
125
143
  }
126
144
 
@@ -49,12 +49,12 @@ class IntegrationRepositoryPostgres extends IntegrationRepositoryInterface {
49
49
  ...integration,
50
50
  id: integration.id?.toString(),
51
51
  userId: integration.userId?.toString(),
52
- entities: integration.entities?.map(e => ({
52
+ entities: integration.entities?.map((e) => ({
53
53
  ...e,
54
54
  id: e.id?.toString(),
55
55
  userId: e.userId?.toString(),
56
- credentialId: e.credentialId?.toString()
57
- }))
56
+ credentialId: e.credentialId?.toString(),
57
+ })),
58
58
  };
59
59
  }
60
60
 
@@ -1,8 +1,4 @@
1
- const { ProcessRepositoryMongo } = require('./process-repository-mongo');
2
1
  const { ProcessRepositoryPostgres } = require('./process-repository-postgres');
3
- const {
4
- ProcessRepositoryDocumentDB,
5
- } = require('./process-repository-documentdb');
6
2
  const config = require('../../database/config');
7
3
 
8
4
  /**
@@ -28,12 +24,14 @@ function createProcessRepository() {
28
24
 
29
25
  switch (dbType) {
30
26
  case 'mongodb':
27
+ const { ProcessRepositoryMongo } = require('./process-repository-mongo');
31
28
  return new ProcessRepositoryMongo();
32
29
 
33
30
  case 'postgresql':
34
31
  return new ProcessRepositoryPostgres();
35
32
 
36
33
  case 'documentdb':
34
+ const { ProcessRepositoryDocumentDB } = require('./process-repository-documentdb');
37
35
  return new ProcessRepositoryDocumentDB();
38
36
 
39
37
  default:
@@ -46,8 +44,7 @@ function createProcessRepository() {
46
44
  module.exports = {
47
45
  createProcessRepository,
48
46
  // Export adapters for direct testing
49
- ProcessRepositoryMongo,
47
+ get ProcessRepositoryMongo() { return require('./process-repository-mongo').ProcessRepositoryMongo; },
50
48
  ProcessRepositoryPostgres,
51
- ProcessRepositoryDocumentDB,
49
+ get ProcessRepositoryDocumentDB() { return require('./process-repository-documentdb').ProcessRepositoryDocumentDB; },
52
50
  };
53
-
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * ProcessRepository Interface
3
- *
3
+ *
4
4
  * Defines the contract for Process data access operations.
5
5
  * Implementations must provide concrete methods for all operations.
6
- *
6
+ *
7
7
  * This interface supports the Hexagonal Architecture pattern by:
8
8
  * - Defining clear boundaries between domain logic and data access
9
9
  * - Allowing multiple implementations (MongoDB, PostgreSQL, in-memory)
@@ -54,7 +54,9 @@ class ProcessRepositoryInterface {
54
54
  * @returns {Promise<Array>} Array of process records
55
55
  */
56
56
  async findByIntegrationAndType(integrationId, type) {
57
- throw new Error('Method findByIntegrationAndType() must be implemented');
57
+ throw new Error(
58
+ 'Method findByIntegrationAndType() must be implemented'
59
+ );
58
60
  }
59
61
 
60
62
  /**
@@ -63,7 +65,10 @@ class ProcessRepositoryInterface {
63
65
  * @param {string[]} [excludeStates=['COMPLETED', 'ERROR']] - States to exclude
64
66
  * @returns {Promise<Array>} Array of active process records
65
67
  */
66
- async findActiveProcesses(integrationId, excludeStates = ['COMPLETED', 'ERROR']) {
68
+ async findActiveProcesses(
69
+ integrationId,
70
+ excludeStates = ['COMPLETED', 'ERROR']
71
+ ) {
67
72
  throw new Error('Method findActiveProcesses() must be implemented');
68
73
  }
69
74
 
@@ -87,4 +92,3 @@ class ProcessRepositoryInterface {
87
92
  }
88
93
 
89
94
  module.exports = { ProcessRepositoryInterface };
90
-
@@ -1,5 +1,7 @@
1
1
  const { prisma } = require('../../database/prisma');
2
- const { ProcessRepositoryInterface } = require('./process-repository-interface');
2
+ const {
3
+ ProcessRepositoryInterface,
4
+ } = require('./process-repository-interface');
3
5
 
4
6
  /**
5
7
  * MongoDB Process Repository Adapter
@@ -118,7 +120,10 @@ class ProcessRepositoryMongo extends ProcessRepositoryInterface {
118
120
  * @param {string[]} [excludeStates=['COMPLETED', 'ERROR']] - States to exclude
119
121
  * @returns {Promise<Array>} Array of active process records
120
122
  */
121
- async findActiveProcesses(integrationId, excludeStates = ['COMPLETED', 'ERROR']) {
123
+ async findActiveProcesses(
124
+ integrationId,
125
+ excludeStates = ['COMPLETED', 'ERROR']
126
+ ) {
122
127
  const processes = await this.prisma.process.findMany({
123
128
  where: {
124
129
  integrationId,
@@ -187,4 +192,3 @@ class ProcessRepositoryMongo extends ProcessRepositoryInterface {
187
192
  }
188
193
 
189
194
  module.exports = { ProcessRepositoryMongo };
190
-
@@ -199,8 +199,8 @@ class ProcessRepositoryPostgres extends ProcessRepositoryInterface {
199
199
  results: process.results,
200
200
  childProcesses: Array.isArray(process.childProcesses)
201
201
  ? process.childProcesses.length > 0 &&
202
- typeof process.childProcesses[0] === 'object' &&
203
- process.childProcesses[0] !== null
202
+ typeof process.childProcesses[0] === 'object' &&
203
+ process.childProcesses[0] !== null
204
204
  ? process.childProcesses.map((child) => String(child.id))
205
205
  : process.childProcesses
206
206
  : [],
@@ -2,7 +2,7 @@ const { IntegrationBase } = require('../../integration-base');
2
2
 
3
3
  class ConfigCapturingModule {
4
4
  static definition = {
5
- getName: () => 'config-capturing-module'
5
+ getName: () => 'config-capturing-module',
6
6
  };
7
7
  }
8
8
 
@@ -11,14 +11,15 @@ class ConfigCapturingIntegration extends IntegrationBase {
11
11
  name: 'config-capturing',
12
12
  version: '1.0.0',
13
13
  modules: {
14
- primary: ConfigCapturingModule
14
+ primary: ConfigCapturingModule,
15
15
  },
16
16
  display: {
17
17
  label: 'Config Capturing Integration',
18
- description: 'Test double for capturing config state during updates',
18
+ description:
19
+ 'Test double for capturing config state during updates',
19
20
  detailsUrl: 'https://example.com',
20
- icon: 'test-icon'
21
- }
21
+ icon: 'test-icon',
22
+ },
22
23
  };
23
24
 
24
25
  static _capturedOnUpdateState = null;
@@ -38,10 +39,10 @@ class ConfigCapturingIntegration extends IntegrationBase {
38
39
  findIntegrationById: jest.fn().mockResolvedValue({}),
39
40
  };
40
41
  this.updateIntegrationStatus = {
41
- execute: jest.fn().mockResolvedValue({})
42
+ execute: jest.fn().mockResolvedValue({}),
42
43
  };
43
44
  this.updateIntegrationMessages = {
44
- execute: jest.fn().mockResolvedValue({})
45
+ execute: jest.fn().mockResolvedValue({}),
45
46
  };
46
47
  }
47
48
 
@@ -52,7 +53,7 @@ class ConfigCapturingIntegration extends IntegrationBase {
52
53
  async onUpdate(params) {
53
54
  ConfigCapturingIntegration._capturedOnUpdateState = {
54
55
  thisConfig: JSON.parse(JSON.stringify(this.config)),
55
- paramsConfig: params.config
56
+ paramsConfig: params.config,
56
57
  };
57
58
 
58
59
  this.config = this._deepMerge(this.config, params.config);