@friggframework/core 2.0.0--canary.684.0da024b.0 → 2.0.0--canary.679.a2bcc2f.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 (104) hide show
  1. package/CLAUDE.md +0 -2
  2. package/README.md +0 -105
  3. package/application/commands/integration-commands.js +2 -2
  4. package/application/commands/user-commands.js +5 -17
  5. package/credential/use-cases/credential-operations-for-user.js +25 -110
  6. package/credential/use-cases/reauthorize-credential-for-user.js +8 -15
  7. package/database/documentdb-utils.js +1 -6
  8. package/generated/prisma-mongodb/edge.js +4 -15
  9. package/generated/prisma-mongodb/index-browser.js +1 -12
  10. package/generated/prisma-mongodb/index.d.ts +86 -2027
  11. package/generated/prisma-mongodb/index.js +4 -15
  12. package/generated/prisma-mongodb/package.json +1 -1
  13. package/generated/prisma-mongodb/schema.prisma +0 -47
  14. package/generated/prisma-mongodb/wasm.js +4 -15
  15. package/generated/prisma-postgresql/edge.js +4 -15
  16. package/generated/prisma-postgresql/index-browser.js +1 -12
  17. package/generated/prisma-postgresql/index.d.ts +63 -2162
  18. package/generated/prisma-postgresql/index.js +4 -15
  19. package/generated/prisma-postgresql/package.json +1 -1
  20. package/generated/prisma-postgresql/schema.prisma +0 -44
  21. package/generated/prisma-postgresql/wasm.js +4 -15
  22. package/handlers/app-definition-loader.js +1 -15
  23. package/index.js +0 -18
  24. package/integrations/integration-router.js +73 -115
  25. package/integrations/repositories/integration-repository-documentdb.js +3 -35
  26. package/integrations/repositories/integration-repository-interface.js +1 -17
  27. package/integrations/repositories/integration-repository-mongo.js +76 -59
  28. package/integrations/repositories/integration-repository-postgres.js +76 -62
  29. package/integrations/tests/doubles/test-integration-repository.js +1 -12
  30. package/integrations/use-cases/create-integration.js +20 -55
  31. package/integrations/use-cases/get-integrations-for-user.js +40 -80
  32. package/integrations/use-cases/index.js +1 -5
  33. package/integrations/use-cases/integration-operations-for-user.js +24 -55
  34. package/management-api/management-api-config.js +0 -3
  35. package/management-api/openapi/components.js +0 -40
  36. package/management-api/openapi/v2-operations.js +1 -13
  37. package/management-api/v2/dependencies.js +15 -40
  38. package/management-api/v2/dto.js +0 -20
  39. package/management-api/v2/handlers/authorization.js +0 -1
  40. package/management-api/v2/handlers/credentials.js +2 -6
  41. package/management-api/v2/handlers/entities.js +7 -10
  42. package/management-api/v2/handlers/integrations.js +3 -6
  43. package/management-api/v2/request.js +1 -32
  44. package/modules/index.js +0 -8
  45. package/modules/repositories/module-repository-documentdb.js +4 -48
  46. package/modules/repositories/module-repository-interface.js +2 -19
  47. package/modules/repositories/module-repository-mongo.js +73 -65
  48. package/modules/repositories/module-repository-postgres.js +73 -64
  49. package/modules/use-cases/entity-operations-for-user.js +13 -50
  50. package/modules/use-cases/entity-types.js +7 -31
  51. package/modules/use-cases/execute-entity-proxy-request.js +0 -15
  52. package/modules/use-cases/get-entities-for-user.js +5 -16
  53. package/modules/use-cases/get-entity-module-for-user.js +3 -23
  54. package/modules/use-cases/get-entity-options-by-id.js +3 -9
  55. package/modules/use-cases/get-module.js +3 -9
  56. package/modules/use-cases/multi-step-authorization.js +12 -39
  57. package/modules/use-cases/process-authorization-callback.js +18 -52
  58. package/modules/use-cases/refresh-entity-options.js +3 -9
  59. package/modules/use-cases/test-module-auth.js +3 -9
  60. package/package.json +5 -6
  61. package/prisma-mongodb/schema.prisma +0 -47
  62. package/prisma-postgresql/schema.prisma +0 -44
  63. package/types/index.d.ts +0 -1
  64. package/types/modules/index.d.ts +0 -87
  65. package/user/repositories/user-repository-documentdb.js +2 -27
  66. package/user/repositories/user-repository-interface.js +0 -18
  67. package/user/repositories/user-repository-mongo.js +0 -24
  68. package/user/repositories/user-repository-postgres.js +0 -18
  69. package/user/use-cases/authenticate-user.js +1 -7
  70. package/user/use-cases/get-user-from-adopter-jwt.js +124 -95
  71. package/user/use-cases/get-user-from-x-frigg-headers.js +80 -83
  72. package/user/user.js +1 -64
  73. package/database/organization-where.js +0 -18
  74. package/database/unique-violation.js +0 -25
  75. package/integrations/repositories/integration-not-found.js +0 -14
  76. package/integrations/use-cases/resolve-integration-ownership.js +0 -177
  77. package/modules/entity-scope.js +0 -15
  78. package/modules/scope-config.js +0 -192
  79. package/modules/scope-policy.js +0 -112
  80. package/modules/scope-resolution.js +0 -288
  81. package/modules/scope-visibility.js +0 -239
  82. package/modules/tests/doubles/ownership-matching.js +0 -18
  83. package/modules/use-cases/authorize-entity-for-user.js +0 -61
  84. package/modules/use-cases/create-entity-from-credential.js +0 -113
  85. package/modules/use-cases/manage-access.js +0 -21
  86. package/prisma-postgresql/migrations/20261015000000_add_organization_membership/migration.sql +0 -26
  87. package/prisma-postgresql/migrations/20261016000000_add_entity_integration_organization/migration.sql +0 -25
  88. package/types/user/index.d.ts +0 -134
  89. package/user/adopter-jwt-config.js +0 -177
  90. package/user/adopter-jwt-errors.js +0 -59
  91. package/user/adopter-token-verifier.js +0 -122
  92. package/user/app-user-helpers.js +0 -97
  93. package/user/create-user-authentication.js +0 -64
  94. package/user/membership.js +0 -51
  95. package/user/repositories/organization-membership-record.js +0 -19
  96. package/user/repositories/organization-membership-repository-documentdb.js +0 -106
  97. package/user/repositories/organization-membership-repository-factory.js +0 -42
  98. package/user/repositories/organization-membership-repository-interface.js +0 -57
  99. package/user/repositories/organization-membership-repository-mongo.js +0 -13
  100. package/user/repositories/organization-membership-repository-postgres.js +0 -29
  101. package/user/repositories/organization-membership-repository-prisma.js +0 -81
  102. package/user/tests/doubles/adopter-jwt-fixtures.js +0 -220
  103. package/user/use-cases/ensure-organization-membership.js +0 -92
  104. package/user/use-cases/find-individual-users-by-organization.js +0 -54
