@friggframework/core 2.0.0-next.55 → 2.0.0-next.57

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.
@@ -203,22 +203,53 @@ class IntegrationBase {
203
203
 
204
204
  /**
205
205
  * Returns the modules as object with keys as module names.
206
+ * Uses the keys from Definition.modules to attach modules correctly.
207
+ *
208
+ * Example:
209
+ * Definition.modules = { attio: {...}, quo: { definition: { getName: () => 'quo-attio' } } }
210
+ * Module with getName()='quo-attio' gets attached as this.quo (not this['quo-attio'])
211
+ *
206
212
  * @private
207
213
  * @param {Array} integrationModules - Array of module instances
208
214
  * @returns {Object} The modules object
209
215
  */
210
216
  _appendModules(integrationModules) {
211
217
  const modules = {};
218
+
219
+ // Build reverse mapping: definition.getName() → referenceKey
220
+ // e.g., 'quo-attio' → 'quo', 'attio' → 'attio'
221
+ const moduleNameToKey = {};
222
+ if (this.constructor.Definition?.modules) {
223
+ for (const [key, moduleConfig] of Object.entries(this.constructor.Definition.modules)) {
224
+ const definition = moduleConfig.definition;
225
+ if (definition) {
226
+ // Use getName() if available, fallback to moduleName
227
+ const definitionName = typeof definition.getName === 'function'
228
+ ? definition.getName()
229
+ : definition.moduleName;
230
+ if (definitionName) {
231
+ moduleNameToKey[definitionName] = key;
232
+ }
233
+ }
234
+ }
235
+ }
236
+
212
237
  for (const module of integrationModules) {
213
- const key =
238
+ const moduleName =
214
239
  typeof module.getName === 'function'
215
240
  ? module.getName()
216
241
  : module.name;
242
+
243
+ // Use the reference key from Definition.modules if available,
244
+ // otherwise fall back to moduleName
245
+ const key = moduleNameToKey[moduleName] || moduleName;
246
+
217
247
  if (key) {
218
248
  modules[key] = module;
219
249
  this[key] = module;
220
250
  }
221
251
  }
252
+
222
253
  return modules;
223
254
  }
224
255
 
@@ -333,7 +364,6 @@ class IntegrationBase {
333
364
  return {};
334
365
  }
335
366
  async loadUserActions({ actionType } = {}) {
336
- console.log('loadUserActions called with actionType:', actionType);
337
367
  const userActions = {};
338
368
  for (const [key, event] of Object.entries(this.events)) {
339
369
  if (event.type === constantsToBeMigrated.types.USER_ACTION) {
@@ -389,7 +419,6 @@ class IntegrationBase {
389
419
 
390
420
  async onWebhook({ data }) {
391
421
  // Default: no-op, integrations override this
392
- console.log('Webhook received:', data);
393
422
  }
394
423
 
395
424
  async queueWebhook(data) {
@@ -601,11 +601,10 @@ function setEntityRoutes(router, authenticateUser, useCases) {
601
601
  router.route('/api/entities/:entityId/test-auth').get(
602
602
  catchAsyncError(async (req, res) => {
603
603
  const user = await authenticateUser.execute(req);
604
- const userId = user.getId();
605
604
  const params = checkRequiredParams(req.params, ['entityId']);
606
605
  const testAuthResponse = await testModuleAuth.execute(
607
606
  params.entityId,
608
- userId
607
+ user // Pass User object for proper validation
609
608
  );
610
609
 
611
610
  if (!testAuthResponse) {
@@ -614,7 +613,7 @@ function setEntityRoutes(router, authenticateUser, useCases) {
614
613
  errors: [
615
614
  {
616
615
  title: 'Authentication Error',
617
- message: `There was an error with your ${module.getName()} Entity. Please reconnect/re-authenticate, or reach out to Support for assistance.`,
616
+ message: `There was an error with your Entity. Please reconnect/re-authenticate, or reach out to Support for assistance.`,
618
617
  timestamp: Date.now(),
619
618
  },
620
619
  ],
@@ -628,9 +627,8 @@ function setEntityRoutes(router, authenticateUser, useCases) {
628
627
  router.route('/api/entities/:entityId').get(
629
628
  catchAsyncError(async (req, res) => {
630
629
  const user = await authenticateUser.execute(req);
631
- const userId = user.getId();
632
630
  const params = checkRequiredParams(req.params, ['entityId']);
633
- const module = await getModule.execute(params.entityId, userId);
631
+ const module = await getModule.execute(params.entityId, user); // Pass User object
634
632
 
635
633
  res.json(module);
636
634
  })
@@ -639,12 +637,11 @@ function setEntityRoutes(router, authenticateUser, useCases) {
639
637
  router.route('/api/entities/:entityId/options').post(
640
638
  catchAsyncError(async (req, res) => {
641
639
  const user = await authenticateUser.execute(req);
642
- const userId = user.getId();
643
640
  const params = checkRequiredParams(req.params, ['entityId']);
644
641
 
645
642
  const entityOptions = await getEntityOptionsById.execute(
646
643
  params.entityId,
647
- userId
644
+ user // Pass User object
648
645
  );
649
646
 
650
647
  res.json(entityOptions);
@@ -654,11 +651,10 @@ function setEntityRoutes(router, authenticateUser, useCases) {
654
651
  router.route('/api/entities/:entityId/options/refresh').post(
655
652
  catchAsyncError(async (req, res) => {
656
653
  const user = await authenticateUser.execute(req);
657
- const userId = user.getId();
658
654
  const params = checkRequiredParams(req.params, ['entityId']);
659
655
  const updatedOptions = await refreshEntityOptions.execute(
660
656
  params.entityId,
661
- userId,
657
+ user, // Pass User object
662
658
  req.body
663
659
  );
664
660
 
@@ -1,4 +1,5 @@
1
1
  const { Requester } = require('./requester');
2
+ const { get } = require('../../assertions');
2
3
  const { ModuleConstants } = require('../ModuleConstants');
3
4
 
4
5
 
@@ -9,27 +10,42 @@ class ApiKeyRequester extends Requester {
9
10
  constructor(params) {
10
11
  super(params);
11
12
  this.requesterType = 'apiKey';
12
- this.API_KEY_NAME = 'key';
13
- this.API_KEY_VALUE = null;
13
+
14
+ // Use snake_case convention consistent with OAuth2Requester and BasicAuthRequester
15
+ this.api_key_name = get(params, 'api_key_name', 'key');
16
+ this.api_key = get(params, 'api_key', null);
17
+
18
+ // Backward compatibility: support old naming convention
19
+ if (!this.api_key && params.API_KEY_VALUE) {
20
+ this.api_key = params.API_KEY_VALUE;
21
+ }
22
+ if (!this.api_key_name && params.API_KEY_NAME) {
23
+ this.api_key_name = params.API_KEY_NAME;
24
+ }
14
25
  }
15
26
 
16
27
  async addAuthHeaders(headers) {
17
- if (this.API_KEY_VALUE) {
18
- headers[this.API_KEY_NAME] = this.API_KEY_VALUE;
28
+ if (this.api_key) {
29
+ headers[this.api_key_name] = this.api_key;
19
30
  }
20
31
  return headers;
21
32
  }
22
33
 
23
34
  isAuthenticated() {
24
35
  return (
25
- this.API_KEY_VALUE !== null &&
26
- this.API_KEY_VALUE !== undefined &&
27
- this.API_KEY_VALUE.trim().length() > 0
36
+ this.api_key !== null &&
37
+ this.api_key !== undefined &&
38
+ typeof this.api_key === 'string' &&
39
+ this.api_key.trim().length > 0
28
40
  );
29
41
  }
30
42
 
31
43
  setApiKey(api_key) {
32
- this.API_KEY_VALUE = api_key;
44
+ this.api_key = api_key;
45
+ }
46
+
47
+ setApiKeyName(api_key_name) {
48
+ this.api_key_name = api_key_name;
33
49
  }
34
50
  }
35
51
 
@@ -12,11 +12,18 @@ class GetEntityOptionsById {
12
12
  }
13
13
 
14
14
  /**
15
- * Retrieve a Module instance for a given user and entity/module type.
16
- * @param {string} userId
17
- * @param {string} entityId
15
+ * Retrieve entity options for a given entity
16
+ *
17
+ * @param {string|number} entityId - Entity ID to retrieve options for
18
+ * @param {string|number|import('../../user/user').User} userIdOrUser - User ID or User object for validation
19
+ * @returns {Promise<Object>} Entity options
18
20
  */
19
- async execute(entityId, userId) {
21
+ async execute(entityId, userIdOrUser) {
22
+ // Support both userId (backward compatible) and User object (new pattern)
23
+ const userId = typeof userIdOrUser === 'object' && userIdOrUser?.getId
24
+ ? userIdOrUser.getId()
25
+ : userIdOrUser;
26
+
20
27
  const entity = await this.moduleRepository.findEntityById(
21
28
  entityId,
22
29
  userId
@@ -26,7 +33,12 @@ class GetEntityOptionsById {
26
33
  throw new Error(`Entity ${entityId} not found`);
27
34
  }
28
35
 
29
- if (entity.userId !== userId) {
36
+ // Validate entity ownership
37
+ const isOwned = typeof userIdOrUser === 'object' && userIdOrUser?.ownsUserId
38
+ ? userIdOrUser.ownsUserId(entity.userId)
39
+ : entity.userId?.toString() === userId?.toString();
40
+
41
+ if (!isOwned) {
30
42
  throw new Error(
31
43
  `Entity ${entityId} does not belong to user ${userId}`
32
44
  );
@@ -6,7 +6,19 @@ class GetModule {
6
6
  this.moduleDefinitions = moduleDefinitions;
7
7
  }
8
8
 
9
- async execute(entityId, userId) {
9
+ /**
10
+ * Get module instance for an entity
11
+ *
12
+ * @param {string|number} entityId - Entity ID to retrieve
13
+ * @param {string|number|import('../../user/user').User} userIdOrUser - User ID or User object for validation
14
+ * @returns {Promise<Object>} Module details
15
+ */
16
+ async execute(entityId, userIdOrUser) {
17
+ // Support both userId (backward compatible) and User object (new pattern)
18
+ const userId = typeof userIdOrUser === 'object' && userIdOrUser?.getId
19
+ ? userIdOrUser.getId()
20
+ : userIdOrUser;
21
+
10
22
  const entity = await this.moduleRepository.findEntityById(
11
23
  entityId,
12
24
  userId
@@ -16,7 +28,14 @@ class GetModule {
16
28
  throw new Error(`Entity ${entityId} not found`);
17
29
  }
18
30
 
19
- if (entity.userId !== userId) {
31
+ // Validate entity ownership
32
+ // If User object provided, use ownsUserId to check linked users
33
+ // Otherwise fall back to simple equality check
34
+ const isOwned = typeof userIdOrUser === 'object' && userIdOrUser?.ownsUserId
35
+ ? userIdOrUser.ownsUserId(entity.userId)
36
+ : entity.userId?.toString() === userId?.toString();
37
+
38
+ if (!isOwned) {
20
39
  throw new Error(
21
40
  `Entity ${entityId} does not belong to user ${userId}`
22
41
  );
@@ -100,9 +100,20 @@ class ProcessAuthorizationCallback {
100
100
  async findOrCreateEntity(entityDetails, moduleName, credentialId) {
101
101
  const { identifiers, details } = entityDetails;
102
102
 
103
+ // Support both 'user' and 'userId' field names from module definitions
104
+ // Some modules use 'user' (legacy), others use 'userId' (newer pattern)
105
+ const userId = identifiers.user || identifiers.userId;
106
+
107
+ if (!userId) {
108
+ throw new Error(
109
+ `Module definition for ${moduleName} must return 'user' or 'userId' in identifiers from getEntityDetails(). ` +
110
+ `Without userId, entity lookup would match across all users (security issue).`
111
+ );
112
+ }
113
+
103
114
  const existingEntity = await this.moduleRepository.findEntity({
104
115
  externalId: identifiers.externalId,
105
- user: identifiers.user,
116
+ user: userId,
106
117
  moduleName: moduleName,
107
118
  });
108
119
 
@@ -12,11 +12,19 @@ class RefreshEntityOptions {
12
12
  }
13
13
 
14
14
  /**
15
- * Retrieve a Module instance for a given user and entity/module type.
16
- * @param {string} userId
17
- * @param {string} entityId
15
+ * Refresh entity options for a given entity
16
+ *
17
+ * @param {string|number} entityId - Entity ID to refresh
18
+ * @param {string|number|import('../../user/user').User} userIdOrUser - User ID or User object for validation
19
+ * @param {Object} options - Refresh options
20
+ * @returns {Promise<Object>} Updated entity options
18
21
  */
19
- async execute(entityId, userId, options) {
22
+ async execute(entityId, userIdOrUser, options) {
23
+ // Support both userId (backward compatible) and User object (new pattern)
24
+ const userId = typeof userIdOrUser === 'object' && userIdOrUser?.getId
25
+ ? userIdOrUser.getId()
26
+ : userIdOrUser;
27
+
20
28
  const entity = await this.moduleRepository.findEntityById(
21
29
  entityId,
22
30
  userId
@@ -26,7 +34,12 @@ class RefreshEntityOptions {
26
34
  throw new Error(`Entity ${entityId} not found`);
27
35
  }
28
36
 
29
- if (entity.userId !== userId) {
37
+ // Validate entity ownership
38
+ const isOwned = typeof userIdOrUser === 'object' && userIdOrUser?.ownsUserId
39
+ ? userIdOrUser.ownsUserId(entity.userId)
40
+ : entity.userId?.toString() === userId?.toString();
41
+
42
+ if (!isOwned) {
30
43
  throw new Error(
31
44
  `Entity ${entityId} does not belong to user ${userId}`
32
45
  );
@@ -11,7 +11,19 @@ class TestModuleAuth {
11
11
  this.moduleDefinitions = moduleDefinitions;
12
12
  }
13
13
 
14
- async execute(entityId, userId) {
14
+ /**
15
+ * Test authentication for a module entity
16
+ *
17
+ * @param {string|number} entityId - Entity ID to test
18
+ * @param {string|number|import('../../user/user').User} userIdOrUser - User ID or User object for validation
19
+ * @returns {Promise<boolean>} Authentication test result
20
+ */
21
+ async execute(entityId, userIdOrUser) {
22
+ // Support both userId (backward compatible) and User object (new pattern)
23
+ const userId = typeof userIdOrUser === 'object' && userIdOrUser?.getId
24
+ ? userIdOrUser.getId()
25
+ : userIdOrUser;
26
+
15
27
  const entity = await this.moduleRepository.findEntityById(
16
28
  entityId,
17
29
  userId
@@ -21,7 +33,12 @@ class TestModuleAuth {
21
33
  throw new Error(`Entity ${entityId} not found`);
22
34
  }
23
35
 
24
- if (entity.userId !== userId) {
36
+ // Validate entity ownership
37
+ const isOwned = typeof userIdOrUser === 'object' && userIdOrUser?.ownsUserId
38
+ ? userIdOrUser.ownsUserId(entity.userId)
39
+ : entity.userId?.toString() === userId?.toString();
40
+
41
+ if (!isOwned) {
25
42
  throw new Error(
26
43
  `Entity ${entityId} does not belong to user ${userId}`
27
44
  );
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/core",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0-next.55",
4
+ "version": "2.0.0-next.57",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-apigatewaymanagementapi": "^3.588.0",
7
7
  "@aws-sdk/client-kms": "^3.588.0",
@@ -38,9 +38,9 @@
38
38
  }
39
39
  },
40
40
  "devDependencies": {
41
- "@friggframework/eslint-config": "2.0.0-next.55",
42
- "@friggframework/prettier-config": "2.0.0-next.55",
43
- "@friggframework/test": "2.0.0-next.55",
41
+ "@friggframework/eslint-config": "2.0.0-next.57",
42
+ "@friggframework/prettier-config": "2.0.0-next.57",
43
+ "@friggframework/test": "2.0.0-next.57",
44
44
  "@prisma/client": "^6.17.0",
45
45
  "@types/lodash": "4.17.15",
46
46
  "@typescript-eslint/eslint-plugin": "^8.0.0",
@@ -80,5 +80,5 @@
80
80
  "publishConfig": {
81
81
  "access": "public"
82
82
  },
83
- "gitHead": "7f86d4b5faaab4de74e6e3676948b62d1e1a5bb1"
83
+ "gitHead": "d09ee09decef14a8cdbf657494ac0dc9bf0b4014"
84
84
  }
@@ -21,49 +21,5 @@ ALTER TABLE "Credential" DROP COLUMN "subType";
21
21
  -- AlterTable
22
22
  ALTER TABLE "Entity" DROP COLUMN "subType";
23
23
 
24
- -- CreateTable
25
- CREATE TABLE "Process" (
26
- "id" SERIAL NOT NULL,
27
- "userId" INTEGER NOT NULL,
28
- "integrationId" INTEGER NOT NULL,
29
- "name" TEXT NOT NULL,
30
- "type" TEXT NOT NULL,
31
- "state" TEXT NOT NULL,
32
- "context" JSONB NOT NULL DEFAULT '{}',
33
- "results" JSONB NOT NULL DEFAULT '{}',
34
- "parentProcessId" INTEGER,
35
- "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
36
- "updatedAt" TIMESTAMP(3) NOT NULL,
37
-
38
- CONSTRAINT "Process_pkey" PRIMARY KEY ("id")
39
- );
40
-
41
- -- CreateIndex
42
- CREATE INDEX "Process_userId_idx" ON "Process"("userId");
43
-
44
- -- CreateIndex
45
- CREATE INDEX "Process_integrationId_idx" ON "Process"("integrationId");
46
-
47
- -- CreateIndex
48
- CREATE INDEX "Process_type_idx" ON "Process"("type");
49
-
50
- -- CreateIndex
51
- CREATE INDEX "Process_state_idx" ON "Process"("state");
52
-
53
- -- CreateIndex
54
- CREATE INDEX "Process_name_idx" ON "Process"("name");
55
-
56
- -- CreateIndex
57
- CREATE INDEX "Process_parentProcessId_idx" ON "Process"("parentProcessId");
58
-
59
24
  -- CreateIndex
60
25
  CREATE UNIQUE INDEX "User_username_appUserId_key" ON "User"("username", "appUserId");
61
-
62
- -- AddForeignKey
63
- ALTER TABLE "Process" ADD CONSTRAINT "Process_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
64
-
65
- -- AddForeignKey
66
- ALTER TABLE "Process" ADD CONSTRAINT "Process_integrationId_fkey" FOREIGN KEY ("integrationId") REFERENCES "Integration"("id") ON DELETE CASCADE ON UPDATE CASCADE;
67
-
68
- -- AddForeignKey
69
- ALTER TABLE "Process" ADD CONSTRAINT "Process_parentProcessId_fkey" FOREIGN KEY ("parentProcessId") REFERENCES "Process"("id") ON DELETE SET NULL ON UPDATE CASCADE;
@@ -4,7 +4,6 @@ const { SQSClient, SendMessageCommand, SendMessageBatchCommand } = require('@aws
4
4
  const awsConfigOptions = () => {
5
5
  const config = {};
6
6
  if (process.env.IS_OFFLINE) {
7
- console.log('Running in offline mode');
8
7
  config.credentials = {
9
8
  accessKeyId: 'test-aws-key',
10
9
  secretAccessKey: 'test-aws-secret',
@@ -21,7 +20,6 @@ const sqs = new SQSClient(awsConfigOptions());
21
20
 
22
21
  const QueuerUtil = {
23
22
  send: async (message, queueUrl) => {
24
- console.log(`Enqueuing message to SQS queue ${queueUrl}`);
25
23
  const command = new SendMessageCommand({
26
24
  MessageBody: JSON.stringify(message),
27
25
  QueueUrl: queueUrl,
@@ -30,9 +28,6 @@ const QueuerUtil = {
30
28
  },
31
29
 
32
30
  batchSend: async (entries = [], queueUrl) => {
33
- console.log(
34
- `Enqueuing ${entries.length} entries on SQS to queue ${queueUrl}`
35
- );
36
31
  const buffer = [];
37
32
  const batchSize = 10;
38
33
 
@@ -43,7 +38,6 @@ const QueuerUtil = {
43
38
  });
44
39
  // Sends 10, then purges the buffer
45
40
  if (buffer.length === batchSize) {
46
- console.log('Buffer at 10, sending batch');
47
41
  const command = new SendMessageBatchCommand({
48
42
  Entries: buffer,
49
43
  QueueUrl: queueUrl,
@@ -53,11 +47,9 @@ const QueuerUtil = {
53
47
  buffer.splice(0, buffer.length);
54
48
  }
55
49
  }
56
- console.log('Buffer at end, sending final batch');
57
50
 
58
51
  // If any remaining entries under 10 are left in the buffer, send and return
59
52
  if (buffer.length > 0) {
60
- console.log(buffer);
61
53
  const command = new SendMessageBatchCommand({
62
54
  Entries: buffer,
63
55
  QueueUrl: queueUrl,
@@ -427,6 +427,26 @@ class UserRepositoryDocumentDB extends UserRepositoryInterface {
427
427
  const date = new Date(value);
428
428
  return isNaN(date.getTime()) ? undefined : date;
429
429
  }
430
+
431
+ /**
432
+ * Link an individual user to an organization user
433
+ * @param {string} individualUserId - Individual user ID (MongoDB ObjectId string)
434
+ * @param {string} organizationUserId - Organization user ID (MongoDB ObjectId string)
435
+ * @returns {Promise<Object>} Updated individual user object
436
+ */
437
+ async linkIndividualToOrganization(individualUserId, organizationUserId) {
438
+ const doc = await updateOne(
439
+ this.prisma,
440
+ 'User',
441
+ { _id: toObjectId(individualUserId), type: 'INDIVIDUAL' },
442
+ { $set: { organizationId: toObjectId(organizationUserId) } }
443
+ );
444
+ const decrypted = await this.encryptionService.decryptFields(
445
+ 'User',
446
+ doc
447
+ );
448
+ return this._mapUser(decrypted);
449
+ }
430
450
  }
431
451
 
432
452
  module.exports = { UserRepositoryDocumentDB };
@@ -193,6 +193,20 @@ class UserRepositoryInterface {
193
193
  async deleteUser(userId) {
194
194
  throw new Error('Method deleteUser must be implemented by subclass');
195
195
  }
196
+
197
+ /**
198
+ * Link an individual user to an organization user
199
+ *
200
+ * @param {string|number} individualUserId - Individual user ID
201
+ * @param {string|number} organizationUserId - Organization user ID
202
+ * @returns {Promise<Object>} Updated individual user object
203
+ * @abstract
204
+ */
205
+ async linkIndividualToOrganization(individualUserId, organizationUserId) {
206
+ throw new Error(
207
+ 'Method linkIndividualToOrganization must be implemented by subclass'
208
+ );
209
+ }
196
210
  }
197
211
 
198
212
  module.exports = { UserRepositoryInterface };
@@ -287,6 +287,24 @@ class UserRepositoryMongo extends UserRepositoryInterface {
287
287
  throw error;
288
288
  }
289
289
  }
290
+
291
+ /**
292
+ * Link an individual user to an organization user
293
+ * @param {string} individualUserId - Individual user ID (MongoDB ObjectId string)
294
+ * @param {string} organizationUserId - Organization user ID (MongoDB ObjectId string)
295
+ * @returns {Promise<Object>} Updated individual user object
296
+ */
297
+ async linkIndividualToOrganization(individualUserId, organizationUserId) {
298
+ return await this.prisma.user.update({
299
+ where: {
300
+ id: individualUserId,
301
+ type: 'INDIVIDUAL',
302
+ },
303
+ data: {
304
+ organizationId: organizationUserId,
305
+ },
306
+ });
307
+ }
290
308
  }
291
309
 
292
310
  module.exports = { UserRepositoryMongo };
@@ -346,6 +346,28 @@ class UserRepositoryPostgres extends UserRepositoryInterface {
346
346
  throw error;
347
347
  }
348
348
  }
349
+
350
+ /**
351
+ * Link an individual user to an organization user
352
+ * @param {string} individualUserId - Individual user ID (string from application layer)
353
+ * @param {string} organizationUserId - Organization user ID (string from application layer)
354
+ * @returns {Promise<Object>} Updated individual user with string IDs
355
+ */
356
+ async linkIndividualToOrganization(individualUserId, organizationUserId) {
357
+ const intIndividualId = this._convertId(individualUserId);
358
+ const intOrganizationId = this._convertId(organizationUserId);
359
+
360
+ const user = await this.prisma.user.update({
361
+ where: {
362
+ id: intIndividualId,
363
+ type: 'INDIVIDUAL',
364
+ },
365
+ data: {
366
+ organizationId: intOrganizationId,
367
+ },
368
+ });
369
+ return this._convertUserIds(user);
370
+ }
349
371
  }
350
372
 
351
373
  module.exports = { UserRepositoryPostgres };