mirror of
https://github.com/vale981/grapher
synced 2025-03-05 09:31:42 -05:00
Merge pull request #232 from cult-of-coders/release/1.3.3
[RFC] Release/1.3.3
This commit is contained in:
commit
1cf94bc8c2
12 changed files with 486 additions and 310 deletions
|
@ -27,5 +27,5 @@ before_script:
|
|||
script:
|
||||
- meteor create --bare test
|
||||
- cd test
|
||||
- meteor npm i --save selenium-webdriver@3.6.0 chromedriver@2.34.1 simpl-schema
|
||||
- meteor npm i --save selenium-webdriver@3.6.0 chromedriver@2.36.0 simpl-schema
|
||||
- METEOR_PACKAGE_DIRS="../" TEST_BROWSER_DRIVER=chrome meteor test-packages --once --driver-package meteortesting:mocha ../
|
|
@ -2,14 +2,18 @@ import genCountEndpoint from '../query/counts/genEndpoint.server.js';
|
|||
import createGraph from '../query/lib/createGraph.js';
|
||||
import recursiveCompose from '../query/lib/recursiveCompose.js';
|
||||
import hypernova from '../query/hypernova/hypernova.js';
|
||||
import {ExposureSchema, ExposureDefaults, validateBody} from './exposure.config.schema.js';
|
||||
import {
|
||||
ExposureSchema,
|
||||
ExposureDefaults,
|
||||
validateBody,
|
||||
} from './exposure.config.schema.js';
|
||||
import enforceMaxDepth from './lib/enforceMaxDepth.js';
|
||||
import enforceMaxLimit from './lib/enforceMaxLimit.js';
|
||||
import cleanBody from './lib/cleanBody.js';
|
||||
import deepClone from 'lodash.clonedeep';
|
||||
import restrictFieldsFn from './lib/restrictFields.js';
|
||||
import restrictLinks from './lib/restrictLinks.js';
|
||||
import {check} from 'meteor/check';
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
let globalConfig = {};
|
||||
|
||||
|
@ -47,7 +51,10 @@ export default class Exposure {
|
|||
}
|
||||
|
||||
if (!this.config.method && !this.config.publication) {
|
||||
throw new Meteor.Error('weird', 'If you want to expose your collection you need to specify at least one of ["method", "publication"] options to true')
|
||||
throw new Meteor.Error(
|
||||
'weird',
|
||||
'If you want to expose your collection you need to specify at least one of ["method", "publication"] options to true'
|
||||
);
|
||||
}
|
||||
|
||||
this.initCountMethod();
|
||||
|
@ -55,12 +62,17 @@ export default class Exposure {
|
|||
}
|
||||
|
||||
_validateAndClean() {
|
||||
if (typeof(this.config) === 'function') {
|
||||
if (typeof this.config === 'function') {
|
||||
const firewall = this.config;
|
||||
this.config = {firewall};
|
||||
this.config = { firewall };
|
||||
}
|
||||
|
||||
this.config = Object.assign({}, ExposureDefaults, Exposure.getConfig(), this.config);
|
||||
this.config = Object.assign(
|
||||
{},
|
||||
ExposureDefaults,
|
||||
Exposure.getConfig(),
|
||||
this.config
|
||||
);
|
||||
check(this.config, ExposureSchema);
|
||||
|
||||
if (this.config.body) {
|
||||
|
@ -94,7 +106,10 @@ export default class Exposure {
|
|||
*/
|
||||
getBody(userId) {
|
||||
if (!this.config.body) {
|
||||
throw new Meteor.Error('missing-body', 'Cannot get exposure body because it was not defined.');
|
||||
throw new Meteor.Error(
|
||||
'missing-body',
|
||||
'Cannot get exposure body because it was not defined.'
|
||||
);
|
||||
}
|
||||
|
||||
let body;
|
||||
|
@ -109,10 +124,7 @@ export default class Exposure {
|
|||
return true;
|
||||
}
|
||||
|
||||
return deepClone(
|
||||
body,
|
||||
userId
|
||||
);
|
||||
return deepClone(body, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -123,7 +135,7 @@ export default class Exposure {
|
|||
const config = this.config;
|
||||
const getTransformedBody = this.getTransformedBody.bind(this);
|
||||
|
||||
Meteor.publishComposite(this.name, function (body) {
|
||||
Meteor.publishComposite(this.name, function(body) {
|
||||
let transformedBody = getTransformedBody(body);
|
||||
|
||||
const rootNode = createGraph(collection, transformedBody);
|
||||
|
@ -132,7 +144,7 @@ export default class Exposure {
|
|||
restrictLinks(rootNode, this.userId);
|
||||
|
||||
return recursiveCompose(rootNode, this.userId, {
|
||||
bypassFirewalls: !!config.body
|
||||
bypassFirewalls: !!config.body,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -159,12 +171,12 @@ export default class Exposure {
|
|||
|
||||
// if there is no exposure body defined, then we need to apply firewalls
|
||||
return hypernova(rootNode, this.userId, {
|
||||
bypassFirewalls: !!config.body
|
||||
bypassFirewalls: !!config.body,
|
||||
});
|
||||
};
|
||||
|
||||
Meteor.methods({
|
||||
[this.name]: methodBody
|
||||
[this.name]: methodBody,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -179,9 +191,11 @@ export default class Exposure {
|
|||
[this.name + '.count'](body) {
|
||||
this.unblock();
|
||||
|
||||
return collection.find(body.$filters || {}, {}, this.userId).count();
|
||||
}
|
||||
})
|
||||
return collection
|
||||
.find(body.$filters || {}, {}, this.userId)
|
||||
.count();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -191,10 +205,14 @@ export default class Exposure {
|
|||
const collection = this.collection;
|
||||
|
||||
genCountEndpoint(this.name, {
|
||||
getCursor(session) {
|
||||
return collection.find(session.filters, {
|
||||
fields: {_id: 1},
|
||||
}, this.userId);
|
||||
getCursor({ session }) {
|
||||
return collection.find(
|
||||
session.filters,
|
||||
{
|
||||
fields: { _id: 1 },
|
||||
},
|
||||
this.userId
|
||||
);
|
||||
},
|
||||
|
||||
getSession(body) {
|
||||
|
@ -209,13 +227,18 @@ export default class Exposure {
|
|||
*/
|
||||
initSecurity() {
|
||||
const collection = this.collection;
|
||||
const {firewall, maxLimit, restrictedFields} = this.config;
|
||||
const { firewall, maxLimit, restrictedFields } = this.config;
|
||||
const find = collection.find.bind(collection);
|
||||
const findOne = collection.findOne.bind(collection);
|
||||
|
||||
collection.firewall = (filters, options, userId) => {
|
||||
if (userId !== undefined) {
|
||||
this._callFirewall({collection: collection}, filters, options, userId);
|
||||
this._callFirewall(
|
||||
{ collection: collection },
|
||||
filters,
|
||||
options,
|
||||
userId
|
||||
);
|
||||
|
||||
enforceMaxLimit(options, maxLimit);
|
||||
|
||||
|
@ -225,7 +248,7 @@ export default class Exposure {
|
|||
}
|
||||
};
|
||||
|
||||
collection.find = function (filters, options = {}, userId = undefined) {
|
||||
collection.find = function(filters, options = {}, userId = undefined) {
|
||||
if (arguments.length == 0) {
|
||||
filters = {};
|
||||
}
|
||||
|
@ -240,27 +263,31 @@ export default class Exposure {
|
|||
return find(filters, options);
|
||||
};
|
||||
|
||||
collection.findOne = function (filters, options = {}, userId = undefined) {
|
||||
collection.findOne = function(
|
||||
filters,
|
||||
options = {},
|
||||
userId = undefined
|
||||
) {
|
||||
// If filters is undefined it should return an empty item
|
||||
if (arguments.length > 0 && filters === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof(filters) === 'string') {
|
||||
filters = {_id: filters};
|
||||
if (typeof filters === 'string') {
|
||||
filters = { _id: filters };
|
||||
}
|
||||
|
||||
collection.firewall(filters, options, userId);
|
||||
|
||||
return findOne(filters, options);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_callFirewall(...args) {
|
||||
const {firewall} = this.config;
|
||||
const { firewall } = this.config;
|
||||
if (!firewall) {
|
||||
return;
|
||||
}
|
||||
|
@ -268,9 +295,9 @@ export default class Exposure {
|
|||
if (_.isArray(firewall)) {
|
||||
firewall.forEach(fire => {
|
||||
fire.call(...args);
|
||||
})
|
||||
});
|
||||
} else {
|
||||
firewall.call(...args);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import NamedQuery from '../namedQuery.js';
|
||||
import {ExposeSchema, ExposeDefaults} from './schema.js';
|
||||
import { ExposeSchema, ExposeDefaults } from './schema.js';
|
||||
import mergeDeep from './lib/mergeDeep.js';
|
||||
import createGraph from '../../query/lib/createGraph.js';
|
||||
import recursiveCompose from '../../query/lib/recursiveCompose.js';
|
||||
|
@ -7,7 +7,7 @@ import prepareForProcess from '../../query/lib/prepareForProcess.js';
|
|||
import deepClone from 'lodash.clonedeep';
|
||||
import intersectDeep from '../../query/lib/intersectDeep';
|
||||
import genCountEndpoint from '../../query/counts/genEndpoint.server';
|
||||
import {check} from 'meteor/check';
|
||||
import { check } from 'meteor/check';
|
||||
|
||||
_.extend(NamedQuery.prototype, {
|
||||
/**
|
||||
|
@ -15,11 +15,17 @@ _.extend(NamedQuery.prototype, {
|
|||
*/
|
||||
expose(config = {}) {
|
||||
if (!Meteor.isServer) {
|
||||
throw new Meteor.Error('invalid-environment', `You must run this in server-side code`);
|
||||
throw new Meteor.Error(
|
||||
'invalid-environment',
|
||||
`You must run this in server-side code`
|
||||
);
|
||||
}
|
||||
|
||||
if (this.isExposed) {
|
||||
throw new Meteor.Error('query-already-exposed', `You have already exposed: "${this.name}" named query`);
|
||||
throw new Meteor.Error(
|
||||
'query-already-exposed',
|
||||
`You have already exposed: "${this.name}" named query`
|
||||
);
|
||||
}
|
||||
|
||||
this.exposeConfig = Object.assign({}, ExposeDefaults, config);
|
||||
|
@ -53,7 +59,10 @@ _.extend(NamedQuery.prototype, {
|
|||
}
|
||||
|
||||
if (!config.method && !config.publication) {
|
||||
throw new Meteor.Error('weird', 'If you want to expose your named query you need to specify at least one of ["method", "publication"] options to true')
|
||||
throw new Meteor.Error(
|
||||
'weird',
|
||||
'If you want to expose your named query you need to specify at least one of ["method", "publication"] options to true'
|
||||
);
|
||||
}
|
||||
|
||||
this._initCountMethod();
|
||||
|
@ -62,8 +71,8 @@ _.extend(NamedQuery.prototype, {
|
|||
|
||||
/**
|
||||
* Returns the embodied body of the request
|
||||
* @param {*} _embody
|
||||
* @param {*} body
|
||||
* @param {*} _embody
|
||||
* @param {*} body
|
||||
*/
|
||||
doEmbodimentIfItApplies(body) {
|
||||
// query is not exposed yet, so it doesn't have embodiment logic
|
||||
|
@ -71,19 +80,16 @@ _.extend(NamedQuery.prototype, {
|
|||
return;
|
||||
}
|
||||
|
||||
const {embody} = this.exposeConfig;
|
||||
const { embody } = this.exposeConfig;
|
||||
|
||||
if (!embody) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_.isFunction(embody)) {
|
||||
embody.call(this, body, this.params)
|
||||
embody.call(this, body, this.params);
|
||||
} else {
|
||||
mergeDeep(
|
||||
body,
|
||||
embody
|
||||
);
|
||||
mergeDeep(body, embody);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -98,8 +104,8 @@ _.extend(NamedQuery.prototype, {
|
|||
|
||||
// security is done in the fetching because we provide a context
|
||||
return self.clone(newParams).fetch(this);
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -115,7 +121,7 @@ _.extend(NamedQuery.prototype, {
|
|||
|
||||
// security is done in the fetching because we provide a context
|
||||
return self.clone(newParams).getCount(this);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -127,7 +133,7 @@ _.extend(NamedQuery.prototype, {
|
|||
const self = this;
|
||||
|
||||
genCountEndpoint(self.name, {
|
||||
getCursor(session) {
|
||||
getCursor({ session }) {
|
||||
const query = self.clone(session.params);
|
||||
return query.getCursorForCounting();
|
||||
},
|
||||
|
@ -136,7 +142,7 @@ _.extend(NamedQuery.prototype, {
|
|||
self.doValidateParams(newParams);
|
||||
self._callFirewall(this, this.userId, params);
|
||||
|
||||
return { params: newParams };
|
||||
return { name: self.name, params: newParams };
|
||||
},
|
||||
});
|
||||
},
|
||||
|
@ -147,7 +153,7 @@ _.extend(NamedQuery.prototype, {
|
|||
_initPublication() {
|
||||
const self = this;
|
||||
|
||||
Meteor.publishComposite(this.name, function (params = {}) {
|
||||
Meteor.publishComposite(this.name, function(params = {}) {
|
||||
self._unblockIfNecessary(this);
|
||||
self.doValidateParams(params);
|
||||
self._callFirewall(this, this.userId, params);
|
||||
|
@ -173,7 +179,7 @@ _.extend(NamedQuery.prototype, {
|
|||
* @private
|
||||
*/
|
||||
_callFirewall(context, userId, params) {
|
||||
const {firewall} = this.exposeConfig;
|
||||
const { firewall } = this.exposeConfig;
|
||||
if (!firewall) {
|
||||
return;
|
||||
}
|
||||
|
@ -181,7 +187,7 @@ _.extend(NamedQuery.prototype, {
|
|||
if (_.isArray(firewall)) {
|
||||
firewall.forEach(fire => {
|
||||
fire.call(context, userId, params);
|
||||
})
|
||||
});
|
||||
} else {
|
||||
firewall.call(context, userId, params);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import postListExposure from './bootstrap/queries/postListExposure.js';
|
||||
import { createQuery } from 'meteor/cultofcoders:grapher';
|
||||
|
||||
describe('Named Query', function () {
|
||||
it('Should return proper values', function (done) {
|
||||
describe('Named Query', function() {
|
||||
it('Should return proper values', function(done) {
|
||||
const query = createQuery({
|
||||
postListExposure: {
|
||||
title: 'User Post - 3'
|
||||
}
|
||||
title: 'User Post - 3',
|
||||
},
|
||||
});
|
||||
|
||||
query.fetch((err, res) => {
|
||||
|
@ -20,11 +20,11 @@ describe('Named Query', function () {
|
|||
});
|
||||
|
||||
done();
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('Should return proper values using query directly via import', function (done) {
|
||||
const query = postListExposure.clone({title: 'User Post - 3'});
|
||||
it('Should return proper values using query directly via import', function(done) {
|
||||
const query = postListExposure.clone({ title: 'User Post - 3' });
|
||||
|
||||
query.fetch((err, res) => {
|
||||
assert.isUndefined(err);
|
||||
|
@ -37,20 +37,20 @@ describe('Named Query', function () {
|
|||
});
|
||||
|
||||
done();
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('Should work with count', function (done) {
|
||||
const query = postListExposure.clone({title: 'User Post - 3'});
|
||||
it('Should work with count', function(done) {
|
||||
const query = postListExposure.clone({ title: 'User Post - 3' });
|
||||
|
||||
query.getCount((err, res) => {
|
||||
assert.equal(6, res);
|
||||
done();
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('Should work with reactive counts', function (done) {
|
||||
const query = postListExposure.clone({title: 'User Post - 3'});
|
||||
it('Should work with reactive counts', function(done) {
|
||||
const query = postListExposure.clone({ title: 'User Post - 3' });
|
||||
|
||||
const handle = query.subscribeCount();
|
||||
Tracker.autorun(c => {
|
||||
|
@ -65,11 +65,11 @@ describe('Named Query', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('Should work with reactive queries', function (done) {
|
||||
it('Should work with reactive queries', function(done) {
|
||||
const query = createQuery({
|
||||
postListExposure: {
|
||||
title: 'User Post - 3'
|
||||
}
|
||||
title: 'User Post - 3',
|
||||
},
|
||||
});
|
||||
|
||||
const handle = query.subscribe();
|
||||
|
@ -90,12 +90,12 @@ describe('Named Query', function () {
|
|||
|
||||
done();
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('Should work with reactive queries via import', function (done) {
|
||||
it('Should work with reactive queries via import', function(done) {
|
||||
const query = postListExposure.clone({
|
||||
title: 'User Post - 3'
|
||||
title: 'User Post - 3',
|
||||
});
|
||||
|
||||
const handle = query.subscribe();
|
||||
|
@ -116,6 +116,6 @@ describe('Named Query', function () {
|
|||
|
||||
done();
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -18,7 +18,12 @@ export default (name, { getCursor, getSession }) => {
|
|||
Meteor.methods({
|
||||
[name + '.count.subscribe'](paramsOrBody) {
|
||||
const session = getSession.call(this, paramsOrBody);
|
||||
const existingSession = collection.findOne({ ...session, userId: this.userId });
|
||||
const sessionId = JSON.stringify(session);
|
||||
|
||||
const existingSession = collection.findOne({
|
||||
session: sessionId,
|
||||
userId: this.userId,
|
||||
});
|
||||
|
||||
// Try to reuse sessions if the user subscribes multiple times with the same data
|
||||
if (existingSession) {
|
||||
|
@ -26,7 +31,7 @@ export default (name, { getCursor, getSession }) => {
|
|||
}
|
||||
|
||||
const token = collection.insert({
|
||||
...session,
|
||||
session: sessionId,
|
||||
query: name,
|
||||
userId: this.userId,
|
||||
});
|
||||
|
@ -41,30 +46,41 @@ export default (name, { getCursor, getSession }) => {
|
|||
const request = collection.findOne({ _id: token, userId: self.userId });
|
||||
|
||||
if (!request) {
|
||||
throw new Error('no-request', `You must acquire a request token via the "${name}.count.subscribe" method first.`);
|
||||
throw new Error(
|
||||
'no-request',
|
||||
`You must acquire a request token via the "${name}.count.subscribe" method first.`
|
||||
);
|
||||
}
|
||||
|
||||
request.session = JSON.parse(request.session);
|
||||
const cursor = getCursor.call(this, request);
|
||||
|
||||
// Start counting
|
||||
let count = 0;
|
||||
self.added(COUNTS_COLLECTION_CLIENT, token, { count });
|
||||
const handle = cursor.observeChanges({
|
||||
added(id) {
|
||||
|
||||
let isReady = false;
|
||||
const handle = cursor.observe({
|
||||
added() {
|
||||
count++;
|
||||
self.changed(COUNTS_COLLECTION_CLIENT, token, { count });
|
||||
isReady &&
|
||||
self.changed(COUNTS_COLLECTION_CLIENT, token, { count });
|
||||
},
|
||||
|
||||
removed(id) {
|
||||
removed() {
|
||||
count--;
|
||||
self.changed(COUNTS_COLLECTION_CLIENT, token, { count });
|
||||
isReady &&
|
||||
self.changed(COUNTS_COLLECTION_CLIENT, token, { count });
|
||||
},
|
||||
});
|
||||
|
||||
isReady = true;
|
||||
self.added(COUNTS_COLLECTION_CLIENT, token, { count });
|
||||
|
||||
self.onStop(() => {
|
||||
handle.stop();
|
||||
collection.remove(token);
|
||||
});
|
||||
|
||||
self.ready();
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,10 +1,33 @@
|
|||
import { createQuery } from 'meteor/cultofcoders:grapher';
|
||||
|
||||
const query = createQuery('counts_posts_query', {
|
||||
export const postsQuery = createQuery('counts_posts_query', {
|
||||
counts_posts: {
|
||||
_id: 1,
|
||||
text: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export default query;
|
||||
export const postsQuery2 = createQuery('counts_posts_query2', {
|
||||
counts_posts: {
|
||||
$filters: {
|
||||
text: 'text 1',
|
||||
},
|
||||
_id: 1,
|
||||
text: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export const postsQuery3 = createQuery('counts_posts_query3', {
|
||||
counts_posts: {
|
||||
$filters: {
|
||||
text: {
|
||||
$regex: 'text',
|
||||
$options: 'i',
|
||||
},
|
||||
},
|
||||
_id: 1,
|
||||
text: 1,
|
||||
},
|
||||
});
|
||||
|
||||
export default postsQuery;
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
import { Tracker } from 'meteor/tracker';
|
||||
import PostsCollection from './bootstrap/collection.test';
|
||||
import NamedQuery from './bootstrap/namedQuery.test';
|
||||
import NamedQuery, {
|
||||
postsQuery,
|
||||
postsQuery2,
|
||||
postsQuery3,
|
||||
} from './bootstrap/namedQuery.test';
|
||||
import callWithPromise from '../../lib/callWithPromise';
|
||||
|
||||
describe('Reactive count tests', function () {
|
||||
it('Should fetch the initial count', function (done) {
|
||||
describe('Reactive count tests', function() {
|
||||
callWithPromise('resetPosts');
|
||||
|
||||
it('Should fetch the initial count', function(done) {
|
||||
const query = NamedQuery.clone();
|
||||
const handle = query.subscribeCount();
|
||||
|
||||
|
@ -20,7 +27,7 @@ describe('Reactive count tests', function () {
|
|||
});
|
||||
|
||||
// TODO: Can these tests fail if assert gets called too quickly?
|
||||
it('Should update when a document is added', function (done) {
|
||||
it('Should update when a document is added', function(done) {
|
||||
const query = NamedQuery.clone();
|
||||
const handle = query.subscribeCount();
|
||||
|
||||
|
@ -42,7 +49,7 @@ describe('Reactive count tests', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('Should update when a document is removed', function (done) {
|
||||
it('Should update when a document is removed', function(done) {
|
||||
const query = NamedQuery.clone();
|
||||
const handle = query.subscribeCount();
|
||||
|
||||
|
@ -52,7 +59,7 @@ describe('Reactive count tests', function () {
|
|||
const count = query.getCount();
|
||||
assert.equal(count, 3);
|
||||
|
||||
Meteor.call('removePost', 'removeid', (error) => {
|
||||
Meteor.call('removePost', 'removeid', error => {
|
||||
const newCount = query.getCount();
|
||||
assert.equal(newCount, 2);
|
||||
|
||||
|
@ -62,4 +69,40 @@ describe('Reactive count tests', function () {
|
|||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should work with two different queries', function(done) {
|
||||
const query1 = postsQuery.clone();
|
||||
const query2 = postsQuery2.clone();
|
||||
|
||||
const handle2 = query2.subscribeCount();
|
||||
const handle1 = query1.subscribeCount();
|
||||
|
||||
Tracker.autorun(c => {
|
||||
if (handle1.ready() && handle2.ready()) {
|
||||
const count1 = query1.getCount();
|
||||
const count2 = query2.getCount();
|
||||
|
||||
assert.equal(count1, 2);
|
||||
assert.equal(count2, 1);
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('Should work with special filter params', function(done) {
|
||||
const query = postsQuery3.clone({
|
||||
$regex: 'BOMB',
|
||||
});
|
||||
|
||||
const handle = query.subscribeCount();
|
||||
|
||||
Tracker.autorun(c => {
|
||||
if (handle.ready()) {
|
||||
const count = query.getCount();
|
||||
|
||||
assert.equal(count, 2);
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
import { Meteor } from 'meteor/meteor';
|
||||
import PostsCollection from './bootstrap/collection.test';
|
||||
import query from './bootstrap/namedQuery.test';
|
||||
import {
|
||||
postsQuery,
|
||||
postsQuery2,
|
||||
postsQuery3,
|
||||
} from './bootstrap/namedQuery.test';
|
||||
|
||||
query.expose();
|
||||
PostsCollection.remove({});
|
||||
PostsCollection.insert({ text: 'text 1' });
|
||||
PostsCollection.insert({ text: 'text 2' });
|
||||
PostsCollection.insert({ _id: 'removeid', text: 'text 3' });
|
||||
postsQuery.expose();
|
||||
postsQuery2.expose();
|
||||
postsQuery3.expose();
|
||||
|
||||
Meteor.methods({
|
||||
resetPosts() {
|
||||
PostsCollection.remove({});
|
||||
PostsCollection.insert({ text: 'text 1' });
|
||||
PostsCollection.insert({ text: 'text 2' });
|
||||
PostsCollection.insert({ _id: 'removeid', text: 'text 3' });
|
||||
},
|
||||
|
||||
addPost(text) {
|
||||
return PostsCollection.insert({ text });
|
||||
},
|
||||
|
|
|
@ -25,8 +25,12 @@ Authors.addReducers({
|
|||
body: {
|
||||
name: 1
|
||||
},
|
||||
reduce(object) {
|
||||
return 'full - ' + object.name;
|
||||
reduce(object, params) {
|
||||
return (
|
||||
'full - ' +
|
||||
object.name +
|
||||
(params && params.suffix ? params.suffix : null)
|
||||
);
|
||||
}
|
||||
},
|
||||
groupNames: {
|
||||
|
@ -66,10 +70,10 @@ Authors.addReducers({
|
|||
},
|
||||
paramBasedReducer: {
|
||||
body: {
|
||||
_id: 1,
|
||||
_id: 1
|
||||
},
|
||||
reduce(object, params) {
|
||||
return params.element;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { createQuery } from 'meteor/cultofcoders:grapher';
|
||||
import waitForHandleToBeReady from './lib/waitForHandleToBeReady';
|
||||
|
||||
describe('Client-side reducers', function () {
|
||||
it('Should work with field only reducers', async function () {
|
||||
describe('Client-side reducers', function() {
|
||||
it('Should work with field only reducers', async function() {
|
||||
const query = createQuery({
|
||||
authors: {
|
||||
fullName: 1
|
||||
}
|
||||
fullName: 1,
|
||||
},
|
||||
});
|
||||
|
||||
let handle = query.subscribe();
|
||||
|
@ -18,17 +18,43 @@ describe('Client-side reducers', function () {
|
|||
data.forEach(author => {
|
||||
assert.isString(author.fullName);
|
||||
assert.isUndefined(author.name);
|
||||
assert.isTrue(author.fullName.substr(0, 7) === 'full - ')
|
||||
assert.isTrue(author.fullName.substr(0, 7) === 'full - ');
|
||||
});
|
||||
|
||||
handle.stop();
|
||||
});
|
||||
|
||||
it('Should work with nested fields reducers', async function () {
|
||||
it('Should work with field only reducers and parameters', async function() {
|
||||
const query = createQuery({
|
||||
authors: {
|
||||
fullNameNested: 1
|
||||
}
|
||||
fullName: 1,
|
||||
},
|
||||
});
|
||||
|
||||
query.setParams({
|
||||
suffix: 'Bomb',
|
||||
});
|
||||
|
||||
let handle = query.subscribe();
|
||||
await waitForHandleToBeReady(handle);
|
||||
const data = query.fetch();
|
||||
|
||||
assert.isTrue(data.length > 0);
|
||||
|
||||
data.forEach(author => {
|
||||
assert.isString(author.fullName);
|
||||
assert.isUndefined(author.name);
|
||||
assert.isTrue(author.fullName.indexOf('Bomb') >= 0);
|
||||
});
|
||||
|
||||
handle.stop();
|
||||
});
|
||||
|
||||
it('Should work with nested fields reducers', async function() {
|
||||
const query = createQuery({
|
||||
authors: {
|
||||
fullNameNested: 1,
|
||||
},
|
||||
});
|
||||
|
||||
let handle = query.subscribe();
|
||||
|
@ -47,14 +73,14 @@ describe('Client-side reducers', function () {
|
|||
handle.stop();
|
||||
});
|
||||
|
||||
it('Should work with nested fields reducers', async function () {
|
||||
it('Should work with nested fields reducers', async function() {
|
||||
const query = createQuery({
|
||||
authors: {
|
||||
profile: {
|
||||
firstName: 1
|
||||
firstName: 1,
|
||||
},
|
||||
fullNameNested: 1,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
let handle = query.subscribe();
|
||||
|
@ -75,11 +101,11 @@ describe('Client-side reducers', function () {
|
|||
handle.stop();
|
||||
});
|
||||
|
||||
it('Should work with links reducers', async function () {
|
||||
it('Should work with links reducers', async function() {
|
||||
const query = createQuery({
|
||||
authors: {
|
||||
groupNames: 1
|
||||
}
|
||||
groupNames: 1,
|
||||
},
|
||||
});
|
||||
|
||||
let handle = query.subscribe();
|
||||
|
@ -96,11 +122,11 @@ describe('Client-side reducers', function () {
|
|||
handle.stop();
|
||||
});
|
||||
|
||||
it('Should work with links and nested reducers', async function () {
|
||||
it('Should work with links and nested reducers', async function() {
|
||||
const query = createQuery({
|
||||
authors: {
|
||||
referenceReducer: 1
|
||||
}
|
||||
referenceReducer: 1,
|
||||
},
|
||||
});
|
||||
|
||||
let handle = query.subscribe();
|
||||
|
@ -112,18 +138,18 @@ describe('Client-side reducers', function () {
|
|||
data.forEach(author => {
|
||||
assert.isString(author.referenceReducer);
|
||||
assert.isUndefined(author.fullName);
|
||||
assert.isTrue(author.referenceReducer.substr(0, 9) === 'nested - ')
|
||||
assert.isTrue(author.referenceReducer.substr(0, 9) === 'nested - ');
|
||||
});
|
||||
|
||||
handle.stop();
|
||||
});
|
||||
|
||||
it('Should not clean nested reducers if not specified', async function () {
|
||||
it('Should not clean nested reducers if not specified', async function() {
|
||||
const query = createQuery({
|
||||
authors: {
|
||||
referenceReducer: 1,
|
||||
fullName: 1,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
let handle = query.subscribe();
|
||||
|
@ -140,16 +166,16 @@ describe('Client-side reducers', function () {
|
|||
handle.stop();
|
||||
});
|
||||
|
||||
it('Should keep previously used items - Part 1', async function () {
|
||||
it('Should keep previously used items - Part 1', async function() {
|
||||
const query = createQuery({
|
||||
authors: {
|
||||
fullName: 1,
|
||||
name: 1,
|
||||
groupNames: 1,
|
||||
groups: {
|
||||
name: 1
|
||||
}
|
||||
}
|
||||
name: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
let handle = query.subscribe();
|
||||
|
@ -163,20 +189,20 @@ describe('Client-side reducers', function () {
|
|||
assert.isDefined(author.groups);
|
||||
assert.isArray(author.groupNames);
|
||||
assert.isString(author.fullName);
|
||||
assert.isTrue(author.fullName.substr(0, 7) === 'full - ')
|
||||
assert.isTrue(author.fullName.substr(0, 7) === 'full - ');
|
||||
});
|
||||
|
||||
handle.stop();
|
||||
});
|
||||
|
||||
it('Should keep previously used items - Part 2', async function () {
|
||||
it('Should keep previously used items - Part 2', async function() {
|
||||
const query = createQuery({
|
||||
authors: {
|
||||
groupNames: 1,
|
||||
groups: {
|
||||
_id: 1
|
||||
}
|
||||
}
|
||||
_id: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
let handle = query.subscribe();
|
||||
|
@ -198,9 +224,9 @@ describe('Client-side reducers', function () {
|
|||
author.groups.forEach(group => {
|
||||
assert.isDefined(group._id);
|
||||
assert.isDefined(group.name);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
handle.stop();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,15 +4,15 @@ import './metaFilters.server.test';
|
|||
import './reducers.server.test';
|
||||
import './link-cache/server.test';
|
||||
|
||||
describe('Hypernova', function () {
|
||||
it('Should fetch One links correctly', function () {
|
||||
describe('Hypernova', function() {
|
||||
it('Should fetch One links correctly', function() {
|
||||
const data = createQuery({
|
||||
comments: {
|
||||
text: 1,
|
||||
author: {
|
||||
name: 1
|
||||
}
|
||||
}
|
||||
name: 1,
|
||||
},
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
assert.lengthOf(data, Comments.find().count());
|
||||
|
@ -23,31 +23,35 @@ describe('Hypernova', function () {
|
|||
assert.isString(comment.author.name);
|
||||
assert.isString(comment.author._id);
|
||||
assert.isTrue(_.keys(comment.author).length == 2);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('Should fetch One links with limit and options', function () {
|
||||
it('Should fetch One links with limit and options', function() {
|
||||
const data = createQuery({
|
||||
comments: {
|
||||
$options: {limit: 5},
|
||||
text: 1
|
||||
}
|
||||
$options: { limit: 5 },
|
||||
text: 1,
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
assert.lengthOf(data, 5);
|
||||
});
|
||||
|
||||
it('Should fetch One-Inversed links with limit and options', function () {
|
||||
const query = createQuery({
|
||||
authors: {
|
||||
$options: {limit: 5},
|
||||
comments: {
|
||||
$filters: {text: 'Good'},
|
||||
$options: {limit: 2},
|
||||
text: 1
|
||||
}
|
||||
}
|
||||
}, {}, {debug: true});
|
||||
it('Should fetch One-Inversed links with limit and options', function() {
|
||||
const query = createQuery(
|
||||
{
|
||||
authors: {
|
||||
$options: { limit: 5 },
|
||||
comments: {
|
||||
$filters: { text: 'Good' },
|
||||
$options: { limit: 2 },
|
||||
text: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{},
|
||||
{ debug: true }
|
||||
);
|
||||
|
||||
const data = query.fetch();
|
||||
|
||||
|
@ -56,19 +60,19 @@ describe('Hypernova', function () {
|
|||
assert.lengthOf(author.comments, 2);
|
||||
_.each(author.comments, comment => {
|
||||
assert.equal('Good', comment.text);
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Should fetch Many links correctly', function () {
|
||||
it('Should fetch Many links correctly', function() {
|
||||
const data = createQuery({
|
||||
posts: {
|
||||
$options: {limit: 5},
|
||||
$options: { limit: 5 },
|
||||
title: 1,
|
||||
tags: {
|
||||
text: 1
|
||||
}
|
||||
}
|
||||
text: 1,
|
||||
},
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
assert.lengthOf(data, 5);
|
||||
|
@ -76,39 +80,39 @@ describe('Hypernova', function () {
|
|||
assert.isString(post.title);
|
||||
assert.isArray(post.tags);
|
||||
assert.isTrue(post.tags.length > 0);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('Should fetch Many - inversed links correctly', function () {
|
||||
it('Should fetch Many - inversed links correctly', function() {
|
||||
const data = createQuery({
|
||||
tags: {
|
||||
name: 1,
|
||||
posts: {
|
||||
$options: {limit: 5},
|
||||
title: 1
|
||||
}
|
||||
}
|
||||
$options: { limit: 5 },
|
||||
title: 1,
|
||||
},
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
|
||||
_.each(data, tag => {
|
||||
assert.isString(tag.name);
|
||||
assert.isArray(tag.posts);
|
||||
assert.isTrue(tag.posts.length <= 5);
|
||||
_.each(tag.posts, post => {
|
||||
assert.isString(post.title);
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Should fetch One-Meta links correctly', function () {
|
||||
it('Should fetch One-Meta links correctly', function() {
|
||||
const data = createQuery({
|
||||
posts: {
|
||||
$options: {limit: 5},
|
||||
$options: { limit: 5 },
|
||||
title: 1,
|
||||
group: {
|
||||
name: 1
|
||||
}
|
||||
}
|
||||
name: 1,
|
||||
},
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
assert.lengthOf(data, 5);
|
||||
|
@ -118,17 +122,17 @@ describe('Hypernova', function () {
|
|||
assert.isObject(post.group);
|
||||
assert.isString(post.group._id);
|
||||
assert.isString(post.group.name);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('Should fetch One-Meta inversed links correctly', function () {
|
||||
it('Should fetch One-Meta inversed links correctly', function() {
|
||||
const data = createQuery({
|
||||
groups: {
|
||||
name: 1,
|
||||
posts: {
|
||||
title: 1
|
||||
}
|
||||
}
|
||||
title: 1,
|
||||
},
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
_.each(data, group => {
|
||||
|
@ -139,19 +143,19 @@ describe('Hypernova', function () {
|
|||
_.each(group.posts, post => {
|
||||
assert.isString(post.title);
|
||||
assert.isString(post._id);
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Should fetch Many-Meta links correctly', function () {
|
||||
it('Should fetch Many-Meta links correctly', function() {
|
||||
const data = createQuery({
|
||||
authors: {
|
||||
name: 1,
|
||||
groups: {
|
||||
$options: {limit: 1},
|
||||
name: 1
|
||||
}
|
||||
}
|
||||
$options: { limit: 1 },
|
||||
name: 1,
|
||||
},
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
_.each(data, author => {
|
||||
|
@ -162,19 +166,19 @@ describe('Hypernova', function () {
|
|||
assert.isObject(group);
|
||||
assert.isString(group._id);
|
||||
assert.isString(group.name);
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Should fetch Many-Meta inversed links correctly', function () {
|
||||
it('Should fetch Many-Meta inversed links correctly', function() {
|
||||
const data = createQuery({
|
||||
groups: {
|
||||
name: 1,
|
||||
authors: {
|
||||
$options: {limit: 2},
|
||||
name: 1
|
||||
}
|
||||
}
|
||||
$options: { limit: 2 },
|
||||
name: 1,
|
||||
},
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
_.each(data, group => {
|
||||
|
@ -185,17 +189,17 @@ describe('Hypernova', function () {
|
|||
assert.isObject(author);
|
||||
assert.isString(author._id);
|
||||
assert.isString(author.name);
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Should fetch direct One & Many Meta links with $metadata', function () {
|
||||
it('Should fetch direct One & Many Meta links with $metadata', function() {
|
||||
let data = createQuery({
|
||||
posts: {
|
||||
group: {
|
||||
name: 1
|
||||
}
|
||||
}
|
||||
name: 1,
|
||||
},
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
_.each(data, post => {
|
||||
|
@ -206,10 +210,10 @@ describe('Hypernova', function () {
|
|||
data = createQuery({
|
||||
authors: {
|
||||
groups: {
|
||||
$options: {limit: 1},
|
||||
name: 1
|
||||
}
|
||||
}
|
||||
$options: { limit: 1 },
|
||||
name: 1,
|
||||
},
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
_.each(data, author => {
|
||||
|
@ -217,21 +221,21 @@ describe('Hypernova', function () {
|
|||
|
||||
_.each(author.groups, group => {
|
||||
assert.isObject(group.$metadata);
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Should fetch direct One Meta links with $metadata that are under a nesting level', function () {
|
||||
it('Should fetch direct One Meta links with $metadata that are under a nesting level', function() {
|
||||
let authors = createQuery({
|
||||
authors: {
|
||||
$options: { limit: 1 },
|
||||
posts: {
|
||||
$options: { limit: 1 },
|
||||
group: {
|
||||
name: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
name: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
let data = authors[0];
|
||||
|
@ -240,63 +244,62 @@ describe('Hypernova', function () {
|
|||
assert.isObject(post.group.$metadata);
|
||||
assert.isDefined(post.group.$metadata.random);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('Should fetch Inversed One & Many Meta links with $metadata', function () {
|
||||
it('Should fetch Inversed One & Many Meta links with $metadata', function() {
|
||||
let data = createQuery({
|
||||
groups: {
|
||||
posts: {
|
||||
group_groups_meta: 1,
|
||||
title: 1
|
||||
}
|
||||
}
|
||||
title: 1,
|
||||
},
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
_.each(data, group => {
|
||||
_.each(group.posts, post => {
|
||||
assert.isObject(post.$metadata);
|
||||
assert.isDefined(post.$metadata.random);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
data = createQuery({
|
||||
groups: {
|
||||
authors: {
|
||||
$options: {limit: 1},
|
||||
name: 1
|
||||
}
|
||||
}
|
||||
$options: { limit: 1 },
|
||||
name: 1,
|
||||
},
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
_.each(data, group => {
|
||||
_.each(group.authors, author => {
|
||||
assert.isObject(author.$metadata);
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('Should fetch in depth properly at any given level.', function () {
|
||||
it('Should fetch in depth properly at any given level.', function() {
|
||||
const data = createQuery({
|
||||
authors: {
|
||||
$options: {limit: 5},
|
||||
$options: { limit: 5 },
|
||||
posts: {
|
||||
$options: {limit: 5},
|
||||
$options: { limit: 5 },
|
||||
comments: {
|
||||
$options: {limit: 5},
|
||||
$options: { limit: 5 },
|
||||
author: {
|
||||
groups: {
|
||||
posts: {
|
||||
$options: {limit: 5},
|
||||
$options: { limit: 5 },
|
||||
author: {
|
||||
name: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
name: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
assert.lengthOf(data, 5);
|
||||
|
@ -310,62 +313,62 @@ describe('Hypernova', function () {
|
|||
assert.isObject(post.author);
|
||||
assert.isString(post.author.name);
|
||||
arrivedInDepth = true;
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
assert.isTrue(arrivedInDepth);
|
||||
});
|
||||
|
||||
it('Should work with filters of $and and $or on subcollections', function () {
|
||||
it('Should work with filters of $and and $or on subcollections', function() {
|
||||
let data = createQuery({
|
||||
posts: {
|
||||
comments: {
|
||||
$filters: {
|
||||
$and: [
|
||||
{
|
||||
text: 'Good'
|
||||
}
|
||||
]
|
||||
text: 'Good',
|
||||
},
|
||||
],
|
||||
},
|
||||
text: 1
|
||||
}
|
||||
}
|
||||
text: 1,
|
||||
},
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
data.forEach(post => {
|
||||
if (post.comments) {
|
||||
post.comments.forEach(comment => {
|
||||
assert.equal(comment.text, 'Good');
|
||||
})
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
it('Should work sorting with options that contain a dot', function () {
|
||||
it('Should work sorting with options that contain a dot', function() {
|
||||
let data = createQuery({
|
||||
posts: {
|
||||
author: {
|
||||
$filter({options}) {
|
||||
$filter({ options }) {
|
||||
options.sort = {
|
||||
'profile.firstName': 1
|
||||
}
|
||||
'profile.firstName': 1,
|
||||
};
|
||||
},
|
||||
profile: 1,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}).fetch();
|
||||
|
||||
assert.isArray(data);
|
||||
});
|
||||
|
||||
it('Should properly clone and work with setParams', function () {
|
||||
it('Should properly clone and work with setParams', function() {
|
||||
let query = createQuery({
|
||||
posts: {
|
||||
$options: {limit: 5}
|
||||
}
|
||||
$options: { limit: 5 },
|
||||
},
|
||||
});
|
||||
|
||||
let clone = query.clone({});
|
||||
|
@ -376,7 +379,7 @@ describe('Hypernova', function () {
|
|||
assert.isFunction(clone.setParams({}).fetchOne);
|
||||
});
|
||||
|
||||
it('Should work with $postFilters', function () {
|
||||
it('Should work with $postFilters', function() {
|
||||
let query = createQuery({
|
||||
posts: {
|
||||
$postFilters: {
|
||||
|
@ -384,9 +387,9 @@ describe('Hypernova', function () {
|
|||
},
|
||||
title: 1,
|
||||
comments: {
|
||||
text: 1
|
||||
}
|
||||
}
|
||||
text: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const data = query.fetch();
|
||||
|
@ -399,43 +402,64 @@ describe('Hypernova', function () {
|
|||
},
|
||||
title: 1,
|
||||
comments: {
|
||||
text: 1
|
||||
}
|
||||
}
|
||||
text: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert.isTrue(query.fetch().length > 0);
|
||||
})
|
||||
});
|
||||
|
||||
it('Should work with $postOptions', function () {
|
||||
it('Should work with $postOptions', function() {
|
||||
let query = createQuery({
|
||||
posts: {
|
||||
$postOptions: {
|
||||
limit:5,
|
||||
skip:5,
|
||||
sort:{title:1}
|
||||
limit: 5,
|
||||
skip: 5,
|
||||
sort: { title: 1 },
|
||||
},
|
||||
title: 1,
|
||||
comments: {
|
||||
text: 1
|
||||
}
|
||||
}
|
||||
text: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const data = query.fetch();
|
||||
assert.lengthOf(data, 5);
|
||||
});
|
||||
|
||||
it('Should work with a nested field from reversedSide using aggregation framework', function () {
|
||||
it('Should work with $postFilter and params', function(done) {
|
||||
let query = createQuery({
|
||||
posts: {
|
||||
$postFilter(results, params) {
|
||||
assert.equal(params.text, 'Good');
|
||||
done();
|
||||
},
|
||||
title: 1,
|
||||
comments: {
|
||||
text: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
query.setParams({
|
||||
text: 'Good',
|
||||
});
|
||||
|
||||
query.fetch();
|
||||
});
|
||||
|
||||
it('Should work with a nested field from reversedSide using aggregation framework', function() {
|
||||
let query = createQuery({
|
||||
groups: {
|
||||
$options: {limit: 1},
|
||||
$options: { limit: 1 },
|
||||
authors: {
|
||||
profile: {
|
||||
firstName: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const data = query.fetch();
|
||||
|
@ -453,19 +477,22 @@ describe('Hypernova', function () {
|
|||
assert.isUndefined(author.profile.lastName);
|
||||
});
|
||||
|
||||
it('Should apply a default filter function to first root', function () {
|
||||
let query = createQuery({
|
||||
groups: {
|
||||
authors: {}
|
||||
it('Should apply a default filter function to first root', function() {
|
||||
let query = createQuery(
|
||||
{
|
||||
groups: {
|
||||
authors: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
params: {
|
||||
options: { limit: 1 },
|
||||
filters: {
|
||||
name: 'JavaScript',
|
||||
},
|
||||
},
|
||||
}
|
||||
}, {
|
||||
params: {
|
||||
options: {limit: 1},
|
||||
filters: {
|
||||
name: 'JavaScript'
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
const data = query.fetch();
|
||||
assert.lengthOf(data, 1);
|
||||
|
@ -477,34 +504,32 @@ describe('Hypernova', function () {
|
|||
const Users = new Mongo.Collection('__many_inversed_users');
|
||||
const Restaurants = new Mongo.Collection('__many_inversed_restaurants');
|
||||
|
||||
it('Should fetch Many - inversed links correctly when the field is not the first', function () {
|
||||
it('Should fetch Many - inversed links correctly when the field is not the first', function() {
|
||||
Restaurants.addLinks({
|
||||
users: {
|
||||
type: 'many',
|
||||
field: 'userIds',
|
||||
collection: Users,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Users.addLinks({
|
||||
restaurants: {
|
||||
collection: Restaurants,
|
||||
inversedBy: 'users'
|
||||
}
|
||||
inversedBy: 'users',
|
||||
},
|
||||
});
|
||||
|
||||
const userId1 = Users.insert({
|
||||
name: 'John'
|
||||
name: 'John',
|
||||
});
|
||||
const userId2 = Users.insert({
|
||||
name: 'John'
|
||||
name: 'John',
|
||||
});
|
||||
|
||||
const restaurantId = Restaurants.insert({
|
||||
name: 'Jamie Oliver',
|
||||
userIds: [
|
||||
userId2, userId1
|
||||
]
|
||||
userIds: [userId2, userId1],
|
||||
});
|
||||
|
||||
const user = Users.createQuery({
|
||||
|
@ -513,7 +538,7 @@ describe('Hypernova', function () {
|
|||
},
|
||||
restaurants: {
|
||||
name: 1,
|
||||
}
|
||||
},
|
||||
}).fetchOne();
|
||||
|
||||
assert.isObject(user);
|
||||
|
|
17
package.js
17
package.js
|
@ -1,23 +1,23 @@
|
|||
Package.describe({
|
||||
name: 'cultofcoders:grapher',
|
||||
version: '1.3.2',
|
||||
version: '1.3.3',
|
||||
// Brief, one-line summary of the package.
|
||||
summary: 'Grapher is a data fetching layer on top of Meteor',
|
||||
// URL to the Git repository containing the source code for this package.
|
||||
git: 'https://github.com/cult-of-coders/grapher',
|
||||
// By default, Meteor will default to using README.md for documentation.
|
||||
// To avoid submitting documentation, set this field to null.
|
||||
documentation: 'README.md'
|
||||
documentation: 'README.md',
|
||||
});
|
||||
|
||||
Npm.depends({
|
||||
'sift': '3.2.6',
|
||||
sift: '3.2.6',
|
||||
'dot-object': '1.5.4',
|
||||
'lodash.clonedeep': '4.5.0',
|
||||
'deep-extend': '0.5.0',
|
||||
});
|
||||
|
||||
Package.onUse(function (api) {
|
||||
Package.onUse(function(api) {
|
||||
api.versionsFrom('1.3');
|
||||
|
||||
var packages = [
|
||||
|
@ -39,7 +39,7 @@ Package.onUse(function (api) {
|
|||
api.mainModule('main.server.js', 'server');
|
||||
});
|
||||
|
||||
Package.onTest(function (api) {
|
||||
Package.onTest(function(api) {
|
||||
api.use('cultofcoders:grapher');
|
||||
|
||||
var packages = [
|
||||
|
@ -49,16 +49,13 @@ Package.onTest(function (api) {
|
|||
'reywood:publish-composite@1.5.2',
|
||||
'dburles:mongo-collection-instances@0.3.5',
|
||||
'herteby:denormalize@0.6.5',
|
||||
'mongo'
|
||||
'mongo',
|
||||
];
|
||||
|
||||
api.use(packages);
|
||||
api.use('tracker');
|
||||
|
||||
api.use([
|
||||
'cultofcoders:mocha',
|
||||
'practicalmeteor:chai'
|
||||
]);
|
||||
api.use(['cultofcoders:mocha', 'practicalmeteor:chai']);
|
||||
|
||||
// LINKS
|
||||
api.addFiles('lib/links/tests/main.js', 'server');
|
||||
|
|
Loading…
Add table
Reference in a new issue