@friggframework/core 2.0.0-next.53 → 2.0.0-next.55
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 +2 -1
- package/application/commands/credential-commands.js +1 -1
- package/application/commands/integration-commands.js +1 -1
- package/application/index.js +1 -1
- package/core/create-handler.js +12 -0
- package/credential/repositories/credential-repository-documentdb.js +304 -0
- package/credential/repositories/credential-repository-factory.js +8 -1
- package/credential/repositories/credential-repository-mongo.js +16 -54
- package/credential/repositories/credential-repository-postgres.js +14 -41
- package/credential/use-cases/get-credential-for-user.js +7 -3
- package/database/config.js +4 -4
- package/database/documentdb-encryption-service.js +330 -0
- package/database/documentdb-utils.js +136 -0
- package/database/encryption/README.md +50 -1
- package/database/encryption/documentdb-encryption-service.md +3270 -0
- package/database/encryption/encryption-schema-registry.js +46 -0
- package/database/prisma.js +7 -47
- package/database/repositories/health-check-repository-documentdb.js +134 -0
- package/database/repositories/health-check-repository-factory.js +6 -1
- package/database/repositories/health-check-repository-interface.js +29 -34
- package/database/repositories/health-check-repository-mongodb.js +1 -3
- package/database/use-cases/check-database-state-use-case.js +3 -3
- package/database/use-cases/run-database-migration-use-case.js +6 -4
- package/database/use-cases/trigger-database-migration-use-case.js +2 -2
- package/database/utils/mongodb-schema-init.js +5 -5
- package/database/utils/prisma-runner.js +15 -9
- 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/edge.js +3 -3
- package/generated/prisma-mongodb/index.d.ts +10 -4
- package/generated/prisma-mongodb/index.js +3 -3
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +1 -3
- package/generated/prisma-mongodb/wasm.js +2 -2
- package/generated/prisma-postgresql/edge.js +3 -3
- package/generated/prisma-postgresql/index.d.ts +10 -4
- package/generated/prisma-postgresql/index.js +3 -3
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +1 -3
- package/generated/prisma-postgresql/wasm.js +2 -2
- package/handlers/routers/db-migration.js +2 -3
- package/handlers/routers/health.js +0 -3
- package/handlers/workers/db-migration.js +8 -8
- package/integrations/integration-router.js +6 -6
- package/integrations/repositories/integration-mapping-repository-documentdb.js +280 -0
- package/integrations/repositories/integration-mapping-repository-factory.js +8 -1
- package/integrations/repositories/integration-repository-documentdb.js +210 -0
- package/integrations/repositories/integration-repository-factory.js +8 -1
- package/integrations/repositories/process-repository-documentdb.js +243 -0
- package/integrations/repositories/process-repository-factory.js +8 -1
- package/modules/repositories/module-repository-documentdb.js +307 -0
- package/modules/repositories/module-repository-factory.js +8 -1
- package/package.json +5 -5
- package/prisma-mongodb/schema.prisma +1 -3
- package/prisma-postgresql/migrations/20251112195422_update_user_unique_constraints/migration.sql +69 -0
- package/prisma-postgresql/schema.prisma +1 -3
- package/syncs/repositories/sync-repository-documentdb.js +240 -0
- package/syncs/repositories/sync-repository-factory.js +6 -1
- package/token/repositories/token-repository-documentdb.js +137 -0
- package/token/repositories/token-repository-factory.js +8 -1
- package/token/repositories/token-repository-mongo.js +10 -3
- package/token/repositories/token-repository-postgres.js +10 -3
- package/user/repositories/user-repository-documentdb.js +432 -0
- package/user/repositories/user-repository-factory.js +6 -1
- package/user/repositories/user-repository-mongo.js +3 -2
- package/user/repositories/user-repository-postgres.js +3 -2
- package/user/use-cases/login-user.js +1 -1
- package/websocket/repositories/websocket-connection-repository-documentdb.js +119 -0
- package/websocket/repositories/websocket-connection-repository-factory.js +8 -1
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const {
|
|
3
|
+
toObjectId,
|
|
4
|
+
fromObjectId,
|
|
5
|
+
findMany,
|
|
6
|
+
findOne,
|
|
7
|
+
insertOne,
|
|
8
|
+
updateOne,
|
|
9
|
+
deleteOne,
|
|
10
|
+
deleteMany,
|
|
11
|
+
} = require('../../database/documentdb-utils');
|
|
12
|
+
const {
|
|
13
|
+
IntegrationMappingRepositoryInterface,
|
|
14
|
+
} = require('./integration-mapping-repository-interface');
|
|
15
|
+
const {
|
|
16
|
+
DocumentDBEncryptionService,
|
|
17
|
+
} = require('../../database/documentdb-encryption-service');
|
|
18
|
+
class IntegrationMappingRepositoryDocumentDB extends IntegrationMappingRepositoryInterface {
|
|
19
|
+
constructor() {
|
|
20
|
+
super();
|
|
21
|
+
this.prisma = prisma;
|
|
22
|
+
this.encryptionService = new DocumentDBEncryptionService();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async findMappingBy(integrationId, sourceId) {
|
|
26
|
+
const filter = this._compositeFilter(integrationId, sourceId);
|
|
27
|
+
const doc = await findOne(this.prisma, 'IntegrationMapping', filter);
|
|
28
|
+
if (!doc) return null;
|
|
29
|
+
|
|
30
|
+
const decryptedMapping = await this.encryptionService.decryptFields(
|
|
31
|
+
'IntegrationMapping',
|
|
32
|
+
doc
|
|
33
|
+
);
|
|
34
|
+
return this._mapMapping(decryptedMapping);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async upsertMapping(integrationId, sourceId, mapping) {
|
|
38
|
+
const filter = this._compositeFilter(integrationId, sourceId);
|
|
39
|
+
const existing = await findOne(
|
|
40
|
+
this.prisma,
|
|
41
|
+
'IntegrationMapping',
|
|
42
|
+
filter
|
|
43
|
+
);
|
|
44
|
+
const now = new Date();
|
|
45
|
+
|
|
46
|
+
if (existing) {
|
|
47
|
+
const decryptedExisting =
|
|
48
|
+
await this.encryptionService.decryptFields(
|
|
49
|
+
'IntegrationMapping',
|
|
50
|
+
existing
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const updateDocument = {
|
|
54
|
+
mapping,
|
|
55
|
+
updatedAt: now,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const encryptedUpdate = await this.encryptionService.encryptFields(
|
|
59
|
+
'IntegrationMapping',
|
|
60
|
+
{ mapping: updateDocument.mapping }
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
await updateOne(
|
|
64
|
+
this.prisma,
|
|
65
|
+
'IntegrationMapping',
|
|
66
|
+
{ _id: existing._id },
|
|
67
|
+
{
|
|
68
|
+
$set: {
|
|
69
|
+
mapping: encryptedUpdate.mapping,
|
|
70
|
+
updatedAt: updateDocument.updatedAt,
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const updated = await findOne(this.prisma, 'IntegrationMapping', {
|
|
76
|
+
_id: existing._id,
|
|
77
|
+
});
|
|
78
|
+
if (!updated) {
|
|
79
|
+
console.error(
|
|
80
|
+
'[IntegrationMappingRepositoryDocumentDB] Mapping not found after update',
|
|
81
|
+
{
|
|
82
|
+
mappingId: fromObjectId(existing._id),
|
|
83
|
+
integrationId,
|
|
84
|
+
sourceId,
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
throw new Error(
|
|
88
|
+
'Failed to update mapping: Document not found after update. ' +
|
|
89
|
+
'This indicates a database consistency issue.'
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const decryptedMapping = await this.encryptionService.decryptFields(
|
|
93
|
+
'IntegrationMapping',
|
|
94
|
+
updated
|
|
95
|
+
);
|
|
96
|
+
return this._mapMapping(decryptedMapping);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const plainDocument = {
|
|
100
|
+
integrationId: integrationId,
|
|
101
|
+
sourceId:
|
|
102
|
+
sourceId === null || sourceId === undefined
|
|
103
|
+
? null
|
|
104
|
+
: String(sourceId),
|
|
105
|
+
mapping,
|
|
106
|
+
createdAt: now,
|
|
107
|
+
updatedAt: now,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const encryptedDocument = await this.encryptionService.encryptFields(
|
|
111
|
+
'IntegrationMapping',
|
|
112
|
+
plainDocument
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
const insertedId = await insertOne(
|
|
116
|
+
this.prisma,
|
|
117
|
+
'IntegrationMapping',
|
|
118
|
+
encryptedDocument
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const created = await findOne(this.prisma, 'IntegrationMapping', {
|
|
122
|
+
_id: insertedId,
|
|
123
|
+
});
|
|
124
|
+
if (!created) {
|
|
125
|
+
console.error(
|
|
126
|
+
'[IntegrationMappingRepositoryDocumentDB] Mapping not found after insert',
|
|
127
|
+
{
|
|
128
|
+
insertedId: fromObjectId(insertedId),
|
|
129
|
+
integrationId,
|
|
130
|
+
sourceId,
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
throw new Error(
|
|
134
|
+
'Failed to create mapping: Document not found after insert. ' +
|
|
135
|
+
'This indicates a database consistency issue.'
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
const decryptedMapping = await this.encryptionService.decryptFields(
|
|
139
|
+
'IntegrationMapping',
|
|
140
|
+
created
|
|
141
|
+
);
|
|
142
|
+
return this._mapMapping(decryptedMapping);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async findMappingsByIntegration(integrationId) {
|
|
146
|
+
const filter = {};
|
|
147
|
+
if (integrationId) filter.integrationId = integrationId;
|
|
148
|
+
const docs = await findMany(this.prisma, 'IntegrationMapping', filter);
|
|
149
|
+
|
|
150
|
+
const decryptedDocs = await Promise.all(
|
|
151
|
+
docs.map((doc) =>
|
|
152
|
+
this.encryptionService.decryptFields('IntegrationMapping', doc)
|
|
153
|
+
)
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
return decryptedDocs.map((doc) => this._mapMapping(doc));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async deleteMapping(integrationId, sourceId) {
|
|
160
|
+
const filter = this._compositeFilter(integrationId, sourceId);
|
|
161
|
+
const result = await deleteOne(
|
|
162
|
+
this.prisma,
|
|
163
|
+
'IntegrationMapping',
|
|
164
|
+
filter
|
|
165
|
+
);
|
|
166
|
+
const deleted = result?.n ?? 0;
|
|
167
|
+
return { acknowledged: true, deletedCount: deleted };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async deleteMappingsByIntegration(integrationId) {
|
|
171
|
+
if (!integrationId) {
|
|
172
|
+
return { acknowledged: true, deletedCount: 0 };
|
|
173
|
+
}
|
|
174
|
+
const result = await deleteMany(this.prisma, 'IntegrationMapping', {
|
|
175
|
+
integrationId: integrationId,
|
|
176
|
+
});
|
|
177
|
+
const deleted = result?.n ?? 0;
|
|
178
|
+
return { acknowledged: true, deletedCount: deleted };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async findMappingById(id) {
|
|
182
|
+
const objectId = toObjectId(id);
|
|
183
|
+
if (!objectId) return null;
|
|
184
|
+
const doc = await findOne(this.prisma, 'IntegrationMapping', {
|
|
185
|
+
_id: objectId,
|
|
186
|
+
});
|
|
187
|
+
if (!doc) return null;
|
|
188
|
+
|
|
189
|
+
const decryptedMapping = await this.encryptionService.decryptFields(
|
|
190
|
+
'IntegrationMapping',
|
|
191
|
+
doc
|
|
192
|
+
);
|
|
193
|
+
return this._mapMapping(decryptedMapping);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async updateMapping(id, updates) {
|
|
197
|
+
const objectId = toObjectId(id);
|
|
198
|
+
if (!objectId) return null;
|
|
199
|
+
|
|
200
|
+
const existing = await findOne(this.prisma, 'IntegrationMapping', {
|
|
201
|
+
_id: objectId,
|
|
202
|
+
});
|
|
203
|
+
if (!existing) return null;
|
|
204
|
+
|
|
205
|
+
const decryptedExisting = await this.encryptionService.decryptFields(
|
|
206
|
+
'IntegrationMapping',
|
|
207
|
+
existing
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
const mergedMapping =
|
|
211
|
+
updates.mapping !== undefined
|
|
212
|
+
? updates.mapping
|
|
213
|
+
: decryptedExisting.mapping;
|
|
214
|
+
|
|
215
|
+
const updateDocument = {
|
|
216
|
+
...updates,
|
|
217
|
+
updatedAt: new Date(),
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
if (mergedMapping !== undefined) {
|
|
221
|
+
const encryptedUpdate = await this.encryptionService.encryptFields(
|
|
222
|
+
'IntegrationMapping',
|
|
223
|
+
{ mapping: mergedMapping }
|
|
224
|
+
);
|
|
225
|
+
updateDocument.mapping = encryptedUpdate.mapping;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
await updateOne(
|
|
229
|
+
this.prisma,
|
|
230
|
+
'IntegrationMapping',
|
|
231
|
+
{ _id: objectId },
|
|
232
|
+
{
|
|
233
|
+
$set: updateDocument,
|
|
234
|
+
}
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const updated = await findOne(this.prisma, 'IntegrationMapping', {
|
|
238
|
+
_id: objectId,
|
|
239
|
+
});
|
|
240
|
+
if (!updated) {
|
|
241
|
+
console.error(
|
|
242
|
+
'[IntegrationMappingRepositoryDocumentDB] Mapping not found after update',
|
|
243
|
+
{
|
|
244
|
+
mappingId: fromObjectId(objectId),
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
throw new Error(
|
|
248
|
+
'Failed to update mapping: Document not found after update. ' +
|
|
249
|
+
'This indicates a database consistency issue.'
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
const decryptedMapping = await this.encryptionService.decryptFields(
|
|
253
|
+
'IntegrationMapping',
|
|
254
|
+
updated
|
|
255
|
+
);
|
|
256
|
+
return this._mapMapping(decryptedMapping);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
_compositeFilter(integrationId, sourceId) {
|
|
260
|
+
const filter = {};
|
|
261
|
+
if (integrationId) filter.integrationId = integrationId;
|
|
262
|
+
if (sourceId !== undefined) {
|
|
263
|
+
filter.sourceId = sourceId === null ? null : String(sourceId);
|
|
264
|
+
}
|
|
265
|
+
return filter;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
_mapMapping(doc) {
|
|
269
|
+
return {
|
|
270
|
+
id: fromObjectId(doc?._id),
|
|
271
|
+
integrationId: doc?.integrationId ?? null,
|
|
272
|
+
sourceId: doc?.sourceId ?? null,
|
|
273
|
+
mapping: doc?.mapping ?? null,
|
|
274
|
+
createdAt: doc?.createdAt,
|
|
275
|
+
updatedAt: doc?.updatedAt,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
module.exports = { IntegrationMappingRepositoryDocumentDB };
|
|
@@ -4,6 +4,9 @@ const {
|
|
|
4
4
|
const {
|
|
5
5
|
IntegrationMappingRepositoryPostgres,
|
|
6
6
|
} = require('./integration-mapping-repository-postgres');
|
|
7
|
+
const {
|
|
8
|
+
IntegrationMappingRepositoryDocumentDB,
|
|
9
|
+
} = require('./integration-mapping-repository-documentdb');
|
|
7
10
|
const config = require('../../database/config');
|
|
8
11
|
|
|
9
12
|
/**
|
|
@@ -35,9 +38,12 @@ function createIntegrationMappingRepository() {
|
|
|
35
38
|
case 'postgresql':
|
|
36
39
|
return new IntegrationMappingRepositoryPostgres();
|
|
37
40
|
|
|
41
|
+
case 'documentdb':
|
|
42
|
+
return new IntegrationMappingRepositoryDocumentDB();
|
|
43
|
+
|
|
38
44
|
default:
|
|
39
45
|
throw new Error(
|
|
40
|
-
`Unsupported database type: ${dbType}. Supported values: 'mongodb', 'postgresql'`
|
|
46
|
+
`Unsupported database type: ${dbType}. Supported values: 'mongodb', 'documentdb', 'postgresql'`
|
|
41
47
|
);
|
|
42
48
|
}
|
|
43
49
|
}
|
|
@@ -47,4 +53,5 @@ module.exports = {
|
|
|
47
53
|
// Export adapters for direct testing
|
|
48
54
|
IntegrationMappingRepositoryMongo,
|
|
49
55
|
IntegrationMappingRepositoryPostgres,
|
|
56
|
+
IntegrationMappingRepositoryDocumentDB,
|
|
50
57
|
};
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
const { prisma } = require('../../database/prisma');
|
|
2
|
+
const {
|
|
3
|
+
toObjectId,
|
|
4
|
+
toObjectIdArray,
|
|
5
|
+
fromObjectId,
|
|
6
|
+
findMany,
|
|
7
|
+
findOne,
|
|
8
|
+
insertOne,
|
|
9
|
+
updateOne,
|
|
10
|
+
deleteOne,
|
|
11
|
+
} = require('../../database/documentdb-utils');
|
|
12
|
+
const {
|
|
13
|
+
IntegrationRepositoryInterface,
|
|
14
|
+
} = require('./integration-repository-interface');
|
|
15
|
+
|
|
16
|
+
class IntegrationRepositoryDocumentDB extends IntegrationRepositoryInterface {
|
|
17
|
+
constructor() {
|
|
18
|
+
super();
|
|
19
|
+
this.prisma = prisma;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async findIntegrationsByUserId(userId) {
|
|
23
|
+
const objectId = toObjectId(userId);
|
|
24
|
+
const filter = objectId ? { userId: objectId } : {};
|
|
25
|
+
const records = await findMany(this.prisma, 'Integration', filter);
|
|
26
|
+
return records.map((doc) => this._mapIntegration(doc));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async deleteIntegrationById(integrationId) {
|
|
30
|
+
const objectId = toObjectId(integrationId);
|
|
31
|
+
if (!objectId) return { acknowledged: true, deletedCount: 0 };
|
|
32
|
+
const result = await deleteOne(this.prisma, 'Integration', { _id: objectId });
|
|
33
|
+
const deleted = result?.n ?? 0;
|
|
34
|
+
return { acknowledged: true, deletedCount: deleted };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async findIntegrationByName(name) {
|
|
38
|
+
const doc = await findOne(this.prisma, 'Integration', { 'config.type': name });
|
|
39
|
+
if (!doc) {
|
|
40
|
+
throw new Error(`Integration with name ${name} not found`);
|
|
41
|
+
}
|
|
42
|
+
return this._mapIntegration(doc);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async findIntegrationById(id) {
|
|
46
|
+
const objectId = toObjectId(id);
|
|
47
|
+
if (!objectId) {
|
|
48
|
+
throw new Error(`Integration with id ${id} not found`);
|
|
49
|
+
}
|
|
50
|
+
const doc = await findOne(this.prisma, 'Integration', { _id: objectId });
|
|
51
|
+
if (!doc) {
|
|
52
|
+
throw new Error(`Integration with id ${id} not found`);
|
|
53
|
+
}
|
|
54
|
+
return this._mapIntegration(doc);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async updateIntegrationStatus(integrationId, status) {
|
|
58
|
+
const objectId = toObjectId(integrationId);
|
|
59
|
+
if (!objectId) return false;
|
|
60
|
+
await updateOne(
|
|
61
|
+
this.prisma,
|
|
62
|
+
'Integration',
|
|
63
|
+
{ _id: objectId },
|
|
64
|
+
{
|
|
65
|
+
$set: { status, updatedAt: new Date() },
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async updateIntegrationMessages(
|
|
72
|
+
integrationId,
|
|
73
|
+
messageType,
|
|
74
|
+
messageTitle,
|
|
75
|
+
messageBody,
|
|
76
|
+
messageTimestamp
|
|
77
|
+
) {
|
|
78
|
+
const objectId = toObjectId(integrationId);
|
|
79
|
+
if (!objectId) {
|
|
80
|
+
throw new Error(`Integration ${integrationId} not found`);
|
|
81
|
+
}
|
|
82
|
+
const existing = await findOne(this.prisma, 'Integration', { _id: objectId });
|
|
83
|
+
if (!existing) {
|
|
84
|
+
throw new Error(`Integration ${integrationId} not found`);
|
|
85
|
+
}
|
|
86
|
+
const messages = this._extractMessages(existing);
|
|
87
|
+
const list = Array.isArray(messages[messageType]) ? [...messages[messageType]] : [];
|
|
88
|
+
list.push({
|
|
89
|
+
title: messageTitle ?? null,
|
|
90
|
+
message: messageBody,
|
|
91
|
+
timestamp: messageTimestamp,
|
|
92
|
+
});
|
|
93
|
+
const updatedMessages = { ...messages, [messageType]: list };
|
|
94
|
+
await updateOne(
|
|
95
|
+
this.prisma,
|
|
96
|
+
'Integration',
|
|
97
|
+
{ _id: objectId },
|
|
98
|
+
{
|
|
99
|
+
$set: {
|
|
100
|
+
messages: updatedMessages,
|
|
101
|
+
errors: updatedMessages.errors ?? [],
|
|
102
|
+
warnings: updatedMessages.warnings ?? [],
|
|
103
|
+
info: updatedMessages.info ?? [],
|
|
104
|
+
logs: updatedMessages.logs ?? [],
|
|
105
|
+
updatedAt: new Date(),
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async createIntegration(entities, userId, config) {
|
|
113
|
+
const now = new Date();
|
|
114
|
+
const document = {
|
|
115
|
+
userId: toObjectId(userId) || null,
|
|
116
|
+
config,
|
|
117
|
+
version: '0.0.0',
|
|
118
|
+
status: 'ENABLED',
|
|
119
|
+
entityIds: toObjectIdArray(entities),
|
|
120
|
+
messages: { errors: [], warnings: [], info: [], logs: [] },
|
|
121
|
+
errors: [],
|
|
122
|
+
warnings: [],
|
|
123
|
+
info: [],
|
|
124
|
+
logs: [],
|
|
125
|
+
createdAt: now,
|
|
126
|
+
updatedAt: now,
|
|
127
|
+
};
|
|
128
|
+
const insertedId = await insertOne(this.prisma, 'Integration', document);
|
|
129
|
+
const created = await findOne(this.prisma, 'Integration', { _id: insertedId });
|
|
130
|
+
if (!created) {
|
|
131
|
+
console.error('[IntegrationRepositoryDocumentDB] Integration not found after insert', {
|
|
132
|
+
insertedId: fromObjectId(insertedId),
|
|
133
|
+
userId,
|
|
134
|
+
config,
|
|
135
|
+
});
|
|
136
|
+
throw new Error(
|
|
137
|
+
'Failed to create integration: Document not found after insert. ' +
|
|
138
|
+
'This indicates a database consistency issue.'
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
return this._mapIntegration(created);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async findIntegrationByUserId(userId) {
|
|
145
|
+
const objectId = toObjectId(userId);
|
|
146
|
+
if (!objectId) return null;
|
|
147
|
+
const doc = await findOne(this.prisma, 'Integration', { userId: objectId });
|
|
148
|
+
return doc ? this._mapIntegration(doc) : null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async updateIntegrationConfig(integrationId, config) {
|
|
152
|
+
if (config === null || config === undefined) {
|
|
153
|
+
throw new Error('Config parameter is required');
|
|
154
|
+
}
|
|
155
|
+
const objectId = toObjectId(integrationId);
|
|
156
|
+
if (!objectId) {
|
|
157
|
+
throw new Error(`Integration with id ${integrationId} not found`);
|
|
158
|
+
}
|
|
159
|
+
await updateOne(
|
|
160
|
+
this.prisma,
|
|
161
|
+
'Integration',
|
|
162
|
+
{ _id: objectId },
|
|
163
|
+
{
|
|
164
|
+
$set: {
|
|
165
|
+
config,
|
|
166
|
+
updatedAt: new Date(),
|
|
167
|
+
},
|
|
168
|
+
}
|
|
169
|
+
);
|
|
170
|
+
const updated = await findOne(this.prisma, 'Integration', { _id: objectId });
|
|
171
|
+
if (!updated) {
|
|
172
|
+
console.error('[IntegrationRepositoryDocumentDB] Integration not found after update', {
|
|
173
|
+
integrationId: fromObjectId(objectId),
|
|
174
|
+
config,
|
|
175
|
+
});
|
|
176
|
+
throw new Error(
|
|
177
|
+
'Failed to update integration: Document not found after update. ' +
|
|
178
|
+
'This indicates a database consistency issue.'
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
return this._mapIntegration(updated);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
_mapIntegration(doc) {
|
|
185
|
+
const messages = this._extractMessages(doc);
|
|
186
|
+
return {
|
|
187
|
+
id: fromObjectId(doc?._id),
|
|
188
|
+
entitiesIds: (doc?.entityIds || []).map((value) => fromObjectId(value)),
|
|
189
|
+
userId: fromObjectId(doc?.userId),
|
|
190
|
+
config: doc?.config ?? null,
|
|
191
|
+
version: doc?.version ?? null,
|
|
192
|
+
status: doc?.status ?? null,
|
|
193
|
+
messages,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
_extractMessages(doc) {
|
|
198
|
+
const base = doc?.messages && typeof doc.messages === 'object' ? doc.messages : {};
|
|
199
|
+
return {
|
|
200
|
+
errors: base.errors ?? doc?.errors ?? [],
|
|
201
|
+
warnings: base.warnings ?? doc?.warnings ?? [],
|
|
202
|
+
info: base.info ?? doc?.info ?? [],
|
|
203
|
+
logs: base.logs ?? doc?.logs ?? [],
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
module.exports = { IntegrationRepositoryDocumentDB };
|
|
209
|
+
|
|
210
|
+
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
const { IntegrationRepositoryMongo } = require('./integration-repository-mongo');
|
|
2
2
|
const { IntegrationRepositoryPostgres } = require('./integration-repository-postgres');
|
|
3
|
+
const {
|
|
4
|
+
IntegrationRepositoryDocumentDB,
|
|
5
|
+
} = require('./integration-repository-documentdb');
|
|
3
6
|
const config = require('../../database/config');
|
|
4
7
|
|
|
5
8
|
/**
|
|
@@ -29,9 +32,12 @@ function createIntegrationRepository() {
|
|
|
29
32
|
case 'postgresql':
|
|
30
33
|
return new IntegrationRepositoryPostgres();
|
|
31
34
|
|
|
35
|
+
case 'documentdb':
|
|
36
|
+
return new IntegrationRepositoryDocumentDB();
|
|
37
|
+
|
|
32
38
|
default:
|
|
33
39
|
throw new Error(
|
|
34
|
-
`Unsupported database type: ${dbType}. Supported values: 'mongodb', 'postgresql'`
|
|
40
|
+
`Unsupported database type: ${dbType}. Supported values: 'mongodb', 'documentdb', 'postgresql'`
|
|
35
41
|
);
|
|
36
42
|
}
|
|
37
43
|
}
|
|
@@ -41,4 +47,5 @@ module.exports = {
|
|
|
41
47
|
// Export adapters for direct testing
|
|
42
48
|
IntegrationRepositoryMongo,
|
|
43
49
|
IntegrationRepositoryPostgres,
|
|
50
|
+
IntegrationRepositoryDocumentDB,
|
|
44
51
|
};
|