@@ -1,10 +1,8 @@
1
1
  const { prisma } = require('../../database/prisma');
2
- const { organizationWhere } = require('../../database/organization-where');
3
2
  const {
4
3
  IntegrationRepositoryInterface,
5
4
  } = require('./integration-repository-interface');
6
5
  const { validateConfigPatch } = require('./config-patch-shared');
7
- const { integrationNotFoundError } = require('./integration-not-found');
8
6
 
9
7
  /**
10
8
  * MongoDB Integration Repository Adapter
@@ -27,25 +25,6 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
27
25
  this.prisma = prisma;
28
26
  }
29
27
 
30
- /**
31
- * The one place a integration integration row becomes the record the
32
- * application sees (ids of its entities, its organization).
33
- * @private
34
- */
35
- _mapIntegration(integration) {
36
- return {
37
- id: integration.id,
38
- entitiesIds: integration.entities.map((e) => e.id),
39
- userId: integration.userId,
40
- organizationId: integration.organizationId ?? null,
41
- config: integration.config,
42
- version: integration.version,
43
- status: integration.status,
44
- messages: integration.messages,
45
- createdAt: integration.createdAt,
46
- };
47
- }
48
-
49
28
  /**
50
29
  * Find all integrations for a user
51
30
  * Replaces: IntegrationModel.find({ user: userId }).populate('entities')
@@ -62,32 +41,16 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
62
41
  });
63
42
 
64
43
  // Map to domain objects (maintains same API)
65
- return integrations.map((integration) => (this._mapIntegration(integration)));
66
- }
67
-
68
- /**
69
- * Find integrations matching any of the owner/organization pairs, in one
70
- * query. `organizationId: null` matches rows with no organization. The
71
- * caller (a use case) decides which pairs; this only runs them.
72
- *
73
- * @param {Array<{userId: string, organizationId: (string|null)}>} clauses
74
- * @returns {Promise<Array>} Array of integration objects
75
- */
76
- async findIntegrationsByOwnership(clauses) {
77
- if (!Array.isArray(clauses) || clauses.length === 0) return [];
78
- const integrations = await this.prisma.integration.findMany({
79
- where: {
80
- OR: clauses.map(({ userId, organizationId }) => ({
81
- userId,
82
- ...organizationWhere(organizationId),
83
- })),
84
- },
85
- include: {
86
- entities: true,
87
- },
88
- });
89
-
90
- return integrations.map((integration) => (this._mapIntegration(integration)));
44
+ return integrations.map((integration) => ({
45
+ id: integration.id,
46
+ entitiesIds: integration.entities.map((e) => e.id),
47
+ userId: integration.userId,
48
+ config: integration.config,
49
+ version: integration.version,
50
+ status: integration.status,
51
+ messages: integration.messages,
52
+ createdAt: integration.createdAt,
53
+ }));
91
54
  }
