@friggframework/core 2.0.0--canary.690.08cd07e.0 → 2.0.0--canary.693.d8859c2.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 (261) hide show
  1. package/CLAUDE.md +0 -28
  2. package/README.md +0 -105
  3. package/application/commands/integration-commands.js +10 -20
  4. package/application/commands/scheduler-commands.js +7 -11
  5. package/application/commands/user-commands.js +5 -17
  6. package/application/index.js +2 -6
  7. package/core/Worker.js +7 -31
  8. package/core/create-handler.js +0 -9
  9. package/credential/repositories/credential-repository-documentdb.js +0 -34
  10. package/credential/repositories/credential-repository-interface.js +0 -15
  11. package/credential/repositories/credential-repository-mongo.js +0 -23
  12. package/credential/repositories/credential-repository-postgres.js +0 -28
  13. package/database/config.js +24 -48
  14. package/database/documentdb-utils.js +0 -2
  15. package/database/encryption/encryption-schema-registry.js +12 -93
  16. package/database/prisma.js +2 -4
  17. package/errors/index.js +4 -0
  18. package/errors/mapping-already-exists-error.js +14 -0
  19. package/generated/prisma-mongodb/edge.js +38 -33
  20. package/generated/prisma-mongodb/index-browser.js +35 -30
  21. package/generated/prisma-mongodb/index.d.ts +1880 -3191
  22. package/generated/prisma-mongodb/index.js +38 -33
  23. package/generated/prisma-mongodb/package.json +1 -1
  24. package/generated/prisma-mongodb/schema.prisma +50 -86
  25. package/generated/prisma-mongodb/wasm.js +38 -33
  26. package/generated/prisma-postgresql/edge.js +38 -33
  27. package/generated/prisma-postgresql/index-browser.js +35 -30
  28. package/generated/prisma-postgresql/index.d.ts +2050 -3390
  29. package/generated/prisma-postgresql/index.js +38 -33
  30. package/generated/prisma-postgresql/package.json +1 -1
  31. package/generated/prisma-postgresql/schema.prisma +48 -81
  32. package/generated/prisma-postgresql/wasm.js +38 -33
  33. package/handlers/WEBHOOKS.md +29 -0
  34. package/handlers/app-definition-loader.js +19 -128
  35. package/handlers/app-handler-helpers.js +3 -25
  36. package/handlers/backend-utils.js +25 -31
  37. package/handlers/middleware/admin-auth.js +1 -14
  38. package/handlers/routers/auth.js +10 -46
  39. package/handlers/routers/health.js +4 -13
  40. package/handlers/routers/integration-defined-routers.js +92 -144
  41. package/handlers/routers/integration-webhook-routers.js +64 -102
  42. package/handlers/workers/integration-defined-workers.js +2 -5
  43. package/index.js +3 -65
  44. package/infrastructure/scheduler/index.js +6 -19
  45. package/infrastructure/scheduler/scheduler-service-factory.js +7 -55
  46. package/integrations/data-defined/README.md +169 -0
  47. package/integrations/data-defined/canonical.js +11 -0
  48. package/integrations/data-defined/capabilities/acmeerp.capabilities.json +608 -0
  49. package/integrations/data-defined/capabilities/asana.capabilities.json +265 -0
  50. package/integrations/data-defined/capabilities/calendly.capabilities.json +385 -0
  51. package/integrations/data-defined/capabilities/docusign.capabilities.json +314 -0
  52. package/integrations/data-defined/capabilities/fathom.capabilities.json +228 -0
  53. package/integrations/data-defined/capabilities/github.capabilities.json +563 -0
  54. package/integrations/data-defined/capabilities/hubspot.capabilities.json +715 -0
  55. package/integrations/data-defined/capabilities/jira.capabilities.json +376 -0
  56. package/integrations/data-defined/capabilities/klaviyo.capabilities.json +475 -0
  57. package/integrations/data-defined/capabilities/linear.capabilities.json +324 -0
  58. package/integrations/data-defined/capabilities/netsuite.capabilities.json +491 -0
  59. package/integrations/data-defined/capabilities/notion.capabilities.json +208 -0
  60. package/integrations/data-defined/capabilities/phone.capabilities.json +470 -0
  61. package/integrations/data-defined/capabilities/pipedrive.capabilities.json +388 -0
  62. package/integrations/data-defined/capabilities/quickbooks.capabilities.json +615 -0
  63. package/integrations/data-defined/capabilities/salesforce.capabilities.json +507 -0
  64. package/integrations/data-defined/capabilities/shopify.capabilities.json +1082 -0
  65. package/integrations/data-defined/capabilities/slack.capabilities.json +363 -0
  66. package/integrations/data-defined/capabilities/stripe.capabilities.json +580 -0
  67. package/integrations/data-defined/capabilities/webflow.capabilities.json +196 -0
  68. package/integrations/data-defined/capabilities/zendesk.capabilities.json +409 -0
  69. package/integrations/data-defined/compile.js +77 -0
  70. package/integrations/data-defined/config-form.js +110 -0
  71. package/integrations/data-defined/data-defined-integration.js +366 -0
  72. package/integrations/data-defined/examples/acme-erp-quickbooks.definition.json +467 -0
  73. package/integrations/data-defined/examples/asana-slack.definition.json +220 -0
  74. package/integrations/data-defined/examples/calendly-salesforce.definition.json +459 -0
  75. package/integrations/data-defined/examples/docusign-salesforce.definition.json +305 -0
  76. package/integrations/data-defined/examples/fathom-hubspot-notion.definition.json +575 -0
  77. package/integrations/data-defined/examples/github-linear.definition.json +316 -0
  78. package/integrations/data-defined/examples/hubspot-slack.definition.json +409 -0
  79. package/integrations/data-defined/examples/klaviyo-hubspot.definition.json +339 -0
  80. package/integrations/data-defined/examples/phone-hubspot.definition.json +637 -0
  81. package/integrations/data-defined/examples/pipedrive-hubspot.definition.json +136 -0
  82. package/integrations/data-defined/examples/pipedrive-phone.definition.json +340 -0
  83. package/integrations/data-defined/examples/records/phone-hubspot.definition-version.json +652 -0
  84. package/integrations/data-defined/examples/records/phone-hubspot.integration.json +86 -0
  85. package/integrations/data-defined/examples/records/phone-hubspot.mappings.json +39 -0
  86. package/integrations/data-defined/examples/records/phone-hubspot.workflow-run.json +62 -0
  87. package/integrations/data-defined/examples/shopify-klaviyo.definition.json +488 -0
  88. package/integrations/data-defined/examples/shopify-netsuite.definition.json +622 -0
  89. package/integrations/data-defined/examples/stripe-quickbooks.definition.json +476 -0
  90. package/integrations/data-defined/examples/webflow-hubspot-slack.definition.json +262 -0
  91. package/integrations/data-defined/examples/zendesk-jira.definition.json +604 -0
  92. package/integrations/data-defined/expressions.js +225 -0
  93. package/integrations/data-defined/index.js +39 -0
  94. package/integrations/data-defined/interpreter.js +89 -0
  95. package/integrations/data-defined/manifests.js +125 -0
  96. package/integrations/data-defined/repositories/definition-version-repository.js +67 -0
  97. package/integrations/data-defined/repositories/workflow-run-repository.js +59 -0
  98. package/integrations/data-defined/router.js +97 -0
  99. package/integrations/data-defined/runtime.js +208 -0
  100. package/integrations/data-defined/schema/capability-manifest.v0.1.schema.json +377 -0
  101. package/integrations/data-defined/schema/installation.v0.1.schema.json +83 -0
  102. package/integrations/data-defined/schema/integration-definition.v0.1.schema.json +848 -0
  103. package/integrations/data-defined/schema/records.md +98 -0
  104. package/integrations/data-defined/schema-check.js +58 -0
  105. package/integrations/data-defined/scripts/validate.js +66 -0
  106. package/integrations/data-defined/steps.js +89 -0
  107. package/integrations/data-defined/transforms.js +91 -0
  108. package/integrations/data-defined/use-cases/definition-use-cases.js +187 -0
  109. package/integrations/data-defined/validate.js +370 -0
  110. package/integrations/data-defined/versioning.js +161 -0
  111. package/integrations/data-defined/webhooks.js +119 -0
  112. package/integrations/index.js +0 -1
  113. package/integrations/integration-base.js +29 -0
  114. package/integrations/integration-router.js +101 -131
  115. package/integrations/repositories/integration-mapping-repository-documentdb.js +50 -0
  116. package/integrations/repositories/integration-mapping-repository-interface.js +15 -0
  117. package/integrations/repositories/integration-mapping-repository-mongo.js +29 -0
  118. package/integrations/repositories/integration-mapping-repository-postgres.js +30 -0
  119. package/integrations/repositories/integration-mapping-repository.js +29 -0
  120. package/integrations/repositories/integration-repository-documentdb.js +3 -35
  121. package/integrations/repositories/integration-repository-interface.js +1 -17
  122. package/integrations/repositories/integration-repository-mongo.js +77 -60
  123. package/integrations/repositories/integration-repository-postgres.js +76 -62
  124. package/integrations/repositories/process-repository-mongo.js +1 -1
  125. package/integrations/tests/doubles/test-integration-repository.js +1 -12
  126. package/integrations/use-cases/create-integration.js +20 -55
  127. package/integrations/use-cases/get-integrations-for-user.js +40 -80
  128. package/integrations/use-cases/index.js +1 -7
  129. package/integrations/utils/map-integration-dto.js +14 -43
  130. package/modules/index.js +0 -14
  131. package/modules/module-factory.js +0 -12
  132. package/modules/module.js +2 -53
  133. package/modules/repositories/module-repository-documentdb.js +6 -51
  134. package/modules/repositories/module-repository-interface.js +2 -19
  135. package/modules/repositories/module-repository-mongo.js +74 -67
  136. package/modules/repositories/module-repository-postgres.js +74 -66
  137. package/modules/repositories/module-repository.js +1 -2
  138. package/modules/requester/requester.js +5 -35
  139. package/modules/use-cases/get-entities-for-user.js +13 -26
  140. package/modules/use-cases/get-entity-options-by-id.js +4 -10
  141. package/modules/use-cases/get-entity-options-by-type.js +1 -1
  142. package/modules/use-cases/get-module-instance-from-type.js +1 -1
  143. package/modules/use-cases/get-module.js +4 -10
  144. package/modules/use-cases/process-authorization-callback.js +19 -53
  145. package/modules/use-cases/refresh-entity-options.js +4 -10
  146. package/modules/use-cases/test-module-auth.js +4 -10
  147. package/package.json +6 -6
  148. package/prisma-mongodb/schema.prisma +50 -86
  149. package/prisma-postgresql/migrations/20261018000000_add_data_defined_integrations/migration.sql +66 -0
  150. package/prisma-postgresql/schema.prisma +48 -81
  151. package/queues/index.js +0 -25
  152. package/types/index.d.ts +0 -2
  153. package/user/repositories/user-repository-documentdb.js +2 -27
  154. package/user/repositories/user-repository-interface.js +0 -18
  155. package/user/repositories/user-repository-mongo.js +0 -24
  156. package/user/repositories/user-repository-postgres.js +0 -18
  157. package/user/use-cases/authenticate-user.js +1 -7
  158. package/user/use-cases/get-user-from-adopter-jwt.js +124 -95
  159. package/user/use-cases/get-user-from-x-frigg-headers.js +80 -83
  160. package/user/user.js +1 -64
  161. package/credential/use-cases/credential-operations-for-user.js +0 -202
  162. package/credential/use-cases/reauthorize-credential-for-user.js +0 -73
  163. package/database/organization-where.js +0 -18
  164. package/database/unique-violation.js +0 -25
  165. package/extensions/bootstrap-runner.js +0 -43
  166. package/extensions/extension-loader.js +0 -187
  167. package/extensions/index.js +0 -41
  168. package/extensions/initialize-app.js +0 -123
  169. package/extensions/route-mounter.js +0 -103
  170. package/extensions/schema-composer.js +0 -188
  171. package/infrastructure/scheduler/netlify-scheduler-adapter.js +0 -216
  172. package/infrastructure/scheduler/scheduled-job-repository.js +0 -118
  173. package/integrations/repositories/integration-not-found.js +0 -14
  174. package/integrations/schedule-config.js +0 -80
  175. package/integrations/use-cases/dispatch-ongoing-syncs.js +0 -100
  176. package/integrations/use-cases/integration-operations-for-user.js +0 -256
  177. package/integrations/use-cases/resolve-integration-ownership.js +0 -177
  178. package/management-api/create-management-api-router.js +0 -49
  179. package/management-api/error-response.js +0 -116
  180. package/management-api/management-api-config.js +0 -151
  181. package/management-api/meta-router.js +0 -76
  182. package/management-api/openapi/build-openapi.js +0 -144
  183. package/management-api/openapi/components.js +0 -415
  184. package/management-api/openapi/v1-operations.js +0 -83
  185. package/management-api/openapi/v2-operations.js +0 -264
  186. package/management-api/route-registry.js +0 -411
  187. package/management-api/test-support/http-client.js +0 -39
  188. package/management-api/use-cases/get-management-api-meta.js +0 -57
  189. package/management-api/v1-deprecation.js +0 -120
  190. package/management-api/v2/create-v2-router.js +0 -85
  191. package/management-api/v2/dependencies.js +0 -281
  192. package/management-api/v2/dto.js +0 -143
  193. package/management-api/v2/handlers/authorization.js +0 -94
  194. package/management-api/v2/handlers/credentials.js +0 -33
  195. package/management-api/v2/handlers/entities.js +0 -77
  196. package/management-api/v2/handlers/integrations.js +0 -128
  197. package/management-api/v2/handlers/proxy.js +0 -34
  198. package/management-api/v2/request.js +0 -85
  199. package/management-api/v2/test-support/fake-dependencies.js +0 -103
  200. package/modules/domain/authorization-session.js +0 -144
  201. package/modules/entity-scope.js +0 -15
  202. package/modules/entity-type-registry.js +0 -311
  203. package/modules/proxy/proxy-bases.js +0 -169
  204. package/modules/proxy/proxy-errors.js +0 -31
  205. package/modules/proxy/proxy-path.js +0 -183
  206. package/modules/proxy/proxy-policy.js +0 -277
  207. package/modules/repositories/authorization-session-repository-documentdb.js +0 -112
  208. package/modules/repositories/authorization-session-repository-factory.js +0 -31
  209. package/modules/repositories/authorization-session-repository-interface.js +0 -47
  210. package/modules/repositories/authorization-session-repository-prisma.js +0 -87
  211. package/modules/repositories/entity-not-found.js +0 -18
  212. package/modules/requester/proxy-request.js +0 -245
  213. package/modules/scope-config.js +0 -192
  214. package/modules/scope-policy.js +0 -112
  215. package/modules/scope-resolution.js +0 -288
  216. package/modules/scope-visibility.js +0 -247
  217. package/modules/tests/doubles/in-memory-authorization-session-repository.js +0 -45
  218. package/modules/tests/doubles/ownership-matching.js +0 -18
  219. package/modules/use-cases/authorize-entity-for-user.js +0 -61
  220. package/modules/use-cases/create-entity-from-credential.js +0 -113
  221. package/modules/use-cases/entity-operations-for-user.js +0 -162
  222. package/modules/use-cases/entity-types.js +0 -139
  223. package/modules/use-cases/execute-entity-proxy-request.js +0 -331
  224. package/modules/use-cases/get-authorization-requirements.js +0 -100
  225. package/modules/use-cases/get-entity-module-for-user.js +0 -113
  226. package/modules/use-cases/manage-access.js +0 -21
  227. package/modules/use-cases/multi-step-authorization.js +0 -329
  228. package/prisma-postgresql/migrations/20261003000000_add_authorization_session/migration.sql +0 -28
  229. package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +0 -26
  230. package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +0 -25
  231. package/prisma-postgresql/migrations/20261017000000_add_scheduled_job/migration.sql +0 -28
  232. package/providers/index.js +0 -23
  233. package/providers/resolve-provider.js +0 -93
  234. package/providers/runtime-detection.js +0 -124
  235. package/queues/providers/index.js +0 -11
  236. package/queues/providers/netlify-background-provider.js +0 -150
  237. package/queues/providers/qstash-queue-provider.js +0 -219
  238. package/queues/providers/sqs-queue-provider.js +0 -70
  239. package/queues/queue-client-interface.js +0 -55
  240. package/queues/queue-message-auth.js +0 -193
  241. package/queues/queue-provider-factory.js +0 -101
  242. package/queues/queue-provider.js +0 -65
  243. package/queues/sqs-queue-client.js +0 -81
  244. package/types/modules/index.d.ts +0 -214
  245. package/types/user/index.d.ts +0 -134
  246. package/user/adopter-jwt-config.js +0 -177
  247. package/user/adopter-jwt-errors.js +0 -59
  248. package/user/adopter-token-verifier.js +0 -122
  249. package/user/app-user-helpers.js +0 -97
  250. package/user/create-user-authentication.js +0 -64
  251. package/user/membership.js +0 -51
  252. package/user/repositories/organization-membership-record.js +0 -19
  253. package/user/repositories/organization-membership-repository-documentdb.js +0 -106
  254. package/user/repositories/organization-membership-repository-factory.js +0 -42
  255. package/user/repositories/organization-membership-repository-interface.js +0 -57
  256. package/user/repositories/organization-membership-repository-mongo.js +0 -13
  257. package/user/repositories/organization-membership-repository-postgres.js +0 -29
  258. package/user/repositories/organization-membership-repository-prisma.js +0 -81
  259. package/user/tests/doubles/adopter-jwt-fixtures.js +0 -220
  260. package/user/use-cases/ensure-organization-membership.js +0 -92
  261. package/user/use-cases/find-individual-users-by-organization.js +0 -54
