Skip to content

Commit aa97b23

Browse files
committed
Async before & after hooks for all methods except find
1 parent 5534b93 commit aa97b23

File tree

11 files changed

+832
-524
lines changed

11 files changed

+832
-524
lines changed

.github/workflows/testsuite.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
# - '--release 2.3'
1616
# - '--release 2.8.1'
1717
# - '--release 2.14'
18-
- '--release 3.0-beta.6'
18+
- '--release 3.0-rc.0'
1919
steps:
2020
- name: Checkout code
2121
uses: actions/checkout@v4

collection-hooks.js

Lines changed: 75 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,23 @@ const advices = {}
1111

1212
export const CollectionHooks = {
1313
defaults: {
14-
before: { insert: {}, update: {}, remove: {}, upsert: {}, find: {}, findOne: {}, all: {} },
15-
after: { insert: {}, update: {}, remove: {}, find: {}, findOne: {}, all: {} },
14+
before: {
15+
insert: {},
16+
update: {},
17+
remove: {},
18+
upsert: {},
19+
find: {},
20+
findOne: {},
21+
all: {}
22+
},
23+
after: {
24+
insert: {},
25+
update: {},
26+
remove: {},
27+
find: {},
28+
findOne: {},
29+
all: {}
30+
},
1631
all: { insert: {}, update: {}, remove: {}, find: {}, findOne: {}, all: {} }
1732
},
1833
directEnv: new Meteor.EnvironmentVariable(),
@@ -25,7 +40,10 @@ export const CollectionHooks = {
2540
}
2641
}
2742

