@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
@@ -58,7 +58,9 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
58
58
  return new Map();
59
59
  }
60
60
 
61
- const validIds = credentialIds.filter(id => id !== null && id !== undefined);
61
+ const validIds = credentialIds.filter(
62
+ (id) => id !== null && id !== undefined
63
+ );
62
64
 
63
65
  if (validIds.length === 0) {
64
66
  return new Map();
@@ -118,7 +120,9 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
118
120
  where: { userId },
119
121
  });
120
122
 
121
- const credentialIds = entities.map(e => e.credentialId).filter(Boolean);
123
+ const credentialIds = entities
124
+ .map((e) => e.credentialId)
125
+ .filter(Boolean);
122
126
  const credentialMap = await this._fetchCredentialsBulk(credentialIds);
123
127
 
124
128
  return entities.map((e) => ({
@@ -144,7 +148,9 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
144
148
  where: { id: { in: entitiesIds } },
145
149
  });
146
150
 
147
- const credentialIds = entities.map(e => e.credentialId).filter(Boolean);
151
+ const credentialIds = entities
152
+ .map((e) => e.credentialId)
153
+ .filter(Boolean);
148
154
  const credentialMap = await this._fetchCredentialsBulk(credentialIds);
149
155
 
150
156
  return entities.map((e) => ({
@@ -174,7 +180,9 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
174
180
  },
175
181
  });
176
182
 
177
- const credentialIds = entities.map(e => e.credentialId).filter(Boolean);
183
+ const credentialIds = entities
184
+ .map((e) => e.credentialId)
185
+ .filter(Boolean);
178
186
  const credentialMap = await this._fetchCredentialsBulk(credentialIds);
179
187
 
180
188
  return entities.map((e) => ({
@@ -234,6 +242,31 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
234
242
  };
235
243
  }
236
244
 
245
+ /**
246
+ * Find entities matching filter criteria
247
+ * @param {Object} filter - Filter criteria (e.g., { isGlobal: true, moduleName: 'api-name' })
248
+ * @returns {Promise<Array>} Array of entity objects with string IDs
249
+ */
250
+ async findEntitiesBy(filter) {
251
+ const where = this._convertFilterToWhere(filter);
252
+ const entities = await this.prisma.entity.findMany({
253
+ where,
254
+ include: { credential: true },
255
+ });
256
+
257
+ return entities.map((e) => ({
258
+ id: e.id,
259
+ accountId: e.accountId,
260
+ credential: e.credential,
261
+ userId: e.userId,
262
+ name: e.name,
263
+ externalId: e.externalId,
264
+ type: e.subType,
265
+ moduleName: e.moduleName,
266
+ isGlobal: e.isGlobal,
267
+ }));
268
+ }
269
+
237
270
  /**
238
271
  * Create a new entity
239
272
  * Replaces: Entity.create(entityData)
@@ -399,7 +432,9 @@ class ModuleRepositoryMongo extends ModuleRepositoryInterface {
399
432
  if (filter.credentialId) where.credentialId = filter.credentialId;
400
433
  if (filter.name) where.name = filter.name;
401
434
  if (filter.moduleName) where.moduleName = filter.moduleName;
402
- if (filter.externalId) where.externalId = this._toString(filter.externalId);
435
+ if (filter.externalId)
436
+ where.externalId = this._toString(filter.externalId);
437
+ if (filter.isGlobal !== undefined) where.isGlobal = filter.isGlobal;
403
438
 
404
439
  return where;
405
440
  }
@@ -87,7 +87,9 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
87
87
  return new Map();
88
88
  }
89
89
 
90
- const validIds = credentialIds.filter(id => id !== null && id !== undefined);
90
+ const validIds = credentialIds.filter(
91
+ (id) => id !== null && id !== undefined
92
+ );
91
93
 
92
94
  if (validIds.length === 0) {
93
95
  return new Map();
@@ -154,7 +156,9 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
154
156
  where: { userId: intUserId },
155
157
  });
156
158
 
157
- const credentialIds = entities.map(e => e.credentialId).filter(Boolean);
159
+ const credentialIds = entities
160
+ .map((e) => e.credentialId)
161
+ .filter(Boolean);
158
162
  const credentialMap = await this._fetchCredentialsBulk(credentialIds);
159
163
 
160
164
  return entities.map((e) => ({
@@ -182,7 +186,9 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
182
186
  where: { id: { in: intIds } },
183
187
  });
184
188
 
185
- const credentialIds = entities.map(e => e.credentialId).filter(Boolean);
189
+ const credentialIds = entities
190
+ .map((e) => e.credentialId)
191
+ .filter(Boolean);
186
192
  const credentialMap = await this._fetchCredentialsBulk(credentialIds);
187
193
 
188
194
  return entities.map((e) => ({
@@ -214,7 +220,9 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
214
220
  },
215
221
  });
216
222
 
217
- const credentialIds = entities.map(e => e.credentialId).filter(Boolean);
223
+ const credentialIds = entities
224
+ .map((e) => e.credentialId)
225
+ .filter(Boolean);
218
226
  const credentialMap = await this._fetchCredentialsBulk(credentialIds);
219
227
 
220
228
  return entities.map((e) => ({
@@ -275,6 +283,33 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
275
283
  };
276
284
  }
277
285
 
286
+ /**
287
+ * Find entities matching filter criteria
288
+ * Replaces: Entity.find(filter).populate('credential')
289
+ *
290
+ * @param {Object} filter - Filter criteria (e.g., { isGlobal: true, type: 'someType', status: 'connected' })
291
+ * @returns {Promise<Array>} Array of entity objects with string IDs
292
+ */
293
+ async findEntitiesBy(filter) {
294
+ const where = this._convertFilterToWhere(filter);
295
+ const entities = await this.prisma.entity.findMany({
296
+ where,
297
+ include: { credential: true },
298
+ });
299
+
300
+ return entities.map((e) => ({
301
+ id: e.id.toString(),
302
+ accountId: e.accountId,
303
+ credential: this._convertCredentialIds(e.credential),
304
+ userId: e.userId?.toString(),
305
+ name: e.name,
306
+ externalId: e.externalId,
307
+ type: e.subType,
308
+ moduleName: e.moduleName,
309
+ isGlobal: e.isGlobal,
310
+ }));
311
+ }
312
+
278
313
  /**
279
314
  * Create a new entity
280
315
  * Replaces: Entity.create(entityData)
@@ -444,7 +479,9 @@ class ModuleRepositoryPostgres extends ModuleRepositoryInterface {
444
479
  where.credentialId = this._convertId(filter.credentialId);
445
480
  if (filter.name) where.name = filter.name;
446
481
  if (filter.moduleName) where.moduleName = filter.moduleName;
447
- if (filter.externalId) where.externalId = this._toString(filter.externalId);
482
+ if (filter.externalId)
483
+ where.externalId = this._toString(filter.externalId);
484
+ if (filter.isGlobal !== undefined) where.isGlobal = filter.isGlobal;
448
485
 
449
486
  return where;
450
487
  }
@@ -172,6 +172,33 @@ class ModuleRepository extends ModuleRepositoryInterface {
172
172
  };
173
173
  }
174
174
 
175
+ /**
176
+ * Find entities matching filter criteria
177
+ * Replaces: Entity.find(filter).populate('credential')
178
+ *
179
+ * @param {Object} filter - Filter criteria
180
+ * @returns {Promise<Array>} Array of entity objects
181
+ */
182
+ async findEntitiesBy(filter) {
183
+ const where = this._convertFilterToWhere(filter);
184
+ const entities = await this.prisma.entity.findMany({
185
+ where,
186
+ include: { credential: true },
187
+ });
188
+
189
+ return entities.map((e) => ({
190
+ id: e.id,
191
+ accountId: e.accountId,
192
+ credential: e.credential,
193
+ userId: e.userId,
194
+ name: e.name,
195
+ externalId: e.externalId,
196
+ type: e.subType,
197
+ moduleName: e.moduleName,
198
+ isGlobal: e.isGlobal,
199
+ }));
200
+ }
201
+
175
202
  /**
176
203
  * Create a new entity
177
204
  * Replaces: Entity.create(entityData)
@@ -337,6 +364,7 @@ class ModuleRepository extends ModuleRepositoryInterface {
337
364
  if (filter.name) where.name = filter.name;
338
365
  if (filter.moduleName) where.moduleName = filter.moduleName;
339
366
  if (filter.externalId) where.externalId = filter.externalId;
367
+ if (filter.isGlobal !== undefined) where.isGlobal = filter.isGlobal;
340
368
 
341
369
  return where;
342
370
  }
@@ -2,9 +2,7 @@ const { Requester } = require('./requester');
2
2
  const { get } = require('../../assertions');
3
3
  const { ModuleConstants } = require('../ModuleConstants');
4
4
 
5
-
6
5
  class ApiKeyRequester extends Requester {
7
-
8
6
  static requesterType = ModuleConstants.authType.apiKey;
9
7
 
10
8
  constructor(params) {
@@ -3,7 +3,6 @@ const { get } = require('../../assertions');
3
3
  const { ModuleConstants } = require('../ModuleConstants');
4
4
 
5
5
  class BasicAuthRequester extends Requester {
6
-
7
6
  static requesterType = ModuleConstants.authType.basic;
8
7
 
9
8
  constructor(params) {
@@ -117,16 +117,6 @@ class OAuth2Requester extends Requester {
117
117
  const newRefreshToken = get(params, 'refresh_token', null);
118
118
  if (newRefreshToken !== null) {
119
119
  this.refresh_token = newRefreshToken;
120
- } else {
121
- if (this.refresh_token) {
122
- console.log(
123
- '[Frigg] No refresh_token in response, preserving existing'
124
- );
125
- } else {
126
- console.log(
127
- '[Frigg] Current refresh_token is null and no new refresh_token in response'
128
- );
129
- }
130
120
  }
131
121
  const accessExpiresIn = get(params, 'expires_in', null);
132
122
  const refreshExpiresIn = get(
@@ -248,7 +238,6 @@ class OAuth2Requester extends Requester {
248
238
  'Content-Type': 'application/x-www-form-urlencoded',
249
239
  },
250
240
  };
251
- console.log('[Frigg] Refreshing access token with options');
252
241
  const response = await this._post(options, false);
253
242
  await this.setTokens(response);
254
243
  return response;
@@ -296,7 +285,7 @@ class OAuth2Requester extends Requester {
296
285
  */
297
286
  async refreshAuth() {
298
287
  try {
299
- console.log('[Frigg] Starting token refresh', {
288
+ console.log('[OAuth2Requester.refreshAuth] Starting token refresh', {
300
289
  grant_type: this.grant_type,
301
290
  has_refresh_token: !!this.refresh_token,
302
291
  has_client_id: !!this.client_id,
@@ -312,10 +301,10 @@ class OAuth2Requester extends Requester {
312
301
  } else {
313
302
  await this.getTokenFromClientCredentials();
314
303
  }
315
- console.log('[Frigg] Token refresh succeeded');
304
+ console.log('[OAuth2Requester.refreshAuth] Token refresh succeeded');
316
305
  return true;
317
306
  } catch (error) {
318
- console.error('[Frigg] Token refresh failed', {
307
+ console.error('[OAuth2Requester.refreshAuth] Token refresh failed', {
319
308
  error_message: error?.message,
320
309
  error_name: error?.name,
321
310
  response_status: error?.response?.status,
@@ -1,7 +1,7 @@
1
1
  require('dotenv').config();
2
2
  const { Api } = require('./api');
3
3
  const { get } = require('../../../assertions');
4
- const config = { name: 'anapi' }
4
+ const config = { name: 'anapi' };
5
5
 
6
6
  const Definition = {
7
7
  API: Api,
@@ -9,7 +9,9 @@ const Definition = {
9
9
  url: 'http://localhost:3000/redirect/anapi',
10
10
  type: 'oauth2',
11
11
  }),
12
- getName: function () { return config.name },
12
+ getName: function () {
13
+ return config.name;
14
+ },
13
15
  moduleName: config.name,
14
16
  modelName: 'AnApi',
15
17
  requiredAuthMethods: {
@@ -17,28 +19,31 @@ const Definition = {
17
19
  const code = get(params.data, 'code');
18
20
  return api.getTokenFromCode(code);
19
21
  },
20
- getEntityDetails: async function (api, callbackParams, tokenResponse, userId) {
22
+ getEntityDetails: async function (
23
+ api,
24
+ callbackParams,
25
+ tokenResponse,
26
+ userId
27
+ ) {
21
28
  const userDetails = await api.getUserDetails();
22
29
  return {
23
30
  identifiers: { externalId: userDetails.portalId, userId },
24
31
  details: { name: userDetails.hub_domain },
25
- }
32
+ };
26
33
  },
27
34
  apiPropertiesToPersist: {
28
- credential: [
29
- 'access_token', 'refresh_token'
30
- ],
35
+ credential: ['access_token', 'refresh_token'],
31
36
  entity: [],
32
37
  },
33
38
  getCredentialDetails: async function (api, userId) {
34
39
  const userDetails = await api.getUserDetails();
35
40
  return {
36
41
  identifiers: { externalId: userDetails.portalId, userId },
37
- details: {}
42
+ details: {},
38
43
  };
39
44
  },
40
45
  testAuthRequest: async function (api) {
41
- return api.getUserDetails()
46
+ return api.getUserDetails();
42
47
  },
43
48
  },
44
49
  env: {
@@ -46,7 +51,7 @@ const Definition = {
46
51
  client_secret: 'test',
47
52
  scope: 'test',
48
53
  redirect_uri: `http://localhost:3000/redirect/anapi`,
49
- }
54
+ },
50
55
  };
51
56
 
52
57
  module.exports = { Definition };
@@ -1,43 +1,43 @@
1
1
  const authorizeResponse = {
2
- "base": "/redirect/hubspot",
3
- "data": {
4
- "code": "test-code",
5
- "state": "null"
6
- }
7
- }
2
+ base: '/redirect/hubspot',
3
+ data: {
4
+ code: 'test-code',
5
+ state: 'null',
6
+ },
7
+ };
8
8
 
9
9
  const tokenResponse = {
10
- "token_type": "bearer",
11
- "refresh_token": "test-refresh-token",
12
- "access_token": "test-access-token",
13
- "expires_in": 1800
14
- }
10
+ token_type: 'bearer',
11
+ refresh_token: 'test-refresh-token',
12
+ access_token: 'test-access-token',
13
+ expires_in: 1800,
14
+ };
15
15
 
16
16
  const userDetailsResponse = {
17
- "portalId": 111111111,
18
- "timeZone": "US/Eastern",
19
- "accountType": "DEVELOPER_TEST",
20
- "currency": "USD",
21
- "utcOffset": "-05:00",
22
- "utcOffsetMilliseconds": -18000000,
23
- "token": "test-token",
24
- "user": "projectteam@lefthook.co",
25
- "hub_domain": "Testing Object Things-dev-44613847.com",
26
- "scopes": [
27
- "content",
28
- "oauth",
29
- "crm.objects.contacts.read",
30
- "crm.objects.contacts.write",
31
- "crm.objects.companies.write",
32
- "crm.objects.companies.read",
33
- "crm.objects.deals.read",
34
- "crm.schemas.deals.read"
17
+ portalId: 111111111,
18
+ timeZone: 'US/Eastern',
19
+ accountType: 'DEVELOPER_TEST',
20
+ currency: 'USD',
21
+ utcOffset: '-05:00',
22
+ utcOffsetMilliseconds: -18000000,
23
+ token: 'test-token',
24
+ user: 'projectteam@lefthook.co',
25
+ hub_domain: 'Testing Object Things-dev-44613847.com',
26
+ scopes: [
27
+ 'content',
28
+ 'oauth',
29
+ 'crm.objects.contacts.read',
30
+ 'crm.objects.contacts.write',
31
+ 'crm.objects.companies.write',
32
+ 'crm.objects.companies.read',
33
+ 'crm.objects.deals.read',
34
+ 'crm.schemas.deals.read',
35
35
  ],
36
- "hub_id": 111111111,
37
- "app_id": 22222222,
38
- "expires_in": 1704,
39
- "user_id": 33333333,
40
- "token_type": "access"
41
- }
36
+ hub_id: 111111111,
37
+ app_id: 22222222,
38
+ expires_in: 1704,
39
+ user_id: 33333333,
40
+ token_type: 'access',
41
+ };
42
42
 
43
- module.exports = { authorizeResponse, tokenResponse, userDetailsResponse }
43
+ module.exports = { authorizeResponse, tokenResponse, userDetailsResponse };
@@ -1,10 +1,16 @@
1
1
  class TestModuleFactory {
2
- constructor() { }
2
+ constructor() {
3
+ this.moduleRepository = {
4
+ findEntity: jest.fn().mockResolvedValue(null),
5
+ findEntitiesBy: jest.fn().mockResolvedValue([]),
6
+ };
7
+ }
3
8
 
4
9
  async getModuleInstance(entityId, userId) {
5
- // return minimal stub module with getName and api property
6
10
  return {
7
- getName() { return 'stubModule'; },
11
+ getName() {
12
+ return 'stubModule';
13
+ },
8
14
  api: {},
9
15
  entityId,
10
16
  userId,
@@ -13,4 +19,4 @@ class TestModuleFactory {
13
19
  }
14
20
  }
15
21
 
16
- module.exports = { TestModuleFactory };
22
+ module.exports = { TestModuleFactory };
@@ -36,4 +36,4 @@ class TestModuleRepository {
36
36
  }
37
37
  }
38
38
 
39
- module.exports = { TestModuleRepository };
39
+ module.exports = { TestModuleRepository };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * DeleteModuleEntity Use Case
3
+ * Deletes a module entity by its ID
4
+ */
5
+ class DeleteModuleEntity {
6
+ constructor({ moduleRepository }) {
7
+ this.moduleRepository = moduleRepository;
8
+ }
9
+
10
+ async execute(entityId) {
11
+ const entity = await this.moduleRepository.findEntityById(entityId);
12
+
13
+ if (!entity) {
14
+ throw new Error(`Entity not found: ${entityId}`);
15
+ }
16
+
17
+ await this.moduleRepository.deleteEntity(entityId);
18
+
19
+ return true;
20
+ }
21
+ }
22
+
23
+ module.exports = { DeleteModuleEntity };
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Get Authorization Requirements Use Case
3
+ * Business logic for retrieving authorization requirements for a specific step
4
+ *
5
+ * Responsibilities:
6
+ * - Find module definition for entity type
7
+ * - Determine step count (single vs multi-step)
8
+ * - Retrieve step-specific requirements (jsonSchema, uiSchema, etc.)
9
+ * - Return structured requirements for frontend rendering
10
+ *
11
+ * Supports both single-step and multi-step modules:
12
+ * - Single-step: Uses getAuthorizationRequirements() (legacy)
13
+ * - Multi-step: Uses getAuthRequirementsForStep(step) (new)
14
+ *
15
+ * @example
16
+ * ```javascript
17
+ * const useCase = new GetAuthorizationRequirementsUseCase({
18
+ * moduleDefinitions: [{ moduleName: 'nagaris', definition: NagarisDefinition }]
19
+ * });
20
+ *
21
+ * // Get requirements for step 1
22
+ * const reqs = await useCase.execute('nagaris', 1);
23
+ * // Returns: { type: 'email', data: { jsonSchema, uiSchema }, step: 1, totalSteps: 2, isMultiStep: true }
24
+ *
25
+ * // Get requirements for step 2
26
+ * const reqs = await useCase.execute('nagaris', 2);
27
+ * // Returns: { type: 'otp', data: { jsonSchema, uiSchema }, step: 2, totalSteps: 2, isMultiStep: true }
28
+ * ```
29
+ */
30
+ class GetAuthorizationRequirementsUseCase {
31
+ /**
32
+ * @param {Object} params - Dependencies
33
+ * @param {Array<Object>} params.moduleDefinitions - Array of module definitions with structure: { moduleName, definition }
34
+ */
35
+ constructor({ moduleDefinitions }) {
36
+ if (!moduleDefinitions || !Array.isArray(moduleDefinitions)) {
37
+ throw new Error('moduleDefinitions array is required');
38
+ }
39
+ this.moduleDefinitions = moduleDefinitions;
40
+ }
41
+
42
+ /**
43
+ * Get authorization requirements for a specific step
44
+ *
45
+ * @param {string} entityType - Entity type (module name)
46
+ * @param {number} [step=1] - Step number (1-indexed)
47
+ * @returns {Promise<Object>} Requirements object with schema and metadata
48
+ * @throws {Error} If module not found or step invalid
49
+ */
50
+ async execute(entityType, step = 1) {
51
+ // Validate inputs
52
+ if (!entityType) {
53
+ throw new Error('entityType is required');
54
+ }
55
+ if (!step || step < 1) {
56
+ throw new Error('step must be >= 1');
57
+ }
58
+
59
+ // Find module definition
60
+ const moduleDefinition = this.moduleDefinitions.find(
61
+ (def) => def.moduleName === entityType
62
+ );
63
+
64
+ if (!moduleDefinition) {
65
+ throw new Error(`Module definition not found: ${entityType}`);
66
+ }
67
+
68
+ const ModuleDefinition = moduleDefinition.definition;
69
+
70
+ // Determine step count (multi-step vs single-step)
71
+ const stepCount = ModuleDefinition.getAuthStepCount
72
+ ? ModuleDefinition.getAuthStepCount()
73
+ : 1;
74
+
75
+ // Validate requested step doesn't exceed max steps
76
+ if (step > stepCount) {
77
+ throw new Error(
78
+ `Step ${step} exceeds maximum steps (${stepCount}) for ${entityType}`
79
+ );
80
+ }
81
+
82
+ // Get requirements for this specific step
83
+ let requirements;
84
+
85
+ if (ModuleDefinition.getAuthRequirementsForStep) {
86
+ // Multi-step module - use step-specific method
87
+ requirements = await ModuleDefinition.getAuthRequirementsForStep(
88
+ step
89
+ );
90
+ } else if (step === 1) {
91
+ // Single-step module (legacy) - use standard method
92
+ requirements =
93
+ await ModuleDefinition.getAuthorizationRequirements();
94
+ } else {
95
+ throw new Error(
96
+ `Module ${entityType} does not support step ${step}`
97
+ );
98
+ }
99
+
100
+ // Return enriched requirements with metadata
101
+ return {
102
+ ...requirements,
103
+ step,
104
+ totalSteps: stepCount,
105
+ isMultiStep: stepCount > 1,
106
+ };
107
+ }
108
+ }
109
+
110
+ module.exports = { GetAuthorizationRequirementsUseCase };
@@ -29,4 +29,4 @@ class GetEntitiesForUser {
29
29
  }
30
30
  }
31
31
 
32
- module.exports = { GetEntitiesForUser };
32
+ module.exports = { GetEntitiesForUser };
@@ -20,9 +20,10 @@ class GetEntityOptionsById {
20
20
  */
21
21
  async execute(entityId, userIdOrUser) {
22
22
  // Support both userId (backward compatible) and User object (new pattern)
23
- const userId = typeof userIdOrUser === 'object' && userIdOrUser?.getId
24
- ? userIdOrUser.getId()
25
- : userIdOrUser;
23
+ const userId =
24
+ typeof userIdOrUser === 'object' && userIdOrUser?.getId
25
+ ? userIdOrUser.getId()
26
+ : userIdOrUser;
26
27
 
27
28
  const entity = await this.moduleRepository.findEntityById(
28
29
  entityId,
@@ -34,9 +35,10 @@ class GetEntityOptionsById {
34
35
  }
35
36
 
36
37
  // Validate entity ownership
37
- const isOwned = typeof userIdOrUser === 'object' && userIdOrUser?.ownsUserId
38
- ? userIdOrUser.ownsUserId(entity.userId)
39
- : entity.userId?.toString() === userId?.toString();
38
+ const isOwned =
39
+ typeof userIdOrUser === 'object' && userIdOrUser?.ownsUserId
40
+ ? userIdOrUser.ownsUserId(entity.userId)
41
+ : entity.userId?.toString() === userId?.toString();
40
42
 
41
43
  if (!isOwned) {
42
44
  throw new Error(
@@ -0,0 +1,16 @@
1
+ /**
2
+ * GetModuleEntityById Use Case
3
+ * Retrieves a module entity by its ID
4
+ */
5
+ class GetModuleEntityById {
6
+ constructor({ moduleRepository }) {
7
+ this.moduleRepository = moduleRepository;
8
+ }
9
+
10
+ async execute(entityId) {
11
+ const entity = await this.moduleRepository.findEntityById(entityId);
12
+ return entity;
13
+ }
14
+ }
15
+
16
+ module.exports = { GetModuleEntityById };