@friggframework/core 2.0.0--canary.689.8c6c948.0 → 2.0.0--canary.688.87a9bf3.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.
@@ -1,12 +1,24 @@
1
1
  const {
2
2
  createModuleRepository,
3
3
  } = require('../../modules/repositories/module-repository-factory');
4
+ const { toCredentialId } = require('../../modules/utils/to-credential-id');
4
5
 
5
6
  const ERROR_CODE_MAP = {
6
7
  ENTITY_NOT_FOUND: 404,
7
8
  INVALID_ENTITY_DATA: 400,
8
9
  };
9
10
 
11
+ function toEntityDto(entity) {
12
+ return {
13
+ id: entity.id,
14
+ userId: entity.userId,
15
+ externalId: entity.externalId,
16
+ name: entity.name,
17
+ moduleName: entity.moduleName,
18
+ credentialId: toCredentialId(entity.credential),
19
+ };
20
+ }
21
+
10
22
  function mapErrorToResponse(error) {
11
23
  const status = ERROR_CODE_MAP[error?.code] || 500;
12
24
  return {
@@ -66,16 +78,7 @@ function createEntityCommands() {
66
78
 
67
79
  const entity = await moduleRepo.createEntity(entityData);
68
80
 
69
- return {
70
- id: entity.id,
71
- userId: entity.userId,
72
- externalId: entity.externalId,
73
- name: entity.name,
74
- moduleName: entity.moduleName,
75
- credentialId: entity.credential?._id
76
- ? entity.credential._id.toString()
77
- : entity.credential,
78
- };
81
+ return toEntityDto(entity);
79
82
  } catch (error) {
80
83
  return mapErrorToResponse(error);
81
84
  }
@@ -109,16 +112,7 @@ function createEntityCommands() {
109
112
  return null;
110
113
  }
111
114
 
112
- return {
113
- id: entity.id,
114
- userId: entity.userId,
115
- externalId: entity.externalId,
116
- name: entity.name,
117
- moduleName: entity.moduleName,
118
- credentialId: entity.credential?._id
119
- ? entity.credential._id.toString()
120
- : entity.credential,
121
- };
115
+ return toEntityDto(entity);
122
116
  } catch (error) {
123
117
  return mapErrorToResponse(error);
124
118
  }
@@ -139,16 +133,7 @@ function createEntityCommands() {
139
133
 
140
134
  const entities = await moduleRepo.findEntitiesByUserId(userId);
141
135
 
142
- return entities.map((entity) => ({
143
- id: entity.id,
144
- userId: entity.userId,
145
- externalId: entity.externalId,
146
- name: entity.name,
147
- moduleName: entity.moduleName,
148
- credentialId: entity.credential?._id
149
- ? entity.credential._id.toString()
150
- : entity.credential,
151
- }));
136
+ return entities.map((entity) => toEntityDto(entity));
152
137
  } catch (error) {
153
138
  if (error.code) {
154
139
  return mapErrorToResponse(error);
@@ -180,16 +165,7 @@ function createEntityCommands() {
180
165
  moduleName
181
166
  );
182
167
 
183
- return entities.map((entity) => ({
184
- id: entity.id,
185
- userId: entity.userId,
186
- externalId: entity.externalId,
187
- name: entity.name,
188
- moduleName: entity.moduleName,
189
- credentialId: entity.credential?._id
190
- ? entity.credential._id.toString()
191
- : entity.credential,
192
- }));
168
+ return entities.map((entity) => toEntityDto(entity));
193
169
  } catch (error) {
194
170
  if (error.code) {
195
171
  return mapErrorToResponse(error);
@@ -213,16 +189,7 @@ function createEntityCommands() {
213
189
 
214
190
  const entity = await moduleRepo.findEntityById(entityId);
215
191
 
216
- return {
217
- id: entity.id,
218
- userId: entity.userId,
219
- externalId: entity.externalId,
220
- name: entity.name,
221
- moduleName: entity.moduleName,
222
- credentialId: entity.credential?._id
223
- ? entity.credential._id.toString()
224
- : entity.credential,
225
- };
192
+ return toEntityDto(entity);
226
193
  } catch (error) {
227
194
  return mapErrorToResponse(error);
228
195
  }
@@ -250,16 +217,7 @@ function createEntityCommands() {
250
217
  throw error;
251
218
  }
252
219
 
253
- return {
254
- id: entity.id,
255
- userId: entity.userId,
256
- externalId: entity.externalId,
257
- name: entity.name,
258
- moduleName: entity.moduleName,
259
- credentialId: entity.credential?._id
260
- ? entity.credential._id.toString()
261
- : entity.credential,
262
- };
220
+ return toEntityDto(entity);
263
221
  } catch (error) {
264
222
  return mapErrorToResponse(error);
265
223
  }
@@ -29,10 +29,6 @@ const {
29
29
  const {
30
30
  PatchIntegrationConfig,
31
31
  } = require('../../integrations/use-cases/patch-integration-config');
32
- const {
33
- AddEntitiesToIntegration,
34
- RemoveEntitiesFromIntegration,
35
- } = require('../../integrations/use-cases/update-integration-entities');
36
32
  const {
37
33
  getModulesDefinitionFromIntegrationClasses,
38
34
  } = require('../../integrations/utils/map-integration-dto');
@@ -44,7 +40,6 @@ const ERROR_CODE_MAP = {
44
40
  EXTERNAL_ID_REQUIRED: 400,
45
41
  TYPE_REQUIRED: 400,
46
42
  INTEGRATION_RECORD_NOT_FOUND: 404,
47
- INVALID_INTEGRATION_DATA: 400,
48
43
  };
49
44
 
50
45
  function mapErrorToResponse(error) {
@@ -168,15 +163,6 @@ function createIntegrationCommands({ integrationClass } = {}) {
168
163
  integrationRepository,
169
164
  });
170
165
 
171
- const addEntitiesToIntegrationUseCase = new AddEntitiesToIntegration({
172
- integrationRepository,
173
- });
174
-
175
- const removeEntitiesFromIntegrationUseCase =
176
- new RemoveEntitiesFromIntegration({
177
- integrationRepository,
178
- });
179
-
180
166
  return {
181
167
  findIntegrationById,
182
168
  listIntegrations,
@@ -321,44 +307,6 @@ function createIntegrationCommands({ integrationClass } = {}) {
321
307
  }
322
308
  },
323
309
 
324
- /**
325
- * Attach entities to an existing integration. Entities that are
326
- * already attached are left as they are.
327
- * @param {Object} params
328
- * @param {string} params.integrationId - Integration ID
329
- * @param {Array<string>} params.entityIds - Entity IDs to attach
330
- * @returns {Promise<Object>} Updated integration
331
- */
332
- async addEntitiesToIntegration({ integrationId, entityIds }) {
333
- try {
334
- return await addEntitiesToIntegrationUseCase.execute(
335
- integrationId,
336
- entityIds
337
- );
338
- } catch (error) {
339
- return mapErrorToResponse(error);
340
- }
341
- },
342
-
343
- /**
344
- * Detach entities from an integration. Entities that are not
345
- * attached are ignored; detached entities are not deleted.
346
- * @param {Object} params
347
- * @param {string} params.integrationId - Integration ID
348
- * @param {Array<string>} params.entityIds - Entity IDs to detach
349
- * @returns {Promise<Object>} Updated integration
350
- */
351
- async removeEntitiesFromIntegration({ integrationId, entityIds }) {
352
- try {
353
- return await removeEntitiesFromIntegrationUseCase.execute(
354
- integrationId,
355
- entityIds
356
- );
357
- } catch (error) {
358
- return mapErrorToResponse(error);
359
- }
360
- },
361
-
362
310
  /**
363
311
  * Delete an integration by ID
364
312
  * @param {string} integrationId - Integration ID to delete
@@ -164,42 +164,6 @@ class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
164
164
  return this._mapIntegration(created);
165
165
  }
166
166
 
167
- async addEntitiesToIntegration(integrationId, entityIds) {
168
- const objectId = toObjectId(integrationId);
169
- if (!objectId) return null;
170
- const existing = await findOne(this.prisma, 'Integration', { _id: objectId });
171
- if (!existing) return null;
172
-
173
- await updateOne(
174
- this.prisma,
175
- 'Integration',
176
- { _id: objectId },
177
- {
178
- $addToSet: { entityIds: { $each: toObjectIdArray(entityIds) } },
179
- $set: { updatedAt: new Date() },
180
- }
181
- );
182
- return this.findIntegrationById(integrationId);
183
- }
184
-
185
- async removeEntitiesFromIntegration(integrationId, entityIds) {
186
- const objectId = toObjectId(integrationId);
187
- if (!objectId) return null;
188
- const existing = await findOne(this.prisma, 'Integration', { _id: objectId });
189
- if (!existing) return null;
190
-
191
- await updateOne(
192
- this.prisma,
193
- 'Integration',
194
- { _id: objectId },
195
- {
196
- $pullAll: { entityIds: toObjectIdArray(entityIds) },
197
- $set: { updatedAt: new Date() },
198
- }
199
- );
200
- return this.findIntegrationById(integrationId);
201
- }
202
-
203
167
  async findIntegrationByUserId(userId) {
204
168
  const objectId = toObjectId(userId);
205
169
  if (!objectId) return null;
@@ -184,36 +184,6 @@ class IntegrationRepositoryInterface {
184
184
  'Method findIntegrationsByEntityId must be implemented by subclass'
185
185
  );
186
186
  }
187
-
188
- /**
189
- * Attach entities to an existing integration. Entities that are already
190
- * attached are left as they are.
191
- *
192
- * @param {string|number} integrationId - Integration ID
193
- * @param {Array<string|number>} entityIds - Entity IDs to attach
194
- * @returns {Promise<Object|null>} Updated integration, or null when the integration does not exist
195
- * @abstract
196
- */
197
- async addEntitiesToIntegration(integrationId, entityIds) {
198
- throw new Error(
199
- 'Method addEntitiesToIntegration must be implemented by subclass'
200
- );
201
- }
202
-
203
- /**
204
- * Detach entities from an integration. Entities that are not attached
205
- * are ignored; the entities themselves are not deleted.
206
- *
207
- * @param {string|number} integrationId - Integration ID
208
- * @param {Array<string|number>} entityIds - Entity IDs to detach
209
- * @returns {Promise<Object|null>} Updated integration, or null when the integration does not exist
210
- * @abstract
211
- */
212
- async removeEntitiesFromIntegration(integrationId, entityIds) {
213
- throw new Error(
214
- 'Method removeEntitiesFromIntegration must be implemented by subclass'
215
- );
216
- }
217
187
  }
218
188
 
219
189
  module.exports = { IntegrationRepositoryInterface };
@@ -338,58 +338,6 @@ class IntegrationRepositoryMongo extends IntegrationRepositoryInterface {
338
338
  };
339
339
  }
340
340
 
341
- /**
342
- * Attach entities to an existing integration. Entities already attached
343
- * are left as they are.
344
- *
345
- * @param {string} integrationId - Integration ID
346
- * @param {Array<string>} entityIds - Entity IDs to attach
347
- * @returns {Promise<Object|null>} Updated integration, or null when the integration does not exist
348
- */
349
- async addEntitiesToIntegration(integrationId, entityIds) {
350
- const existing = await this.prisma.integration.findUnique({
351
- where: { id: integrationId },
352
- select: { id: true },
353
- });
354
- if (!existing) return null;
355
-
356
- await this.prisma.integration.update({
357
- where: { id: integrationId },
358
- data: {
359
- entities: {
360
- connect: entityIds.map((id) => ({ id })),
361
- },
362
- },
363
- });
364
- return this.findIntegrationById(integrationId);
365
- }
366
-
367
- /**
368
- * Detach entities from an integration. Entities that are not attached
369
- * are ignored. The entities themselves are not deleted.
370
- *
371
- * @param {string} integrationId - Integration ID
372
- * @param {Array<string>} entityIds - Entity IDs to detach
373
- * @returns {Promise<Object|null>} Updated integration, or null when the integration does not exist
374
- */
375
- async removeEntitiesFromIntegration(integrationId, entityIds) {
376
- const existing = await this.prisma.integration.findUnique({
377
- where: { id: integrationId },
378
- select: { id: true },
379
- });
380
- if (!existing) return null;
381
-
382
- await this.prisma.integration.update({
383
- where: { id: integrationId },
384
- data: {
385
- entities: {
386
- disconnect: entityIds.map((id) => ({ id })),
387
- },
388
- },
389
- });
390
- return this.findIntegrationById(integrationId);
391
- }
392
-
393
341
  /**
394
342
  * Find integration by user ID (returns single integration)
395
343
  * Replaces: IntegrationModel.findOne({ user: userId }).populate('entities')
@@ -365,64 +365,6 @@ class IntegrationRepositoryPostgres extends IntegrationRepositoryInterface {
365
365
  };
366
366
  }
367
367
 
368
- /**
369
- * Attach entities to an existing integration. Entities already attached
370
- * are left as they are.
371
- *
372
- * @param {string} integrationId - Integration ID (string from application layer)
373
- * @param {Array<string>} entityIds - Entity IDs to attach
374
- * @returns {Promise<Object|null>} Updated integration, or null when the integration does not exist
375
- */
376
- async addEntitiesToIntegration(integrationId, entityIds) {
377
- const intId = this._convertId(integrationId);
378
- const existing = await this.prisma.integration.findUnique({
379
- where: { id: intId },
380
- select: { id: true },
381
- });
382
- if (!existing) return null;
383
-
384
- await this.prisma.integration.update({
385
- where: { id: intId },
386
- data: {
387
- entities: {
388
- connect: entityIds.map((id) => ({
389
- id: this._convertId(id),
390
- })),
391
- },
392
- },
393
- });
394
- return this.findIntegrationById(integrationId);
395
- }
396
-
397
- /**
398
- * Detach entities from an integration. Entities that are not attached
399
- * are ignored. The entities themselves are not deleted.
400
- *
401
- * @param {string} integrationId - Integration ID (string from application layer)
402
- * @param {Array<string>} entityIds - Entity IDs to detach
403
- * @returns {Promise<Object|null>} Updated integration, or null when the integration does not exist
404
- */
405
- async removeEntitiesFromIntegration(integrationId, entityIds) {
406
- const intId = this._convertId(integrationId);
407
- const existing = await this.prisma.integration.findUnique({
408
- where: { id: intId },
409
- select: { id: true },
410
- });
411
- if (!existing) return null;
412
-
413
- await this.prisma.integration.update({
414
- where: { id: intId },
415
- data: {
416
- entities: {
417
- disconnect: entityIds.map((id) => ({
418
- id: this._convertId(id),
419
- })),
420
- },
421
- },
422
- });
423
- return this.findIntegrationById(integrationId);
424
- }
425
-
426
368
  /**
427
369
  * Find integration by user ID (returns single integration)
428
370
  *
@@ -1,3 +1,5 @@
1
+ const { toCredentialId } = require('./to-credential-id');
2
+
1
3
  /**
2
4
  * @param {import('../module').Module} moduleInstance
3
5
  * Convert a Module domain instance to a plain DTO suitable for JSON responses.
@@ -10,9 +12,9 @@ function mapModuleClassToModuleDTO(moduleInstance) {
10
12
  name: moduleInstance.name,
11
13
  userId: moduleInstance.userId,
12
14
  entity: moduleInstance.entity,
13
- credentialId: moduleInstance.credential?._id?.toString(),
14
- type: moduleInstance.getName()
15
+ credentialId: toCredentialId(moduleInstance.credential),
16
+ type: moduleInstance.getName(),
15
17
  };
16
18
  }
17
19
 
18
- module.exports = { mapModuleClassToModuleDTO };
20
+ module.exports = { mapModuleClassToModuleDTO };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Resolve the credential id from an entity's or module's `credential`, which
3
+ * repositories return as the decrypted credential object (or a bare id).
4
+ * @param {Object|string|number|null|undefined} credential
5
+ * @returns {string|null}
6
+ */
7
+ function toCredentialId(credential) {
8
+ if (credential && typeof credential === 'object') {
9
+ return credential.id?.toString() ?? null;
10
+ }
11
+ return credential?.toString() ?? null;
12
+ }
13
+
14
+ module.exports = { toCredentialId };
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--canary.689.8c6c948.0",
4
+ "version": "2.0.0--canary.688.87a9bf3.0",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-apigatewaymanagementapi": "^3.588.0",
7
7
  "@aws-sdk/client-kms": "^3.588.0",
@@ -47,9 +47,9 @@
47
47
  }
48
48
  },