28-
CollectionHooks.extendCollectionInstance = function extendCollectionInstance (self, constructor) {
43+
CollectionHooks.extendCollectionInstance = function extendCollectionInstance (
44+
self,
45+
constructor
46+
) {
2947
// Offer a public API to allow the user to define aspects
3048
// Example: collection.before.insert(func);
3149
['before', 'after'].forEach(function (pointcut) {
@@ -49,7 +67,7 @@ CollectionHooks.extendCollectionInstance = function extendCollectionInstance (se
4967
// replacing is done by determining the actual index of a given target
5068
// and replace this with the new one
5169
const src = self._hookAspects[method][pointcut]
52-
const targetIndex = src.findIndex(entry => entry === target)
70+
const targetIndex = src.findIndex((entry) => entry === target)
5371
const newTarget = {
5472
aspect,
5573
options: CollectionHooks.initOptions(options, pointcut, method)
@@ -62,7 +80,7 @@ CollectionHooks.extendCollectionInstance = function extendCollectionInstance (se
6280
// removing a hook is done by determining the actual index of a given target
6381
// and removing it form the source array
6482
const src = self._hookAspects[method][pointcut]
65-
const targetIndex = src.findIndex(entry => entry === target)
83+
const targetIndex = src.findIndex((entry) => entry === target)
6684
self._hookAspects[method][pointcut].splice(targetIndex, 1)
6785
}
6886
}
@@ -79,7 +97,8 @@ CollectionHooks.extendCollectionInstance = function extendCollectionInstance (se
7997
Object.entries(advices).forEach(function ([method, advice]) {
8098
// For client side, it wraps around minimongo LocalCollection
8199
// For server side, it wraps around mongo Collection._collection (i.e. driver directly)
82-
const collection = Meteor.isClient || method === 'upsert' ? self : self._collection
100+
const collection =
101+
Meteor.isClient || method === 'upsert' ? self : self._collection
83102

84103
// Store a reference to the original mutator method
85104
const _super = collection[method]
@@ -109,7 +128,8 @@ CollectionHooks.extendCollectionInstance = function extendCollectionInstance (se
109128
if (
110129
(method === 'update' && this.update.isCalledFromAsync) ||
111130
(method === 'remove' && this.remove.isCalledFromAsync) ||
112-
CollectionHooks.directEnv.get() === true) {
131+
CollectionHooks.directEnv.get() === true
132+
) {
113133
return _super.apply(collection, args)
114134
}
115135

@@ -123,7 +143,8 @@ CollectionHooks.extendCollectionInstance = function extendCollectionInstance (se
123143
// advice = CollectionHooks.getAdvice(method);
124144
// }
125145

126-
return advice.call(this,
146+
return advice.call(
147+
this,
127148
CollectionHooks.getUserId(),
128149
_super,
129150
self,
@@ -135,11 +156,13 @@ CollectionHooks.extendCollectionInstance = function extendCollectionInstance (se
135156
}
136157
: self._hookAspects[method] || {},
137158
function (doc) {
138-
return (
139-
typeof self._transform === 'function'
140-
? function (d) { return self._transform(d || doc) }
141-
: function (d) { return d || doc }
142-
)
159+
return typeof self._transform === 'function'
160+
? function (d) {
161+
return self._transform(d || doc)
162+
}
163+
: function (d) {
164+
return d || doc
165+
}
143166
},
144167
args,
145168
false
@@ -162,15 +185,31 @@ CollectionHooks.defineAdvice = (method, advice) => {
162185
advices[method] = advice
163186
}
164187

165-
CollectionHooks.getAdvice = method => advices[method]
188+
CollectionHooks.getAdvice = (method) => advices[method]
166189

167190
CollectionHooks.initOptions = (options, pointcut, method) =>
168-
CollectionHooks.extendOptions(CollectionHooks.defaults, options, pointcut, method)
169-
170-
CollectionHooks.extendOptions = (source, options, pointcut, method) =>
171-
({ ...options, ...source.all.all, ...source[pointcut].all, ...source.all[method], ...source[pointcut][method] })
172-
173-
CollectionHooks.getDocs = function getDocs (collection, selector, options, fetchFields = {}, { useDirect = false } = {}) {
191+
CollectionHooks.extendOptions(
192+
CollectionHooks.defaults,
193+
options,
194+
pointcut,
195+
method
196+
)
197+
198+
CollectionHooks.extendOptions = (source, options, pointcut, method) => ({
199+
...options,
200+
...source.all.all,
201+
...source[pointcut].all,
202+
...source.all[method],
203+
...source[pointcut][method]
204+
})
205+
206+
CollectionHooks.getDocs = function getDocs (
207+
collection,
208+
selector,
209+
options,
210+
fetchFields = {},
211+
{ useDirect = false } = {}
212+
) {
174213
const findOptions = { transform: null, reactive: false }
175214

176215
if (Object.keys(fetchFields).length > 0) {
@@ -203,12 +242,18 @@ CollectionHooks.getDocs = function getDocs (collection, selector, options, fetch
203242

204243
// Unlike validators, we iterate over multiple docs, so use
205244
// find instead of findOne:
206-
return (useDirect ? collection.direct : collection).find(selector, findOptions)
245+
return (useDirect ? collection.direct : collection).find(
246+
selector,
247+
findOptions
248+
)
207249
}
208250

209251
// This function normalizes the selector (converting it to an Object)
210252
CollectionHooks.normalizeSelector = function (selector) {
211-
if (typeof selector === 'string' || (selector && selector.constructor === Mongo.ObjectID)) {
253+
if (
254+
typeof selector === 'string' ||
255+
(selector && selector.constructor === Mongo.ObjectID)
256+
) {
212257
return {
213258
_id: selector
214259
}
@@ -245,7 +290,7 @@ CollectionHooks.getFields = function getFields (mutator) {
245290
Object.entries(mutator).forEach(function ([op, params]) {
246291
// ====ADDED START=======================
247292
if (operators.includes(op)) {
248-
// ====ADDED END=========================
293+
// ====ADDED END=========================
249294
Object.keys(params).forEach(function (field) {
250295
// treat dotted fields as if they are replacing their
251296
// top-level part
@@ -268,22 +313,26 @@ CollectionHooks.getFields = function getFields (mutator) {
268313
return fields
269314
}
270315

271-
CollectionHooks.reassignPrototype = function reassignPrototype (instance, constr) {
316+
CollectionHooks.reassignPrototype = function reassignPrototype (
317+
instance,
318+
constr
319+
) {
272320
const hasSetPrototypeOf = typeof Object.setPrototypeOf === 'function'
273321
constr = constr || Mongo.Collection
274322

275323
// __proto__ is not available in < IE11
276324
// Note: Assigning a prototype dynamically has performance implications
277325
if (hasSetPrototypeOf) {
278326
Object.setPrototypeOf(instance, constr.prototype)
279-
} else if (instance.__proto__) { // eslint-disable-line no-proto
327+
} else if (instance.__proto__) {
328+
// eslint-disable-line no-proto
280329
instance.__proto__ = constr.prototype // eslint-disable-line no-proto
281330
}
282331
}
283332

284333
CollectionHooks.wrapCollection = function wrapCollection (ns, as) {
285334
if (!as._CollectionConstructor) as._CollectionConstructor = as.Collection
286-
if (!as._CollectionPrototype) as._CollectionPrototype = new as.Collection(null)
335+
if (!as._CollectionPrototype) { as._CollectionPrototype = new as.Collection(null) }
287336

288337
const constructor = ns._NewCollectionContructor || as._CollectionConstructor
289338
const proto = as._CollectionPrototype
@@ -308,17 +357,6 @@ CollectionHooks.wrapCollection = function wrapCollection (ns, as) {
308357
ns.Collection.apply = Function.prototype.apply
309358
}
310359

311-
CollectionHooks.isPromise = (value) => {
312-
return value && typeof value.then === 'function'
313-
}
314-
315-
CollectionHooks.callAfterValueOrPromise = (value, cb) => {
316-
if (CollectionHooks.isPromise(value)) {
317-
return value.then((res) => cb(res))
318-
}
319-
return cb(value)
320-
}
321-
322360
CollectionHooks.modify = LocalCollection._modify
323361

324362
if (typeof Mongo !== 'undefined') {

findone.js

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,33 @@
11
import { CollectionHooks } from './collection-hooks'
22

3-
CollectionHooks.defineAdvice('findOne', function (userId, _super, instance, aspects, getTransform, args, suppressAspects) {
3+
CollectionHooks.defineAdvice('findOne', async function (userId, _super, instance, aspects, getTransform, args, suppressAspects) {
44
const ctx = { context: this, _super, args }
55
const selector = CollectionHooks.normalizeSelector(instance._getFindSelector(args))
66
const options = instance._getFindOptions(args)
77
let abort
88

99
// before
1010
if (!suppressAspects) {
11-
aspects.before.forEach((o) => {
12-
const r = o.aspect.call(ctx, userId, selector, options)
13-
if (r === false) abort = true
14-
})
11+
for (const o of aspects.before) {
12+
const r = await o.aspect.call(ctx, userId, selector, options)
13+
if (r === false) {
14+
abort = true
15+
break
16+
}
17+
}
1518

1619
if (abort) return
1720
}
1821

19-
function after (doc) {
22+
async function after (doc) {
2023
if (!suppressAspects) {
21-
aspects.after.forEach((o) => {
24+
for (const o of aspects.after) {
2225
o.aspect.call(ctx, userId, selector, options, doc)
23-
})
26+
}
2427
}
25-
26-
// return because of callAfterValueOrPromise
27-
return doc
2828
}
2929

30-
const ret = _super.call(this, selector, options)
31-
return CollectionHooks.callAfterValueOrPromise(ret, (ret) => after(ret))
30+
const ret = await _super.call(this, selector, options)
31+
await after(ret)
32+
return ret
3233
})

package.js

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,16 @@
22

33
Package.describe({
44
name: 'matb33:collection-hooks',
5-
summary: 'Extends Mongo.Collection with before/after hooks for insert/update/upsert/remove/find/findOne',
5+
summary:
6+
'Extends Mongo.Collection with before/after hooks for insert/update/upsert/remove/find/findOne',
67
version: '1.3.1',
78
git: 'https://github.com/Meteor-Community-Packages/meteor-collection-hooks'
89
})
910

1011
Package.onUse(function (api) {
11-
api.versionsFrom(['2.3', '2.8.1', '3.0-beta.0'])
12+
api.versionsFrom(['2.13', '3.0-beta.0'])
1213

13-
api.use([
14-
'mongo',
15-
'tracker',
16-
'ejson',
17-
'minimongo',
18-
'ecmascript'
19-
])
14+
api.use(['mongo', 'tracker', 'ejson', 'minimongo', 'ecmascript'])
2015

2116
api.use('zodern:[email protected]', 'server')
2217

@@ -31,7 +26,7 @@ Package.onUse(function (api) {
3126
Package.onTest(function (api) {
3227
// var isTravisCI = process && process.env && process.env.TRAVIS
3328

34-
api.versionsFrom(['1.12', '2.3', '3.0-beta.0'])
29+
api.versionsFrom(['2.13', '3.0-beta.0'])
3530

3631
api.use([
3732
'matb33:collection-hooks',

0 commit comments

Comments
 (0)