@@ -3,7 +3,6 @@ const {
3
3
  IntegrationRepositoryInterface,
4
4
  } = require('./integration-repository-interface');
5
5
  const { validateConfigPatch } = require('./config-patch-shared');
6
- const { integrationNotFoundError } = require('./integration-not-found');
7
6
  const { strictIntId } = require('./report-id');
8
7
 
9
8
  /**
@@ -52,7 +51,6 @@ class IntegrationRepositoryPostgres extends IntegrationRepositoryInterface {
52
51
  ...integration,
53
52
  id: integration.id?.toString(),
54
53
  userId: integration.userId?.toString(),
55
- organizationId: integration.organizationId?.toString() ?? null,
56
54
  entities: integration.entities?.map(e => ({
57
55
  ...e,
58
56
  id: e.id?.toString(),
@@ -62,25 +60,6 @@ class IntegrationRepositoryPostgres extends IntegrationRepositoryInterface {
62
60
  };
63
61
  }
64
62
 
65
- /**
66
- * The one place a converted integration row becomes the record the
67
- * application sees (string ids, ids of its entities, its organization).
68
- * @private
69
- */
70
- _mapIntegration(converted) {
71
- return {
72
- id: converted.id,
73
- entitiesIds: converted.entities.map((e) => e.id),
74
- userId: converted.userId,
75
- organizationId: converted.organizationId,
76
- config: converted.config,
77
- version: converted.version,
78
- status: converted.status,
79
- messages: converted.messages,
80
- createdAt: converted.createdAt,
81
- };
82
- }
83
-
84
63
  /**
85
64
  * Find all integrations for a user
86
65
  *
@@ -99,35 +78,16 @@ class IntegrationRepositoryPostgres extends IntegrationRepositoryInterface {
99
78
  // Map to domain objects with string IDs
100
79
  return integrations.map((integration) => {
101
80
  const converted = this._convertIntegrationIds(integration);
102
- return this._mapIntegration(converted);
103
- });
104
- }
105
-
106
- /**
107
- * Find integrations matching any of the owner/organization pairs, in one
108
- * query. `organizationId: null` matches rows with no organization. The
109
- * caller (a use case) decides which pairs; this only runs them.
110
- *
111
- * @param {Array<{userId: string, organizationId: (string|null)}>} clauses
112
- * @returns {Promise<Array>} Array of integration objects with string IDs
113
- */
114
- async findIntegrationsByOwnership(clauses) {
115
- if (!Array.isArray(clauses) || clauses.length === 0) return [];
116
- const integrations = await this.prisma.integration.findMany({
117
- where: {
118
- OR: clauses.map(({ userId, organizationId }) => ({
119
- userId: this._convertId(userId),
120
- organizationId: this._convertId(organizationId),
121
- })),
122
- },
123
- include: {
124
- entities: true,
125
- },
126
- });
127
-
128
- return integrations.map((integration) => {
129
- const converted = this._convertIntegrationIds(integration);
130
- return this._mapIntegration(converted);
81
+ return {
82
+ id: converted.id,
83
+ entitiesIds: converted.entities.map((e) => e.id),
84
+ userId: converted.userId,
85
+ config: converted.config,
86
+ version: converted.version,
87
+ status: converted.status,
88
+ messages: converted.messages,
89
+ createdAt: converted.createdAt,
90
+ };
131
91
  });
132
92
  }
