Skip to content

Commit 7103eba

Browse files
Modify mongoose call xxxx() throwing "is not a function" (#128)
1 parent 4a61016 commit 7103eba

3 files changed

Lines changed: 54 additions & 4 deletions

File tree

src/plugins/MongoosePlugin.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ class MongoosePlugin implements SwPlugin {
118118
arguments[arguments.length - 1] = function () {
119119
// in case of immediate synchronous callback from mongoose
120120
(span as any).mongooseInCall = false;
121-
122121
wrappedCallback.apply(this, arguments as any);
123122
};
124123
}
@@ -130,7 +129,42 @@ class MongoosePlugin implements SwPlugin {
130129
if (!hasCB) {
131130
if (ret && typeof ret.then === 'function') {
132131
// generic Promise check
133-
ret = wrapPromise(span, ret);
132+
133+
if (ret.constructor.name != 'Query') {
134+
ret = wrapPromise(span, ret);
135+
} else {
136+
// Mongoose Query object
137+
const chainMethods = ['select', 'sort', 'skip', 'limit', 'populate'];
138+
139+
// Mongoose Query object
140+
const originalThen = ret.then;
141+
const originalExec = ret.exec;
142+
const originalLean = ret.lean;
143+
144+
// Preserve the query chain methods using arrow functions to maintain context
145+
ret.then = (...args: any[]) => wrapPromise(span, originalThen.apply(ret, args));
146+
ret.exec = (...args: any[]) => wrapPromise(span, originalExec.apply(ret, args));
147+
ret.lean = (...args: any[]) => {
148+
const leanQuery = originalLean.apply(ret, args);
149+
// Preserve other chain methods on the lean result
150+
leanQuery.then = ret.then;
151+
leanQuery.exec = ret.exec;
152+
return leanQuery;
153+
};
154+
// Wrap other common query methods that might be chained
155+
chainMethods.forEach((method) => {
156+
if (ret[method]) {
157+
const originalMethod = ret[method];
158+
ret[method] = (...args: any[]) => {
159+
const result = originalMethod.apply(ret, args);
160+
result.then = ret.then;
161+
result.exec = ret.exec;
162+
result.lean = ret.lean;
163+
return result;
164+
};
165+
}
166+
});
167+
}
134168
} else {
135169
// no callback passed in and no Promise or Cursor returned, play it safe
136170
span.stop();

tests/plugins/mongoose/client.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import * as http from 'http';
2121
import agent from '../../../src';
2222

2323
process.env.SW_AGENT_LOGGING_LEVEL = 'ERROR';
24+
process.env.SW_AGENT_DISABLE_PLUGINS = 'MongoDBPlugin';
2425

2526
agent.start({
2627
serviceName: 'client',

tests/plugins/mongoose/expected.data.yaml

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ segmentItems:
3535
tags:
3636
- { key: db.type, value: MongoDB }
3737
- { key: db.instance, value: admin }
38-
- { key: db.statement, value: 'collection("tests")' }
38+
- { key: db.statement, 'collection("tests")' }
3939
- operationName: Mongoose/ensureIndexes
4040
operationId: 0
4141
parentSpanId: 0
@@ -50,6 +50,21 @@ segmentItems:
5050
tags:
5151
- { key: db.type, value: MongoDB }
5252
- { key: db.instance, value: admin }
53+
- operationName: MongoDB/find # This is the required span
54+
operationId: 0
55+
parentSpanId: 0
56+
spanId: 4
57+
spanLayer: Database
58+
startTime: gt 0
59+
endTime: gt 0
60+
componentId: 9
61+
spanType: Exit
62+
peer: mongo:27017
63+
skipAnalysis: false
64+
tags:
65+
- { key: db.type, value: MongoDB }
66+
- { key: db.instance, value: admin }
67+
- { key: db.statement, 'tests.find({})' }
5368
- operationName: Mongoose/find
5469
operationId: 0
5570
parentSpanId: 0
@@ -64,7 +79,7 @@ segmentItems:
6479
tags:
6580
- { key: db.type, value: MongoDB }
6681
- { key: db.instance, value: admin }
67-
- { key: db.statement, value: 'tests.find({})' }
82+
# The mongoose find span might not have the db.statement, which is fine
6883
- operationName: GET:/mongoose
6984
operationId: 0
7085
parentSpanId: -1

0 commit comments

Comments
 (0)