@friggframework/core 2.0.0-next.41 → 2.0.0-next.42
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.
- package/CLAUDE.md +693 -0
- package/README.md +931 -50
- package/application/commands/README.md +421 -0
- package/application/commands/credential-commands.js +224 -0
- package/application/commands/entity-commands.js +315 -0
- package/application/commands/integration-commands.js +160 -0
- package/application/commands/integration-commands.test.js +123 -0
- package/application/commands/user-commands.js +213 -0
- package/application/index.js +69 -0
- package/core/CLAUDE.md +690 -0
- package/core/create-handler.js +0 -6
- package/credential/repositories/credential-repository-factory.js +47 -0
- package/credential/repositories/credential-repository-interface.js +98 -0
- package/credential/repositories/credential-repository-mongo.js +301 -0
- package/credential/repositories/credential-repository-postgres.js +307 -0
- package/credential/repositories/credential-repository.js +307 -0
- package/credential/use-cases/get-credential-for-user.js +21 -0
- package/credential/use-cases/update-authentication-status.js +15 -0
- package/database/config.js +117 -0
- package/database/encryption/README.md +683 -0
- package/database/encryption/encryption-integration.test.js +553 -0
- package/database/encryption/encryption-schema-registry.js +141 -0
- package/database/encryption/encryption-schema-registry.test.js +392 -0
- package/database/encryption/field-encryption-service.js +226 -0
- package/database/encryption/field-encryption-service.test.js +525 -0
- package/database/encryption/logger.js +79 -0
- package/database/encryption/mongo-decryption-fix-verification.test.js +348 -0
- package/database/encryption/postgres-decryption-fix-verification.test.js +371 -0
- package/database/encryption/postgres-relation-decryption.test.js +245 -0
- package/database/encryption/prisma-encryption-extension.js +222 -0
- package/database/encryption/prisma-encryption-extension.test.js +439 -0
- package/database/index.js +25 -12
- package/database/models/readme.md +1 -0
- package/database/prisma.js +162 -0
- package/database/repositories/health-check-repository-factory.js +38 -0
- package/database/repositories/health-check-repository-interface.js +86 -0
- package/database/repositories/health-check-repository-mongodb.js +72 -0
- package/database/repositories/health-check-repository-postgres.js +75 -0
- package/database/repositories/health-check-repository.js +108 -0
- package/database/use-cases/check-database-health-use-case.js +34 -0
- package/database/use-cases/check-encryption-health-use-case.js +82 -0
- package/database/use-cases/test-encryption-use-case.js +252 -0
- package/encrypt/Cryptor.js +20 -152
- package/encrypt/index.js +1 -2
- package/encrypt/test-encrypt.js +0 -2
- package/handlers/app-definition-loader.js +38 -0
- package/handlers/app-handler-helpers.js +0 -3
- package/handlers/auth-flow.integration.test.js +147 -0
- package/handlers/backend-utils.js +25 -45
- package/handlers/integration-event-dispatcher.js +54 -0
- package/handlers/integration-event-dispatcher.test.js +141 -0
- package/handlers/routers/HEALTHCHECK.md +103 -1
- package/handlers/routers/auth.js +3 -14
- package/handlers/routers/health.js +63 -424
- package/handlers/routers/health.test.js +7 -0
- package/handlers/routers/integration-defined-routers.js +8 -5
- package/handlers/routers/user.js +25 -5
- package/handlers/routers/websocket.js +5 -3
- package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
- package/handlers/use-cases/check-integrations-health-use-case.js +32 -0
- package/handlers/workers/integration-defined-workers.js +6 -3
- package/index.js +45 -22
- package/integrations/index.js +12 -10
- package/integrations/integration-base.js +224 -53
- package/integrations/integration-router.js +386 -178
- package/integrations/options.js +1 -1
- package/integrations/repositories/integration-mapping-repository-factory.js +50 -0
- package/integrations/repositories/integration-mapping-repository-interface.js +106 -0
- package/integrations/repositories/integration-mapping-repository-mongo.js +161 -0
- package/integrations/repositories/integration-mapping-repository-postgres.js +227 -0
- package/integrations/repositories/integration-mapping-repository.js +156 -0
- package/integrations/repositories/integration-repository-factory.js +44 -0
- package/integrations/repositories/integration-repository-interface.js +115 -0
- package/integrations/repositories/integration-repository-mongo.js +271 -0
- package/integrations/repositories/integration-repository-postgres.js +319 -0
- package/integrations/tests/doubles/dummy-integration-class.js +90 -0
- package/integrations/tests/doubles/test-integration-repository.js +99 -0
- package/integrations/tests/use-cases/create-integration.test.js +131 -0
- package/integrations/tests/use-cases/delete-integration-for-user.test.js +150 -0
- package/integrations/tests/use-cases/find-integration-context-by-external-entity-id.test.js +92 -0
- package/integrations/tests/use-cases/get-integration-for-user.test.js +150 -0
- package/integrations/tests/use-cases/get-integration-instance.test.js +176 -0
- package/integrations/tests/use-cases/get-integrations-for-user.test.js +176 -0
- package/integrations/tests/use-cases/get-possible-integrations.test.js +188 -0
- package/integrations/tests/use-cases/update-integration-messages.test.js +142 -0
- package/integrations/tests/use-cases/update-integration-status.test.js +103 -0
- package/integrations/tests/use-cases/update-integration.test.js +141 -0
- package/integrations/use-cases/create-integration.js +83 -0
- package/integrations/use-cases/delete-integration-for-user.js +73 -0
- package/integrations/use-cases/find-integration-context-by-external-entity-id.js +72 -0
- package/integrations/use-cases/get-integration-for-user.js +78 -0
- package/integrations/use-cases/get-integration-instance-by-definition.js +67 -0
- package/integrations/use-cases/get-integration-instance.js +83 -0
- package/integrations/use-cases/get-integrations-for-user.js +87 -0
- package/integrations/use-cases/get-possible-integrations.js +27 -0
- package/integrations/use-cases/index.js +11 -0
- package/integrations/use-cases/load-integration-context-full.test.js +329 -0
- package/integrations/use-cases/load-integration-context.js +71 -0
- package/integrations/use-cases/load-integration-context.test.js +114 -0
- package/integrations/use-cases/update-integration-messages.js +44 -0
- package/integrations/use-cases/update-integration-status.js +32 -0
- package/integrations/use-cases/update-integration.js +93 -0
- package/integrations/utils/map-integration-dto.js +36 -0
- package/jest-global-setup-noop.js +3 -0
- package/jest-global-teardown-noop.js +3 -0
- package/{module-plugin → modules}/entity.js +1 -0
- package/{module-plugin → modules}/index.js +0 -8
- package/modules/module-factory.js +56 -0
- package/modules/module-hydration.test.js +205 -0
- package/modules/module.js +221 -0
- package/modules/repositories/module-repository-factory.js +33 -0
- package/modules/repositories/module-repository-interface.js +129 -0
- package/modules/repositories/module-repository-mongo.js +386 -0
- package/modules/repositories/module-repository-postgres.js +437 -0
- package/modules/repositories/module-repository.js +327 -0
- package/{module-plugin → modules}/test/mock-api/api.js +8 -3
- package/{module-plugin → modules}/test/mock-api/definition.js +12 -8
- package/modules/tests/doubles/test-module-factory.js +16 -0
- package/modules/tests/doubles/test-module-repository.js +39 -0
- package/modules/use-cases/get-entities-for-user.js +32 -0
- package/modules/use-cases/get-entity-options-by-id.js +59 -0
- package/modules/use-cases/get-entity-options-by-type.js +34 -0
- package/modules/use-cases/get-module-instance-from-type.js +31 -0
- package/modules/use-cases/get-module.js +56 -0
- package/modules/use-cases/process-authorization-callback.js +121 -0
- package/modules/use-cases/refresh-entity-options.js +59 -0
- package/modules/use-cases/test-module-auth.js +55 -0
- package/modules/utils/map-module-dto.js +18 -0
- package/package.json +14 -6
- package/prisma-mongodb/schema.prisma +321 -0
- package/prisma-postgresql/migrations/20250930193005_init/migration.sql +315 -0
- package/prisma-postgresql/migrations/20251006135218_init/migration.sql +9 -0
- package/prisma-postgresql/migrations/migration_lock.toml +3 -0
- package/prisma-postgresql/schema.prisma +303 -0
- package/syncs/manager.js +468 -443
- package/syncs/repositories/sync-repository-factory.js +38 -0
- package/syncs/repositories/sync-repository-interface.js +109 -0
- package/syncs/repositories/sync-repository-mongo.js +239 -0
- package/syncs/repositories/sync-repository-postgres.js +319 -0
- package/syncs/sync.js +0 -1
- package/token/repositories/token-repository-factory.js +33 -0
- package/token/repositories/token-repository-interface.js +131 -0
- package/token/repositories/token-repository-mongo.js +212 -0
- package/token/repositories/token-repository-postgres.js +257 -0
- package/token/repositories/token-repository.js +219 -0
- package/types/integrations/index.d.ts +2 -6
- package/types/module-plugin/index.d.ts +5 -57
- package/types/syncs/index.d.ts +0 -2
- package/user/repositories/user-repository-factory.js +46 -0
- package/user/repositories/user-repository-interface.js +198 -0
- package/user/repositories/user-repository-mongo.js +250 -0
- package/user/repositories/user-repository-postgres.js +311 -0
- package/user/tests/doubles/test-user-repository.js +72 -0
- package/user/tests/use-cases/create-individual-user.test.js +24 -0
- package/user/tests/use-cases/create-organization-user.test.js +28 -0
- package/user/tests/use-cases/create-token-for-user-id.test.js +19 -0
- package/user/tests/use-cases/get-user-from-bearer-token.test.js +64 -0
- package/user/tests/use-cases/login-user.test.js +140 -0
- package/user/use-cases/create-individual-user.js +61 -0
- package/user/use-cases/create-organization-user.js +47 -0
- package/user/use-cases/create-token-for-user-id.js +30 -0
- package/user/use-cases/get-user-from-bearer-token.js +77 -0
- package/user/use-cases/login-user.js +122 -0
- package/user/user.js +77 -0
- package/websocket/repositories/websocket-connection-repository-factory.js +37 -0
- package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
- package/websocket/repositories/websocket-connection-repository-mongo.js +155 -0
- package/websocket/repositories/websocket-connection-repository-postgres.js +195 -0
- package/websocket/repositories/websocket-connection-repository.js +160 -0
- package/database/models/State.js +0 -9
- package/database/models/Token.js +0 -70
- package/database/mongo.js +0 -171
- package/encrypt/Cryptor.test.js +0 -32
- package/encrypt/encrypt.js +0 -104
- package/encrypt/encrypt.test.js +0 -1069
- package/handlers/routers/middleware/loadUser.js +0 -15
- package/handlers/routers/middleware/requireLoggedInUser.js +0 -12
- package/integrations/create-frigg-backend.js +0 -31
- package/integrations/integration-factory.js +0 -251
- package/integrations/integration-mapping.js +0 -43
- package/integrations/integration-model.js +0 -46
- package/integrations/integration-user.js +0 -144
- package/integrations/test/integration-base.test.js +0 -144
- package/module-plugin/auther.js +0 -393
- package/module-plugin/credential.js +0 -22
- package/module-plugin/entity-manager.js +0 -70
- package/module-plugin/manager.js +0 -169
- package/module-plugin/module-factory.js +0 -61
- package/module-plugin/test/auther.test.js +0 -97
- /package/{module-plugin → modules}/ModuleConstants.js +0 -0
- /package/{module-plugin → modules}/requester/api-key.js +0 -0
- /package/{module-plugin → modules}/requester/basic.js +0 -0
- /package/{module-plugin → modules}/requester/oauth-2.js +0 -0
- /package/{module-plugin → modules}/requester/requester.js +0 -0
- /package/{module-plugin → modules}/requester/requester.test.js +0 -0
- /package/{module-plugin → modules}/test/mock-api/mocks/hubspot.js +0 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module Repository Interface
|
|
3
|
+
* Abstract base class defining the contract for Entity (module) persistence adapters
|
|
4
|
+
*
|
|
5
|
+
* This follows the Port in Hexagonal Architecture:
|
|
6
|
+
* - Domain layer depends on this abstraction
|
|
7
|
+
* - Concrete adapters implement this interface
|
|
8
|
+
* - Use cases receive repositories via dependency injection
|
|
9
|
+
*
|
|
10
|
+
* Note: Currently, Entity model has identical structure across MongoDB and PostgreSQL,
|
|
11
|
+
* so ModuleRepository serves both. This interface exists for consistency and
|
|
12
|
+
* future-proofing if database-specific implementations become needed.
|
|
13
|
+
*
|
|
14
|
+
* @abstract
|
|
15
|
+
*/
|
|
16
|
+
class ModuleRepositoryInterface {
|
|
17
|
+
/**
|
|
18
|
+
* Find entity by ID with credential
|
|
19
|
+
*
|
|
20
|
+
* @param {string|number} entityId - Entity ID
|
|
21
|
+
* @returns {Promise<Object>} Entity object
|
|
22
|
+
* @abstract
|
|
23
|
+
*/
|
|
24
|
+
async findEntityById(entityId) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
'Method findEntityById must be implemented by subclass'
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Find all entities for a user
|
|
32
|
+
*
|
|
33
|
+
* @param {string|number} userId - User ID
|
|
34
|
+
* @returns {Promise<Array>} Array of entity objects
|
|
35
|
+
* @abstract
|
|
36
|
+
*/
|
|
37
|
+
async findEntitiesByUserId(userId) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
'Method findEntitiesByUserId must be implemented by subclass'
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Find entities by IDs
|
|
45
|
+
*
|
|
46
|
+
* @param {Array<string|number>} entitiesIds - Array of entity IDs
|
|
47
|
+
* @returns {Promise<Array>} Array of entity objects
|
|
48
|
+
* @abstract
|
|
49
|
+
*/
|
|
50
|
+
async findEntitiesByIds(entitiesIds) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
'Method findEntitiesByIds must be implemented by subclass'
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Find entities by user ID and module name
|
|
58
|
+
*
|
|
59
|
+
* @param {string|number} userId - User ID
|
|
60
|
+
* @param {string} moduleName - Module name
|
|
61
|
+
* @returns {Promise<Array>} Array of entity objects
|
|
62
|
+
* @abstract
|
|
63
|
+
*/
|
|
64
|
+
async findEntitiesByUserIdAndModuleName(userId, moduleName) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
'Method findEntitiesByUserIdAndModuleName must be implemented by subclass'
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Unset credential from entity
|
|
72
|
+
*
|
|
73
|
+
* @param {string|number} entityId - Entity ID
|
|
74
|
+
* @returns {Promise<Object>} Update result
|
|
75
|
+
* @abstract
|
|
76
|
+
*/
|
|
77
|
+
async unsetCredential(entityId) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
'Method unsetCredential must be implemented by subclass'
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Find entity by filter
|
|
85
|
+
*
|
|
86
|
+
* @param {Object} filter - Filter criteria
|
|
87
|
+
* @returns {Promise<Object|null>} Entity object or null
|
|
88
|
+
* @abstract
|
|
89
|
+
*/
|
|
90
|
+
async findEntity(filter) {
|
|
91
|
+
throw new Error('Method findEntity must be implemented by subclass');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Create a new entity
|
|
96
|
+
*
|
|
97
|
+
* @param {Object} entityData - Entity data
|
|
98
|
+
* @returns {Promise<Object>} Created entity object
|
|
99
|
+
* @abstract
|
|
100
|
+
*/
|
|
101
|
+
async createEntity(entityData) {
|
|
102
|
+
throw new Error('Method createEntity must be implemented by subclass');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Update entity by ID
|
|
107
|
+
*
|
|
108
|
+
* @param {string|number} entityId - Entity ID to update
|
|
109
|
+
* @param {Object} updates - Fields to update
|
|
110
|
+
* @returns {Promise<Object>} Updated entity object
|
|
111
|
+
* @abstract
|
|
112
|
+
*/
|
|
113
|
+
async updateEntity(entityId, updates) {
|
|
114
|
+
throw new Error('Method updateEntity must be implemented by subclass');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Delete entity by ID
|
|
119
|
+
*
|
|
120
|
+
* @param {string|number} entityId - Entity ID to delete
|
|
121
|
+
* @returns {Promise<Object>} Deletion result
|
|
122
|
+
* @abstract
|
|
123
|
+
*/
|
|
124
|
+
async deleteEntity(entityId) {
|
|
125
|
+
throw new Error('Method deleteEntity must be implemented by subclass');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
module.exports = { ModuleRepositoryInterface };
|
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const { ModuleRepositoryInterface } = require('./module-repository-interface');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* MongoDB Module Repository Adapter
|
|
6
|
+
* Handles Entity model operations for external service entities with MongoDB
|
|
7
|
+
*
|
|
8
|
+
* MongoDB-specific characteristics:
|
|
9
|
+
* - Uses String IDs (ObjectId)
|
|
10
|
+
* - No ID conversion needed (IDs are already strings)
|
|
11
|
+
* - entity.__t (discriminator) → entity.subType
|
|
12
|
+
*/
|
|
13
|
+
class ModuleRepositoryMongo extends ModuleRepositoryInterface {
|
|
14
|
+
constructor() {
|
|
15
|
+
super();
|
|
16
|
+
this.prisma = prisma;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Convert any value to string (handles null/undefined)
|
|
21
|
+
* @private
|
|
22
|
+
* @param {*} value - Value to convert
|
|
23
|
+
* @returns {string|null|undefined} String value or null/undefined
|
|
24
|
+
*/
|
|
25
|
+
_toString(value) {
|
|
26
|
+
if (value === null || value === undefined) return value;
|
|
27
|
+
return String(value);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Fetch credential by ID separately to ensure encryption extension processes it
|
|
32
|
+
* This fixes the bug where credentials fetched via include bypass decryption
|
|
33
|
+
* @private
|
|
34
|
+
* @param {string|null|undefined} credentialId - Credential ID
|
|
35
|
+
* @returns {Promise<Object|null>} Decrypted credential or null
|
|
36
|
+
*/
|
|
37
|
+
async _fetchCredential(credentialId) {
|
|
38
|
+
if (!credentialId) return null;
|
|
39
|
+
|
|
40
|
+
const credential = await this.prisma.credential.findUnique({
|
|
41
|
+
where: { id: credentialId },
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return credential;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Fetch multiple credentials in bulk separately to ensure decryption
|
|
49
|
+
* More efficient than fetching one-by-one for arrays of entities
|
|
50
|
+
* @private
|
|
51
|
+
* @param {Array<string>} credentialIds - Array of credential IDs
|
|
52
|
+
* @returns {Promise<Map<string, Object>>} Map of credentialId -> credential object
|
|
53
|
+
*/
|
|
54
|
+
async _fetchCredentialsBulk(credentialIds) {
|
|
55
|
+
if (!credentialIds || credentialIds.length === 0) {
|
|
56
|
+
return new Map();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const validIds = credentialIds.filter(id => id !== null && id !== undefined);
|
|
60
|
+
|
|
61
|
+
if (validIds.length === 0) {
|
|
62
|
+
return new Map();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const credentials = await this.prisma.credential.findMany({
|
|
66
|
+
where: { id: { in: validIds } },
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const credentialMap = new Map();
|
|
70
|
+
for (const credential of credentials) {
|
|
71
|
+
credentialMap.set(credential.id, credential);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return credentialMap;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Find entity by ID with credential
|
|
79
|
+
* Replaces: Entity.findById(entityId).populate('credential')
|
|
80
|
+
*
|
|
81
|
+
* @param {string} entityId - Entity ID
|
|
82
|
+
* @returns {Promise<Object>} Entity object with string IDs
|
|
83
|
+
* @throws {Error} If entity not found
|
|
84
|
+
*/
|
|
85
|
+
async findEntityById(entityId) {
|
|
86
|
+
const entity = await this.prisma.entity.findUnique({
|
|
87
|
+
where: { id: entityId },
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (!entity) {
|
|
91
|
+
throw new Error(`Entity ${entityId} not found`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const credential = await this._fetchCredential(entity.credentialId);
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
id: entity.id,
|
|
98
|
+
accountId: entity.accountId,
|
|
99
|
+
credential,
|
|
100
|
+
userId: entity.userId,
|
|
101
|
+
name: entity.name,
|
|
102
|
+
externalId: entity.externalId,
|
|
103
|
+
type: entity.subType,
|
|
104
|
+
moduleName: entity.moduleName,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Find all entities for a user
|
|
110
|
+
* Replaces: Entity.find({ user: userId }).populate('credential')
|
|
111
|
+
*
|
|
112
|
+
* @param {string} userId - User ID
|
|
113
|
+
* @returns {Promise<Array>} Array of entity objects with string IDs
|
|
114
|
+
*/
|
|
115
|
+
async findEntitiesByUserId(userId) {
|
|
116
|
+
const entities = await this.prisma.entity.findMany({
|
|
117
|
+
where: { userId },
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const credentialIds = entities.map(e => e.credentialId).filter(Boolean);
|
|
121
|
+
const credentialMap = await this._fetchCredentialsBulk(credentialIds);
|
|
122
|
+
|
|
123
|
+
return entities.map((e) => ({
|
|
124
|
+
id: e.id,
|
|
125
|
+
accountId: e.accountId,
|
|
126
|
+
credential: credentialMap.get(e.credentialId) || null,
|
|
127
|
+
userId: e.userId,
|
|
128
|
+
name: e.name,
|
|
129
|
+
externalId: e.externalId,
|
|
130
|
+
type: e.subType,
|
|
131
|
+
moduleName: e.moduleName,
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Find entities by array of IDs
|
|
137
|
+
* Replaces: Entity.find({ _id: { $in: entitiesIds } }).populate('credential')
|
|
138
|
+
*
|
|
139
|
+
* @param {Array<string>} entitiesIds - Array of entity IDs
|
|
140
|
+
* @returns {Promise<Array>} Array of entity objects with string IDs
|
|
141
|
+
*/
|
|
142
|
+
async findEntitiesByIds(entitiesIds) {
|
|
143
|
+
const entities = await this.prisma.entity.findMany({
|
|
144
|
+
where: { id: { in: entitiesIds } },
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const credentialIds = entities.map(e => e.credentialId).filter(Boolean);
|
|
148
|
+
const credentialMap = await this._fetchCredentialsBulk(credentialIds);
|
|
149
|
+
|
|
150
|
+
return entities.map((e) => ({
|
|
151
|
+
id: e.id,
|
|
152
|
+
accountId: e.accountId,
|
|
153
|
+
credential: credentialMap.get(e.credentialId) || null,
|
|
154
|
+
userId: e.userId,
|
|
155
|
+
name: e.name,
|
|
156
|
+
externalId: e.externalId,
|
|
157
|
+
type: e.subType,
|
|
158
|
+
moduleName: e.moduleName,
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Find entities by user ID and module name
|
|
164
|
+
* Replaces: Entity.find({ user: userId, moduleName: moduleName }).populate('credential')
|
|
165
|
+
*
|
|
166
|
+
* @param {string} userId - User ID
|
|
167
|
+
* @param {string} moduleName - Module name
|
|
168
|
+
* @returns {Promise<Array>} Array of entity objects with string IDs
|
|
169
|
+
*/
|
|
170
|
+
async findEntitiesByUserIdAndModuleName(userId, moduleName) {
|
|
171
|
+
const entities = await this.prisma.entity.findMany({
|
|
172
|
+
where: {
|
|
173
|
+
userId,
|
|
174
|
+
moduleName,
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const credentialIds = entities.map(e => e.credentialId).filter(Boolean);
|
|
179
|
+
const credentialMap = await this._fetchCredentialsBulk(credentialIds);
|
|
180
|
+
|
|
181
|
+
return entities.map((e) => ({
|
|
182
|
+
id: e.id,
|
|
183
|
+
accountId: e.accountId,
|
|
184
|
+
credential: credentialMap.get(e.credentialId) || null,
|
|
185
|
+
userId: e.userId,
|
|
186
|
+
name: e.name,
|
|
187
|
+
externalId: e.externalId,
|
|
188
|
+
type: e.subType,
|
|
189
|
+
moduleName: e.moduleName,
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Remove credential reference from entity
|
|
195
|
+
* Replaces: Entity.updateOne({ _id: entityId }, { $unset: { credential: "" } })
|
|
196
|
+
*
|
|
197
|
+
* @param {string} entityId - Entity ID
|
|
198
|
+
* @returns {Promise<boolean>} Success indicator
|
|
199
|
+
*/
|
|
200
|
+
async unsetCredential(entityId) {
|
|
201
|
+
await this.prisma.entity.update({
|
|
202
|
+
where: { id: entityId },
|
|
203
|
+
data: { credentialId: null },
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Find entity by filter criteria
|
|
211
|
+
* Replaces: Entity.findOne(filter).populate('credential')
|
|
212
|
+
*
|
|
213
|
+
* @param {Object} filter - Filter criteria
|
|
214
|
+
* @returns {Promise<Object|null>} Entity object with string IDs or null
|
|
215
|
+
*/
|
|
216
|
+
async findEntity(filter) {
|
|
217
|
+
const where = this._convertFilterToWhere(filter);
|
|
218
|
+
const entity = await this.prisma.entity.findFirst({
|
|
219
|
+
where,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
if (!entity) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const credential = await this._fetchCredential(entity.credentialId);
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
id: entity.id,
|
|
230
|
+
accountId: entity.accountId,
|
|
231
|
+
credential,
|
|
232
|
+
userId: entity.userId,
|
|
233
|
+
name: entity.name,
|
|
234
|
+
externalId: entity.externalId,
|
|
235
|
+
type: entity.subType,
|
|
236
|
+
moduleName: entity.moduleName,
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Create a new entity
|
|
242
|
+
* Replaces: Entity.create(entityData)
|
|
243
|
+
*
|
|
244
|
+
* @param {Object} entityData - Entity data
|
|
245
|
+
* @returns {Promise<Object>} Created entity object with string IDs
|
|
246
|
+
*/
|
|
247
|
+
async createEntity(entityData) {
|
|
248
|
+
const data = {
|
|
249
|
+
userId: entityData.user || entityData.userId,
|
|
250
|
+
credentialId: entityData.credential || entityData.credentialId,
|
|
251
|
+
subType: entityData.type || entityData.subType,
|
|
252
|
+
name: entityData.name,
|
|
253
|
+
moduleName: entityData.moduleName,
|
|
254
|
+
externalId: entityData.externalId,
|
|
255
|
+
accountId: entityData.accountId,
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const entity = await this.prisma.entity.create({
|
|
259
|
+
data,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const credential = await this._fetchCredential(entity.credentialId);
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
id: entity.id,
|
|
266
|
+
accountId: entity.accountId,
|
|
267
|
+
credential,
|
|
268
|
+
userId: entity.userId,
|
|
269
|
+
name: entity.name,
|
|
270
|
+
externalId: entity.externalId,
|
|
271
|
+
type: entity.subType,
|
|
272
|
+
moduleName: entity.moduleName,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Update an entity by ID
|
|
278
|
+
* Replaces: Entity.findByIdAndUpdate(entityId, updates, { new: true })
|
|
279
|
+
*
|
|
280
|
+
* @param {string} entityId - Entity ID to update
|
|
281
|
+
* @param {Object} updates - Fields to update
|
|
282
|
+
* @returns {Promise<Object|null>} Updated entity object with string IDs or null if not found
|
|
283
|
+
*/
|
|
284
|
+
async updateEntity(entityId, updates) {
|
|
285
|
+
const data = {};
|
|
286
|
+
if (updates.user !== undefined) data.userId = updates.user;
|
|
287
|
+
if (updates.userId !== undefined) data.userId = updates.userId;
|
|
288
|
+
if (updates.credential !== undefined)
|
|
289
|
+
data.credentialId = updates.credential;
|
|
290
|
+
if (updates.credentialId !== undefined)
|
|
291
|
+
data.credentialId = updates.credentialId;
|
|
292
|
+
if (updates.type !== undefined) data.subType = updates.type;
|
|
293
|
+
if (updates.subType !== undefined) data.subType = updates.subType;
|
|
294
|
+
if (updates.name !== undefined) data.name = updates.name;
|
|
295
|
+
if (updates.moduleName !== undefined)
|
|
296
|
+
data.moduleName = updates.moduleName;
|
|
297
|
+
if (updates.externalId !== undefined)
|
|
298
|
+
data.externalId = updates.externalId;
|
|
299
|
+
if (updates.accountId !== undefined) data.accountId = updates.accountId;
|
|
300
|
+
|
|
301
|
+
try {
|
|
302
|
+
const entity = await this.prisma.entity.update({
|
|
303
|
+
where: { id: entityId },
|
|
304
|
+
data,
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const credential = await this._fetchCredential(entity.credentialId);
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
id: entity.id,
|
|
311
|
+
accountId: entity.accountId,
|
|
312
|
+
credential,
|
|
313
|
+
userId: entity.userId,
|
|
314
|
+
name: entity.name,
|
|
315
|
+
externalId: entity.externalId,
|
|
316
|
+
type: entity.subType,
|
|
317
|
+
moduleName: entity.moduleName,
|
|
318
|
+
};
|
|
319
|
+
} catch (error) {
|
|
320
|
+
if (error.code === 'P2025') {
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
throw error;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Delete an entity by ID
|
|
329
|
+
* Replaces: Entity.deleteOne({ _id: entityId })
|
|
330
|
+
*
|
|
331
|
+
* @param {string} entityId - Entity ID to delete
|
|
332
|
+
* @returns {Promise<boolean>} True if deleted successfully
|
|
333
|
+
*/
|
|
334
|
+
async deleteEntity(entityId) {
|
|
335
|
+
try {
|
|
336
|
+
await this.prisma.entity.delete({
|
|
337
|
+
where: { id: entityId },
|
|
338
|
+
});
|
|
339
|
+
return true;
|
|
340
|
+
} catch (error) {
|
|
341
|
+
if (error.code === 'P2025') {
|
|
342
|
+
// Record not found
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
throw error;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Convert Mongoose-style filter to Prisma where clause
|
|
351
|
+
* @private
|
|
352
|
+
* @param {Object} filter - Mongoose filter
|
|
353
|
+
* @returns {Object} Prisma where clause
|
|
354
|
+
*/
|
|
355
|
+
_convertFilterToWhere(filter) {
|
|
356
|
+
const where = {};
|
|
357
|
+
|
|
358
|
+
// Handle _id field (Mongoose uses _id, Prisma uses id)
|
|
359
|
+
if (filter._id) {
|
|
360
|
+
where.id = filter._id;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Handle user field (Mongoose uses user, Prisma uses userId)
|
|
364
|
+
if (filter.user) {
|
|
365
|
+
where.userId = filter.user;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Handle credential field (Mongoose uses credential, Prisma uses credentialId)
|
|
369
|
+
if (filter.credential) {
|
|
370
|
+
where.credentialId = filter.credential;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Copy other fields directly
|
|
374
|
+
if (filter.id) where.id = filter.id;
|
|
375
|
+
if (filter.userId) where.userId = filter.userId;
|
|
376
|
+
if (filter.credentialId) where.credentialId = filter.credentialId;
|
|
377
|
+
if (filter.name) where.name = filter.name;
|
|
378
|
+
if (filter.moduleName) where.moduleName = filter.moduleName;
|
|
379
|
+
if (filter.externalId) where.externalId = this._toString(filter.externalId);
|
|
380
|
+
if (filter.subType) where.subType = filter.subType;
|
|
381
|
+
|
|
382
|
+
return where;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
module.exports = { ModuleRepositoryMongo };
|