92
55
 
93
56
  /**
@@ -114,7 +77,16 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
114
77
  },
115
78
  });
116
79
 
117
- return integrations.map((integration) => (this._mapIntegration(integration)));
80
+ return integrations.map((integration) => ({
81
+ id: integration.id,
82
+ entitiesIds: integration.entities.map((e) => e.id),
83
+ userId: integration.userId,
84
+ config: integration.config,
85
+ version: integration.version,
86
+ status: integration.status,
87
+ messages: integration.messages,
88
+ createdAt: integration.createdAt,
89
+ }));
118
90
  }
119
91
 
120
92
  /**
@@ -193,7 +165,15 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
193
165
  throw new Error(`Integration with name ${name} not found`);
194
166
  }
195
167
 
196
- return this._mapIntegration(integration);
168
+ return {
169
+ id: integration.id,
170
+ entitiesIds: integration.entities.map((e) => e.id),
171
+ userId: integration.userId,
172
+ config: integration.config,
173
+ version: integration.version,
174
+ status: integration.status,
175
+ messages: integration.messages,
176
+ };
197
177
  }
198
178
 
199
179
  /**
@@ -212,10 +192,18 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
212
192
  });
213
193
 
214
194
  if (!integration) {
215
- throw integrationNotFoundError(id);
195
+ throw new Error(`Integration with id ${id} not found`);
216
196
  }
217
197
 
218
- return this._mapIntegration(integration);
198
+ return {
199
+ id: integration.id,
200
+ entitiesIds: integration.entities.map((e) => e.id),
201
+ userId: integration.userId,
202
+ config: integration.config,
203
+ version: integration.version,
204
+ status: integration.status,
205
+ messages: integration.messages,
206
+ };
219
207
  }
220
208
 
221
209
  /**
@@ -302,7 +290,15 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
302
290
  },
303
291
  });
304
292
 
305
- return integrations.map((integration) => (this._mapIntegration(integration)));
293
+ return integrations.map((integration) => ({
294
+ id: integration.id,
295
+ entitiesIds: integration.entities.map((e) => e.id),
296
+ userId: integration.userId,
297
+ config: integration.config,
298
+ version: integration.version,
299
+ status: integration.status,
300
+ messages: integration.messages,
301
+ }));
306
302
  }
307
303
 
308
304
  /**
@@ -314,16 +310,13 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
314
310
  * @param {Array<string>} entities - Array of entity IDs (MongoDB ObjectIds)
315
311
  * @param {string} userId - User ID (MongoDB ObjectId)
316
312
  * @param {Object} config - Integration configuration
317
- * @param {{organizationId?: (string|null)}} [tenancy] - the organization the
318
- * integration is created in (ADR-056 §3.6)
319
313
  * @returns {Promise<Object>} Created integration object
320
314
  */
321
- async createIntegration(entities, userId, config, tenancy = {}) {
315
+ async createIntegration(entities, userId, config) {
322
316
  const data = {
323
317
  config,
324
318
  version: '0.0.0',
325
319
  userId: userId,
326
- organizationId: tenancy.organizationId ?? null,
327
320
  entityIds: entities,
328
321
  };
329
322
 
@@ -334,7 +327,15 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
334
327
  },
335
328
  });
336
329
 
337
- return this._mapIntegration(integration);
330
+ return {
331
+ id: integration.id,
332
+ entitiesIds: integration.entities.map((e) => e.id),
333
+ userId: integration.userId,
334
+ config: integration.config,
335
+ version: integration.version,
336
+ status: integration.status,
337
+ messages: integration.messages,
338
+ };
338
339
  }
339
340
 
340
341
  /**
@@ -356,7 +357,15 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
356
357
  return null;
357
358
  }
358
359
 
359
- return this._mapIntegration(integration);
360
+ return {
361
+ id: integration.id,
362
+ entitiesIds: integration.entities.map((e) => e.id),
363
+ userId: integration.userId,
364
+ config: integration.config,
365
+ version: integration.version,
366
+ status: integration.status,
367
+ messages: integration.messages,
368
+ };
360
369
  }
361
370
 
362
371
  /**
@@ -380,7 +389,15 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
380
389
  },
381
390
  });
382
391
 
383
- return this._mapIntegration(integration);
392
+ return {
393
+ id: integration.id,
394
+ entitiesIds: integration.entities.map((e) => e.id),
395
+ userId: integration.userId,
396
+ config: integration.config,
397
+ version: integration.version,
398
+ status: integration.status,
399
+ messages: integration.messages,
400
+ };
384
401
  }
385
402
 
386
403
  /**
@@ -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
  }
@@ -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,