Added subscription scope to named queries

This commit is contained in:
Berislav 2018-06-14 20:40:21 +02:00
parent 5a927d2714
commit a673f16d1f
8 changed files with 105 additions and 3 deletions

6
lib/namedQuery/expose/extension.js Normal file → Executable file
View file

@ -154,6 +154,12 @@ _.extend(NamedQuery.prototype, {
const self = this;
Meteor.publishComposite(this.name, function(params = {}) {
const isScoped = !!self.options.scoped;
if (isScoped) {
this.enableScope();
}
self._unblockIfNecessary(this);
self.doValidateParams(params);
self._callFirewall(this, this.userId, params);

1
lib/namedQuery/expose/schema.js Normal file → Executable file
View file

@ -19,4 +19,5 @@ export const ExposeSchema = {
validateParams: Match.Maybe(
Match.OneOf(Object, Function)
),
scoped: Match.Maybe(Boolean),
};

38
lib/namedQuery/namedQuery.client.js Normal file → Executable file
View file

@ -178,8 +178,40 @@ export default class extends Base {
delete body.$options.skip;
}
return recursiveFetch(
createGraph(this.collection, body)
);
const rootNode = createGraph(this.collection, body, {
scopeField: `_sub_${this.subscriptionHandle.subscriptionId}`,
});
const subscriptionHandle = this.subscriptionHandle;
const unmaskFns = [];
function maskFind(node) {
const oldFind = node.collection.find;
node.collection.find = function (query, ...args) {
const scopedQuery = {
...(query || {}),
[`_sub_${subscriptionHandle.subscriptionId}`]: {$exists: true},
};
return oldFind.call(this, scopedQuery, ...args);
};
unmaskFns.push(function () {
node.collection.find = oldFind;
});
node.collectionNodes.map(n => maskFind(n));
}
if (this.options.scoped) {
maskFind(rootNode);
}
const results = recursiveFetch(rootNode);
if (this.options.scoped) {
unmaskFns.map(f => f());
}
return results;
}
}

2
lib/namedQuery/testing/bootstrap/queries/index.js Normal file → Executable file
View file

@ -1,6 +1,7 @@
import postList from './postList';
import postListCached from './postListCached';
import postListExposure from './postListExposure';
import postListExposureScoped from './postListExposureScoped';
import postListParamsCheck from './postListParamsCheck';
import postListParamsCheckServer from './postListParamsCheckServer';
import postListResolver from './postListResolver';
@ -10,6 +11,7 @@ export {
postList,
postListCached,
postListExposure,
postListExposureScoped,
postListParamsCheck,
postListParamsCheckServer,
postListResolver,

2
lib/namedQuery/testing/bootstrap/queries/postList.js Normal file → Executable file
View file

@ -19,6 +19,8 @@ const postList = createQuery('postList', {
name: 1
}
}
}, {
scoped: true,
});
export default postList;

View file

@ -0,0 +1,29 @@
import { createQuery } from 'meteor/cultofcoders:grapher';
const postListExposureScoped = createQuery('postListExposureScoped', {
posts: {
title: 1,
author: {
name: 1
},
group: {
name: 1
}
}
}, {
scoped: true,
});
if (Meteor.isServer) {
postListExposureScoped.expose({
firewall(userId, params) {
},
embody: {
$filter({filters, params}) {
filters.title = params.title
}
}
});
}
export default postListExposureScoped;

28
lib/namedQuery/testing/client.test.js Normal file → Executable file
View file

@ -1,5 +1,7 @@
import postListExposure from './bootstrap/queries/postListExposure.js';
import postListExposureScoped from './bootstrap/queries/postListExposureScoped';
import { createQuery } from 'meteor/cultofcoders:grapher';
import Posts from '../../query/testing/bootstrap/posts/collection';
describe('Named Query', function() {
it('Should return proper values', function(done) {
@ -93,6 +95,32 @@ describe('Named Query', function() {
});
});
it('Should work with reactive scoped queries', function(done) {
const query = postListExposureScoped.clone({ title: 'User Post - 3' });
const handle = query.subscribe();
Tracker.autorun(c => {
if (handle.ready()) {
c.stop();
const data = query.fetch();
handle.stop();
assert.isTrue(data.length > 0);
const docMap = Posts._collection._docs._map;
const scopeField = `_sub_${handle.subscriptionId}`;
data.forEach(post => {
// no scope field returned from find
assert.isUndefined(post[scopeField]);
assert.isObject(docMap[post._id]);
assert.equal(docMap[post._id][scopeField], 1);
});
done();
}
});
});
it('Should work with reactive queries via import', function(done) {
const query = postListExposure.clone({
title: 'User Post - 3',

2
package.js Normal file → Executable file
View file

@ -31,6 +31,7 @@ Package.onUse(function(api) {
'reywood:publish-composite@1.5.2',
'dburles:mongo-collection-instances@0.3.5',
'herteby:denormalize@0.6.5',
'peerlibrary:subscription-scope',
];
api.use(packages);
@ -49,6 +50,7 @@ Package.onTest(function(api) {
'reywood:publish-composite@1.5.2',
'dburles:mongo-collection-instances@0.3.5',
'herteby:denormalize@0.6.5',
'peerlibrary:subscription-scope',
'mongo',
];