mirror of
https://github.com/vale981/grapher
synced 2025-03-06 10:01:40 -05:00
207 lines
5.3 KiB
JavaScript
207 lines
5.3 KiB
JavaScript
import NamedQuery from '../namedQuery.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';
|
|
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';
|
|
|
|
_.extend(NamedQuery.prototype, {
|
|
/**
|
|
* @param config
|
|
*/
|
|
expose(config = {}) {
|
|
if (!Meteor.isServer) {
|
|
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`
|
|
);
|
|
}
|
|
|
|
this.exposeConfig = Object.assign({}, ExposeDefaults, config);
|
|
check(this.exposeConfig, ExposeSchema);
|
|
|
|
if (this.exposeConfig.validateParams) {
|
|
this.options.validateParams = this.exposeConfig.validateParams;
|
|
}
|
|
|
|
if (!this.isResolver) {
|
|
this._initNormalQuery();
|
|
} else {
|
|
this._initMethod();
|
|
}
|
|
|
|
this.isExposed = true;
|
|
},
|
|
|
|
/**
|
|
* Initializes a normal NamedQuery (normal == not a resolver)
|
|
* @private
|
|
*/
|
|
_initNormalQuery() {
|
|
const config = this.exposeConfig;
|
|
if (config.method) {
|
|
this._initMethod();
|
|
}
|
|
|
|
if (config.publication) {
|
|
this._initPublication();
|
|
}
|
|
|
|
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'
|
|
);
|
|
}
|
|
|
|
this._initCountMethod();
|
|
this._initCountPublication();
|
|
},
|
|
|
|
/**
|
|
* Returns the embodied body of the request
|
|
* @param {*} _embody
|
|
* @param {*} body
|
|
*/
|
|
doEmbodimentIfItApplies(body) {
|
|
// query is not exposed yet, so it doesn't have embodiment logic
|
|
if (!this.exposeConfig) {
|
|
return;
|
|
}
|
|
|
|
const { embody } = this.exposeConfig;
|
|
|
|
if (!embody) {
|
|
return;
|
|
}
|
|
|
|
if (_.isFunction(embody)) {
|
|
embody.call(this, body, this.params);
|
|
} else {
|
|
mergeDeep(body, embody);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
_initMethod() {
|
|
const self = this;
|
|
Meteor.methods({
|
|
[this.name](newParams) {
|
|
self._unblockIfNecessary(this);
|
|
|
|
// security is done in the fetching because we provide a context
|
|
return self.clone(newParams).fetch(this);
|
|
},
|
|
});
|
|
},
|
|
|
|
/**
|
|
* @returns {void}
|
|
* @private
|
|
*/
|
|
_initCountMethod() {
|
|
const self = this;
|
|
|
|
Meteor.methods({
|
|
[this.name + '.count'](newParams) {
|
|
self._unblockIfNecessary(this);
|
|
|
|
// security is done in the fetching because we provide a context
|
|
return self.clone(newParams).getCount(this);
|
|
},
|
|
});
|
|
},
|
|
|
|
/**
|
|
* @returns {*}
|
|
* @private
|
|
*/
|
|
_initCountPublication() {
|
|
const self = this;
|
|
|
|
genCountEndpoint(self.name, {
|
|
getCursor({ session }) {
|
|
const query = self.clone(session.params);
|
|
return query.getCursorForCounting();
|
|
},
|
|
|
|
getSession(newParams) {
|
|
self.doValidateParams(newParams);
|
|
self._callFirewall(this, this.userId, params);
|
|
|
|
return { name: self.name, params: newParams };
|
|
},
|
|
});
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
_initPublication() {
|
|
const self = this;
|
|
|
|
Meteor.publishComposite(this.name, function(params = {}) {
|
|
self._unblockIfNecessary(this);
|
|
self.doValidateParams(params);
|
|
self._callFirewall(this, this.userId, params);
|
|
|
|
let body = deepClone(self.body);
|
|
if (params.$body) {
|
|
body = intersectDeep(body, params.$body);
|
|
}
|
|
|
|
self.doEmbodimentIfItApplies(body);
|
|
body = prepareForProcess(body, params);
|
|
|
|
const rootNode = createGraph(self.collection, body);
|
|
|
|
return recursiveCompose(rootNode);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* @param context
|
|
* @param userId
|
|
* @param params
|
|
* @private
|
|
*/
|
|
_callFirewall(context, userId, params) {
|
|
const { firewall } = this.exposeConfig;
|
|
if (!firewall) {
|
|
return;
|
|
}
|
|
|
|
if (_.isArray(firewall)) {
|
|
firewall.forEach(fire => {
|
|
fire.call(context, userId, params);
|
|
});
|
|
} else {
|
|
firewall.call(context, userId, params);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @param context
|
|
* @private
|
|
*/
|
|
_unblockIfNecessary(context) {
|
|
if (this.exposeConfig.unblock) {
|
|
if (context.unblock) {
|
|
context.unblock();
|
|
}
|
|
}
|
|
},
|
|
});
|