@reproapp/node-sdk 0.0.31 → 0.0.33
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.d.ts +27 -0
- package/dist/index.js +235 -158
- package/package.json +3 -3
- package/src/index.ts +218 -149
- package/test/mongodb-integration.test.js +71 -1
- package/test/mongodb-mongoose-native-suppression.testcontainers.test.js +158 -0
- package/test/mongodb-suppression-cleanup.test.js +72 -0
package/src/index.ts
CHANGED
|
@@ -1163,6 +1163,7 @@ type NativeMongoSuppression = {
|
|
|
1163
1163
|
collection?: string | null;
|
|
1164
1164
|
ops?: Set<string>;
|
|
1165
1165
|
startedAt: number;
|
|
1166
|
+
ownerStore?: Ctx;
|
|
1166
1167
|
};
|
|
1167
1168
|
|
|
1168
1169
|
type RequestCaptureScopeState = {
|
|
@@ -1236,6 +1237,7 @@ function pushNativeMongoSuppression(collection: any, ops?: string[] | readonly s
|
|
|
1236
1237
|
collection: collection == null ? null : String(collection),
|
|
1237
1238
|
ops: normalizedOps.length ? new Set(normalizedOps) : undefined,
|
|
1238
1239
|
startedAt: Date.now(),
|
|
1240
|
+
ownerStore: store,
|
|
1239
1241
|
};
|
|
1240
1242
|
if (!Array.isArray(store.nativeMongoSuppressions)) {
|
|
1241
1243
|
store.nativeMongoSuppressions = [];
|
|
@@ -1246,28 +1248,64 @@ function pushNativeMongoSuppression(collection: any, ops?: string[] | readonly s
|
|
|
1246
1248
|
|
|
1247
1249
|
function popNativeMongoSuppression(token: NativeMongoSuppression | null | undefined): void {
|
|
1248
1250
|
if (!token) return;
|
|
1249
|
-
const store = als.getStore();
|
|
1251
|
+
const store = token.ownerStore ?? als.getStore();
|
|
1250
1252
|
const suppressions = store?.nativeMongoSuppressions;
|
|
1251
|
-
if (
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1253
|
+
if (Array.isArray(suppressions) && suppressions.length > 0) {
|
|
1254
|
+
const index = suppressions.indexOf(token);
|
|
1255
|
+
if (index >= 0) {
|
|
1256
|
+
suppressions.splice(index, 1);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
token.ownerStore = undefined;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
function releaseNativeMongoSuppression(target: any): void {
|
|
1263
|
+
const token = target?.__repro_native_mongo_suppression;
|
|
1264
|
+
popNativeMongoSuppression(token);
|
|
1265
|
+
try { delete target.__repro_native_mongo_suppression; } catch {
|
|
1266
|
+
try { target.__repro_native_mongo_suppression = undefined; } catch {}
|
|
1255
1267
|
}
|
|
1256
1268
|
}
|
|
1257
1269
|
|
|
1270
|
+
function isMongooseInternalNativeMongoCall(): boolean {
|
|
1271
|
+
let stack = '';
|
|
1272
|
+
try {
|
|
1273
|
+
stack = new Error().stack ?? '';
|
|
1274
|
+
} catch {
|
|
1275
|
+
stack = '';
|
|
1276
|
+
}
|
|
1277
|
+
return stack.includes('/node_modules/mongoose/') ||
|
|
1278
|
+
stack.includes('\\node_modules\\mongoose\\') ||
|
|
1279
|
+
stack.includes('mongoose/lib/');
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1258
1282
|
function isNativeMongoSuppressed(collection: any, op: string): boolean {
|
|
1259
1283
|
const suppressions = (getCtx() as Ctx).nativeMongoSuppressions;
|
|
1260
1284
|
if (!Array.isArray(suppressions) || suppressions.length === 0) return false;
|
|
1261
1285
|
const normalizedCollection = collection == null ? null : String(collection);
|
|
1262
1286
|
const normalizedOp = normalizeNativeMongoSuppressionOp(op);
|
|
1263
1287
|
const now = Date.now();
|
|
1264
|
-
|
|
1265
|
-
|
|
1288
|
+
const matchingTokens: NativeMongoSuppression[] = [];
|
|
1289
|
+
for (let i = suppressions.length - 1; i >= 0; i -= 1) {
|
|
1290
|
+
const token = suppressions[i];
|
|
1291
|
+
if (!token || now - token.startedAt > 60000) {
|
|
1292
|
+
suppressions.splice(i, 1);
|
|
1293
|
+
continue;
|
|
1294
|
+
}
|
|
1266
1295
|
if (token.collection && normalizedCollection && token.collection !== normalizedCollection) continue;
|
|
1267
1296
|
if (token.ops && token.ops.size > 0 && !token.ops.has(normalizedOp)) continue;
|
|
1268
|
-
|
|
1297
|
+
matchingTokens.push(token);
|
|
1269
1298
|
}
|
|
1270
|
-
return false;
|
|
1299
|
+
if (matchingTokens.length === 0) return false;
|
|
1300
|
+
|
|
1301
|
+
if (!isMongooseInternalNativeMongoCall()) {
|
|
1302
|
+
return false;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
for (const token of matchingTokens) {
|
|
1306
|
+
popNativeMongoSuppression(token);
|
|
1307
|
+
}
|
|
1308
|
+
return true;
|
|
1271
1309
|
}
|
|
1272
1310
|
|
|
1273
1311
|
function currentClockSkewMs(): number {
|
|
@@ -5572,41 +5610,44 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
|
|
|
5572
5610
|
});
|
|
5573
5611
|
|
|
5574
5612
|
schema.post('save', { document: true }, function () {
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
const meta = (this as any).__repro_meta || {};
|
|
5580
|
-
const before = null;
|
|
5581
|
-
const after = this;
|
|
5582
|
-
const collection = meta.collection || resolveCollectionOrWarn(this, 'doc');
|
|
5583
|
-
const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
|
|
5584
|
-
if (!shouldCaptureDbSpan(spanContext)) return;
|
|
5585
|
-
const modifiedPaths = Array.isArray(meta.modifiedPaths) ? meta.modifiedPaths : [];
|
|
5586
|
-
|
|
5587
|
-
const query = meta.wasNew
|
|
5588
|
-
? { op: 'insertOne', doc: after }
|
|
5589
|
-
: {
|
|
5590
|
-
filter: { _id: this._id },
|
|
5591
|
-
update: buildModifiedPathUpdate(this, modifiedPaths),
|
|
5592
|
-
options: { upsert: false },
|
|
5593
|
-
};
|
|
5613
|
+
try {
|
|
5614
|
+
const { sid, aid } = getCtx();
|
|
5615
|
+
if (!sid || !aid) return;
|
|
5616
|
+
if ((this as any).$isSubdocument) return;
|
|
5594
5617
|
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5618
|
+
const meta = (this as any).__repro_meta || {};
|
|
5619
|
+
const before = null;
|
|
5620
|
+
const after = this;
|
|
5621
|
+
const collection = meta.collection || resolveCollectionOrWarn(this, 'doc');
|
|
5622
|
+
const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
|
|
5623
|
+
if (!shouldCaptureDbSpan(spanContext)) return;
|
|
5624
|
+
const modifiedPaths = Array.isArray(meta.modifiedPaths) ? meta.modifiedPaths : [];
|
|
5625
|
+
|
|
5626
|
+
const query = meta.wasNew
|
|
5627
|
+
? { op: 'insertOne', doc: after }
|
|
5628
|
+
: {
|
|
5629
|
+
filter: { _id: this._id },
|
|
5630
|
+
update: buildModifiedPathUpdate(this, modifiedPaths),
|
|
5631
|
+
options: { upsert: false },
|
|
5632
|
+
};
|
|
5633
|
+
|
|
5634
|
+
emitDbQuery(cfg, sid, aid ?? undefined, {
|
|
5635
|
+
collection,
|
|
5636
|
+
pk: { _id: (this as any)._id },
|
|
5637
|
+
before,
|
|
5638
|
+
after,
|
|
5639
|
+
op: meta.wasNew ? 'insert' : 'update',
|
|
5640
|
+
query,
|
|
5641
|
+
t: alignTimestamp(meta.t0 ?? Date.now()),
|
|
5642
|
+
spanContext,
|
|
5643
|
+
});
|
|
5644
|
+
} finally {
|
|
5645
|
+
releaseNativeMongoSuppression(this);
|
|
5646
|
+
}
|
|
5606
5647
|
});
|
|
5607
5648
|
|
|
5608
5649
|
schema.post('save', { document: true }, function (error: any, _doc: any, next: Function) {
|
|
5609
|
-
|
|
5650
|
+
releaseNativeMongoSuppression(this);
|
|
5610
5651
|
next(error);
|
|
5611
5652
|
});
|
|
5612
5653
|
|
|
@@ -5631,35 +5672,38 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
|
|
|
5631
5672
|
});
|
|
5632
5673
|
|
|
5633
5674
|
schema.post<Query<any, any>>('findOneAndUpdate', function (res: any) {
|
|
5634
|
-
|
|
5635
|
-
|
|
5675
|
+
try {
|
|
5676
|
+
const { sid, aid } = getCtx();
|
|
5677
|
+
if (!sid || !aid) return;
|
|
5636
5678
|
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5679
|
+
const before = null;
|
|
5680
|
+
const after = res ?? null;
|
|
5681
|
+
const collection = (this as any).__repro_collection || resolveCollectionOrWarn(this, 'query');
|
|
5682
|
+
const spanContext = (this as any).__repro_spanContext || captureSpanContextFromTracer(this);
|
|
5683
|
+
if (!shouldCaptureDbSpan(spanContext)) return;
|
|
5684
|
+
const filter = (this as any).__repro_filter;
|
|
5685
|
+
const pk = after?._id ?? (filter && typeof filter === 'object' ? (filter as any)._id : undefined);
|
|
5644
5686
|
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5687
|
+
emitDbQuery(cfg, sid, aid ?? undefined, {
|
|
5688
|
+
collection,
|
|
5689
|
+
pk: { _id: pk },
|
|
5690
|
+
before,
|
|
5691
|
+
after,
|
|
5692
|
+
op: 'update',
|
|
5693
|
+
queryRaw: {
|
|
5694
|
+
filter,
|
|
5695
|
+
update: (this as any).__repro_update,
|
|
5696
|
+
},
|
|
5697
|
+
t: alignTimestamp((this as any).__repro_t0 ?? Date.now()),
|
|
5698
|
+
spanContext,
|
|
5699
|
+
});
|
|
5700
|
+
} finally {
|
|
5701
|
+
releaseNativeMongoSuppression(this);
|
|
5702
|
+
}
|
|
5659
5703
|
});
|
|
5660
5704
|
|
|
5661
5705
|
schema.post<Query<any, any>>('findOneAndUpdate', function (this: any, error: any, _res: any, next: Function) {
|
|
5662
|
-
|
|
5706
|
+
releaseNativeMongoSuppression(this);
|
|
5663
5707
|
next(error);
|
|
5664
5708
|
} as any);
|
|
5665
5709
|
|
|
@@ -5681,27 +5725,30 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
|
|
|
5681
5725
|
});
|
|
5682
5726
|
|
|
5683
5727
|
schema.post<Query<any, any>>('deleteOne', { document: false, query: true }, function () {
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5728
|
+
try {
|
|
5729
|
+
const { sid, aid } = getCtx(); if (!sid || !aid) return;
|
|
5730
|
+
const before = null;
|
|
5731
|
+
const collection = (this as any).__repro_collection || resolveCollectionOrWarn(this, 'query');
|
|
5732
|
+
const filter = (this as any).__repro_filter ?? undefined;
|
|
5733
|
+
const spanContext = (this as any).__repro_spanContext || captureSpanContextFromTracer(this);
|
|
5734
|
+
if (!shouldCaptureDbSpan(spanContext)) return;
|
|
5735
|
+
emitDbQuery(cfg, sid, aid ?? undefined, {
|
|
5736
|
+
collection,
|
|
5737
|
+
pk: filter && typeof filter === 'object' && '_id' in filter ? { _id: (filter as any)._id } : undefined,
|
|
5738
|
+
before,
|
|
5739
|
+
after: null,
|
|
5740
|
+
op: 'delete',
|
|
5741
|
+
queryRaw: { filter },
|
|
5742
|
+
t: alignTimestamp((this as any).__repro_t0 ?? Date.now()),
|
|
5743
|
+
spanContext,
|
|
5744
|
+
});
|
|
5745
|
+
} finally {
|
|
5746
|
+
releaseNativeMongoSuppression(this);
|
|
5747
|
+
}
|
|
5701
5748
|
});
|
|
5702
5749
|
|
|
5703
5750
|
schema.post<Query<any, any>>('deleteOne', { document: false, query: true }, function (this: any, error: any, _res: any, next: Function) {
|
|
5704
|
-
|
|
5751
|
+
releaseNativeMongoSuppression(this);
|
|
5705
5752
|
next(error);
|
|
5706
5753
|
} as any);
|
|
5707
5754
|
|
|
@@ -5766,31 +5813,34 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
|
|
|
5766
5813
|
});
|
|
5767
5814
|
|
|
5768
5815
|
schema.post(op as any, function (this: any, res: any) {
|
|
5769
|
-
|
|
5770
|
-
|
|
5816
|
+
try {
|
|
5817
|
+
const { sid, aid } = getCtx();
|
|
5818
|
+
if (!sid) return;
|
|
5771
5819
|
|
|
5772
|
-
|
|
5773
|
-
|
|
5820
|
+
const meta = (this as any).__repro_qmeta || { t0: Date.now(), collection: 'unknown', op };
|
|
5821
|
+
const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
|
|
5774
5822
|
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5823
|
+
emitDbQuery(cfg, sid, aid, {
|
|
5824
|
+
collection: meta.collection,
|
|
5825
|
+
op,
|
|
5826
|
+
queryRaw: {
|
|
5827
|
+
filter: meta.rawFilter,
|
|
5828
|
+
update: meta.rawUpdate,
|
|
5829
|
+
projection: meta.rawProjection,
|
|
5830
|
+
options: meta.rawOptions,
|
|
5831
|
+
},
|
|
5832
|
+
resultMetaRaw: res,
|
|
5833
|
+
durMs: Date.now() - meta.t0,
|
|
5834
|
+
t: alignedNow(),
|
|
5835
|
+
spanContext,
|
|
5836
|
+
});
|
|
5837
|
+
} finally {
|
|
5838
|
+
releaseNativeMongoSuppression(this);
|
|
5839
|
+
}
|
|
5790
5840
|
});
|
|
5791
5841
|
|
|
5792
5842
|
schema.post(op as any, function (this: any, error: any, _res: any, next: Function) {
|
|
5793
|
-
|
|
5843
|
+
releaseNativeMongoSuppression(this);
|
|
5794
5844
|
next(error);
|
|
5795
5845
|
} as any);
|
|
5796
5846
|
}
|
|
@@ -5824,25 +5874,28 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
|
|
|
5824
5874
|
} as any);
|
|
5825
5875
|
|
|
5826
5876
|
schema.post<any>('insertMany' as any, { document: false, query: false } as any, function (this: any, docs: any[]) {
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5877
|
+
try {
|
|
5878
|
+
const { sid, aid } = getCtx();
|
|
5879
|
+
if (!sid) return;
|
|
5880
|
+
const meta = (this as any).__repro_insert_meta || { t0: Date.now(), collection: 'unknown' };
|
|
5881
|
+
const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
|
|
5882
|
+
|
|
5883
|
+
emitDbQuery(cfg, sid, aid ?? undefined, {
|
|
5884
|
+
collection: meta.collection,
|
|
5885
|
+
op: 'insertMany',
|
|
5886
|
+
queryRaw: { docs: meta.rawDocs ?? undefined },
|
|
5887
|
+
resultMetaRaw: docs,
|
|
5888
|
+
durMs: Date.now() - meta.t0,
|
|
5889
|
+
t: alignedNow(),
|
|
5890
|
+
spanContext,
|
|
5891
|
+
});
|
|
5892
|
+
} finally {
|
|
5893
|
+
releaseNativeMongoSuppression(this);
|
|
5894
|
+
}
|
|
5842
5895
|
} as any);
|
|
5843
5896
|
|
|
5844
5897
|
schema.post<any>('insertMany' as any, { document: false, query: false } as any, function (this: any, error: any, _res: any, next: Function) {
|
|
5845
|
-
|
|
5898
|
+
releaseNativeMongoSuppression(this);
|
|
5846
5899
|
next(error);
|
|
5847
5900
|
} as any);
|
|
5848
5901
|
|
|
@@ -5871,25 +5924,28 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
|
|
|
5871
5924
|
} as any);
|
|
5872
5925
|
|
|
5873
5926
|
schema.post<any>('bulkWrite' as any, { document: false, query: false } as any, function (this: any, res: any) {
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5927
|
+
try {
|
|
5928
|
+
const { sid, aid } = getCtx();
|
|
5929
|
+
if (!sid) return;
|
|
5930
|
+
const meta = (this as any).__repro_bulk_meta || { t0: Date.now(), collection: 'unknown' };
|
|
5931
|
+
const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
|
|
5932
|
+
|
|
5933
|
+
emitDbQuery(cfg, sid, aid, {
|
|
5934
|
+
collection: meta.collection,
|
|
5935
|
+
op: 'bulkWrite',
|
|
5936
|
+
queryRaw: { ops: meta.rawOps ?? undefined },
|
|
5937
|
+
resultMetaRaw: res,
|
|
5938
|
+
durMs: Date.now() - meta.t0,
|
|
5939
|
+
t: alignedNow(),
|
|
5940
|
+
spanContext,
|
|
5941
|
+
});
|
|
5942
|
+
} finally {
|
|
5943
|
+
releaseNativeMongoSuppression(this);
|
|
5944
|
+
}
|
|
5889
5945
|
} as any);
|
|
5890
5946
|
|
|
5891
5947
|
schema.post<any>('bulkWrite' as any, { document: false, query: false } as any, function (this: any, error: any, _res: any, next: Function) {
|
|
5892
|
-
|
|
5948
|
+
releaseNativeMongoSuppression(this);
|
|
5893
5949
|
next(error);
|
|
5894
5950
|
} as any);
|
|
5895
5951
|
|
|
@@ -5924,26 +5980,29 @@ export function reproMongoosePlugin(cfg: ReproMiddlewareConfig) {
|
|
|
5924
5980
|
});
|
|
5925
5981
|
|
|
5926
5982
|
schema.post('aggregate', function (this: any, res: any[]) {
|
|
5927
|
-
|
|
5928
|
-
|
|
5983
|
+
try {
|
|
5984
|
+
const { sid, aid } = getCtx();
|
|
5985
|
+
if (!sid) return;
|
|
5929
5986
|
|
|
5930
|
-
|
|
5931
|
-
|
|
5987
|
+
const meta = (this as any).__repro_aggmeta || { t0: Date.now(), collection: 'unknown' };
|
|
5988
|
+
const spanContext = meta.spanContext || captureSpanContextFromTracer(this);
|
|
5932
5989
|
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5990
|
+
emitDbQuery(cfg, sid, aid, {
|
|
5991
|
+
collection: meta.collection,
|
|
5992
|
+
op: 'aggregate',
|
|
5993
|
+
queryRaw: { pipeline: meta.rawPipeline },
|
|
5994
|
+
resultMetaRaw: res,
|
|
5995
|
+
durMs: Date.now() - meta.t0,
|
|
5996
|
+
t: alignedNow(),
|
|
5997
|
+
spanContext,
|
|
5998
|
+
});
|
|
5999
|
+
} finally {
|
|
6000
|
+
releaseNativeMongoSuppression(this);
|
|
6001
|
+
}
|
|
5943
6002
|
});
|
|
5944
6003
|
|
|
5945
6004
|
schema.post('aggregate', function (this: any, error: any, _res: any, next: Function) {
|
|
5946
|
-
|
|
6005
|
+
releaseNativeMongoSuppression(this);
|
|
5947
6006
|
next(error);
|
|
5948
6007
|
} as any);
|
|
5949
6008
|
};
|
|
@@ -9966,6 +10025,16 @@ export const __reproTestHooks = {
|
|
|
9966
10025
|
lastClientActivityAt = 0;
|
|
9967
10026
|
sdkBackgroundQuietUntil = 0;
|
|
9968
10027
|
},
|
|
10028
|
+
runWithRequestContextForTest<T>(ctx: Partial<Ctx>, fn: () => T): T {
|
|
10029
|
+
return als.run({
|
|
10030
|
+
...ctx,
|
|
10031
|
+
excludedSpanIds: ctx.excludedSpanIds ?? new Set<string>(),
|
|
10032
|
+
}, fn);
|
|
10033
|
+
},
|
|
10034
|
+
getNativeMongoSuppressionCountForTest(): number {
|
|
10035
|
+
const suppressions = getCtx().nativeMongoSuppressions;
|
|
10036
|
+
return Array.isArray(suppressions) ? suppressions.length : 0;
|
|
10037
|
+
},
|
|
9969
10038
|
getRuntimePrivacyPolicyForTest(cfg: ReproMiddlewareConfig): NormalizedRuntimePrivacyPolicy | null {
|
|
9970
10039
|
return getRuntimePrivacyState(cfg).policy;
|
|
9971
10040
|
},
|
|
@@ -8,6 +8,7 @@ process.env.TRACE_LINGER_AFTER_FINISH_MS = '0';
|
|
|
8
8
|
const assert = require('assert');
|
|
9
9
|
const { EventEmitter } = require('events');
|
|
10
10
|
const mongodb = require('mongodb');
|
|
11
|
+
const mongoose = require('mongoose');
|
|
11
12
|
|
|
12
13
|
const originalNativeMongoInsertOne = mongodb.Collection.prototype.insertOne;
|
|
13
14
|
|
|
@@ -18,7 +19,7 @@ mongodb.Collection.prototype.insertOne = async function reproFakeNativeMongoInse
|
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
const { flushIngestQueue } = require('../dist/ingest/client');
|
|
21
|
-
const { reproMiddleware, __reproTestHooks } = require('../dist');
|
|
22
|
+
const { reproMiddleware, reproMongoosePlugin, __reproTestHooks } = require('../dist');
|
|
22
23
|
|
|
23
24
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
24
25
|
|
|
@@ -126,12 +127,80 @@ async function runCapturedRequest(cfg, sessionId, actionId, fn) {
|
|
|
126
127
|
res.end();
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
function execPre(hooks, name, context) {
|
|
131
|
+
return new Promise((resolve, reject) => {
|
|
132
|
+
hooks.execPre(name, context, [], (error) => (error ? reject(error) : resolve()));
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
129
136
|
function dbChanges(events) {
|
|
130
137
|
return events
|
|
131
138
|
.filter((event) => event.event_type === 'db_change')
|
|
132
139
|
.flatMap((event) => Array.isArray(event.payload?.db) ? event.payload.db : []);
|
|
133
140
|
}
|
|
134
141
|
|
|
142
|
+
async function testMongooseSuppressionDoesNotHideExplicitNativeInsertOne() {
|
|
143
|
+
await withCapturedFetch(async (capturedBodies) => {
|
|
144
|
+
__reproTestHooks.resetSdkBackgroundQueuesForTest();
|
|
145
|
+
const sessionId = 'S_mongo_mixed_driver_insert';
|
|
146
|
+
const actionId = 'A_mongo_mixed_driver_insert';
|
|
147
|
+
const cfg = makeConfig(sessionId, actionId);
|
|
148
|
+
|
|
149
|
+
const schema = new mongoose.Schema({ subjectNumber: String, status: String });
|
|
150
|
+
reproMongoosePlugin(cfg)(schema);
|
|
151
|
+
const Model = mongoose.model(`ReproMixedNativeMongo${Date.now()}`, schema, 'subjects');
|
|
152
|
+
const pendingMongooseDoc = new Model({ subjectNumber: 'pre-save', status: 'pending' });
|
|
153
|
+
|
|
154
|
+
class FakeCollection {
|
|
155
|
+
constructor() {
|
|
156
|
+
this.collectionName = 'subjects';
|
|
157
|
+
this.dbName = 'clinical';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async insertOne(doc, options) {
|
|
161
|
+
assert.strictEqual(options.session.id, 'native-explicit-session-1');
|
|
162
|
+
doc._id = doc._id ?? 'subject-explicit-1';
|
|
163
|
+
return { acknowledged: true, insertedId: doc._id };
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
__reproTestHooks.patchMongoCollectionPrototypeForTest(FakeCollection.prototype, cfg);
|
|
168
|
+
const subjectsCollection = new FakeCollection();
|
|
169
|
+
|
|
170
|
+
await runCapturedRequest(cfg, sessionId, actionId, async () => {
|
|
171
|
+
await execPre(schema.s.hooks, 'save', pendingMongooseDoc);
|
|
172
|
+
assert.strictEqual(__reproTestHooks.getNativeMongoSuppressionCountForTest(), 1);
|
|
173
|
+
|
|
174
|
+
const result = await subjectsCollection.insertOne(
|
|
175
|
+
{ subjectNumber: '10013038', status: 'screened' },
|
|
176
|
+
{
|
|
177
|
+
session: {
|
|
178
|
+
id: 'native-explicit-session-1',
|
|
179
|
+
hasEnded: false,
|
|
180
|
+
inTransaction: () => true,
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
);
|
|
184
|
+
assert.deepStrictEqual(result, { acknowledged: true, insertedId: 'subject-explicit-1' });
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
await waitFor(() => dbChanges(flattenEvents(capturedBodies)).length === 1);
|
|
188
|
+
await drainSdk();
|
|
189
|
+
|
|
190
|
+
const [change] = dbChanges(flattenEvents(capturedBodies));
|
|
191
|
+
assert.strictEqual(change.type, 'mongo');
|
|
192
|
+
assert.strictEqual(change.collection, 'subjects');
|
|
193
|
+
assert.strictEqual(change.op, 'insertOne');
|
|
194
|
+
assert.strictEqual(change.pk._id, 'subject-explicit-1');
|
|
195
|
+
assert.strictEqual(change.query.driver, 'mongodb');
|
|
196
|
+
assert.strictEqual(change.query.db, 'clinical');
|
|
197
|
+
assert.strictEqual(change.query.doc.subjectNumber, '10013038');
|
|
198
|
+
assert.strictEqual(change.query.options.session.__kind, 'mongodb_client_session');
|
|
199
|
+
assert.strictEqual(change.query.options.session.id, 'native-explicit-session-1');
|
|
200
|
+
assert.strictEqual(change.after.subjectNumber, '10013038');
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
135
204
|
async function testReproMiddlewarePatchesPreloadedNativeMongoClientInsertOne() {
|
|
136
205
|
await withCapturedFetch(async (capturedBodies) => {
|
|
137
206
|
__reproTestHooks.resetSdkBackgroundQueuesForTest();
|
|
@@ -452,6 +521,7 @@ async function testNativeOperationFailureIsCapturedAndRethrown() {
|
|
|
452
521
|
}
|
|
453
522
|
|
|
454
523
|
(async () => {
|
|
524
|
+
await testMongooseSuppressionDoesNotHideExplicitNativeInsertOne();
|
|
455
525
|
await testReproMiddlewarePatchesPreloadedNativeMongoClientInsertOne();
|
|
456
526
|
await testMongoClientDbCollectionChainIsPatchedWithoutCollectionExport();
|
|
457
527
|
await testNativeInsertOneIsCaptured();
|