133
93
 
@@ -157,7 +117,16 @@ class IntegrationRepositoryPostgres extends IntegrationRepositoryInterface {
157
117
 
158
118
  return integrations.map((integration) => {
159
119
  const converted = this._convertIntegrationIds(integration);
160
- return this._mapIntegration(converted);
120
+ return {
121
+ id: converted.id,
122
+ entitiesIds: converted.entities.map((e) => e.id),
123
+ userId: converted.userId,
124
+ config: converted.config,
125
+ version: converted.version,
126
+ status: converted.status,
127
+ messages: converted.messages,
128
+ createdAt: converted.createdAt,
129
+ };
161
130
  });
162
131
  }
163
132
 
@@ -240,7 +209,15 @@ class IntegrationRepositoryPostgres extends IntegrationRepositoryInterface {
240
209
  }
241
210
 
242
211
  const converted = this._convertIntegrationIds(integration);
243
- return this._mapIntegration(converted);
212
+ return {
213
+ id: converted.id,
214
+ entitiesIds: converted.entities.map((e) => e.id),
215
+ userId: converted.userId,
216
+ config: converted.config,
217
+ version: converted.version,
218
+ status: converted.status,
219
+ messages: converted.messages,
220
+ };
244
221
  }
245
222
 
246
223
  /**
@@ -259,11 +236,19 @@ class IntegrationRepositoryPostgres extends IntegrationRepositoryInterface {
259
236
  });
260
237
 
261
238
  if (!integration) {
262
- throw integrationNotFoundError(id);
239
+ throw new Error(`Integration with id ${id} not found`);
263
240
  }
264
241
 
265
242
  const converted = this._convertIntegrationIds(integration);
266
- return this._mapIntegration(converted);
243
+ return {
244
+ id: converted.id,
245
+ entitiesIds: converted.entities.map((e) => e.id),
246
+ userId: converted.userId,
247
+ config: converted.config,
248
+ version: converted.version,
249
+ status: converted.status,
250
+ messages: converted.messages,
251
+ };
267
252
  }
268
253
 
269
254
  /**
@@ -343,15 +328,12 @@ class IntegrationRepositoryPostgres extends IntegrationRepositoryInterface {
343
328
  * @param {Array<string>} entities - Array of entity IDs (strings from application layer)
344
329
  * @param {string} userId - User ID (string from application layer)
345
330
  * @param {Object} config - Integration configuration
346
- * @param {{organizationId?: (string|null)}} [tenancy] - the organization the
347
- * integration is created in (ADR-056 §3.6)
348
331
  * @returns {Promise<Object>} Created integration object with string IDs
349
332
  */
