@friggframework/core 2.0.0-next.6 → 2.0.0-next.60
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 +694 -0
- package/README.md +959 -50
- package/application/commands/README.md +451 -0
- package/application/commands/credential-commands.js +245 -0
- package/application/commands/entity-commands.js +336 -0
- package/application/commands/integration-commands.js +210 -0
- package/application/commands/user-commands.js +283 -0
- package/application/index.js +69 -0
- package/core/CLAUDE.md +690 -0
- package/core/Worker.js +8 -21
- package/core/create-handler.js +14 -7
- package/credential/repositories/credential-repository-documentdb.js +304 -0
- package/credential/repositories/credential-repository-factory.js +54 -0
- package/credential/repositories/credential-repository-interface.js +98 -0
- package/credential/repositories/credential-repository-mongo.js +269 -0
- package/credential/repositories/credential-repository-postgres.js +291 -0
- package/credential/repositories/credential-repository.js +302 -0
- package/credential/use-cases/get-credential-for-user.js +25 -0
- package/credential/use-cases/update-authentication-status.js +15 -0
- package/database/MONGODB_TRANSACTION_FIX.md +198 -0
- package/database/adapters/lambda-invoker.js +97 -0
- package/database/config.js +154 -0
- package/database/documentdb-encryption-service.js +330 -0
- package/database/documentdb-utils.js +136 -0
- package/database/encryption/README.md +839 -0
- package/database/encryption/documentdb-encryption-service.md +3575 -0
- package/database/encryption/encryption-schema-registry.js +268 -0
- package/database/encryption/field-encryption-service.js +226 -0
- package/database/encryption/logger.js +79 -0
- package/database/encryption/prisma-encryption-extension.js +222 -0
- package/database/index.js +61 -21
- package/database/models/WebsocketConnection.js +16 -10
- package/database/models/readme.md +1 -0
- package/database/prisma.js +182 -0
- package/database/repositories/health-check-repository-documentdb.js +134 -0
- package/database/repositories/health-check-repository-factory.js +48 -0
- package/database/repositories/health-check-repository-interface.js +82 -0
- package/database/repositories/health-check-repository-mongodb.js +89 -0
- package/database/repositories/health-check-repository-postgres.js +82 -0
- package/database/repositories/health-check-repository.js +108 -0
- package/database/repositories/migration-status-repository-s3.js +137 -0
- package/database/use-cases/check-database-health-use-case.js +29 -0
- package/database/use-cases/check-database-state-use-case.js +81 -0
- package/database/use-cases/check-encryption-health-use-case.js +83 -0
- package/database/use-cases/get-database-state-via-worker-use-case.js +61 -0
- package/database/use-cases/get-migration-status-use-case.js +93 -0
- package/database/use-cases/run-database-migration-use-case.js +139 -0
- package/database/use-cases/test-encryption-use-case.js +253 -0
- package/database/use-cases/trigger-database-migration-use-case.js +157 -0
- package/database/utils/mongodb-collection-utils.js +91 -0
- package/database/utils/mongodb-schema-init.js +106 -0
- package/database/utils/prisma-runner.js +477 -0
- package/database/utils/prisma-schema-parser.js +182 -0
- package/docs/PROCESS_MANAGEMENT_QUEUE_SPEC.md +517 -0
- package/encrypt/Cryptor.js +34 -168
- package/encrypt/index.js +1 -2
- package/encrypt/test-encrypt.js +0 -2
- package/errors/client-safe-error.js +26 -0
- package/errors/fetch-error.js +2 -1
- package/errors/index.js +2 -0
- package/generated/prisma-mongodb/client.d.ts +1 -0
- package/generated/prisma-mongodb/client.js +4 -0
- package/generated/prisma-mongodb/default.d.ts +1 -0
- package/generated/prisma-mongodb/default.js +4 -0
- package/generated/prisma-mongodb/edge.d.ts +1 -0
- package/generated/prisma-mongodb/edge.js +334 -0
- package/generated/prisma-mongodb/index-browser.js +316 -0
- package/generated/prisma-mongodb/index.d.ts +22903 -0
- package/generated/prisma-mongodb/index.js +359 -0
- package/generated/prisma-mongodb/package.json +183 -0
- package/generated/prisma-mongodb/query-engine-debian-openssl-3.0.x +0 -0
- package/generated/prisma-mongodb/query-engine-rhel-openssl-3.0.x +0 -0
- package/generated/prisma-mongodb/runtime/binary.d.ts +1 -0
- package/generated/prisma-mongodb/runtime/binary.js +289 -0
- package/generated/prisma-mongodb/runtime/edge-esm.js +34 -0
- package/generated/prisma-mongodb/runtime/edge.js +34 -0
- package/generated/prisma-mongodb/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-mongodb/runtime/index-browser.js +16 -0
- package/generated/prisma-mongodb/runtime/library.d.ts +3977 -0
- package/generated/prisma-mongodb/runtime/react-native.js +83 -0
- package/generated/prisma-mongodb/runtime/wasm-compiler-edge.js +84 -0
- package/generated/prisma-mongodb/runtime/wasm-engine-edge.js +36 -0
- package/generated/prisma-mongodb/schema.prisma +360 -0
- package/generated/prisma-mongodb/wasm-edge-light-loader.mjs +4 -0
- package/generated/prisma-mongodb/wasm-worker-loader.mjs +4 -0
- package/generated/prisma-mongodb/wasm.d.ts +1 -0
- package/generated/prisma-mongodb/wasm.js +341 -0
- package/generated/prisma-postgresql/client.d.ts +1 -0
- package/generated/prisma-postgresql/client.js +4 -0
- package/generated/prisma-postgresql/default.d.ts +1 -0
- package/generated/prisma-postgresql/default.js +4 -0
- package/generated/prisma-postgresql/edge.d.ts +1 -0
- package/generated/prisma-postgresql/edge.js +356 -0
- package/generated/prisma-postgresql/index-browser.js +338 -0
- package/generated/prisma-postgresql/index.d.ts +25077 -0
- package/generated/prisma-postgresql/index.js +381 -0
- package/generated/prisma-postgresql/package.json +183 -0
- package/generated/prisma-postgresql/query-engine-debian-openssl-3.0.x +0 -0
- package/generated/prisma-postgresql/query-engine-rhel-openssl-3.0.x +0 -0
- package/generated/prisma-postgresql/query_engine_bg.js +2 -0
- package/generated/prisma-postgresql/query_engine_bg.wasm +0 -0
- package/generated/prisma-postgresql/runtime/binary.d.ts +1 -0
- package/generated/prisma-postgresql/runtime/binary.js +289 -0
- package/generated/prisma-postgresql/runtime/edge-esm.js +34 -0
- package/generated/prisma-postgresql/runtime/edge.js +34 -0
- package/generated/prisma-postgresql/runtime/index-browser.d.ts +370 -0
- package/generated/prisma-postgresql/runtime/index-browser.js +16 -0
- package/generated/prisma-postgresql/runtime/library.d.ts +3977 -0
- package/generated/prisma-postgresql/runtime/react-native.js +83 -0
- package/generated/prisma-postgresql/runtime/wasm-compiler-edge.js +84 -0
- package/generated/prisma-postgresql/runtime/wasm-engine-edge.js +36 -0
- package/generated/prisma-postgresql/schema.prisma +343 -0
- package/generated/prisma-postgresql/wasm-edge-light-loader.mjs +4 -0
- package/generated/prisma-postgresql/wasm-worker-loader.mjs +4 -0
- package/generated/prisma-postgresql/wasm.d.ts +1 -0
- package/generated/prisma-postgresql/wasm.js +363 -0
- package/handlers/WEBHOOKS.md +653 -0
- package/handlers/app-definition-loader.js +38 -0
- package/handlers/app-handler-helpers.js +56 -0
- package/handlers/backend-utils.js +186 -0
- package/handlers/database-migration-handler.js +227 -0
- package/handlers/integration-event-dispatcher.js +54 -0
- package/handlers/routers/HEALTHCHECK.md +342 -0
- package/handlers/routers/auth.js +15 -0
- package/handlers/routers/db-migration.handler.js +29 -0
- package/handlers/routers/db-migration.js +326 -0
- package/handlers/routers/health.js +516 -0
- package/handlers/routers/integration-defined-routers.js +45 -0
- package/handlers/routers/integration-webhook-routers.js +67 -0
- package/handlers/routers/user.js +63 -0
- package/handlers/routers/websocket.js +57 -0
- package/handlers/use-cases/check-external-apis-health-use-case.js +81 -0
- package/handlers/use-cases/check-integrations-health-use-case.js +44 -0
- package/handlers/workers/db-migration.js +352 -0
- package/handlers/workers/integration-defined-workers.js +27 -0
- package/index.js +77 -22
- package/integrations/WEBHOOK-QUICKSTART.md +151 -0
- package/integrations/index.js +12 -10
- package/integrations/integration-base.js +326 -55
- package/integrations/integration-router.js +374 -179
- package/integrations/options.js +1 -1
- package/integrations/repositories/integration-mapping-repository-documentdb.js +280 -0
- package/integrations/repositories/integration-mapping-repository-factory.js +57 -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-documentdb.js +210 -0
- package/integrations/repositories/integration-repository-factory.js +51 -0
- package/integrations/repositories/integration-repository-interface.js +127 -0
- package/integrations/repositories/integration-repository-mongo.js +303 -0
- package/integrations/repositories/integration-repository-postgres.js +352 -0
- package/integrations/repositories/process-repository-documentdb.js +243 -0
- package/integrations/repositories/process-repository-factory.js +53 -0
- package/integrations/repositories/process-repository-interface.js +90 -0
- package/integrations/repositories/process-repository-mongo.js +190 -0
- package/integrations/repositories/process-repository-postgres.js +217 -0
- package/integrations/tests/doubles/dummy-integration-class.js +83 -0
- package/integrations/tests/doubles/test-integration-repository.js +99 -0
- package/integrations/use-cases/create-integration.js +83 -0
- package/integrations/use-cases/create-process.js +128 -0
- package/integrations/use-cases/delete-integration-for-user.js +101 -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 +88 -0
- package/integrations/use-cases/get-possible-integrations.js +27 -0
- package/integrations/use-cases/get-process.js +87 -0
- package/integrations/use-cases/index.js +19 -0
- package/integrations/use-cases/load-integration-context.js +71 -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/use-cases/update-process-metrics.js +201 -0
- package/integrations/use-cases/update-process-state.js +119 -0
- package/integrations/utils/map-integration-dto.js +37 -0
- package/jest-global-setup-noop.js +3 -0
- package/jest-global-teardown-noop.js +3 -0
- package/logs/logger.js +0 -4
- package/{module-plugin → modules}/entity.js +1 -1
- package/{module-plugin → modules}/index.js +0 -8
- package/modules/module-factory.js +56 -0
- package/modules/module.js +221 -0
- package/modules/repositories/module-repository-documentdb.js +307 -0
- package/modules/repositories/module-repository-factory.js +40 -0
- package/modules/repositories/module-repository-interface.js +129 -0
- package/modules/repositories/module-repository-mongo.js +377 -0
- package/modules/repositories/module-repository-postgres.js +426 -0
- package/modules/repositories/module-repository.js +316 -0
- package/modules/requester/api-key.js +52 -0
- package/{module-plugin → modules}/requester/requester.js +1 -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 +71 -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 +74 -0
- package/modules/use-cases/process-authorization-callback.js +133 -0
- package/modules/use-cases/refresh-entity-options.js +72 -0
- package/modules/use-cases/test-module-auth.js +72 -0
- package/modules/utils/map-module-dto.js +18 -0
- package/package.json +82 -50
- package/prisma-mongodb/schema.prisma +360 -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/20251010000000_remove_unused_entity_reference_map/migration.sql +3 -0
- package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +25 -0
- package/prisma-postgresql/migrations/migration_lock.toml +3 -0
- package/prisma-postgresql/schema.prisma +343 -0
- package/queues/queuer-util.js +27 -22
- package/syncs/manager.js +468 -443
- package/syncs/repositories/sync-repository-documentdb.js +240 -0
- package/syncs/repositories/sync-repository-factory.js +43 -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-documentdb.js +137 -0
- package/token/repositories/token-repository-factory.js +40 -0
- package/token/repositories/token-repository-interface.js +131 -0
- package/token/repositories/token-repository-mongo.js +219 -0
- package/token/repositories/token-repository-postgres.js +264 -0
- package/token/repositories/token-repository.js +219 -0
- package/types/core/index.d.ts +2 -2
- package/types/integrations/index.d.ts +2 -6
- package/types/module-plugin/index.d.ts +5 -59
- package/types/syncs/index.d.ts +0 -2
- package/user/repositories/user-repository-documentdb.js +441 -0
- package/user/repositories/user-repository-factory.js +52 -0
- package/user/repositories/user-repository-interface.js +201 -0
- package/user/repositories/user-repository-mongo.js +308 -0
- package/user/repositories/user-repository-postgres.js +360 -0
- package/user/tests/doubles/test-user-repository.js +72 -0
- package/user/use-cases/authenticate-user.js +127 -0
- package/user/use-cases/authenticate-with-shared-secret.js +48 -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-adopter-jwt.js +149 -0
- package/user/use-cases/get-user-from-bearer-token.js +77 -0
- package/user/use-cases/get-user-from-x-frigg-headers.js +132 -0
- package/user/use-cases/login-user.js +122 -0
- package/user/user.js +125 -0
- package/utils/backend-path.js +38 -0
- package/utils/index.js +6 -0
- package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
- package/websocket/repositories/websocket-connection-repository-factory.js +44 -0
- package/websocket/repositories/websocket-connection-repository-interface.js +106 -0
- package/websocket/repositories/websocket-connection-repository-mongo.js +156 -0
- package/websocket/repositories/websocket-connection-repository-postgres.js +196 -0
- package/websocket/repositories/websocket-connection-repository.js +161 -0
- package/database/models/State.js +0 -9
- package/database/models/Token.js +0 -70
- package/database/mongo.js +0 -45
- package/encrypt/Cryptor.test.js +0 -32
- package/encrypt/encrypt.js +0 -132
- package/encrypt/encrypt.test.js +0 -1069
- package/errors/base-error.test.js +0 -32
- package/errors/fetch-error.test.js +0 -79
- package/errors/halt-error.test.js +0 -11
- package/errors/validation-errors.test.js +0 -120
- 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/lambda/TimeoutCatcher.test.js +0 -68
- package/logs/logger.test.js +0 -76
- 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/requester/api-key.js +0 -36
- package/module-plugin/requester/requester.test.js +0 -28
- package/module-plugin/test/auther.test.js +0 -97
- /package/{module-plugin → modules}/ModuleConstants.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}/test/mock-api/mocks/hubspot.js +0 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const {
|
|
3
|
+
toObjectId,
|
|
4
|
+
fromObjectId,
|
|
5
|
+
findMany,
|
|
6
|
+
findOne,
|
|
7
|
+
insertOne,
|
|
8
|
+
updateOne,
|
|
9
|
+
deleteOne,
|
|
10
|
+
} = require('../../database/documentdb-utils');
|
|
11
|
+
const { ModuleRepositoryInterface } = require('./module-repository-interface');
|
|
12
|
+
const { DocumentDBEncryptionService } = require('../../database/documentdb-encryption-service');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Module/Entity repository for DocumentDB.
|
|
16
|
+
* Uses DocumentDBEncryptionService for credential decryption.
|
|
17
|
+
*
|
|
18
|
+
* Encrypted fields: Credential.data.*
|
|
19
|
+
*
|
|
20
|
+
* Note: This repository only reads credentials. CredentialRepository
|
|
21
|
+
* handles credential creation/updates with encryption.
|
|
22
|
+
*
|
|
23
|
+
* @see DocumentDBEncryptionService
|
|
24
|
+
* @see CredentialRepositoryDocumentDB
|
|
25
|
+
*/
|
|
26
|
+
class ModuleRepositoryDocumentDB extends ModuleRepositoryInterface {
|
|
27
|
+
constructor() {
|
|
28
|
+
super();
|
|
29
|
+
this.prisma = prisma;
|
|
30
|
+
this.encryptionService = new DocumentDBEncryptionService();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async findEntityById(entityId) {
|
|
34
|
+
const objectId = toObjectId(entityId);
|
|
35
|
+
if (!objectId) {
|
|
36
|
+
throw new Error(`Entity ${entityId} not found`);
|
|
37
|
+
}
|
|
38
|
+
const doc = await findOne(this.prisma, 'Entity', { _id: objectId });
|
|
39
|
+
if (!doc) {
|
|
40
|
+
throw new Error(`Entity ${entityId} not found`);
|
|
41
|
+
}
|
|
42
|
+
const credential = await this._fetchCredential(doc.credentialId);
|
|
43
|
+
return this._mapEntity(doc, credential);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async findEntitiesByUserId(userId) {
|
|
47
|
+
const objectId = toObjectId(userId);
|
|
48
|
+
if (!objectId) {
|
|
49
|
+
throw new Error(`Invalid userId: ${userId}`);
|
|
50
|
+
}
|
|
51
|
+
const filter = { userId: objectId };
|
|
52
|
+
const docs = await findMany(this.prisma, 'Entity', filter);
|
|
53
|
+
const credentialMap = await this._fetchCredentialsBulk(docs.map((doc) => doc.credentialId));
|
|
54
|
+
return docs.map((doc) => this._mapEntity(doc, credentialMap.get(fromObjectId(doc.credentialId)) || null));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async findEntitiesByIds(entitiesIds) {
|
|
58
|
+
const ids = (entitiesIds || []).map((id) => toObjectId(id)).filter(Boolean);
|
|
59
|
+
if (ids.length === 0) return [];
|
|
60
|
+
const docs = await findMany(this.prisma, 'Entity', { _id: { $in: ids } });
|
|
61
|
+
const credentialMap = await this._fetchCredentialsBulk(docs.map((doc) => doc.credentialId));
|
|
62
|
+
return docs.map((doc) => this._mapEntity(doc, credentialMap.get(fromObjectId(doc.credentialId)) || null));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async findEntitiesByUserIdAndModuleName(userId, moduleName) {
|
|
66
|
+
const objectId = toObjectId(userId);
|
|
67
|
+
if (!objectId) {
|
|
68
|
+
throw new Error(`Invalid userId: ${userId}`);
|
|
69
|
+
}
|
|
70
|
+
const filter = {
|
|
71
|
+
userId: objectId,
|
|
72
|
+
moduleName,
|
|
73
|
+
};
|
|
74
|
+
const docs = await findMany(this.prisma, 'Entity', filter);
|
|
75
|
+
const credentialMap = await this._fetchCredentialsBulk(docs.map((doc) => doc.credentialId));
|
|
76
|
+
return docs.map((doc) => this._mapEntity(doc, credentialMap.get(fromObjectId(doc.credentialId)) || null));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async unsetCredential(entityId) {
|
|
80
|
+
const objectId = toObjectId(entityId);
|
|
81
|
+
if (!objectId) return false;
|
|
82
|
+
await updateOne(
|
|
83
|
+
this.prisma,
|
|
84
|
+
'Entity',
|
|
85
|
+
{ _id: objectId },
|
|
86
|
+
{
|
|
87
|
+
$set: {
|
|
88
|
+
credentialId: null,
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
);
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async findEntity(filter) {
|
|
96
|
+
const query = this._buildFilter(filter);
|
|
97
|
+
const doc = await findOne(this.prisma, 'Entity', query);
|
|
98
|
+
if (!doc) return null;
|
|
99
|
+
const credential = await this._fetchCredential(doc.credentialId);
|
|
100
|
+
return this._mapEntity(doc, credential);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async createEntity(entityData) {
|
|
104
|
+
const document = {
|
|
105
|
+
userId: toObjectId(entityData.user || entityData.userId),
|
|
106
|
+
credentialId: toObjectId(entityData.credential || entityData.credentialId) || null,
|
|
107
|
+
name: entityData.name ?? null,
|
|
108
|
+
moduleName: entityData.moduleName ?? null,
|
|
109
|
+
externalId: entityData.externalId ?? null,
|
|
110
|
+
accountId: entityData.accountId ?? null,
|
|
111
|
+
};
|
|
112
|
+
const insertedId = await insertOne(this.prisma, 'Entity', document);
|
|
113
|
+
const created = await findOne(this.prisma, 'Entity', { _id: insertedId });
|
|
114
|
+
const credential = await this._fetchCredential(created?.credentialId);
|
|
115
|
+
return this._mapEntity(created, credential);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async updateEntity(entityId, updates) {
|
|
119
|
+
const objectId = toObjectId(entityId);
|
|
120
|
+
if (!objectId) return null;
|
|
121
|
+
const updatePayload = {};
|
|
122
|
+
if (updates.user !== undefined || updates.userId !== undefined) {
|
|
123
|
+
const userVal = updates.user !== undefined ? updates.user : updates.userId;
|
|
124
|
+
updatePayload.userId = toObjectId(userVal) || null;
|
|
125
|
+
}
|
|
126
|
+
if (updates.credential !== undefined || updates.credentialId !== undefined) {
|
|
127
|
+
const credVal = updates.credential !== undefined ? updates.credential : updates.credentialId;
|
|
128
|
+
updatePayload.credentialId = toObjectId(credVal) || null;
|
|
129
|
+
}
|
|
130
|
+
if (updates.name !== undefined) updatePayload.name = updates.name;
|
|
131
|
+
if (updates.moduleName !== undefined) updatePayload.moduleName = updates.moduleName;
|
|
132
|
+
if (updates.externalId !== undefined) updatePayload.externalId = updates.externalId;
|
|
133
|
+
if (updates.accountId !== undefined) updatePayload.accountId = updates.accountId;
|
|
134
|
+
const result = await updateOne(
|
|
135
|
+
this.prisma,
|
|
136
|
+
'Entity',
|
|
137
|
+
{ _id: objectId },
|
|
138
|
+
{ $set: updatePayload }
|
|
139
|
+
);
|
|
140
|
+
const modified = result?.nModified ?? result?.n ?? 0;
|
|
141
|
+
if (modified === 0) return null;
|
|
142
|
+
const updated = await findOne(this.prisma, 'Entity', { _id: objectId });
|
|
143
|
+
const credential = await this._fetchCredential(updated?.credentialId);
|
|
144
|
+
return this._mapEntity(updated, credential);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async deleteEntity(entityId) {
|
|
148
|
+
const objectId = toObjectId(entityId);
|
|
149
|
+
if (!objectId) return false;
|
|
150
|
+
const result = await deleteOne(this.prisma, 'Entity', { _id: objectId });
|
|
151
|
+
const deleted = result?.n ?? 0;
|
|
152
|
+
return deleted > 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async _fetchCredential(credentialId) {
|
|
156
|
+
const id = fromObjectId(credentialId);
|
|
157
|
+
if (!id) return null;
|
|
158
|
+
|
|
159
|
+
try {
|
|
160
|
+
// Convert to ObjectId for raw query
|
|
161
|
+
const objectId = toObjectId(id);
|
|
162
|
+
if (!objectId) return null;
|
|
163
|
+
|
|
164
|
+
// Use raw findOne to bypass Prisma encryption extension
|
|
165
|
+
const rawCredential = await findOne(this.prisma, 'Credential', {
|
|
166
|
+
_id: objectId
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
if (!rawCredential) return null;
|
|
170
|
+
|
|
171
|
+
// Decrypt sensitive fields using service
|
|
172
|
+
const decryptedCredential = await this.encryptionService.decryptFields('Credential', rawCredential);
|
|
173
|
+
|
|
174
|
+
// Return in same format
|
|
175
|
+
const credential = {
|
|
176
|
+
id: fromObjectId(decryptedCredential._id),
|
|
177
|
+
userId: fromObjectId(decryptedCredential.userId),
|
|
178
|
+
externalId: decryptedCredential.externalId ?? null,
|
|
179
|
+
authIsValid: decryptedCredential.authIsValid ?? null,
|
|
180
|
+
createdAt: decryptedCredential.createdAt,
|
|
181
|
+
updatedAt: decryptedCredential.updatedAt,
|
|
182
|
+
data: decryptedCredential.data
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
return this._convertCredentialIds(credential);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
console.error(`Failed to fetch/decrypt credential ${id}:`, error.message);
|
|
188
|
+
// Return null instead of throwing to allow graceful degradation
|
|
189
|
+
// This repository is read-only (doesn't create/update credentials)
|
|
190
|
+
// Entities can still be loaded even if their credential is corrupted/unreadable
|
|
191
|
+
// The entity will have null credential, which calling code must handle
|
|
192
|
+
// This is intentional behavior: prefer partial data over complete failure
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async _fetchCredentialsBulk(credentialIds) {
|
|
198
|
+
const ids = (credentialIds || [])
|
|
199
|
+
.map((value) => fromObjectId(value))
|
|
200
|
+
.filter((value) => value !== null && value !== undefined);
|
|
201
|
+
if (ids.length === 0) return new Map();
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
// Convert string IDs to ObjectIds for bulk query
|
|
205
|
+
const objectIds = ids.map(id => toObjectId(id)).filter(Boolean);
|
|
206
|
+
if (objectIds.length === 0) return new Map();
|
|
207
|
+
|
|
208
|
+
// Use raw findMany to bypass Prisma encryption extension
|
|
209
|
+
const rawCredentials = await findMany(this.prisma, 'Credential', {
|
|
210
|
+
_id: { $in: objectIds }
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Decrypt all credentials in parallel
|
|
214
|
+
const decryptionPromises = rawCredentials.map(async (rawCredential) => {
|
|
215
|
+
try {
|
|
216
|
+
// Decrypt sensitive fields using service
|
|
217
|
+
const decryptedCredential = await this.encryptionService.decryptFields('Credential', rawCredential);
|
|
218
|
+
|
|
219
|
+
// Build credential object in same format as Prisma would return
|
|
220
|
+
const credential = {
|
|
221
|
+
id: fromObjectId(decryptedCredential._id),
|
|
222
|
+
userId: fromObjectId(decryptedCredential.userId),
|
|
223
|
+
externalId: decryptedCredential.externalId ?? null,
|
|
224
|
+
authIsValid: decryptedCredential.authIsValid ?? null,
|
|
225
|
+
createdAt: decryptedCredential.createdAt,
|
|
226
|
+
updatedAt: decryptedCredential.updatedAt,
|
|
227
|
+
data: decryptedCredential.data
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
return this._convertCredentialIds(credential);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
const credId = fromObjectId(rawCredential._id);
|
|
233
|
+
console.error(`Failed to decrypt credential ${credId}:`, error.message);
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Wait for all decryptions to complete
|
|
239
|
+
const decryptedCredentials = await Promise.all(decryptionPromises);
|
|
240
|
+
|
|
241
|
+
// Build Map from results, filtering out nulls
|
|
242
|
+
const map = new Map();
|
|
243
|
+
decryptedCredentials.forEach(credential => {
|
|
244
|
+
if (credential) {
|
|
245
|
+
map.set(credential.id, credential);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return map;
|
|
250
|
+
} catch (error) {
|
|
251
|
+
console.error('Failed to fetch credentials bulk:', error.message);
|
|
252
|
+
return new Map();
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Convert credential object IDs to strings for application layer
|
|
258
|
+
* Ensures consistent credential format across database adapters
|
|
259
|
+
* @private
|
|
260
|
+
* @param {Object|null} credential - Credential object from database
|
|
261
|
+
* @returns {Object|null} Credential with properly formatted IDs
|
|
262
|
+
*/
|
|
263
|
+
_convertCredentialIds(credential) {
|
|
264
|
+
if (!credential) return credential;
|
|
265
|
+
return {
|
|
266
|
+
...credential,
|
|
267
|
+
id: credential.id ? String(credential.id) : null,
|
|
268
|
+
userId: credential.userId ? String(credential.userId) : null,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
_buildFilter(filter) {
|
|
273
|
+
const query = {};
|
|
274
|
+
if (!filter) return query;
|
|
275
|
+
if (filter._id || filter.id) {
|
|
276
|
+
const idObj = toObjectId(filter._id || filter.id);
|
|
277
|
+
if (idObj) query._id = idObj;
|
|
278
|
+
}
|
|
279
|
+
if (filter.user || filter.userId) {
|
|
280
|
+
const userObj = toObjectId(filter.user || filter.userId);
|
|
281
|
+
if (userObj) query.userId = userObj;
|
|
282
|
+
}
|
|
283
|
+
if (filter.credential || filter.credentialId) {
|
|
284
|
+
const credObj = toObjectId(filter.credential || filter.credentialId);
|
|
285
|
+
if (credObj) query.credentialId = credObj;
|
|
286
|
+
}
|
|
287
|
+
if (filter.name) query.name = filter.name;
|
|
288
|
+
if (filter.moduleName) query.moduleName = filter.moduleName;
|
|
289
|
+
if (filter.externalId) query.externalId = filter.externalId;
|
|
290
|
+
return query;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
_mapEntity(doc, credential) {
|
|
294
|
+
return {
|
|
295
|
+
id: fromObjectId(doc?._id),
|
|
296
|
+
accountId: doc?.accountId ?? null,
|
|
297
|
+
credential,
|
|
298
|
+
userId: fromObjectId(doc?.userId),
|
|
299
|
+
name: doc?.name ?? null,
|
|
300
|
+
externalId: doc?.externalId ?? null,
|
|
301
|
+
moduleName: doc?.moduleName ?? null,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
module.exports = { ModuleRepositoryDocumentDB };
|
|
307
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const { ModuleRepositoryMongo } = require('./module-repository-mongo');
|
|
2
|
+
const { ModuleRepositoryPostgres } = require('./module-repository-postgres');
|
|
3
|
+
const {
|
|
4
|
+
ModuleRepositoryDocumentDB,
|
|
5
|
+
} = require('./module-repository-documentdb');
|
|
6
|
+
const config = require('../../database/config');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Module Repository Factory
|
|
10
|
+
* Creates the appropriate repository adapter based on database type
|
|
11
|
+
*
|
|
12
|
+
* @returns {ModuleRepositoryInterface} Configured repository adapter
|
|
13
|
+
*/
|
|
14
|
+
function createModuleRepository() {
|
|
15
|
+
const dbType = config.DB_TYPE;
|
|
16
|
+
|
|
17
|
+
switch (dbType) {
|
|
18
|
+
case 'mongodb':
|
|
19
|
+
return new ModuleRepositoryMongo();
|
|
20
|
+
|
|
21
|
+
case 'postgresql':
|
|
22
|
+
return new ModuleRepositoryPostgres();
|
|
23
|
+
|
|
24
|
+
case 'documentdb':
|
|
25
|
+
return new ModuleRepositoryDocumentDB();
|
|
26
|
+
|
|
27
|
+
default:
|
|
28
|
+
throw new Error(
|
|
29
|
+
`Unsupported database type: ${dbType}. Supported values: 'mongodb', 'documentdb', 'postgresql'`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
createModuleRepository,
|
|
36
|
+
// Export adapters for direct testing
|
|
37
|
+
ModuleRepositoryMongo,
|
|
38
|
+
ModuleRepositoryPostgres,
|
|
39
|
+
ModuleRepositoryDocumentDB,
|
|
40
|
+
};
|
|
@@ -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 };
|