Skip to content

Commit cd611a9

Browse files
Added subbody capability to namedQuery
1 parent e916775 commit cd611a9

File tree

10 files changed

+92
-10
lines changed

10 files changed

+92
-10
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 1.3
2+
- Added link caching
3+
- Added named query results caching
4+
- Added subbody to NamedQuery
5+
16
## 1.2.5
27
- Support for promises via .fetchSync and .fetchOneSync for client-side queries
38
- Support for autoremove from inverse side as well

LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright (c) 2016 Theodor Diaconu <[email protected]>
1+
Copyright (c) 2016-2018 Theodor Diaconu <[email protected]>
22

33
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
44

lib/namedQuery/expose/extension.js

+9-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import recursiveCompose from '../../query/lib/recursiveCompose.js';
88
import prepareForProcess from '../../query/lib/prepareForProcess.js';
99
import deepClone from 'lodash.cloneDeep';
1010
import genCountEndpoint from '../../query/counts/genEndpoint.server';
11+
import {SimpleSchema} from 'meteor/aldeed:simple-schema';
12+
13+
const specialParameters = ['$body'];
1114

1215
_.extend(NamedQuery.prototype, {
1316
expose(config = {}) {
@@ -114,7 +117,7 @@ _.extend(NamedQuery.prototype, {
114117
}
115118

116119
let params = _.extend({}, self.params, newParams);
117-
let body = prepareForProcess(self.body, params);
120+
const body = prepareForProcess(self.body, params);
118121

119122
const rootNode = createGraph(self.collection, body);
120123

@@ -123,16 +126,18 @@ _.extend(NamedQuery.prototype, {
123126
},
124127

125128
_validateParams(params) {
126-
if (params && this.exposeConfig.schema) {
129+
if (this.exposeConfig.schema) {
130+
const paramsToValidate = _.omit(params, ...specialParameters);
131+
127132
if (process.env.NODE_ENV !== 'production') {
128133
try {
129-
this._paramSchema.validate(params);
134+
this._paramSchema.validate(paramsToValidate);
130135
} catch (validationError) {
131136
console.error(`Invalid parameters supplied to query ${this.queryName}`, validationError);
132137
throw validationError; // rethrow
133138
}
134139
} else {
135-
this._paramSchema.validate(params);
140+
this._paramSchema.validate(paramsToValidate);
136141
}
137142
}
138143
}

lib/namedQuery/testing/bootstrap/server.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@ import postListExposure from './queries/postListExposure.js';
33

44
const postList = createQuery('postList', {
55
posts: {
6-
$filter({filters, params}) {
7-
filters.title = params.title
6+
$filter({filters, options, params}) {
7+
if (params.title) {
8+
filters.title = params.title;
9+
}
10+
11+
if (params.limit) {
12+
options.limit = params.limit;
13+
}
814
},
915
title: 1,
1016
author: {

lib/namedQuery/testing/server.test.js

+27
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,31 @@ describe('Named Query', function () {
7070
done();
7171
}, 500)
7272
});
73+
74+
it('Should allow to securely fetch a subbody of a namedQuery', function () {
75+
const query = createQuery({
76+
postListExposure: {
77+
limit: 5,
78+
$body: {
79+
title: 1,
80+
createdAt: 1, // should fail
81+
group: {
82+
name: 1,
83+
createdAt: 1, // should fail
84+
}
85+
}
86+
}
87+
});
88+
89+
const data = query.fetch();
90+
91+
assert.isTrue(data.length > 1);
92+
93+
_.each(data, post => {
94+
assert.isUndefined(post.createdAt);
95+
assert.isUndefined(post.author);
96+
assert.isObject(post.group);
97+
assert.isUndefined(post.group.createdAt);
98+
})
99+
})
73100
});

lib/query/lib/createGraph.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,13 @@ import ReducerNode from '../nodes/reducerNode.js';
44
import dotize from './dotize.js';
55
import createReducers from '../reducers/lib/createReducers';
66

7-
const specialFields = ['$filters', '$options', '$postFilters', '$postOptions', '$postFilter']; //keep $postFilter for legacy support
7+
const specialFields = [
8+
'$filters',
9+
'$options',
10+
'$postFilters',
11+
'$postOptions',
12+
'$postProcessing'
13+
];
814

915
/**
1016
* Creates node objects from the body

lib/query/lib/intersectDeep.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import dot from 'dot-object';
2+
import {_} from 'meteor/underscore';
3+
4+
/**
5+
* Given a named query that has a specific body, you can query its subbody
6+
* This performs an intersection of the bodies allowed in each
7+
*
8+
* @param allowedBody
9+
* @param clientBody
10+
*/
11+
export default function (allowedBody, clientBody) {
12+
const allowedBodyDot = _.keys(dot.dot(allowedBody));
13+
const clientBodyDot = _.keys(dot.dot(clientBody));
14+
15+
const intersection = _.intersection(allowedBodyDot, clientBodyDot);
16+
17+
const build = {};
18+
intersection.forEach(intersectedField => {
19+
build[intersectedField] = 1;
20+
});
21+
22+
return dot.object(build);
23+
}

lib/query/lib/prepareForDelivery.js

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export function applyPostFilters(node) {
3737
applyPostFilters(collectionNode);
3838
})
3939
}
40+
4041
export function applyPostOptions(node) {
4142
const options = node.props.$postOptions;
4243
if (options) {

lib/query/lib/prepareForProcess.js

+5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import deepClone from 'lodash.cloneDeep';
2+
import intersectDeep from './intersectDeep';
23

34
function defaultFilterFunction({
45
filters,
@@ -62,6 +63,10 @@ function applyPagination(body, _params) {
6263
}
6364

6465
export default (_body, _params = {}) => {
66+
if (_params.$body) {
67+
_body = intersectDeep(_body, _params.$body);
68+
}
69+
6570
let body = deepClone(_body);
6671
let params = deepClone(_params);
6772

lib/query/testing/bootstrap/fixtures.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ const COMMENT_TEXT_SAMPLES = [
2626
console.log('[testing] Loading test fixtures ...');
2727

2828
let tags = TAGS.map(name => Tags.insert({name}));
29-
let groups = GROUPS.map(name => Groups.insert({name}));
29+
let groups = GROUPS.map(name => Groups.insert({
30+
name,
31+
createdAt: new Date(),
32+
}));
3033
let authors = _.range(AUTHORS).map(idx => {
3134
return Authors.insert({
3235
name: 'Author - ' + idx,
@@ -47,7 +50,8 @@ _.each(authors, (author) => {
4750

4851
_.each(_.range(POST_PER_USER), (idx) => {
4952
let post = {
50-
title: `User Post - ${idx}`
53+
title: `User Post - ${idx}`,
54+
createdAt: new Date(),
5155
};
5256

5357
authorPostLink.add(post);

0 commit comments

Comments
 (0)