@@ -8,6 +8,7 @@ const MongooseError = require('../error/mongooseError');
8
8
const Readable = require ( 'stream' ) . Readable ;
9
9
const eachAsync = require ( '../helpers/cursor/eachAsync' ) ;
10
10
const immediate = require ( '../helpers/immediate' ) ;
11
+ const kareem = require ( 'kareem' ) ;
11
12
const util = require ( 'util' ) ;
12
13
13
14
/**
@@ -62,7 +63,11 @@ util.inherits(AggregationCursor, Readable);
62
63
63
64
function _init ( model , c , agg ) {
64
65
if ( ! model . collection . buffer ) {
65
- model . hooks . execPre ( 'aggregate' , agg , function ( ) {
66
+ model . hooks . execPre ( 'aggregate' , agg , function ( err ) {
67
+ if ( err != null ) {
68
+ _handlePreHookError ( c , err ) ;
69
+ return ;
70
+ }
66
71
if ( typeof agg . options ?. cursor ?. transform === 'function' ) {
67
72
c . _transforms . push ( agg . options . cursor . transform ) ;
68
73
}
@@ -72,7 +77,12 @@ function _init(model, c, agg) {
72
77
} ) ;
73
78
} else {
74
79
model . collection . emitter . once ( 'queue' , function ( ) {
75
- model . hooks . execPre ( 'aggregate' , agg , function ( ) {
80
+ model . hooks . execPre ( 'aggregate' , agg , function ( err ) {
81
+ if ( err != null ) {
82
+ _handlePreHookError ( c , err ) ;
83
+ return ;
84
+ }
85
+
76
86
if ( typeof agg . options ?. cursor ?. transform === 'function' ) {
77
87
c . _transforms . push ( agg . options . cursor . transform ) ;
78
88
}
@@ -84,6 +94,38 @@ function _init(model, c, agg) {
84
94
}
85
95
}
86
96
97
+ /**
98
+ * Handles error emitted from pre middleware. In particular, checks for `skipWrappedFunction`, which allows skipping
99
+ * the actual aggregation and overwriting the function's return value. Because aggregation cursors don't return a value,
100
+ * we need to make sure the user doesn't accidentally set a value in skipWrappedFunction.
101
+ *
102
+ * @param {QueryCursor } queryCursor
103
+ * @param {Error } err
104
+ * @returns
105
+ */
106
+
107
+ function _handlePreHookError ( queryCursor , err ) {
108
+ if ( err instanceof kareem . skipWrappedFunction ) {
109
+ const resultValue = err . args [ 0 ] ;
110
+ if ( resultValue != null && ( ! Array . isArray ( resultValue ) || resultValue . length ) ) {
111
+ const err = new MongooseError (
112
+ 'Cannot `skipMiddlewareFunction()` with a value when using ' +
113
+ '`.aggregate().cursor()`, value must be nullish or empty array, got "' +
114
+ util . inspect ( resultValue ) +
115
+ '".'
116
+ ) ;
117
+ queryCursor . _markError ( err ) ;
118
+ queryCursor . listeners ( 'error' ) . length > 0 && queryCursor . emit ( 'error' , err ) ;
119
+ return ;
120
+ }
121
+ queryCursor . emit ( 'cursor' , null ) ;
122
+ return ;
123
+ }
124
+ queryCursor . _markError ( err ) ;
125
+ queryCursor . listeners ( 'error' ) . length > 0 && queryCursor . emit ( 'error' , err ) ;
126
+ }
127
+
128
+
87
129
/**
88
130
* Necessary to satisfy the Readable API
89
131
* @method _read
@@ -424,6 +466,7 @@ function _next(ctx, cb) {
424
466
err => callback ( err )
425
467
) ;
426
468
} else {
469
+ ctx . once ( 'error' , cb ) ;
427
470
ctx . once ( 'cursor' , function ( ) {
428
471
_next ( ctx , cb ) ;
429
472
} ) ;
0 commit comments