350
- async createIntegration(entities, userId, config, tenancy = {}) {
333
+ async createIntegration(entities, userId, config) {
351
334
  const data = {
352
335
  config,
353
336
  version: '0.0.0',
354
- organizationId: this._convertId(tenancy.organizationId ?? null),
355
337
  };
356
338
 
357
339
  // PostgreSQL: use nested relations with ID conversion
@@ -372,7 +354,15 @@ class IntegrationRepositoryPostgres extends IntegrationRepositoryInterface {
372
354
  });
373
355
 
374
356
  const converted = this._convertIntegrationIds(integration);
375
- return this._mapIntegration(converted);
357
+ return {
358
+ id: converted.id,
359
+ entitiesIds: converted.entities.map((e) => e.id),
360
+ userId: converted.userId,
361
+ config: converted.config,
362
+ version: converted.version,
363
+ status: converted.status,
364
+ messages: converted.messages,
365
+ };
376
366
  }
377
367
 
378
368
  /**
@@ -395,7 +385,15 @@ class IntegrationRepositoryPostgres extends IntegrationRepositoryInterface {
395
385
  }
396
386
 
397
387
  const converted = this._convertIntegrationIds(integration);
398
- return this._mapIntegration(converted);
388
+ return {
389
+ id: converted.id,
390
+ entitiesIds: converted.entities.map((e) => e.id),
391
+ userId: converted.userId,
392
+ config: converted.config,
393
+ version: converted.version,
394
+ status: converted.status,
395
+ messages: converted.messages,
396
+ };
399
397
  }
400
398
 
401
399
  /**
@@ -420,7 +418,15 @@ class IntegrationRepositoryPostgres extends IntegrationRepositoryInterface {
420
418
  });
421
419
 
422
420
  const converted = this._convertIntegrationIds(integration);
423
- return this._mapIntegration(converted);
421
+ return {
422
+ id: converted.id,
423
+ entitiesIds: converted.entities.map((e) => e.id),
424
+ userId: converted.userId,
425
+ config: converted.config,
426
+ version: converted.version,
427
+ status: converted.status,
428
+ messages: converted.messages,
429
+ };
424
430
  }
425
431
 
426
432
  /**
@@ -470,7 +476,15 @@ class IntegrationRepositoryPostgres extends IntegrationRepositoryInterface {
470
476
 
471
477
  return integrations.map((integration) => {
472
478
  const converted = this._convertIntegrationIds(integration);
473
- return this._mapIntegration(converted);
479
+ return {
480
+ id: converted.id,
481
+ entitiesIds: converted.entities.map((e) => e.id),
482
+ userId: converted.userId,
483
+ config: converted.config,
484
+ version: converted.version,
485
+ status: converted.status,
486
+ messages: converted.messages,
487
+ };
474
488
  });
475
489
  }
476
490
  }
@@ -120,7 +120,7 @@ class ProcessRepositoryMongo extends ProcessRepositoryInterface {
120
120
  if (normalized.newState !== null) {
121
121
  $set.state = normalized.newState;
122
122
  }
123
- $set.updatedAt = new Date();
123
+ $set.updatedAt = { $date: new Date().toISOString() };
124
124
  update.$set = $set;
125
125
 
126
126
  if (Object.keys(normalized.pushSlice).length > 0) {
@@ -1,6 +1,5 @@
1
1
  const { v4: uuid } = require('uuid');
2
2
  const { validateConfigPatch } = require('../../repositories/config-patch-shared');
3
- const { matchesAnyClause } = require('../../../modules/tests/doubles/ownership-matching');
4
3
 
5
4
  class TestIntegrationRepository {
6
5
  constructor() {
@@ -8,14 +7,13 @@ class TestIntegrationRepository {
8
7
  this.operationHistory = [];
9
8
  }
10
9
 
11
- async createIntegration(entities, userId, config, tenancy = {}) {
10
+ async createIntegration(entities, userId, config) {
12
11
  const id = uuid();
13
12
  const record = {
14
13
  id,
15
14
  _id: id,
16
15
  entitiesIds: entities,
17
16
  userId: userId,
18
- organizationId: tenancy.organizationId ?? null,
19
17
  config,
20
18
  version: '0.0.0',
21
19
  status: 'IN_CREATION',
@@ -40,15 +38,6 @@ class TestIntegrationRepository {
40
38
  return results;
41
39
  }
42
40
 
43
- // Answers the way a database does for the clauses a use case hands it.
44
- async findIntegrationsByOwnership(clauses) {
45
- const results = Array.from(this.store.values()).filter((r) =>
46
- matchesAnyClause(r, clauses)
47
- );
48
- this.operationHistory.push({ operation: 'findByOwnership', clauses, count: results.length });
49
- return results;
50
- }
51
-
52
41
  async findIntegrations(filter = {}) {
53
42
  const { type, status } = filter;
54
43
  const results = Array.from(this.store.values()).filter((r) => {
@@ -27,19 +27,13 @@ class CreateIntegration {
27
27
  * and entity set, instead of creating a duplicate.
28
28
  * @async
29
29
  * @param {string[]} entities - Array of entity IDs to associate with the integration.
30
- * @param {string} userId - ID of the OWNER of the integration (ADR-056 §3):
31
- * the person or the organization's user, as ResolveIntegrationOwnership decided.
30
+ * @param {string} userId - ID of the user creating the integration.
32
31
  * @param {Object} config - Configuration object for the integration.
33
32
  * @param {string} config.type - Type of integration to create.
34
- * @param {{organizationId?: (string|null), adoptUnscoped?: boolean}} [tenancy]
35
- * the organization it is created in (§3.6). A duplicate is only reused in
36
- * the same organization, so one person's integration in organization A is
37
- * not handed back as the one they asked for in B. `adoptUnscoped` also
38
- * accepts a duplicate written before scoping (no organization).
39
33
  * @returns {Promise<Object>} The created or reused integration DTO.
40
34
  * @throws {Error} When integration class is not found for the specified type.
41
35
  */
