@friggframework/core 2.0.0-next.6 → 2.0.0-next.61
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/config-capturing-integration.js +81 -0
- package/integrations/tests/doubles/dummy-integration-class.js +105 -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 +92 -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,240 @@
|
|
|
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 { SyncRepositoryInterface } = require('./sync-repository-interface');
|
|
12
|
+
|
|
13
|
+
class SyncRepositoryDocumentDB extends SyncRepositoryInterface {
|
|
14
|
+
constructor() {
|
|
15
|
+
super();
|
|
16
|
+
this.prisma = prisma;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async getSyncObject(name, dataIdentifier, entity) {
|
|
20
|
+
const pipeline = [
|
|
21
|
+
{
|
|
22
|
+
$match: {
|
|
23
|
+
name,
|
|
24
|
+
dataIdentifiers: {
|
|
25
|
+
$elemMatch: {
|
|
26
|
+
idData: dataIdentifier,
|
|
27
|
+
entityId: toObjectId(entity),
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
$limit: 2,
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const result = await this.prisma.$runCommandRaw({
|
|
38
|
+
aggregate: 'Sync',
|
|
39
|
+
pipeline,
|
|
40
|
+
cursor: {},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const syncList = result?.cursor?.firstBatch || [];
|
|
44
|
+
|
|
45
|
+
if (syncList.length === 1) {
|
|
46
|
+
const doc = syncList[0];
|
|
47
|
+
return this._mapSync(doc);
|
|
48
|
+
} else if (syncList.length === 0) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
throw new Error(
|
|
52
|
+
`There are multiple sync objects with the name ${name}, for entities [${syncList[0]?.entities}] [${syncList[1]?.entities}]`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async upsertSync(filter, syncData) {
|
|
57
|
+
const query = this._convertFilter(filter);
|
|
58
|
+
const existing = await findOne(this.prisma, 'Sync', query);
|
|
59
|
+
|
|
60
|
+
const now = new Date();
|
|
61
|
+
const documentData = this._prepareSyncData(syncData, now);
|
|
62
|
+
|
|
63
|
+
if (existing) {
|
|
64
|
+
await updateOne(
|
|
65
|
+
this.prisma,
|
|
66
|
+
'Sync',
|
|
67
|
+
{ _id: existing._id },
|
|
68
|
+
{
|
|
69
|
+
$set: documentData,
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
const updated = await findOne(this.prisma, 'Sync', { _id: existing._id });
|
|
73
|
+
return this._mapSync(updated);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const insertedId = await insertOne(this.prisma, 'Sync', {
|
|
77
|
+
...documentData,
|
|
78
|
+
createdAt: now,
|
|
79
|
+
});
|
|
80
|
+
const created = await findOne(this.prisma, 'Sync', { _id: insertedId });
|
|
81
|
+
return this._mapSync(created);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async updateSync(id, updates) {
|
|
85
|
+
const objectId = toObjectId(id);
|
|
86
|
+
if (!objectId) return null;
|
|
87
|
+
const documentData = this._prepareSyncData(updates, new Date());
|
|
88
|
+
await updateOne(
|
|
89
|
+
this.prisma,
|
|
90
|
+
'Sync',
|
|
91
|
+
{ _id: objectId },
|
|
92
|
+
{
|
|
93
|
+
$set: documentData,
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
const updated = await findOne(this.prisma, 'Sync', { _id: objectId });
|
|
97
|
+
return updated ? this._mapSync(updated) : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async addDataIdentifier(syncId, dataIdentifier) {
|
|
101
|
+
const syncObjectId = toObjectId(syncId);
|
|
102
|
+
if (!syncObjectId) return null;
|
|
103
|
+
const doc = await findOne(this.prisma, 'Sync', { _id: syncObjectId });
|
|
104
|
+
if (!doc) return null;
|
|
105
|
+
|
|
106
|
+
const identifiers = Array.isArray(doc.dataIdentifiers) ? [...doc.dataIdentifiers] : [];
|
|
107
|
+
identifiers.push({
|
|
108
|
+
syncId: syncObjectId,
|
|
109
|
+
entityId: toObjectId(dataIdentifier.entity),
|
|
110
|
+
idData: dataIdentifier.id,
|
|
111
|
+
hash: dataIdentifier.hash,
|
|
112
|
+
createdAt: new Date(),
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
await updateOne(
|
|
116
|
+
this.prisma,
|
|
117
|
+
'Sync',
|
|
118
|
+
{ _id: syncObjectId },
|
|
119
|
+
{
|
|
120
|
+
$set: {
|
|
121
|
+
dataIdentifiers: identifiers,
|
|
122
|
+
updatedAt: new Date(),
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const updated = await findOne(this.prisma, 'Sync', { _id: syncObjectId });
|
|
128
|
+
return updated ? this._mapSync(updated) : null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
getEntityObjIdForEntityIdFromObject(syncObj, entityId) {
|
|
132
|
+
if (!syncObj || !Array.isArray(syncObj.dataIdentifiers)) {
|
|
133
|
+
throw new Error('Sync object must include dataIdentifiers');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const entry = syncObj.dataIdentifiers.find(
|
|
137
|
+
(identifier) => fromObjectId(identifier.entityId) === String(entityId)
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
if (entry) {
|
|
141
|
+
return entry.idData;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
throw new Error(
|
|
145
|
+
`Sync object ${syncObj.id} does not contain a data identifier for entity ${entityId}`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async findSyncs(filter) {
|
|
150
|
+
const query = this._convertFilter(filter);
|
|
151
|
+
const docs = await findMany(this.prisma, 'Sync', query);
|
|
152
|
+
return docs.map((doc) => this._mapSync(doc));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async findOneSync(filter) {
|
|
156
|
+
const query = this._convertFilter(filter);
|
|
157
|
+
const doc = await findOne(this.prisma, 'Sync', query);
|
|
158
|
+
return doc ? this._mapSync(doc) : null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async deleteSync(id) {
|
|
162
|
+
const objectId = toObjectId(id);
|
|
163
|
+
if (!objectId) return { acknowledged: true, deletedCount: 0 };
|
|
164
|
+
const result = await deleteOne(this.prisma, 'Sync', { _id: objectId });
|
|
165
|
+
const deleted = result?.n ?? 0;
|
|
166
|
+
return { acknowledged: true, deletedCount: deleted };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
_convertFilter(filter = {}) {
|
|
170
|
+
const query = { ...filter };
|
|
171
|
+
if (filter._id || filter.id) {
|
|
172
|
+
const idObj = toObjectId(filter._id || filter.id);
|
|
173
|
+
if (idObj) query._id = idObj;
|
|
174
|
+
delete query._id;
|
|
175
|
+
delete query.id;
|
|
176
|
+
}
|
|
177
|
+
if (filter.integrationId) {
|
|
178
|
+
query.integrationId = toObjectId(filter.integrationId);
|
|
179
|
+
}
|
|
180
|
+
if (filter.entities) {
|
|
181
|
+
query.entityIds = (filter.entities || []).map((id) => toObjectId(id)).filter(Boolean);
|
|
182
|
+
delete query.entities;
|
|
183
|
+
}
|
|
184
|
+
return query;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
_prepareSyncData(data = {}, timestamp) {
|
|
188
|
+
const prepared = {};
|
|
189
|
+
if (data.integrationId !== undefined) {
|
|
190
|
+
prepared.integrationId = toObjectId(data.integrationId);
|
|
191
|
+
}
|
|
192
|
+
if (data.entities !== undefined || data.entityIds !== undefined) {
|
|
193
|
+
const list = data.entities !== undefined ? data.entities : data.entityIds;
|
|
194
|
+
prepared.entityIds = (list || []).map((id) => toObjectId(id)).filter(Boolean);
|
|
195
|
+
}
|
|
196
|
+
if (data.hash !== undefined) prepared.hash = data.hash;
|
|
197
|
+
if (data.name !== undefined) prepared.name = data.name;
|
|
198
|
+
if (data.context !== undefined) prepared.context = data.context;
|
|
199
|
+
if (data.results !== undefined) prepared.results = data.results;
|
|
200
|
+
if (timestamp) prepared.updatedAt = timestamp;
|
|
201
|
+
if (data.dataIdentifiers !== undefined) {
|
|
202
|
+
prepared.dataIdentifiers = (data.dataIdentifiers || []).map((identifier) => ({
|
|
203
|
+
syncId: toObjectId(identifier.syncId),
|
|
204
|
+
entityId: toObjectId(identifier.entityId),
|
|
205
|
+
idData: identifier.idData,
|
|
206
|
+
hash: identifier.hash,
|
|
207
|
+
createdAt: identifier.createdAt ? new Date(identifier.createdAt) : new Date(),
|
|
208
|
+
}));
|
|
209
|
+
}
|
|
210
|
+
return prepared;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
_mapSync(doc) {
|
|
214
|
+
if (!doc) return null;
|
|
215
|
+
return {
|
|
216
|
+
id: fromObjectId(doc._id),
|
|
217
|
+
integrationId: doc.integrationId ? fromObjectId(doc.integrationId) : null,
|
|
218
|
+
entities: Array.isArray(doc.entityIds)
|
|
219
|
+
? doc.entityIds.map((id) => fromObjectId(id))
|
|
220
|
+
: [],
|
|
221
|
+
entityIds: Array.isArray(doc.entityIds)
|
|
222
|
+
? doc.entityIds.map((id) => fromObjectId(id))
|
|
223
|
+
: [],
|
|
224
|
+
hash: doc.hash ?? null,
|
|
225
|
+
name: doc.name ?? null,
|
|
226
|
+
dataIdentifiers: Array.isArray(doc.dataIdentifiers)
|
|
227
|
+
? doc.dataIdentifiers.map((identifier) => ({
|
|
228
|
+
syncId: identifier.syncId ? fromObjectId(identifier.syncId) : null,
|
|
229
|
+
entityId: identifier.entityId ? fromObjectId(identifier.entityId) : null,
|
|
230
|
+
idData: identifier.idData,
|
|
231
|
+
hash: identifier.hash,
|
|
232
|
+
}))
|
|
233
|
+
: [],
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
module.exports = { SyncRepositoryDocumentDB };
|
|
239
|
+
|
|
240
|
+
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const { SyncRepositoryMongo } = require('./sync-repository-mongo');
|
|
2
|
+
const { SyncRepositoryPostgres } = require('./sync-repository-postgres');
|
|
3
|
+
const { SyncRepositoryDocumentDB } = require('./sync-repository-documentdb');
|
|
4
|
+
const config = require('../../database/config');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Sync Repository Factory
|
|
8
|
+
* Creates the appropriate repository adapter based on database type
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```javascript
|
|
12
|
+
* const repository = createSyncRepository();
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* @returns {SyncRepositoryInterface} Configured repository adapter
|
|
16
|
+
*/
|
|
17
|
+
function createSyncRepository() {
|
|
18
|
+
const dbType = config.DB_TYPE;
|
|
19
|
+
|
|
20
|
+
switch (dbType) {
|
|
21
|
+
case 'mongodb':
|
|
22
|
+
return new SyncRepositoryMongo();
|
|
23
|
+
|
|
24
|
+
case 'postgresql':
|
|
25
|
+
return new SyncRepositoryPostgres();
|
|
26
|
+
|
|
27
|
+
case 'documentdb':
|
|
28
|
+
return new SyncRepositoryDocumentDB();
|
|
29
|
+
|
|
30
|
+
default:
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Unsupported database type: ${dbType}. Supported values: 'mongodb', 'documentdb', 'postgresql'`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = {
|
|
38
|
+
createSyncRepository,
|
|
39
|
+
// Export adapters for direct testing
|
|
40
|
+
SyncRepositoryMongo,
|
|
41
|
+
SyncRepositoryPostgres,
|
|
42
|
+
SyncRepositoryDocumentDB,
|
|
43
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Repository Interface
|
|
3
|
+
* Abstract base class defining the contract for sync persistence adapters
|
|
4
|
+
*
|
|
5
|
+
* This follows the Port in Hexagonal Architecture:
|
|
6
|
+
* - Domain layer depends on this abstraction
|
|
7
|
+
* - Concrete adapters (MongoDB, PostgreSQL) implement this interface
|
|
8
|
+
* - Use cases receive repositories via dependency injection
|
|
9
|
+
*
|
|
10
|
+
* @abstract
|
|
11
|
+
*/
|
|
12
|
+
class SyncRepositoryInterface {
|
|
13
|
+
/**
|
|
14
|
+
* Get a sync object by name, data identifier, and entity
|
|
15
|
+
*
|
|
16
|
+
* @param {string} name - The sync object name
|
|
17
|
+
* @param {Object} dataIdentifier - The data identifier object
|
|
18
|
+
* @param {string|number} entity - The entity ID
|
|
19
|
+
* @returns {Promise<Object|null>} The sync object or null
|
|
20
|
+
* @abstract
|
|
21
|
+
*/
|
|
22
|
+
async getSyncObject(name, dataIdentifier, entity) {
|
|
23
|
+
throw new Error('Method getSyncObject must be implemented by subclass');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Create or update a sync object
|
|
28
|
+
*
|
|
29
|
+
* @param {Object} filter - Filter criteria for finding existing sync
|
|
30
|
+
* @param {Object} syncData - Sync data to create/update
|
|
31
|
+
* @returns {Promise<Object>} The created or updated sync object
|
|
32
|
+
* @abstract
|
|
33
|
+
*/
|
|
34
|
+
async upsertSync(filter, syncData) {
|
|
35
|
+
throw new Error('Method upsertSync must be implemented by subclass');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Update a sync object by ID
|
|
40
|
+
*
|
|
41
|
+
* @param {string|number} id - The sync object ID
|
|
42
|
+
* @param {Object} updates - Updates to apply
|
|
43
|
+
* @returns {Promise<Object>} The updated sync object
|
|
44
|
+
* @abstract
|
|
45
|
+
*/
|
|
46
|
+
async updateSync(id, updates) {
|
|
47
|
+
throw new Error('Method updateSync must be implemented by subclass');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Add a data identifier to a sync object
|
|
52
|
+
*
|
|
53
|
+
* @param {string|number} syncId - The sync object ID
|
|
54
|
+
* @param {Object} dataIdentifier - The data identifier to add
|
|
55
|
+
* @returns {Promise<Object>} The updated sync object
|
|
56
|
+
* @abstract
|
|
57
|
+
*/
|
|
58
|
+
async addDataIdentifier(syncId, dataIdentifier) {
|
|
59
|
+
throw new Error('Method addDataIdentifier must be implemented by subclass');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get entity object ID for entity ID from sync object
|
|
64
|
+
* This is a pure helper method (no database access)
|
|
65
|
+
*
|
|
66
|
+
* @param {Object} syncObj - The sync object
|
|
67
|
+
* @param {string|number} entityId - The entity ID
|
|
68
|
+
* @returns {Object} The entity object ID
|
|
69
|
+
* @abstract
|
|
70
|
+
*/
|
|
71
|
+
getEntityObjIdForEntityIdFromObject(syncObj, entityId) {
|
|
72
|
+
throw new Error('Method getEntityObjIdForEntityIdFromObject must be implemented by subclass');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Find sync objects by filter
|
|
77
|
+
*
|
|
78
|
+
* @param {Object} filter - Filter criteria
|
|
79
|
+
* @returns {Promise<Array>} Array of sync objects
|
|
80
|
+
* @abstract
|
|
81
|
+
*/
|
|
82
|
+
async findSyncs(filter) {
|
|
83
|
+
throw new Error('Method findSyncs must be implemented by subclass');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Find one sync object by filter
|
|
88
|
+
*
|
|
89
|
+
* @param {Object} filter - Filter criteria
|
|
90
|
+
* @returns {Promise<Object|null>} The sync object or null
|
|
91
|
+
* @abstract
|
|
92
|
+
*/
|
|
93
|
+
async findOneSync(filter) {
|
|
94
|
+
throw new Error('Method findOneSync must be implemented by subclass');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Delete a sync object by ID
|
|
99
|
+
*
|
|
100
|
+
* @param {string|number} id - The sync object ID
|
|
101
|
+
* @returns {Promise<Object>} The deletion result
|
|
102
|
+
* @abstract
|
|
103
|
+
*/
|
|
104
|
+
async deleteSync(id) {
|
|
105
|
+
throw new Error('Method deleteSync must be implemented by subclass');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports = { SyncRepositoryInterface };
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const { SyncRepositoryInterface } = require('./sync-repository-interface');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* MongoDB Sync Repository Adapter
|
|
6
|
+
* Handles sync persistence using Prisma with MongoDB
|
|
7
|
+
*
|
|
8
|
+
* MongoDB-specific characteristics:
|
|
9
|
+
* - Uses scalar fields for entity relations (entityIds)
|
|
10
|
+
* - IDs are strings with @db.ObjectId
|
|
11
|
+
* - Arrays used for many-to-many relationships
|
|
12
|
+
*
|
|
13
|
+
* Migration from Mongoose:
|
|
14
|
+
* - Mongoose static methods → Repository instance methods
|
|
15
|
+
* - Mongoose populate() → Prisma include
|
|
16
|
+
* - Nested arrays → Separate DataIdentifier model
|
|
17
|
+
*/
|
|
18
|
+
class SyncRepositoryMongo extends SyncRepositoryInterface {
|
|
19
|
+
constructor() {
|
|
20
|
+
super();
|
|
21
|
+
this.prisma = prisma;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get a sync object by name, data identifier, and entity
|
|
26
|
+
* Replaces: Sync.getSyncObject(name, dataIdentifier, entity)
|
|
27
|
+
*
|
|
28
|
+
* @param {string} name - The sync object name
|
|
29
|
+
* @param {Object} dataIdentifier - The data identifier object
|
|
30
|
+
* @param {string} entity - The entity ID (MongoDB ObjectId)
|
|
31
|
+
* @returns {Promise<Object|null>} The sync object or null
|
|
32
|
+
*/
|
|
33
|
+
async getSyncObject(name, dataIdentifier, entity) {
|
|
34
|
+
const syncList = await this.prisma.sync.findMany({
|
|
35
|
+
where: {
|
|
36
|
+
name,
|
|
37
|
+
dataIdentifiers: {
|
|
38
|
+
some: {
|
|
39
|
+
idData: dataIdentifier,
|
|
40
|
+
entityId: entity,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
include: {
|
|
45
|
+
entities: true,
|
|
46
|
+
dataIdentifiers: {
|
|
47
|
+
include: {
|
|
48
|
+
entity: true,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
if (syncList.length === 1) {
|
|
55
|
+
return syncList[0];
|
|
56
|
+
} else if (syncList.length === 0) {
|
|
57
|
+
return null;
|
|
58
|
+
} else {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`There are multiple sync objects with the name ${name}, for entities [${syncList[0].entities}] [${syncList[1].entities}]`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Create or update a sync object
|
|
67
|
+
* Replaces: Sync.upsert(filter, syncData)
|
|
68
|
+
*
|
|
69
|
+
* @param {Object} filter - Filter criteria for finding existing sync
|
|
70
|
+
* @param {Object} syncData - Sync data to create/update
|
|
71
|
+
* @returns {Promise<Object>} The created or updated sync object
|
|
72
|
+
*/
|
|
73
|
+
async upsertSync(filter, syncData) {
|
|
74
|
+
// Find existing sync
|
|
75
|
+
const where = this._convertFilterToWhere(filter);
|
|
76
|
+
const existing = await this.prisma.sync.findFirst({ where });
|
|
77
|
+
|
|
78
|
+
if (existing) {
|
|
79
|
+
// Update existing
|
|
80
|
+
return await this.prisma.sync.update({
|
|
81
|
+
where: { id: existing.id },
|
|
82
|
+
data: syncData,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Create new
|
|
87
|
+
return await this.prisma.sync.create({
|
|
88
|
+
data: syncData,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Update a sync object by ID
|
|
94
|
+
* Replaces: Sync.update({ _id: id }, updates)
|
|
95
|
+
*
|
|
96
|
+
* @param {string} id - The sync object ID
|
|
97
|
+
* @param {Object} updates - Updates to apply
|
|
98
|
+
* @returns {Promise<Object>} The updated sync object
|
|
99
|
+
*/
|
|
100
|
+
async updateSync(id, updates) {
|
|
101
|
+
return await this.prisma.sync.update({
|
|
102
|
+
where: { id },
|
|
103
|
+
data: updates,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Add a data identifier to a sync object
|
|
109
|
+
* Replaces: Sync.addDataIdentifier(syncId, dataIdentifier)
|
|
110
|
+
*
|
|
111
|
+
* @param {string} syncId - The sync object ID
|
|
112
|
+
* @param {Object} dataIdentifier - The data identifier to add
|
|
113
|
+
* @returns {Promise<Object>} The updated sync object
|
|
114
|
+
*/
|
|
115
|
+
async addDataIdentifier(syncId, dataIdentifier) {
|
|
116
|
+
// In Prisma, we create a new DataIdentifier record linked to the Sync
|
|
117
|
+
await this.prisma.dataIdentifier.create({
|
|
118
|
+
data: {
|
|
119
|
+
syncId,
|
|
120
|
+
entityId: dataIdentifier.entity,
|
|
121
|
+
idData: dataIdentifier.id,
|
|
122
|
+
hash: dataIdentifier.hash,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Return updated sync object
|
|
127
|
+
return await this.prisma.sync.findUnique({
|
|
128
|
+
where: { id: syncId },
|
|
129
|
+
include: {
|
|
130
|
+
dataIdentifiers: true,
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Get entity object ID for entity ID from sync object
|
|
137
|
+
* Replaces: Sync.getEntityObjIdForEntityIdFromObject(syncObj, entityId)
|
|
138
|
+
*
|
|
139
|
+
* This is a pure helper method (no database access)
|
|
140
|
+
*
|
|
141
|
+
* @param {Object} syncObj - The sync object
|
|
142
|
+
* @param {string} entityId - The entity ID
|
|
143
|
+
* @returns {Object} The entity object ID
|
|
144
|
+
*/
|
|
145
|
+
getEntityObjIdForEntityIdFromObject(syncObj, entityId) {
|
|
146
|
+
if (!syncObj.dataIdentifiers) {
|
|
147
|
+
throw new Error('Sync object must include dataIdentifiers');
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
for (let dataIdentifier of syncObj.dataIdentifiers) {
|
|
151
|
+
if (dataIdentifier.entityId === entityId) {
|
|
152
|
+
return dataIdentifier.idData;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
throw new Error(
|
|
157
|
+
`Sync object ${syncObj.id} does not contain a data identifier for entity ${entityId}`
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Find sync objects by filter
|
|
163
|
+
* Replaces: Sync.find(filter)
|
|
164
|
+
*
|
|
165
|
+
* @param {Object} filter - Filter criteria
|
|
166
|
+
* @returns {Promise<Array>} Array of sync objects
|
|
167
|
+
*/
|
|
168
|
+
async findSyncs(filter) {
|
|
169
|
+
const where = this._convertFilterToWhere(filter);
|
|
170
|
+
return await this.prisma.sync.findMany({
|
|
171
|
+
where,
|
|
172
|
+
include: {
|
|
173
|
+
entities: true,
|
|
174
|
+
dataIdentifiers: {
|
|
175
|
+
include: {
|
|
176
|
+
entity: true,
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Find one sync object by filter
|
|
185
|
+
* Replaces: Sync.findOne(filter)
|
|
186
|
+
*
|
|
187
|
+
* @param {Object} filter - Filter criteria
|
|
188
|
+
* @returns {Promise<Object|null>} The sync object or null
|
|
189
|
+
*/
|
|
190
|
+
async findOneSync(filter) {
|
|
191
|
+
const where = this._convertFilterToWhere(filter);
|
|
192
|
+
return await this.prisma.sync.findFirst({
|
|
193
|
+
where,
|
|
194
|
+
include: {
|
|
195
|
+
entities: true,
|
|
196
|
+
dataIdentifiers: {
|
|
197
|
+
include: {
|
|
198
|
+
entity: true,
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Delete a sync object by ID
|
|
207
|
+
* Replaces: Sync.deleteOne({ _id: id })
|
|
208
|
+
*
|
|
209
|
+
* @param {string} id - The sync object ID
|
|
210
|
+
* @returns {Promise<Object>} The deletion result
|
|
211
|
+
*/
|
|
212
|
+
async deleteSync(id) {
|
|
213
|
+
// Prisma will cascade delete dataIdentifiers automatically
|
|
214
|
+
return await this.prisma.sync.delete({
|
|
215
|
+
where: { id },
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Convert Mongoose-style filter to Prisma where clause
|
|
221
|
+
* @private
|
|
222
|
+
* @param {Object} filter - Mongoose filter
|
|
223
|
+
* @returns {Object} Prisma where clause
|
|
224
|
+
*/
|
|
225
|
+
_convertFilterToWhere(filter) {
|
|
226
|
+
const where = {};
|
|
227
|
+
|
|
228
|
+
// Handle _id field (Mongoose uses _id, Prisma uses id)
|
|
229
|
+
if (filter._id) {
|
|
230
|
+
where.id = filter._id;
|
|
231
|
+
delete filter._id;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Copy remaining filters
|
|
235
|
+
return { ...where, ...filter };
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
module.exports = { SyncRepositoryMongo };
|