49
49
  "devDependencies": {
50
- "@friggframework/eslint-config": "2.0.0--canary.689.8c6c948.0",
51
- "@friggframework/prettier-config": "2.0.0--canary.689.8c6c948.0",
52
- "@friggframework/test": "2.0.0--canary.689.8c6c948.0",
50
+ "@friggframework/eslint-config": "2.0.0--canary.688.87a9bf3.0",
51
+ "@friggframework/prettier-config": "2.0.0--canary.688.87a9bf3.0",
52
+ "@friggframework/test": "2.0.0--canary.688.87a9bf3.0",
53
53
  "@prisma/client": "^6.19.3",
54
54
  "@types/lodash": "4.17.15",
55
55
  "@typescript-eslint/eslint-plugin": "^8.0.0",
@@ -89,5 +89,5 @@
89
89
  "publishConfig": {
90
90
  "access": "public"
91
91
  },
92
- "gitHead": "8c6c948c740134af02e7dc17da7bc41c43d0bea3"
92
+ "gitHead": "87a9bf3315fc2daa2b7f38b7b1ddd5bbcb323b20"
93
93
  }
@@ -1,80 +0,0 @@
1
- function validate(integrationId, entityIds) {
2
- if (!integrationId || !Array.isArray(entityIds) || entityIds.length === 0) {
3
- const error = new Error(
4
- 'integrationId and a non-empty entityIds array are required'
5
- );
6
- error.code = 'INVALID_INTEGRATION_DATA';
7
- throw error;
8
- }
9
- }
10
-
11
- function notFound(integrationId) {
12
- const error = new Error(`Integration ${integrationId} not found`);
13
- error.code = 'INTEGRATION_NOT_FOUND';
14
- return error;
15
- }
16
-
17
- /**
18
- * Attaches entities to an existing integration, e.g. when a user grants
19
- * access to more accounts after the integration was created. Entities that
20
- * are already attached are left as they are.
21
- * @class AddEntitiesToIntegration
22
- */
23
- class AddEntitiesToIntegration {
24
- /**
25
- * @param {Object} params
26
- * @param {import('../repositories/integration-repository-interface').IntegrationRepositoryInterface} params.integrationRepository
27
- */
28
- constructor({ integrationRepository }) {
29
- this.integrationRepository = integrationRepository;
30
- }
31
-
32
- /**
33
- * @param {string} integrationId
34
- * @param {string[]} entityIds
35
- * @returns {Promise<Object>} The updated integration record.
36
- */
37
- async execute(integrationId, entityIds) {
38
- validate(integrationId, entityIds);
39
- const integration =
40
- await this.integrationRepository.addEntitiesToIntegration(
41
- integrationId,
42
- entityIds
43
- );
44
- if (!integration) throw notFound(integrationId);
45
- return integration;
46
- }
47
- }
48
-
49
- /**
50
- * Detaches entities from an integration. Entities that are not attached are
51
- * ignored, and detached entities are not deleted.
52
- * @class RemoveEntitiesFromIntegration
53
- */
54
- class RemoveEntitiesFromIntegration {
55
- /**
56
- * @param {Object} params
57
- * @param {import('../repositories/integration-repository-interface').IntegrationRepositoryInterface} params.integrationRepository
58
- */
59
- constructor({ integrationRepository }) {
60
- this.integrationRepository = integrationRepository;
61
- }
62
-
63
- /**
64
- * @param {string} integrationId
65
- * @param {string[]} entityIds
66
- * @returns {Promise<Object>} The updated integration record.
67
- */
68
- async execute(integrationId, entityIds) {
69
- validate(integrationId, entityIds);
70
- const integration =
71
- await this.integrationRepository.removeEntitiesFromIntegration(
72
- integrationId,
73
- entityIds
74
- );
75
- if (!integration) throw notFound(integrationId);
76
- return integration;
77
- }
78
- }
79
-
80
- module.exports = { AddEntitiesToIntegration, RemoveEntitiesFromIntegration };