Vulcan/packages/vulcan-lib/lib/server/query.js

98 lines
3.1 KiB
JavaScript
Raw Normal View History

/*
2018-06-06 12:46:14 +02:00
Run a GraphQL request from the server with the proper context
*/
import { graphql } from 'graphql';
import { executableSchema } from './apollo_server.js';
import { Collections } from '../modules/collections.js';
import DataLoader from 'dataloader';
import findByIds from '../modules/findbyids.js';
import { getDefaultFragmentText, extractFragmentName, getFragmentText } from '../modules/fragments.js';
import { getSetting } from '../modules/settings';
2018-05-10 18:34:59 +09:00
import merge from 'lodash/merge';
2018-06-06 12:46:14 +02:00
// note: if no context is passed, default to running requests with full admin privileges
export const runGraphQL = async (query, variables = {}, context ) => {
2018-05-10 18:34:59 +09:00
const defaultContext = { currentUser: {isAdmin: true}, locale: getSetting('locale') };
const queryContext = merge(defaultContext, context);
2018-06-06 12:46:14 +02:00
// within the scope of this specific request,
// decorate each collection with a new Dataloader object and add it to context
Collections.forEach(collection => {
2018-05-10 18:34:59 +09:00
collection.loader = new DataLoader(ids => findByIds(collection, ids, queryContext), { cache: true });
queryContext[collection.options.collectionName] = collection;
});
// see http://graphql.org/graphql-js/graphql/#graphql
2018-05-10 18:34:59 +09:00
const result = await graphql(executableSchema, query, {}, queryContext, variables);
if (result.errors) {
2018-01-25 15:03:03 -06:00
// eslint-disable-next-line no-console
2018-06-06 12:46:14 +02:00
console.error(`runGraphQL error: ${result.errors[0].message}`);
2018-05-10 09:39:35 +09:00
// eslint-disable-next-line no-console
2018-04-27 12:39:48 -07:00
console.error(result.errors);
throw new Error(result.errors[0].message);
}
return result;
2017-08-20 15:24:13 +09:00
}
2018-06-06 12:46:14 +02:00
export const runQuery = runGraphQL; //backwards compatibility
2017-08-20 15:24:13 +09:00
/*
Given a collection and a fragment, build a query to fetch one document.
If no fragment is passed, default to default fragment
*/
export const buildQuery = (collection, {fragmentName, fragmentText}) => {
2017-08-20 15:24:13 +09:00
const collectionName = collection.options.collectionName;
const resolverName = `${collectionName}Single`;
const defaultFragmentName = `${collectionName}DefaultFragment`;
const defaultFragmentText = getDefaultFragmentText(collection, { onlyViewable: false });
// default to default name and text
let name = defaultFragmentName;
let text = defaultFragmentText;
if (fragmentName) { // if fragmentName is passed, use that to get name and text
name = fragmentName;
text = getFragmentText(fragmentName);
} else if (fragmentText) { // if fragmentText is passed, use that to get name and text
name = extractFragmentName(fragmentText);
text = fragmentText;
}
2017-08-20 15:24:13 +09:00
const query = `
query ${resolverName}Query ($documentId: String){
${resolverName}(documentId: $documentId){
...${name}
}
}
${text}
`
return query;
}
Meteor.startup(() => {
Collections.forEach(collection => {
const collectionName = collection.options.collectionName;
const resolverName = `${collectionName}Single`;
collection.queryOne = async (documentId, { fragmentName, fragmentText, context }) => {
const query = buildQuery(collection, { fragmentName, fragmentText });
const result = await runQuery(query, { documentId }, context);
2017-08-20 15:24:13 +09:00
return result.data[resolverName];
}
});
2018-06-06 12:46:14 +02:00
});