@reproapp/node-sdk 0.0.27 → 0.0.29
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/dist/index.js +102 -10
- package/package.json +1 -1
- package/src/index.ts +92 -7
- package/test/mongodb-integration.test.js +135 -0
package/dist/index.js
CHANGED
|
@@ -1028,10 +1028,6 @@ function scheduleTraceMaterializerIdleShutdown() {
|
|
|
1028
1028
|
catch { }
|
|
1029
1029
|
}
|
|
1030
1030
|
}, TRACE_MATERIALIZE_WORKER_IDLE_SHUTDOWN_MS);
|
|
1031
|
-
try {
|
|
1032
|
-
traceMaterializerIdleTimer.unref?.();
|
|
1033
|
-
}
|
|
1034
|
-
catch { }
|
|
1035
1031
|
}
|
|
1036
1032
|
function rejectTraceMaterializerPending(error) {
|
|
1037
1033
|
for (const [taskId, pending] of traceMaterializerPending.entries()) {
|
|
@@ -1373,11 +1369,43 @@ function waitForSessionDrain(sessionId) {
|
|
|
1373
1369
|
if (remaining <= 0)
|
|
1374
1370
|
return Promise.resolve();
|
|
1375
1371
|
return new Promise(resolve => {
|
|
1372
|
+
let settled = false;
|
|
1373
|
+
let timeout = null;
|
|
1374
|
+
const removeWaiter = (waiter) => {
|
|
1375
|
+
const list = sessionWaiters.get(sessionId);
|
|
1376
|
+
if (!list)
|
|
1377
|
+
return;
|
|
1378
|
+
const next = list.filter(fn => fn !== waiter);
|
|
1379
|
+
if (next.length) {
|
|
1380
|
+
sessionWaiters.set(sessionId, next);
|
|
1381
|
+
}
|
|
1382
|
+
else {
|
|
1383
|
+
sessionWaiters.delete(sessionId);
|
|
1384
|
+
}
|
|
1385
|
+
};
|
|
1386
|
+
const done = () => {
|
|
1387
|
+
if (settled)
|
|
1388
|
+
return;
|
|
1389
|
+
settled = true;
|
|
1390
|
+
if (timeout) {
|
|
1391
|
+
try {
|
|
1392
|
+
clearTimeout(timeout);
|
|
1393
|
+
}
|
|
1394
|
+
catch { }
|
|
1395
|
+
timeout = null;
|
|
1396
|
+
}
|
|
1397
|
+
removeWaiter(done);
|
|
1398
|
+
resolve();
|
|
1399
|
+
};
|
|
1376
1400
|
const list = sessionWaiters.get(sessionId) || [];
|
|
1377
|
-
list.push(
|
|
1401
|
+
list.push(done);
|
|
1378
1402
|
sessionWaiters.set(sessionId, list);
|
|
1379
1403
|
if (SESSION_DRAIN_TIMEOUT_MS > 0 && Number.isFinite(SESSION_DRAIN_TIMEOUT_MS)) {
|
|
1380
|
-
setTimeout(
|
|
1404
|
+
timeout = setTimeout(done, SESSION_DRAIN_TIMEOUT_MS);
|
|
1405
|
+
try {
|
|
1406
|
+
timeout.unref?.();
|
|
1407
|
+
}
|
|
1408
|
+
catch { }
|
|
1381
1409
|
}
|
|
1382
1410
|
});
|
|
1383
1411
|
}
|
|
@@ -3997,6 +4025,7 @@ async function maybeCaptureDbValueAsync(params, sink) {
|
|
|
3997
4025
|
}
|
|
3998
4026
|
function reproMiddleware(cfg) {
|
|
3999
4027
|
const masking = normalizeMaskingConfig(cfg.masking);
|
|
4028
|
+
patchMongoDb(cfg);
|
|
4000
4029
|
return async function (req, res, next) {
|
|
4001
4030
|
const querySid = readRequestQueryParam(req, REPRO_QUERY_SESSION_PARAM);
|
|
4002
4031
|
const queryAid = readRequestQueryParam(req, REPRO_QUERY_ACTION_PARAM);
|
|
@@ -4545,6 +4574,7 @@ function nativeMongoOpsForMongooseOp(op) {
|
|
|
4545
4574
|
return [op];
|
|
4546
4575
|
}
|
|
4547
4576
|
function reproMongoosePlugin(cfg) {
|
|
4577
|
+
patchMongoDb(cfg);
|
|
4548
4578
|
return function (schema) {
|
|
4549
4579
|
// -------- pre/post save (unchanged) --------
|
|
4550
4580
|
schema.pre('save', { document: true }, function (next) {
|
|
@@ -7941,6 +7971,50 @@ function patchMongoCollectionPrototype(proto, cfg) {
|
|
|
7941
7971
|
patchMongoCollectionMethod(proto, op, true, cfg);
|
|
7942
7972
|
}
|
|
7943
7973
|
}
|
|
7974
|
+
function patchMongoCollectionInstance(collection, cfg) {
|
|
7975
|
+
if (!collection || typeof collection !== 'object')
|
|
7976
|
+
return collection;
|
|
7977
|
+
patchMongoCollectionPrototype(Object.getPrototypeOf(collection), cfg);
|
|
7978
|
+
patchMongoCollectionPrototype(collection, cfg);
|
|
7979
|
+
return collection;
|
|
7980
|
+
}
|
|
7981
|
+
function patchMongoDbPrototype(proto, cfg) {
|
|
7982
|
+
if (!proto)
|
|
7983
|
+
return;
|
|
7984
|
+
const marker = '__repro_mongodb_db_collection_patched';
|
|
7985
|
+
if (proto[marker])
|
|
7986
|
+
return;
|
|
7987
|
+
const original = proto.collection;
|
|
7988
|
+
if (typeof original !== 'function')
|
|
7989
|
+
return;
|
|
7990
|
+
proto[marker] = true;
|
|
7991
|
+
proto.collection = function reproPatchedMongoDbCollection(...args) {
|
|
7992
|
+
const collection = original.apply(this, args);
|
|
7993
|
+
return patchMongoCollectionInstance(collection, cfg);
|
|
7994
|
+
};
|
|
7995
|
+
}
|
|
7996
|
+
function patchMongoDbInstance(db, cfg) {
|
|
7997
|
+
if (!db || typeof db !== 'object')
|
|
7998
|
+
return db;
|
|
7999
|
+
patchMongoDbPrototype(Object.getPrototypeOf(db), cfg);
|
|
8000
|
+
patchMongoDbPrototype(db, cfg);
|
|
8001
|
+
return db;
|
|
8002
|
+
}
|
|
8003
|
+
function patchMongoClientPrototype(proto, cfg) {
|
|
8004
|
+
if (!proto)
|
|
8005
|
+
return;
|
|
8006
|
+
const marker = '__repro_mongodb_client_db_patched';
|
|
8007
|
+
if (proto[marker])
|
|
8008
|
+
return;
|
|
8009
|
+
const original = proto.db;
|
|
8010
|
+
if (typeof original !== 'function')
|
|
8011
|
+
return;
|
|
8012
|
+
proto[marker] = true;
|
|
8013
|
+
proto.db = function reproPatchedMongoClientDb(...args) {
|
|
8014
|
+
const db = original.apply(this, args);
|
|
8015
|
+
return patchMongoDbInstance(db, cfg);
|
|
8016
|
+
};
|
|
8017
|
+
}
|
|
7944
8018
|
function patchMongoDbFromModuleExports(mongoModule, cfg) {
|
|
7945
8019
|
if (!mongoModule)
|
|
7946
8020
|
return;
|
|
@@ -7955,6 +8029,28 @@ function patchMongoDbFromModuleExports(mongoModule, cfg) {
|
|
|
7955
8029
|
for (const ctor of constructors) {
|
|
7956
8030
|
patchMongoCollectionPrototype(ctor.prototype, cfg);
|
|
7957
8031
|
}
|
|
8032
|
+
const dbConstructors = new Set();
|
|
8033
|
+
const clientConstructors = new Set();
|
|
8034
|
+
const addDbCtor = (candidate) => {
|
|
8035
|
+
if (typeof candidate === 'function' && candidate.prototype) {
|
|
8036
|
+
dbConstructors.add(candidate);
|
|
8037
|
+
}
|
|
8038
|
+
};
|
|
8039
|
+
const addClientCtor = (candidate) => {
|
|
8040
|
+
if (typeof candidate === 'function' && candidate.prototype) {
|
|
8041
|
+
clientConstructors.add(candidate);
|
|
8042
|
+
}
|
|
8043
|
+
};
|
|
8044
|
+
addDbCtor(mongoModule?.Db);
|
|
8045
|
+
addDbCtor(mongoModule?.default?.Db);
|
|
8046
|
+
addClientCtor(mongoModule?.MongoClient);
|
|
8047
|
+
addClientCtor(mongoModule?.default?.MongoClient);
|
|
8048
|
+
for (const ctor of dbConstructors) {
|
|
8049
|
+
patchMongoDbPrototype(ctor.prototype, cfg);
|
|
8050
|
+
}
|
|
8051
|
+
for (const ctor of clientConstructors) {
|
|
8052
|
+
patchMongoClientPrototype(ctor.prototype, cfg);
|
|
8053
|
+
}
|
|
7958
8054
|
}
|
|
7959
8055
|
function installMongoDbRequireHook() {
|
|
7960
8056
|
if (mongoDbRequireHookInstalled)
|
|
@@ -7979,10 +8075,6 @@ function patchMongoDb(cfg) {
|
|
|
7979
8075
|
mongoDbPatchConfigs.push(cfg);
|
|
7980
8076
|
installMongoDbRequireHook();
|
|
7981
8077
|
const mongoModules = new Set();
|
|
7982
|
-
const resolvedMongoModule = requireOptionalHostModule('mongodb');
|
|
7983
|
-
if (resolvedMongoModule) {
|
|
7984
|
-
mongoModules.add(resolvedMongoModule);
|
|
7985
|
-
}
|
|
7986
8078
|
for (const loadedMongoModule of getLoadedModuleExportsByPackage('mongodb')) {
|
|
7987
8079
|
mongoModules.add(loadedMongoModule);
|
|
7988
8080
|
}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1429,7 +1429,6 @@ function scheduleTraceMaterializerIdleShutdown(): void {
|
|
|
1429
1429
|
try { void worker.terminate?.(); } catch {}
|
|
1430
1430
|
}
|
|
1431
1431
|
}, TRACE_MATERIALIZE_WORKER_IDLE_SHUTDOWN_MS);
|
|
1432
|
-
try { (traceMaterializerIdleTimer as any).unref?.(); } catch {}
|
|
1433
1432
|
}
|
|
1434
1433
|
|
|
1435
1434
|
function rejectTraceMaterializerPending(error: Error): void {
|
|
@@ -1745,11 +1744,34 @@ function waitForSessionDrain(sessionId: string): Promise<void> {
|
|
|
1745
1744
|
if (remaining <= 0) return Promise.resolve();
|
|
1746
1745
|
|
|
1747
1746
|
return new Promise(resolve => {
|
|
1747
|
+
let settled = false;
|
|
1748
|
+
let timeout: NodeJS.Timeout | null = null;
|
|
1749
|
+
const removeWaiter = (waiter: () => void) => {
|
|
1750
|
+
const list = sessionWaiters.get(sessionId);
|
|
1751
|
+
if (!list) return;
|
|
1752
|
+
const next = list.filter(fn => fn !== waiter);
|
|
1753
|
+
if (next.length) {
|
|
1754
|
+
sessionWaiters.set(sessionId, next);
|
|
1755
|
+
} else {
|
|
1756
|
+
sessionWaiters.delete(sessionId);
|
|
1757
|
+
}
|
|
1758
|
+
};
|
|
1759
|
+
const done = () => {
|
|
1760
|
+
if (settled) return;
|
|
1761
|
+
settled = true;
|
|
1762
|
+
if (timeout) {
|
|
1763
|
+
try { clearTimeout(timeout); } catch {}
|
|
1764
|
+
timeout = null;
|
|
1765
|
+
}
|
|
1766
|
+
removeWaiter(done);
|
|
1767
|
+
resolve();
|
|
1768
|
+
};
|
|
1748
1769
|
const list = sessionWaiters.get(sessionId) || [];
|
|
1749
|
-
list.push(
|
|
1770
|
+
list.push(done);
|
|
1750
1771
|
sessionWaiters.set(sessionId, list);
|
|
1751
1772
|
if (SESSION_DRAIN_TIMEOUT_MS > 0 && Number.isFinite(SESSION_DRAIN_TIMEOUT_MS)) {
|
|
1752
|
-
setTimeout(
|
|
1773
|
+
timeout = setTimeout(done, SESSION_DRAIN_TIMEOUT_MS);
|
|
1774
|
+
try { (timeout as any).unref?.(); } catch {}
|
|
1753
1775
|
}
|
|
1754
1776
|
});
|
|
1755
1777
|
}
|
|
@@ -4892,6 +4914,7 @@ export type ReproInitConfig = ReproMiddlewareConfig & {
|
|
|
4892
4914
|
|
|
4893
4915
|
export function reproMiddleware(cfg: ReproMiddlewareConfig) {
|
|
4894
4916
|
const masking = normalizeMaskingConfig(cfg.masking);
|
|
4917
|
+
patchMongoDb(cfg);
|
|
4895
4918
|
return async function (req: Request, res: Response, next: NextFunction) {
|
|
4896
4919
|
const querySid = readRequestQueryParam(req, REPRO_QUERY_SESSION_PARAM);
|
|
4897
4920
|
const queryAid = readRequestQueryParam(req, REPRO_QUERY_ACTION_PARAM);
|
|
@@ -5489,6 +5512,7 @@ function nativeMongoOpsForMongooseOp(op: string): string[] {
|
|
|
5489
5512
|
}
|
|
5490
5513
|
|
|
5491
5514
|
export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
|
|
5515
|
+
patchMongoDb(cfg);
|
|
5492
5516
|
return function (schema: Schema) {
|
|
5493
5517
|
// -------- pre/post save (unchanged) --------
|
|
5494
5518
|
schema.pre('save', { document: true }, function (next) {
|
|
@@ -9184,6 +9208,46 @@ function patchMongoCollectionPrototype(proto: any, cfg: MongoDbPatchConfig): voi
|
|
|
9184
9208
|
}
|
|
9185
9209
|
}
|
|
9186
9210
|
|
|
9211
|
+
function patchMongoCollectionInstance(collection: any, cfg: MongoDbPatchConfig): any {
|
|
9212
|
+
if (!collection || typeof collection !== 'object') return collection;
|
|
9213
|
+
patchMongoCollectionPrototype(Object.getPrototypeOf(collection), cfg);
|
|
9214
|
+
patchMongoCollectionPrototype(collection, cfg);
|
|
9215
|
+
return collection;
|
|
9216
|
+
}
|
|
9217
|
+
|
|
9218
|
+
function patchMongoDbPrototype(proto: any, cfg: MongoDbPatchConfig): void {
|
|
9219
|
+
if (!proto) return;
|
|
9220
|
+
const marker = '__repro_mongodb_db_collection_patched';
|
|
9221
|
+
if (proto[marker]) return;
|
|
9222
|
+
const original = proto.collection;
|
|
9223
|
+
if (typeof original !== 'function') return;
|
|
9224
|
+
proto[marker] = true;
|
|
9225
|
+
proto.collection = function reproPatchedMongoDbCollection(this: any, ...args: any[]) {
|
|
9226
|
+
const collection = original.apply(this, args);
|
|
9227
|
+
return patchMongoCollectionInstance(collection, cfg);
|
|
9228
|
+
};
|
|
9229
|
+
}
|
|
9230
|
+
|
|
9231
|
+
function patchMongoDbInstance(db: any, cfg: MongoDbPatchConfig): any {
|
|
9232
|
+
if (!db || typeof db !== 'object') return db;
|
|
9233
|
+
patchMongoDbPrototype(Object.getPrototypeOf(db), cfg);
|
|
9234
|
+
patchMongoDbPrototype(db, cfg);
|
|
9235
|
+
return db;
|
|
9236
|
+
}
|
|
9237
|
+
|
|
9238
|
+
function patchMongoClientPrototype(proto: any, cfg: MongoDbPatchConfig): void {
|
|
9239
|
+
if (!proto) return;
|
|
9240
|
+
const marker = '__repro_mongodb_client_db_patched';
|
|
9241
|
+
if (proto[marker]) return;
|
|
9242
|
+
const original = proto.db;
|
|
9243
|
+
if (typeof original !== 'function') return;
|
|
9244
|
+
proto[marker] = true;
|
|
9245
|
+
proto.db = function reproPatchedMongoClientDb(this: any, ...args: any[]) {
|
|
9246
|
+
const db = original.apply(this, args);
|
|
9247
|
+
return patchMongoDbInstance(db, cfg);
|
|
9248
|
+
};
|
|
9249
|
+
}
|
|
9250
|
+
|
|
9187
9251
|
function patchMongoDbFromModuleExports(mongoModule: any, cfg: MongoDbPatchConfig): void {
|
|
9188
9252
|
if (!mongoModule) return;
|
|
9189
9253
|
const constructors = new Set<any>();
|
|
@@ -9199,6 +9263,31 @@ function patchMongoDbFromModuleExports(mongoModule: any, cfg: MongoDbPatchConfig
|
|
|
9199
9263
|
for (const ctor of constructors) {
|
|
9200
9264
|
patchMongoCollectionPrototype(ctor.prototype, cfg);
|
|
9201
9265
|
}
|
|
9266
|
+
|
|
9267
|
+
const dbConstructors = new Set<any>();
|
|
9268
|
+
const clientConstructors = new Set<any>();
|
|
9269
|
+
const addDbCtor = (candidate: any) => {
|
|
9270
|
+
if (typeof candidate === 'function' && candidate.prototype) {
|
|
9271
|
+
dbConstructors.add(candidate);
|
|
9272
|
+
}
|
|
9273
|
+
};
|
|
9274
|
+
const addClientCtor = (candidate: any) => {
|
|
9275
|
+
if (typeof candidate === 'function' && candidate.prototype) {
|
|
9276
|
+
clientConstructors.add(candidate);
|
|
9277
|
+
}
|
|
9278
|
+
};
|
|
9279
|
+
|
|
9280
|
+
addDbCtor(mongoModule?.Db);
|
|
9281
|
+
addDbCtor(mongoModule?.default?.Db);
|
|
9282
|
+
addClientCtor(mongoModule?.MongoClient);
|
|
9283
|
+
addClientCtor(mongoModule?.default?.MongoClient);
|
|
9284
|
+
|
|
9285
|
+
for (const ctor of dbConstructors) {
|
|
9286
|
+
patchMongoDbPrototype(ctor.prototype, cfg);
|
|
9287
|
+
}
|
|
9288
|
+
for (const ctor of clientConstructors) {
|
|
9289
|
+
patchMongoClientPrototype(ctor.prototype, cfg);
|
|
9290
|
+
}
|
|
9202
9291
|
}
|
|
9203
9292
|
|
|
9204
9293
|
function installMongoDbRequireHook(): void {
|
|
@@ -9225,10 +9314,6 @@ export function patchMongoDb(cfg: MongoDbPatchConfig): void {
|
|
|
9225
9314
|
installMongoDbRequireHook();
|
|
9226
9315
|
|
|
9227
9316
|
const mongoModules = new Set<any>();
|
|
9228
|
-
const resolvedMongoModule = requireOptionalHostModule('mongodb');
|
|
9229
|
-
if (resolvedMongoModule) {
|
|
9230
|
-
mongoModules.add(resolvedMongoModule);
|
|
9231
|
-
}
|
|
9232
9317
|
for (const loadedMongoModule of getLoadedModuleExportsByPackage('mongodb')) {
|
|
9233
9318
|
mongoModules.add(loadedMongoModule);
|
|
9234
9319
|
}
|
|
@@ -7,6 +7,16 @@ process.env.TRACE_LINGER_AFTER_FINISH_MS = '0';
|
|
|
7
7
|
|
|
8
8
|
const assert = require('assert');
|
|
9
9
|
const { EventEmitter } = require('events');
|
|
10
|
+
const mongodb = require('mongodb');
|
|
11
|
+
|
|
12
|
+
const originalNativeMongoInsertOne = mongodb.Collection.prototype.insertOne;
|
|
13
|
+
|
|
14
|
+
mongodb.Collection.prototype.insertOne = async function reproFakeNativeMongoInsertOne(doc, options) {
|
|
15
|
+
assert.strictEqual(options.session.id, 'native-real-session-1');
|
|
16
|
+
doc._id = doc._id ?? 'subject-real-1';
|
|
17
|
+
return { acknowledged: true, insertedId: doc._id };
|
|
18
|
+
};
|
|
19
|
+
|
|
10
20
|
const { flushIngestQueue } = require('../dist/ingest/client');
|
|
11
21
|
const { reproMiddleware, __reproTestHooks } = require('../dist');
|
|
12
22
|
|
|
@@ -122,6 +132,127 @@ function dbChanges(events) {
|
|
|
122
132
|
.flatMap((event) => Array.isArray(event.payload?.db) ? event.payload.db : []);
|
|
123
133
|
}
|
|
124
134
|
|
|
135
|
+
async function testReproMiddlewarePatchesPreloadedNativeMongoClientInsertOne() {
|
|
136
|
+
await withCapturedFetch(async (capturedBodies) => {
|
|
137
|
+
__reproTestHooks.resetSdkBackgroundQueuesForTest();
|
|
138
|
+
const sessionId = 'S_mongo_real_client_insert';
|
|
139
|
+
const actionId = 'A_mongo_real_client_insert';
|
|
140
|
+
const cfg = makeConfig(sessionId, actionId);
|
|
141
|
+
|
|
142
|
+
const collection = new mongodb.MongoClient('mongodb://127.0.0.1:27017/clinical')
|
|
143
|
+
.db('clinical')
|
|
144
|
+
.collection('subjects');
|
|
145
|
+
|
|
146
|
+
await runCapturedRequest(cfg, sessionId, actionId, async () => {
|
|
147
|
+
const result = await collection.insertOne(
|
|
148
|
+
{ subjectNumber: '10013038', status: 'screened' },
|
|
149
|
+
{
|
|
150
|
+
session: {
|
|
151
|
+
id: 'native-real-session-1',
|
|
152
|
+
hasEnded: false,
|
|
153
|
+
inTransaction: () => true,
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
);
|
|
157
|
+
assert.deepStrictEqual(result, { acknowledged: true, insertedId: 'subject-real-1' });
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
await waitFor(() => dbChanges(flattenEvents(capturedBodies)).length === 1);
|
|
161
|
+
await drainSdk();
|
|
162
|
+
|
|
163
|
+
const [change] = dbChanges(flattenEvents(capturedBodies));
|
|
164
|
+
assert.strictEqual(change.type, 'mongo');
|
|
165
|
+
assert.strictEqual(change.collection, 'subjects');
|
|
166
|
+
assert.strictEqual(change.op, 'insertOne');
|
|
167
|
+
assert.strictEqual(change.pk._id, 'subject-real-1');
|
|
168
|
+
assert.strictEqual(change.query.driver, 'mongodb');
|
|
169
|
+
assert.strictEqual(change.query.db, 'clinical');
|
|
170
|
+
assert.strictEqual(change.query.doc.subjectNumber, '10013038');
|
|
171
|
+
assert.strictEqual(change.query.options.session.__kind, 'mongodb_client_session');
|
|
172
|
+
assert.strictEqual(change.query.options.session.id, 'native-real-session-1');
|
|
173
|
+
assert.strictEqual(change.after.subjectNumber, '10013038');
|
|
174
|
+
assert.strictEqual(change.resultMeta.insertedId, 'subject-real-1');
|
|
175
|
+
assert.strictEqual(change.resultMeta.acknowledged, true);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function testMongoClientDbCollectionChainIsPatchedWithoutCollectionExport() {
|
|
180
|
+
await withCapturedFetch(async (capturedBodies) => {
|
|
181
|
+
__reproTestHooks.resetSdkBackgroundQueuesForTest();
|
|
182
|
+
const sessionId = 'S_mongo_client_chain';
|
|
183
|
+
const actionId = 'A_mongo_client_chain';
|
|
184
|
+
const cfg = makeConfig(sessionId, actionId);
|
|
185
|
+
|
|
186
|
+
class FakeCollection {
|
|
187
|
+
constructor(dbName, collectionName) {
|
|
188
|
+
this.dbName = dbName;
|
|
189
|
+
this.collectionName = collectionName;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async insertOne(doc, options) {
|
|
193
|
+
assert.strictEqual(options.session.id, 'native-chain-session-1');
|
|
194
|
+
doc._id = doc._id ?? 'subject-chain-1';
|
|
195
|
+
return { acknowledged: true, insertedId: doc._id };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
class FakeDb {
|
|
200
|
+
constructor(dbName) {
|
|
201
|
+
this.databaseName = dbName;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
collection(collectionName) {
|
|
205
|
+
return new FakeCollection(this.databaseName, collectionName);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
class FakeMongoClient {
|
|
210
|
+
db(dbName) {
|
|
211
|
+
return new FakeDb(dbName);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
__reproTestHooks.patchMongoDbModuleForTest({
|
|
216
|
+
MongoClient: FakeMongoClient,
|
|
217
|
+
Db: FakeDb,
|
|
218
|
+
}, cfg);
|
|
219
|
+
|
|
220
|
+
const dbClient = new FakeMongoClient();
|
|
221
|
+
const subjectsCollection = dbClient.db('clinical').collection('subjects');
|
|
222
|
+
|
|
223
|
+
await runCapturedRequest(cfg, sessionId, actionId, async () => {
|
|
224
|
+
const result = await subjectsCollection.insertOne(
|
|
225
|
+
{ subjectNumber: '10013038', status: 'screened' },
|
|
226
|
+
{
|
|
227
|
+
session: {
|
|
228
|
+
id: 'native-chain-session-1',
|
|
229
|
+
hasEnded: false,
|
|
230
|
+
inTransaction: () => true,
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
);
|
|
234
|
+
assert.deepStrictEqual(result, { acknowledged: true, insertedId: 'subject-chain-1' });
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
await waitFor(() => dbChanges(flattenEvents(capturedBodies)).length === 1);
|
|
238
|
+
await drainSdk();
|
|
239
|
+
|
|
240
|
+
const [change] = dbChanges(flattenEvents(capturedBodies));
|
|
241
|
+
assert.strictEqual(change.type, 'mongo');
|
|
242
|
+
assert.strictEqual(change.collection, 'subjects');
|
|
243
|
+
assert.strictEqual(change.op, 'insertOne');
|
|
244
|
+
assert.strictEqual(change.pk._id, 'subject-chain-1');
|
|
245
|
+
assert.strictEqual(change.query.driver, 'mongodb');
|
|
246
|
+
assert.strictEqual(change.query.db, 'clinical');
|
|
247
|
+
assert.strictEqual(change.query.doc.subjectNumber, '10013038');
|
|
248
|
+
assert.strictEqual(change.query.options.session.__kind, 'mongodb_client_session');
|
|
249
|
+
assert.strictEqual(change.query.options.session.id, 'native-chain-session-1');
|
|
250
|
+
assert.strictEqual(change.after.subjectNumber, '10013038');
|
|
251
|
+
assert.strictEqual(change.resultMeta.insertedId, 'subject-chain-1');
|
|
252
|
+
assert.strictEqual(change.resultMeta.acknowledged, true);
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
125
256
|
async function testNativeInsertOneIsCaptured() {
|
|
126
257
|
await withCapturedFetch(async (capturedBodies) => {
|
|
127
258
|
__reproTestHooks.resetSdkBackgroundQueuesForTest();
|
|
@@ -321,6 +452,8 @@ async function testNativeOperationFailureIsCapturedAndRethrown() {
|
|
|
321
452
|
}
|
|
322
453
|
|
|
323
454
|
(async () => {
|
|
455
|
+
await testReproMiddlewarePatchesPreloadedNativeMongoClientInsertOne();
|
|
456
|
+
await testMongoClientDbCollectionChainIsPatchedWithoutCollectionExport();
|
|
324
457
|
await testNativeInsertOneIsCaptured();
|
|
325
458
|
await testNativeUpdateOneUpsertIsCaptured();
|
|
326
459
|
await testNativeFindCursorToArrayIsCaptured();
|
|
@@ -329,4 +462,6 @@ async function testNativeOperationFailureIsCapturedAndRethrown() {
|
|
|
329
462
|
})().catch((err) => {
|
|
330
463
|
console.error(err);
|
|
331
464
|
process.exit(1);
|
|
465
|
+
}).finally(() => {
|
|
466
|
+
mongodb.Collection.prototype.insertOne = originalNativeMongoInsertOne;
|
|
332
467
|
});
|