42
- async execute(entities, userId, config, tenancy = {}) {
36
+ async execute(entities, userId, config) {
43
37
  console.log(
44
38
  `[Frigg] Creating ${config?.type} integration for user ${userId} with entities [${entities}]`
45
39
  );
@@ -47,8 +41,7 @@ class CreateIntegration {
47
41
  const existing = await this._findDuplicate(
48
42
  userId,
49
43
  config?.type,
50
- entities,
51
- tenancy
44
+ entities
52
45
  );
53
46
  if (existing) {
54
47
  console.log(
@@ -57,18 +50,11 @@ class CreateIntegration {
57
50
  return this._reuseExisting(existing, config);
58
51
  }
59
52
 
60
- // Everything that can be refused is refused before a row is written: an
61
- // unknown type, or an entity the owner cannot load, would otherwise
62
- // leave an IN_CREATION integration behind that nothing moves on.
63
- const integrationClass = this._requireClass(config?.type);
64
- const modules = await this._loadModules(entities, userId);
65
-
66
53
  const integrationRecord =
67
54
  await this.integrationRepository.createIntegration(
68
55
  entities,
69
56
  userId,
70
- config,
71
- { organizationId: tenancy.organizationId ?? null }
57
+ config
72
58
  );
73
59
  console.log(
74
60
  `[Frigg] Created integration ${integrationRecord.id} for user ${userId}`
@@ -79,8 +65,7 @@ class CreateIntegration {
79
65
  const survivor = await this._findDuplicate(
80
66
  userId,
81
67
  config?.type,
82
- entities,
83
- tenancy
68
+ entities
84
69
  );
85
70
  if (survivor && String(survivor.id) !== String(integrationRecord.id)) {
86
71
  console.log(
@@ -93,8 +78,7 @@ class CreateIntegration {
93
78
  }
94
79
 
95
80
  const integrationInstance = await this._buildInstance(
96
- integrationRecord,
97
- { integrationClass, modules }
81
+ integrationRecord
98
82
  );
99
83
  console.log(
100
84
  `[Frigg] Sending ON_CREATE for integration ${integrationRecord.id}`
@@ -151,7 +135,7 @@ class CreateIntegration {
151
135
  return mapIntegrationClassToIntegrationDTO(integrationInstance);
152
136
  }
153
137
 
154
- async _findDuplicate(userId, type, entityIds, tenancy = {}) {
138
+ async _findDuplicate(userId, type, entityIds) {
155
139
  const candidates =
156
140
  await this.integrationRepository.findIntegrationsByUserId(userId);
157
141
  const target = [...(entityIds ?? [])].map(String).sort();
@@ -166,7 +150,6 @@ class CreateIntegration {
166
150
  // matching how teardown failures are already handled here.
167
151
  if (integration.status === 'IN_DELETION') return false;
168
152
  if (integration.config?.type !== type) return false;
169
- if (!this._sameOrganization(integration, tenancy)) return false;
170
153
  const current = [...(integration.entitiesIds ?? [])]
171
154
  .map(String)
172
155
  .sort();
@@ -179,15 +162,6 @@ class CreateIntegration {
179
162
  return matches.length ? this._pickOldest(matches) : null;
180
163
  }
181
164
 
182
- /** Same organization, or a row from before scoping that belongs here. */
183
- _sameOrganization(integration, tenancy) {
184
- const wanted = tenancy.organizationId ?? null;
185
- const has = integration.organizationId ?? null;
186
- if (wanted === null && has === null) return true;
187
- if (wanted !== null && has !== null) return String(wanted) === String(has);
188
- return has === null && tenancy.adoptUnscoped === true;
189
- }
190
-
191
165
  _pickOldest(records) {
192
166
  return [...records].sort((a, b) => {
193
167
  const diff =
@@ -199,36 +173,27 @@ class CreateIntegration {
199
173
  })[0];
200
174
  }
201
175
 
202
- _requireClass(type) {
176
+ async _buildInstance(integrationRecord) {
203
177
  const integrationClass = this.integrationClasses.find(
204
- (integrationClass) => integrationClass.Definition.name === type
178
+ (integrationClass) =>
179
+ integrationClass.Definition.name ===
180
+ integrationRecord.config.type
205
181
  );
182
+
206
183
  if (!integrationClass) {
207
- throw new Error(`No integration class found for type: ${type}`);
184
+ throw new Error(
185
+ `No integration class found for type: ${integrationRecord.config.type}`
186
+ );
208
187
  }
209
- return integrationClass;
210
- }
211
188
 
212
- async _loadModules(entityIds, userId) {
213
189
  const modules = [];
214
- for (const entityId of entityIds) {
215
- modules.push(
216
- await this.moduleFactory.getModuleInstance(entityId, userId)
190
+ for (const entityId of integrationRecord.entitiesIds) {
191
+ const moduleInstance = await this.moduleFactory.getModuleInstance(
192
+ entityId,
193
+ integrationRecord.userId
217
194
  );
195
+ modules.push(moduleInstance);
218
196
  }
219
- return modules;
220
- }
221
-
222
- async _buildInstance(integrationRecord, preloaded) {
223
- const integrationClass =
224
- preloaded?.integrationClass ??
225
- this._requireClass(integrationRecord.config.type);
226
- const modules =
227
- preloaded?.modules ??
228
- (await this._loadModules(
229
- integrationRecord.entitiesIds,
230
- integrationRecord.userId
231
- ));
232
197
 
233
198
  const integrationInstance = new integrationClass({
234
199
  id: integrationRecord.id,
@@ -2,17 +2,9 @@
2
2
  const {
3
3
  mapIntegrationClassToIntegrationDTO,
4
4
  } = require('../utils/map-integration-dto');
5
- const {
6
- describeRecord,
7
- visibilityClauses,
8
- matchesListFilters,
9
- } = require('../../modules/scope-visibility');
10
- const { DEFAULT_SCOPE_POLICY } = require('../../modules/scope-policy');
11
5
 
12
6
  /**
13
- * Use case for retrieving the integrations a caller may see: the ones they own
14
- * or their active organization owns, created in that organization (ADR-056
15
- * §3.6), each with `tenancy` ({scope, owner, organizationId, access}).
7
+ * Use case for retrieving all integrations for a specific user.
16
8
  * @class GetIntegrationsForUser
17
9
  */
18
10
  class GetIntegrationsForUser {
@@ -23,16 +15,13 @@ class GetIntegrationsForUser {
23
15
  * @param {Array<import('../integration').Integration>} params.integrationClasses - Array of available integration classes.
24
16
  * @param {import('../../modules/module-factory').ModuleFactory} params.moduleFactory - Service for module instantiation and management.
25
17
  * @param {import('../../modules/repositories/module-repository-interface').ModuleRepositoryInterface} params.moduleRepository - Repository for module and entity data operations.
26
- * @param {Object} [params.scopePolicy] - from createScopePolicy
27
18
  */
28
19
  constructor({
29
20
  integrationRepository,
30
21
  integrationClasses,
31
22
  moduleFactory,
32
23
  moduleRepository,
33
- scopePolicy,
34
24
  }) {
35
- this.scopePolicy = scopePolicy || DEFAULT_SCOPE_POLICY;
36
25
  /**
37
26
  * @type {import('../repositories/integration-repository-interface').IntegrationRepositoryInterface}
38
27
  */
@@ -43,86 +32,57 @@ class GetIntegrationsForUser {
43
32
  }
44
33
 
45
34
  /**
35
+ * Executes the retrieval of all integrations for a user.
46
36
  * @async
47
- * @param {Object} user - authenticated User
48
- * @param {{scope?: string, owner?: string}} [filters] - `?scope=` and `?owner=`
49
- * @returns {Promise<Object[]>} Array of integration DTOs.
37
+ * @param {string} userId - ID of the user whose integrations to retrieve.
38
+ * @returns {Promise<Object[]>} Array of integration DTOs for the specified user.
50
39
  */
51
- async execute(user, filters = {}) {
52
- const ctx = this.scopePolicy.accessContext(user);
53
- const records = await this.integrationRepository.findIntegrationsByOwnership(
54
- visibilityClauses(ctx, 'integration')
55
- );
40
+ async execute(userId) {
41
+ const integrationRecords =
42
+ await this.integrationRepository.findIntegrationsByUserId(userId);
56
43
 
57
44
  const integrations = [];
58
- for (const record of records) {
59
- // The query is the filter; the same rule again means a repository
60
- // that returns too much cannot widen the list.
61
- const tenancy = describeRecord(record, ctx, 'integration');
62
- if (!tenancy || !matchesListFilters(tenancy, filters)) continue;
63
- integrations.push({ ...(await this.toDto(record)), tenancy });
64
- }
65
- return integrations;
66
- }
67
45
 
68
- /** Hydrates one stored integration record into its DTO. */
69
- async toDto(integrationRecord) {
70
- const entities = await this.moduleRepository.findEntitiesByIds(
71
- integrationRecord.entitiesIds
72
- );
73
-
74
- const integrationClass = this.integrationClasses.find(
75
- (integrationClass) =>
76
- integrationClass.Definition.name ===
77
- integrationRecord.config.type
78
- );
46
+ for (const integrationRecord of integrationRecords) {
47
+ const entities = await this.moduleRepository.findEntitiesByIds(
48
+ integrationRecord.entitiesIds
49
+ );
79
50
 
80
- const modules = [];
81
- for (const entity of entities) {
82
- const moduleInstance = await this.moduleFactory.getModuleInstance(
83
- entity.id,
84
- integrationRecord.userId
51
+ const integrationClass = this.integrationClasses.find(
52
+ (integrationClass) =>
53
+ integrationClass.Definition.name ===
54
+ integrationRecord.config.type
85
55
  );
86
- modules.push(moduleInstance);
87
- }
88
56
 
89
- return mapIntegrationClassToIntegrationDTO({
90
- id: integrationRecord.id,
91
- userId: integrationRecord.userId,
92
- entities: entities,
93
- config: integrationRecord.config,
94
- status: integrationRecord.status,
95
- version: integrationRecord.version,
96
- messages: integrationRecord.messages || { errors: [], warnings: [] },
97
- modules,
98
- options: integrationClass.getOptionDetails(),
99
- });
100
- }
101
- }
57
+ const modules = [];
58
+ for (const entity of entities) {
59
+ const moduleInstance =
60
+ await this.moduleFactory.getModuleInstance(
61
+ entity.id,
62
+ integrationRecord.userId
63
+ );
64
+ modules.push(moduleInstance);
65
+ }
102
66
 
103
- /**
104
- * Every integration one user id owns, with no organization rules: the lookup
105
- * behind the app-level `friggCommands.findIntegrationsByUserId`, which has a
106
- * user id and no request.
107
- */
108
- class GetIntegrationsByUserId {
109
- /** @param {ConstructorParameters<typeof GetIntegrationsForUser>[0]} params */
110
- constructor(params) {
111
- this.integrationRepository = params.integrationRepository;
112
- this.hydrator = new GetIntegrationsForUser(params);
113
- }
67
+ const integrationData = {
68
+ id: integrationRecord.id,
69
+ userId: integrationRecord.userId,
70
+ entities: entities,
71
+ config: integrationRecord.config,
72
+ status: integrationRecord.status,
73
+ version: integrationRecord.version,
74
+ messages: integrationRecord.messages || { errors: [], warnings: [] },
75
+ modules,
76
+ options: integrationClass.getOptionDetails(),
77
+ };
114
78
 
115
- /** @param {string} userId */
116
- async execute(userId) {
117
- const records = await this.integrationRepository.findIntegrationsByUserId(
118
- userId
119
- );
120
- const integrations = [];
121
- for (const record of records) {
122
- integrations.push(await this.hydrator.toDto(record));
79
+ integrations.push(
80
+ mapIntegrationClassToIntegrationDTO(integrationData)
81
+ );
123
82
  }
83
+
124
84
  return integrations;
125
85
  }
126
86
  }
127
87
 
128
- module.exports = { GetIntegrationsForUser, GetIntegrationsByUserId };
88
+ module.exports = { GetIntegrationsForUser };
@@ -1,7 +1,4 @@
1
- const {
2
- GetIntegrationsForUser,
3
- GetIntegrationsByUserId,
4
- } = require('./get-integrations-for-user');
1
+ const { GetIntegrationsForUser } = require('./get-integrations-for-user');
5
2
  const { DeleteIntegrationForUser } = require('./delete-integration-for-user');
6
3
  const { CreateIntegration } = require('./create-integration');
7
4
  const { GetIntegration } = require('./get-integration');
@@ -9,11 +6,9 @@ const { CreateProcess } = require('./create-process');
9
6
  const { UpdateProcessState } = require('./update-process-state');
10
7
  const { UpdateProcessMetrics } = require('./update-process-metrics');
11
8
  const { GetProcess } = require('./get-process');
12
- const { DispatchOngoingSyncs } = require('./dispatch-ongoing-syncs');
13
9
 
14
10
  module.exports = {
15
11
  GetIntegrationsForUser,
16
- GetIntegrationsByUserId,
17
12
  DeleteIntegrationForUser,
18
13
  CreateIntegration,
19
14
  GetIntegration,
@@ -21,5 +16,4 @@ module.exports = {
21
16
  UpdateProcessState,
22
17
  UpdateProcessMetrics,
23
18
  GetProcess,
24
- DispatchOngoingSyncs,
25
19
  };
@@ -19,48 +19,19 @@ function mapIntegrationClassToIntegrationDTO(integration) {
19
19
  };
20
20
  }
21
21
 
22
- let deprecationLogged = false;
23
22
 
24
- /**
25
- * @deprecated Since ADR-059. Read `entityTypeRegistry` / `moduleDefinitions`
26
- * from `loadAppDefinition()`, or call `getEntityTypeRegistry(appDefinition)`
27
- * and read each entry's `definition`.
28
- *
29
- * Kept as a wrapper over the entity type registry so callers that imported it
30
- * keep working. It returns the registry's definitions, so it follows the
31
- * registry's rules: one definition per entity type name (the first wins when
32
- * two integrations bind different definitions under one name, as
33
- * ModuleFactory already picked), and, when `entities` is passed, registry-only
34
- * types are included and conflicts throw `EntityRegistryError`.
35
- *
36
- * @param {Array<Function>} integrationClasses - integration classes
37
- * @param {Object} [entities] - `appDefinition.entities` (ADR-059)
38
- * @returns {Array<Object>} API module definitions
39
- */
40
- function getModulesDefinitionFromIntegrationClasses(
41
- integrationClasses,
42
- entities
43
- ) {
44
- // Required here, not at the top: modules/ must not load at require time
45
- // of every integration use case that only wants the DTO mapper.
46
- const {
47
- getEntityTypeRegistry,
48
- getEntityTypeDefinitions,
49
- } = require('../../modules/entity-type-registry');
50
- if (!deprecationLogged) {
51
- deprecationLogged = true;
52
- const { getLogger } = require('../../logs');
53
- getLogger('frigg.integrations').warn(
54
- 'getModulesDefinitionFromIntegrationClasses is deprecated; use getEntityTypeRegistry (ADR-059)',
55
- { eventName: 'frigg.integrations.deprecated_module_definitions' }
56
- );
57
- }
58
- return getEntityTypeDefinitions(
59
- getEntityTypeRegistry({ integrations: integrationClasses, entities })
60
- );
61
- }
62
-
63
- module.exports = {
64
- mapIntegrationClassToIntegrationDTO,
65
- getModulesDefinitionFromIntegrationClasses,
23
+ const getModulesDefinitionFromIntegrationClasses = (integrationClasses) => {
24
+ return [
25
+ ...new Set(
26
+ integrationClasses
27
+ .map((integration) =>
28
+ Object.values(integration.Definition.modules).map(
29
+ (module) => module.definition
30
+ )
31
+ )
32
+ .flat()
33
+ ),
34
+ ];
66
35
  };
36
+
37
+ module.exports = { mapIntegrationClassToIntegrationDTO, getModulesDefinitionFromIntegrationClasses };