forked from Meteor-Community-Packages/meteor-partitioner
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathhooks.js
98 lines (85 loc) · 3.89 KB
/
hooks.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
let publishUserId;
if (Meteor.isServer) {
publishUserId = new Meteor.EnvironmentVariable();
const _publish = Meteor.publish;
Meteor.publish = function(name, handler, options) {
return _publish.call(this, name, function(...args) {
// This function is called repeatedly in publications
return publishUserId.withValue(this && this.userId, () => handler.apply(this, args));
}, options)
}
}
function getUserId () {
let userId;
try {
// Will throw an error unless within method call.
// Attempt to recover gracefully by catching:
userId = Meteor.userId && Meteor.userId();
} catch (e) {}
if (userId == null && Meteor.isServer) {
// Get the userId if we are in a publish function.
userId = publishUserId.get();
}
return userId
}
const proto = Mongo.Collection.prototype;
const directEnv = new Meteor.EnvironmentVariable(false);
const methods = ['find', 'findOne', 'insert', 'update', 'remove', 'upsert'];
// create the collection._partitionerBefore.* methods
// have to create it initially using a getter so we can store self=this and create a new group of functions which have access to self
Object.defineProperty(proto, '_partitionerBefore', {
get() {
// console.log('creating before functions', this._name);
const self = this;
const fns = {};
methods.forEach(method => fns[method] = function(hookFn) {
self['_groupingBefore_'+method] = hookFn;
});
// replace the .direct prototype with the created object, so we don't have to recreate it every time.
Object.defineProperty(this, '_partitionerBefore', {value: fns});
return fns;
}
});
// create the collection._partitionerDirect.* methods
// have to create it initially using a getter so we can store self=this and create a new group of functions which have access to self
Object.defineProperty(proto, '_partitionerDirect', {
get() {
// console.log('creating direct functions', this._name);
const self = this;
const fns = {};
methods.forEach(method => fns[method] = function(...args) {
return directEnv.withValue(true, () => proto[method].apply(self, args));
});
// replace the .direct prototype with the created object, so we don't have to recreate it every time.
Object.defineProperty(this, '_partitionerDirect', {value: fns});
return fns;
}
});
global.hookLogging = false;
// if (Meteor.isServer) global.hookLogging = true;
methods.forEach(method => {
const _orig = proto[method];
proto[method] = function(...args) {
if (directEnv.get()) return _orig.apply(this, args);
// give the hooks a private context so that they can modify this.args without putting this.args onto the prototype
const context = {args};
const userId = getUserId();
global.hookLogging && typeof args[0]!='string' && console.log('hook', '\n\n');
// if the method is update or remove, automatically apply the find hooks to limit the update/remove to the user's group
if ((method=='update' || method=='remove') && this._groupingBefore_find) {
global.hookLogging && typeof args[0]!='string' && console.log('hook', 'b4i', this._name+"."+method, JSON.stringify(args[0]), JSON.stringify(args[1]));
// don't send args[1] for update or remove.
// need to send empty object instead to prevent args[1] being modified
this._groupingBefore_find.call(context, userId, args[0], {});
global.hookLogging && typeof args[0]!='string' && console.log('hook', 'afi', this._name+"."+method, JSON.stringify(args[0]), JSON.stringify(args[1]));
}
// run the hook
if (this['_groupingBefore_'+method]) {
global.hookLogging && typeof args[0]!='string' && console.log('hook', 'b4', this._name+"."+method, JSON.stringify(args[0]), JSON.stringify(args[1]));
this['_groupingBefore_'+method].call(context, userId, args[0], args[1], args[2]);
global.hookLogging && typeof args[0]!='string' && console.log('hook', 'af', this._name+"."+method, JSON.stringify(args[0]), JSON.stringify(args[1]));
}
// run the original method
return _orig.apply(this, args);
}
});