mirror of
https://github.com/vale981/Vulcan
synced 2025-03-04 17:21:37 -05:00
Linting
This commit is contained in:
parent
6d1e8da40b
commit
a13327340c
48 changed files with 492 additions and 801 deletions
|
@ -8,6 +8,7 @@
|
|||
"start": "meteor --settings settings.json",
|
||||
"visualizer": "meteor --extra-packages bundle-visualizer --production --settings settings.json",
|
||||
"lint": "eslint --cache --ext .jsx,js packages",
|
||||
"lintfix": "eslint --cache --ext .jsx,js packages --fix",
|
||||
"test-unit": "TEST_WATCH=1 meteor test-packages ./packages/* --port 3002 --driver-package meteortesting:mocha --raw-logs",
|
||||
"test": "npm run test-unit",
|
||||
"prettier": "node ./.vulcan/prettier/index.js write-changed",
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import { ApolloProvider } from 'react-apollo';
|
||||
import { runCallbacks } from '../../modules'
|
||||
import { runCallbacks } from '../../modules';
|
||||
|
||||
import { Components } from 'meteor/vulcan:lib'
|
||||
import { Components } from 'meteor/vulcan:lib';
|
||||
import { CookiesProvider } from 'react-cookie';
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
|
||||
const AppGenerator = ({ apolloClient }) => {
|
||||
const App = (
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { onPageLoad } from 'meteor/server-render';
|
||||
import AppGenerator from './components/AppGenerator'
|
||||
import AppGenerator from './components/AppGenerator';
|
||||
|
||||
import {
|
||||
createApolloClient,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {addCallback, getActions} from 'meteor/vulcan:lib';
|
||||
import { addCallback, getActions } from 'meteor/vulcan:lib';
|
||||
|
||||
/*
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ const RouteWithLayout = ({ layoutName, component, currentRoute, ...rest }) => {
|
|||
return React.createElement(layout, layoutProps, <ErrorCatcher>{React.createElement(component, childComponentProps)}</ErrorCatcher>);
|
||||
}}
|
||||
/>
|
||||
)};
|
||||
);};
|
||||
|
||||
class App extends PureComponent {
|
||||
constructor(props) {
|
||||
|
|
|
@ -6,11 +6,8 @@ HoC that provides access to flash messages stored in Redux state and actions to
|
|||
of the apollo-link-state mutation patterns
|
||||
*/
|
||||
|
||||
import {
|
||||
registerStateLinkMutation,
|
||||
registerStateLinkDefault,
|
||||
} from 'meteor/vulcan:lib';
|
||||
import {graphql, compose} from 'react-apollo';
|
||||
import { registerStateLinkMutation, registerStateLinkDefault } from 'meteor/vulcan:lib';
|
||||
import { graphql, compose } from 'react-apollo';
|
||||
import gql from 'graphql-tag';
|
||||
// 1. Define the queries
|
||||
// the @client tag tells graphQL that we fetch data from the cache
|
||||
|
@ -53,13 +50,12 @@ registerStateLinkMutation({
|
|||
name: 'flashMessagesFlash',
|
||||
mutation: (obj, args, context, info) => {
|
||||
// get relevant values from args
|
||||
const {cache} = context;
|
||||
const {content} = args;
|
||||
const { cache } = context;
|
||||
const { content } = args;
|
||||
// retrieve current state
|
||||
const currentFlashMessages = cache.readData({query: getMessagesQuery});
|
||||
const currentFlashMessages = cache.readData({ query: getMessagesQuery });
|
||||
// transform content
|
||||
const flashType =
|
||||
content && typeof content.type !== 'undefined' ? content.type : 'error';
|
||||
const flashType = content && typeof content.type !== 'undefined' ? content.type : 'error';
|
||||
const _id = currentFlashMessages.length;
|
||||
const flashMessage = {
|
||||
_id,
|
||||
|
@ -75,50 +71,50 @@ registerStateLinkMutation({
|
|||
const data = {
|
||||
flashMessages: [...currentFlashMessages, flashMessage],
|
||||
};
|
||||
cache.writeData({data});
|
||||
cache.writeData({ data });
|
||||
return null;
|
||||
},
|
||||
});
|
||||
registerStateLinkMutation({
|
||||
name: 'flashMessagesMarkAsSeen',
|
||||
mutation: (obj, args, context) => {
|
||||
const {cache} = context;
|
||||
const {i} = args;
|
||||
const currentFlashMessages = cache.readData({query: getMessagesQuery});
|
||||
currentFlashMessages[i] = {...currentFlashMessages[i], seen: true};
|
||||
const { cache } = context;
|
||||
const { i } = args;
|
||||
const currentFlashMessages = cache.readData({ query: getMessagesQuery });
|
||||
currentFlashMessages[i] = { ...currentFlashMessages[i], seen: true };
|
||||
const data = {
|
||||
flashMessages: currentFlashMessages,
|
||||
};
|
||||
cache.writeData({data});
|
||||
cache.writeData({ data });
|
||||
return null;
|
||||
},
|
||||
});
|
||||
registerStateLinkMutation({
|
||||
name: 'flashMessagesClear',
|
||||
mutation: (obj, args, context) => {
|
||||
const {cache} = context;
|
||||
const {i} = args;
|
||||
const currentFlashMessages = cache.readData({query: getMessagesQuery});
|
||||
currentFlashMessages[i] = {...currentFlashMessages[i], show: false};
|
||||
const { cache } = context;
|
||||
const { i } = args;
|
||||
const currentFlashMessages = cache.readData({ query: getMessagesQuery });
|
||||
currentFlashMessages[i] = { ...currentFlashMessages[i], show: false };
|
||||
const data = {
|
||||
flashMessages: currentFlashMessages,
|
||||
};
|
||||
cache.writeData({data});
|
||||
cache.writeData({ data });
|
||||
return null;
|
||||
},
|
||||
});
|
||||
registerStateLinkMutation({
|
||||
name: 'flashMessagesClearSeen',
|
||||
mutation: (obj, args, context) => {
|
||||
const {cache} = context;
|
||||
const currentFlashMessages = cache.readData({query: getMessagesQuery});
|
||||
const { cache } = context;
|
||||
const currentFlashMessages = cache.readData({ query: getMessagesQuery });
|
||||
const newValue = currentFlashMessages.map(message =>
|
||||
message.seen ? {...message, show: false} : message
|
||||
message.seen ? { ...message, show: false } : message
|
||||
);
|
||||
const data = {
|
||||
flashMessages: newValue,
|
||||
};
|
||||
cache.writeData({data});
|
||||
cache.writeData({ data });
|
||||
return null;
|
||||
},
|
||||
});
|
||||
|
@ -140,9 +136,9 @@ const withMessages = compose(
|
|||
|
||||
// equivalent to mapStateToProps (map the graphql query to the component props)
|
||||
graphql(getMessagesQuery, {
|
||||
props: ({ownProps, data /*: { flashMessages }*/}) => {
|
||||
const {flashMessages} = data;
|
||||
return {...ownProps, messages: flashMessages};
|
||||
props: ({ ownProps, data /*: { flashMessages }*/ }) => {
|
||||
const { flashMessages } = data;
|
||||
return { ...ownProps, messages: flashMessages };
|
||||
},
|
||||
})
|
||||
);
|
||||
|
|
|
@ -34,7 +34,7 @@ Terms object can have the following properties:
|
|||
|
||||
*/
|
||||
|
||||
import {withApollo, graphql} from 'react-apollo';
|
||||
import { withApollo, graphql } from 'react-apollo';
|
||||
import gql from 'graphql-tag';
|
||||
import update from 'immutability-helper';
|
||||
import {
|
||||
|
@ -63,15 +63,15 @@ export default function withMulti(options) {
|
|||
// see https://github.com/apollographql/apollo-client/issues/1704#issuecomment-322995855
|
||||
pollInterval = typeof window === 'undefined' ? null : pollInterval;
|
||||
|
||||
const {collectionName, collection} = extractCollectionInfo(options);
|
||||
const {fragmentName, fragment} = extractFragmentInfo(options, collectionName);
|
||||
const { collectionName, collection } = extractCollectionInfo(options);
|
||||
const { fragmentName, fragment } = extractFragmentInfo(options, collectionName);
|
||||
|
||||
const typeName = collection.options.typeName;
|
||||
const resolverName = collection.options.multiResolverName;
|
||||
|
||||
// build graphql query from options
|
||||
const query = gql`
|
||||
${multiClientTemplate({typeName, fragmentName, extraQueries})}
|
||||
${multiClientTemplate({ typeName, fragmentName, extraQueries })}
|
||||
${fragment}
|
||||
`;
|
||||
|
||||
|
@ -99,9 +99,9 @@ export default function withMulti(options) {
|
|||
alias: `with${Utils.pluralize(typeName)}`,
|
||||
|
||||
// graphql query options
|
||||
options({terms, paginationTerms, client: apolloClient, currentUser}) {
|
||||
options({ terms, paginationTerms, client: apolloClient, currentUser }) {
|
||||
// get terms from options, then props, then pagination
|
||||
const mergedTerms = {...options.terms, ...terms, ...paginationTerms};
|
||||
const mergedTerms = { ...options.terms, ...terms, ...paginationTerms };
|
||||
|
||||
const graphQLOptions = {
|
||||
variables: {
|
||||
|
@ -121,8 +121,7 @@ export default function withMulti(options) {
|
|||
|
||||
// set to true if running into https://github.com/apollographql/apollo-client/issues/1186
|
||||
if (options.notifyOnNetworkStatusChange) {
|
||||
graphQLOptions.notifyOnNetworkStatusChange =
|
||||
options.notifyOnNetworkStatusChange;
|
||||
graphQLOptions.notifyOnNetworkStatusChange = options.notifyOnNetworkStatusChange;
|
||||
}
|
||||
|
||||
return graphQLOptions;
|
||||
|
@ -133,10 +132,8 @@ export default function withMulti(options) {
|
|||
// see https://github.com/apollographql/apollo-client/blob/master/packages/apollo-client/src/core/networkStatus.ts
|
||||
const refetch = props.data.refetch,
|
||||
// results = Utils.convertDates(collection, props.data[listResolverName]),
|
||||
results =
|
||||
props.data[resolverName] && props.data[resolverName].results,
|
||||
totalCount =
|
||||
props.data[resolverName] && props.data[resolverName].totalCount,
|
||||
results = props.data[resolverName] && props.data[resolverName].results,
|
||||
totalCount = props.data[resolverName] && props.data[resolverName].totalCount,
|
||||
networkStatus = props.data.networkStatus,
|
||||
loadingInitial = props.data.networkStatus === 1,
|
||||
loading = props.data.networkStatus === 1,
|
||||
|
@ -168,11 +165,8 @@ export default function withMulti(options) {
|
|||
const newTerms =
|
||||
typeof providedTerms === 'undefined'
|
||||
? {
|
||||
/*...props.ownProps.terms,*/ ...props.ownProps
|
||||
.paginationTerms,
|
||||
limit:
|
||||
results.length +
|
||||
props.ownProps.paginationTerms.itemsPerPage,
|
||||
/*...props.ownProps.terms,*/ ...props.ownProps.paginationTerms,
|
||||
limit: results.length + props.ownProps.paginationTerms.itemsPerPage,
|
||||
}
|
||||
: providedTerms;
|
||||
|
||||
|
@ -193,8 +187,8 @@ export default function withMulti(options) {
|
|||
: providedTerms;
|
||||
|
||||
return props.data.fetchMore({
|
||||
variables: {input: {terms: newTerms}}, // ??? not sure about 'terms: newTerms'
|
||||
updateQuery(previousResults, {fetchMoreResult}) {
|
||||
variables: { input: { terms: newTerms } }, // ??? not sure about 'terms: newTerms'
|
||||
updateQuery(previousResults, { fetchMoreResult }) {
|
||||
// no more post to fetch
|
||||
if (!fetchMoreResult.data) {
|
||||
return previousResults;
|
||||
|
@ -205,7 +199,7 @@ export default function withMulti(options) {
|
|||
...fetchMoreResult.data[resolverName],
|
||||
];
|
||||
// return the previous results "augmented" with more
|
||||
return {...previousResults, ...newResults};
|
||||
return { ...previousResults, ...newResults };
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
@ -25,7 +25,7 @@ import {
|
|||
import Users from 'meteor/vulcan:users';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
|
||||
const defaultOptions = {create: true, update: true, upsert: true, delete: true};
|
||||
const defaultOptions = { create: true, update: true, upsert: true, delete: true };
|
||||
|
||||
export function getDefaultMutations(options) {
|
||||
let typeName, collectionName, mutationOptions;
|
||||
|
@ -34,12 +34,12 @@ export function getDefaultMutations(options) {
|
|||
// new single-argument API
|
||||
typeName = arguments[0].typeName;
|
||||
collectionName = arguments[0].collectionName || getCollectionName(typeName);
|
||||
mutationOptions = {...defaultOptions, ...arguments[0].options};
|
||||
mutationOptions = { ...defaultOptions, ...arguments[0].options };
|
||||
} else {
|
||||
// OpenCRUD backwards compatibility
|
||||
collectionName = arguments[0];
|
||||
typeName = getTypeName(collectionName);
|
||||
mutationOptions = {...defaultOptions, ...arguments[1]};
|
||||
mutationOptions = { ...defaultOptions, ...arguments[1] };
|
||||
}
|
||||
|
||||
// register callbacks for documentation purposes
|
||||
|
@ -72,7 +72,7 @@ export function getDefaultMutations(options) {
|
|||
]);
|
||||
},
|
||||
|
||||
async mutation(root, {data}, context) {
|
||||
async mutation(root, { data }, context) {
|
||||
const collection = context[collectionName];
|
||||
|
||||
// check if current user can pass check function; else throw error
|
||||
|
@ -105,44 +105,34 @@ export function getDefaultMutations(options) {
|
|||
|
||||
*/
|
||||
if (Meteor.isClient) {
|
||||
registerWatchedMutation(
|
||||
mutationName,
|
||||
multiQueryName,
|
||||
({mutation, query}) => {
|
||||
// get mongo selector and options objects based on current terms
|
||||
const terms = query.variables.input.terms;
|
||||
const collection = Collections.find(c => c.typeName === typeName);
|
||||
const parameters = collection.getParameters(terms /* apolloClient */);
|
||||
const {selector, options} = parameters;
|
||||
let results = query.result;
|
||||
const document = mutation.result.data[mutationName].data;
|
||||
registerWatchedMutation(mutationName, multiQueryName, ({ mutation, query }) => {
|
||||
// get mongo selector and options objects based on current terms
|
||||
const terms = query.variables.input.terms;
|
||||
const collection = Collections.find(c => c.typeName === typeName);
|
||||
const parameters = collection.getParameters(terms /* apolloClient */);
|
||||
const { selector, options } = parameters;
|
||||
let results = query.result;
|
||||
const document = mutation.result.data[mutationName].data;
|
||||
|
||||
if (belongsToSet(document, selector)) {
|
||||
if (!isInSet(results[multiResolverName], document)) {
|
||||
// make sure document hasn't been already added as this may be called several times
|
||||
results[multiResolverName] = addToSet(
|
||||
results[multiResolverName],
|
||||
document
|
||||
);
|
||||
}
|
||||
results[multiResolverName] = reorderSet(
|
||||
results[multiResolverName],
|
||||
options.sort
|
||||
);
|
||||
if (belongsToSet(document, selector)) {
|
||||
if (!isInSet(results[multiResolverName], document)) {
|
||||
// make sure document hasn't been already added as this may be called several times
|
||||
results[multiResolverName] = addToSet(results[multiResolverName], document);
|
||||
}
|
||||
|
||||
results[multiResolverName].__typename = `Multi${typeName}Output`;
|
||||
|
||||
// console.log('// create');
|
||||
// console.log(mutation);
|
||||
// console.log(query);
|
||||
// console.log(collection);
|
||||
// console.log(parameters);
|
||||
// console.log(results);
|
||||
|
||||
return results;
|
||||
results[multiResolverName] = reorderSet(results[multiResolverName], options.sort);
|
||||
}
|
||||
);
|
||||
|
||||
results[multiResolverName].__typename = `Multi${typeName}Output`;
|
||||
|
||||
// console.log('// create');
|
||||
// console.log(mutation);
|
||||
// console.log(query);
|
||||
// console.log(collection);
|
||||
// console.log(parameters);
|
||||
// console.log(results);
|
||||
|
||||
return results;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,7 +168,7 @@ export function getDefaultMutations(options) {
|
|||
]);
|
||||
},
|
||||
|
||||
async mutation(root, {selector, data}, context) {
|
||||
async mutation(root, { selector, data }, context) {
|
||||
const collection = context[collectionName];
|
||||
|
||||
if (isEmpty(selector)) {
|
||||
|
@ -190,9 +180,7 @@ export function getDefaultMutations(options) {
|
|||
|
||||
if (!document) {
|
||||
throw new Error(
|
||||
`Could not find document to update for selector: ${JSON.stringify(
|
||||
selector
|
||||
)}`
|
||||
`Could not find document to update for selector: ${JSON.stringify(selector)}`
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -229,57 +217,44 @@ export function getDefaultMutations(options) {
|
|||
|
||||
*/
|
||||
if (Meteor.isClient) {
|
||||
registerWatchedMutation(
|
||||
mutationName,
|
||||
multiQueryName,
|
||||
({mutation, query}) => {
|
||||
// get mongo selector and options objects based on current terms
|
||||
const terms = query.variables.input.terms;
|
||||
const collection = Collections.find(c => c.typeName === typeName);
|
||||
const parameters = collection.getParameters(terms /* apolloClient */);
|
||||
const {selector, options} = parameters;
|
||||
let results = query.result;
|
||||
const document = mutation.result.data[mutationName].data;
|
||||
registerWatchedMutation(mutationName, multiQueryName, ({ mutation, query }) => {
|
||||
// get mongo selector and options objects based on current terms
|
||||
const terms = query.variables.input.terms;
|
||||
const collection = Collections.find(c => c.typeName === typeName);
|
||||
const parameters = collection.getParameters(terms /* apolloClient */);
|
||||
const { selector, options } = parameters;
|
||||
let results = query.result;
|
||||
const document = mutation.result.data[mutationName].data;
|
||||
|
||||
if (belongsToSet(document, selector)) {
|
||||
// edited document belongs to the list
|
||||
if (!isInSet(results[multiResolverName], document)) {
|
||||
// if document wasn't already in list, add it
|
||||
results[multiResolverName] = addToSet(
|
||||
results[multiResolverName],
|
||||
document
|
||||
);
|
||||
} else {
|
||||
// if document was already in the list, update it
|
||||
results[multiResolverName] = updateInSet(
|
||||
results[multiResolverName],
|
||||
document
|
||||
);
|
||||
}
|
||||
results[multiResolverName] = reorderSet(
|
||||
results[multiResolverName],
|
||||
options.sort,
|
||||
selector
|
||||
);
|
||||
if (belongsToSet(document, selector)) {
|
||||
// edited document belongs to the list
|
||||
if (!isInSet(results[multiResolverName], document)) {
|
||||
// if document wasn't already in list, add it
|
||||
results[multiResolverName] = addToSet(results[multiResolverName], document);
|
||||
} else {
|
||||
// if edited doesn't belong to current list anymore (based on view selector), remove it
|
||||
results[multiResolverName] = removeFromSet(
|
||||
results[multiResolverName],
|
||||
document
|
||||
);
|
||||
// if document was already in the list, update it
|
||||
results[multiResolverName] = updateInSet(results[multiResolverName], document);
|
||||
}
|
||||
|
||||
results[multiResolverName].__typename = `Multi${typeName}Output`;
|
||||
|
||||
// console.log('// update');
|
||||
// console.log(mutation);
|
||||
// console.log(query);
|
||||
// console.log(parameters);
|
||||
// console.log(results);
|
||||
|
||||
return results;
|
||||
results[multiResolverName] = reorderSet(
|
||||
results[multiResolverName],
|
||||
options.sort,
|
||||
selector
|
||||
);
|
||||
} else {
|
||||
// if edited doesn't belong to current list anymore (based on view selector), remove it
|
||||
results[multiResolverName] = removeFromSet(results[multiResolverName], document);
|
||||
}
|
||||
);
|
||||
|
||||
results[multiResolverName].__typename = `Multi${typeName}Output`;
|
||||
|
||||
// console.log('// update');
|
||||
// console.log(mutation);
|
||||
// console.log(query);
|
||||
// console.log(parameters);
|
||||
// console.log(results);
|
||||
|
||||
return results;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (mutationOptions.upsert) {
|
||||
|
@ -287,26 +262,22 @@ export function getDefaultMutations(options) {
|
|||
mutations.upsert = {
|
||||
description: `Mutation for upserting a ${typeName} document`,
|
||||
|
||||
async mutation(root, {selector, data}, context) {
|
||||
async mutation(root, { selector, data }, context) {
|
||||
const collection = context[collectionName];
|
||||
|
||||
// check if document exists already
|
||||
const existingDocument = await Connectors.get(collection, selector, {
|
||||
fields: {_id: 1},
|
||||
fields: { _id: 1 },
|
||||
});
|
||||
|
||||
if (existingDocument) {
|
||||
return await collection.options.mutations.update.mutation(
|
||||
root,
|
||||
{selector, data},
|
||||
{ selector, data },
|
||||
context
|
||||
);
|
||||
} else {
|
||||
return await collection.options.mutations.create.mutation(
|
||||
root,
|
||||
{data},
|
||||
context
|
||||
);
|
||||
return await collection.options.mutations.create.mutation(root, { data }, context);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -321,8 +292,7 @@ export function getDefaultMutations(options) {
|
|||
|
||||
check(user, document) {
|
||||
// OpenCRUD backwards compatibility
|
||||
const check =
|
||||
mutationOptions.deleteCheck || mutationOptions.removeCheck;
|
||||
const check = mutationOptions.deleteCheck || mutationOptions.removeCheck;
|
||||
if (check) {
|
||||
return check(user, document);
|
||||
}
|
||||
|
@ -340,7 +310,7 @@ export function getDefaultMutations(options) {
|
|||
]);
|
||||
},
|
||||
|
||||
async mutation(root, {selector}, context) {
|
||||
async mutation(root, { selector }, context) {
|
||||
const collection = context[collectionName];
|
||||
|
||||
if (isEmpty(selector)) {
|
||||
|
@ -351,9 +321,7 @@ export function getDefaultMutations(options) {
|
|||
|
||||
if (!document) {
|
||||
throw new Error(
|
||||
`Could not find document to delete for selector: ${JSON.stringify(
|
||||
selector
|
||||
)}`
|
||||
`Could not find document to delete for selector: ${JSON.stringify(selector)}`
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -388,25 +356,18 @@ export function getDefaultMutations(options) {
|
|||
|
||||
*/
|
||||
if (Meteor.isClient) {
|
||||
registerWatchedMutation(
|
||||
mutationName,
|
||||
multiQueryName,
|
||||
({mutation, query}) => {
|
||||
let results = query.result;
|
||||
const document = mutation.result.data[mutationName].data;
|
||||
results[multiResolverName] = removeFromSet(
|
||||
results[multiResolverName],
|
||||
document
|
||||
);
|
||||
results[multiResolverName].__typename = `Multi${typeName}Output`;
|
||||
// console.log('// delete')
|
||||
// console.log(mutation);
|
||||
// console.log(query);
|
||||
// console.log(parameters);
|
||||
// console.log(results);
|
||||
return results;
|
||||
}
|
||||
);
|
||||
registerWatchedMutation(mutationName, multiQueryName, ({ mutation, query }) => {
|
||||
let results = query.result;
|
||||
const document = mutation.result.data[mutationName].data;
|
||||
results[multiResolverName] = removeFromSet(results[multiResolverName], document);
|
||||
results[multiResolverName].__typename = `Multi${typeName}Output`;
|
||||
// console.log('// delete')
|
||||
// console.log(mutation);
|
||||
// console.log(query);
|
||||
// console.log(parameters);
|
||||
// console.log(results);
|
||||
return results;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -424,7 +385,7 @@ const registerCollectionCallbacks = (typeName, options) => {
|
|||
{ document: 'The document being inserted' },
|
||||
{ currentUser: 'The current user' },
|
||||
{ collection: 'The collection the document belongs to' },
|
||||
{ context: 'The context of the mutation'},
|
||||
{ context: 'The context of the mutation' },
|
||||
],
|
||||
runs: 'sync',
|
||||
returns: 'document',
|
||||
|
@ -433,33 +394,32 @@ const registerCollectionCallbacks = (typeName, options) => {
|
|||
});
|
||||
registerCallback({
|
||||
name: `${typeName}.create.before`,
|
||||
iterator: {document: 'The document being inserted'},
|
||||
properties: [{currentUser: 'The current user'}],
|
||||
iterator: { document: 'The document being inserted' },
|
||||
properties: [{ currentUser: 'The current user' }],
|
||||
runs: 'sync',
|
||||
returns: 'document',
|
||||
description:
|
||||
"Perform operations on a new document before it's inserted in the database.",
|
||||
description: 'Perform operations on a new document before it\'s inserted in the database.',
|
||||
});
|
||||
registerCallback({
|
||||
name: `${typeName}.create.after`,
|
||||
iterator: {document: 'The document being inserted'},
|
||||
properties: [{currentUser: 'The current user'}],
|
||||
iterator: { document: 'The document being inserted' },
|
||||
properties: [{ currentUser: 'The current user' }],
|
||||
runs: 'sync',
|
||||
returns: 'document',
|
||||
description:
|
||||
"Perform operations on a new document after it's inserted in the database but *before* the mutation returns it.",
|
||||
'Perform operations on a new document after it\'s inserted in the database but *before* the mutation returns it.',
|
||||
});
|
||||
registerCallback({
|
||||
name: `${typeName}.create.async`,
|
||||
iterator: {document: 'The document being inserted'},
|
||||
iterator: { document: 'The document being inserted' },
|
||||
properties: [
|
||||
{currentUser: 'The current user'},
|
||||
{collection: 'The collection the document belongs to'},
|
||||
{ currentUser: 'The current user' },
|
||||
{ collection: 'The collection the document belongs to' },
|
||||
],
|
||||
runs: 'async',
|
||||
returns: null,
|
||||
description:
|
||||
"Perform operations on a new document after it's inserted in the database asynchronously.",
|
||||
'Perform operations on a new document after it\'s inserted in the database asynchronously.',
|
||||
});
|
||||
}
|
||||
if (options.update) {
|
||||
|
@ -471,7 +431,7 @@ const registerCollectionCallbacks = (typeName, options) => {
|
|||
{ data: 'The client data' },
|
||||
{ currentUser: 'The current user' },
|
||||
{ collection: 'The collection the document belongs to' },
|
||||
{ context: 'The context of the mutation'},
|
||||
{ context: 'The context of the mutation' },
|
||||
],
|
||||
runs: 'sync',
|
||||
returns: 'modifier',
|
||||
|
@ -480,40 +440,33 @@ const registerCollectionCallbacks = (typeName, options) => {
|
|||
});
|
||||
registerCallback({
|
||||
name: `${typeName}.update.before`,
|
||||
iterator: {data: 'The client data'},
|
||||
properties: [
|
||||
{document: 'The document being edited'},
|
||||
{currentUser: 'The current user'},
|
||||
],
|
||||
iterator: { data: 'The client data' },
|
||||
properties: [{ document: 'The document being edited' }, { currentUser: 'The current user' }],
|
||||
runs: 'sync',
|
||||
returns: 'modifier',
|
||||
description:
|
||||
"Perform operations on a document before it's updated in the database.",
|
||||
description: 'Perform operations on a document before it\'s updated in the database.',
|
||||
});
|
||||
registerCallback({
|
||||
name: `${typeName}.update.after`,
|
||||
iterator: {newDocument: 'The document after the update'},
|
||||
properties: [
|
||||
{document: 'The document being edited'},
|
||||
{currentUser: 'The current user'},
|
||||
],
|
||||
iterator: { newDocument: 'The document after the update' },
|
||||
properties: [{ document: 'The document being edited' }, { currentUser: 'The current user' }],
|
||||
runs: 'sync',
|
||||
returns: 'document',
|
||||
description:
|
||||
"Perform operations on a document after it's updated in the database but *before* the mutation returns it.",
|
||||
'Perform operations on a document after it\'s updated in the database but *before* the mutation returns it.',
|
||||
});
|
||||
registerCallback({
|
||||
name: `${typeName}.update.async`,
|
||||
iterator: {newDocument: 'The document after the edit'},
|
||||
iterator: { newDocument: 'The document after the edit' },
|
||||
properties: [
|
||||
{document: 'The document before the edit'},
|
||||
{currentUser: 'The current user'},
|
||||
{collection: 'The collection the document belongs to'},
|
||||
{ document: 'The document before the edit' },
|
||||
{ currentUser: 'The current user' },
|
||||
{ collection: 'The collection the document belongs to' },
|
||||
],
|
||||
runs: 'async',
|
||||
returns: null,
|
||||
description:
|
||||
"Perform operations on a document after it's updated in the database asynchronously.",
|
||||
'Perform operations on a document after it\'s updated in the database asynchronously.',
|
||||
});
|
||||
}
|
||||
if (options.delete) {
|
||||
|
@ -523,8 +476,8 @@ const registerCollectionCallbacks = (typeName, options) => {
|
|||
properties: [
|
||||
{ currentUser: 'The current user' },
|
||||
{ document: 'The document being removed' },
|
||||
{ collection: 'The collection the document belongs to'},
|
||||
{ context: 'The context of this mutation'}
|
||||
{ collection: 'The collection the document belongs to' },
|
||||
{ context: 'The context of this mutation' },
|
||||
],
|
||||
runs: 'sync',
|
||||
returns: 'document',
|
||||
|
@ -533,24 +486,23 @@ const registerCollectionCallbacks = (typeName, options) => {
|
|||
});
|
||||
registerCallback({
|
||||
name: `${typeName}.delete.before`,
|
||||
iterator: {document: 'The document being removed'},
|
||||
properties: [{currentUser: 'The current user'}],
|
||||
iterator: { document: 'The document being removed' },
|
||||
properties: [{ currentUser: 'The current user' }],
|
||||
runs: 'sync',
|
||||
returns: null,
|
||||
description:
|
||||
"Perform operations on a document before it's removed from the database.",
|
||||
description: 'Perform operations on a document before it\'s removed from the database.',
|
||||
});
|
||||
registerCallback({
|
||||
name: `${typeName}.delete.async`,
|
||||
properties: [
|
||||
{document: 'The document being removed'},
|
||||
{currentUser: 'The current user'},
|
||||
{collection: 'The collection the document belongs to'},
|
||||
{ document: 'The document being removed' },
|
||||
{ currentUser: 'The current user' },
|
||||
{ collection: 'The collection the document belongs to' },
|
||||
],
|
||||
runs: 'async',
|
||||
returns: null,
|
||||
description:
|
||||
"Perform operations on a document after it's removed from the database asynchronously.",
|
||||
'Perform operations on a document after it\'s removed from the database asynchronously.',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -26,12 +26,12 @@ export function getDefaultResolvers(options) {
|
|||
// new single-argument API
|
||||
typeName = arguments[0].typeName;
|
||||
collectionName = arguments[0].collectionName || getCollectionName(typeName);
|
||||
resolverOptions = {...defaultOptions, ...arguments[0].options};
|
||||
resolverOptions = { ...defaultOptions, ...arguments[0].options };
|
||||
} else {
|
||||
// OpenCRUD backwards compatibility
|
||||
collectionName = arguments[0];
|
||||
typeName = getTypeName(collectionName);
|
||||
resolverOptions = {...defaultOptions, ...arguments[1]};
|
||||
resolverOptions = { ...defaultOptions, ...arguments[1] };
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -40,8 +40,8 @@ export function getDefaultResolvers(options) {
|
|||
multi: {
|
||||
description: `A list of ${typeName} documents matching a set of query terms`,
|
||||
|
||||
async resolver(root, {input = {}}, context, {cacheControl}) {
|
||||
const {terms = {}, enableCache = false, enableTotal = true} = input;
|
||||
async resolver(root, { input = {} }, context, { cacheControl }) {
|
||||
const { terms = {}, enableCache = false, enableTotal = true } = input;
|
||||
|
||||
debug('');
|
||||
debugGroup(
|
||||
|
@ -51,27 +51,22 @@ export function getDefaultResolvers(options) {
|
|||
debug(`Terms: ${JSON.stringify(terms)}`);
|
||||
|
||||
if (cacheControl && enableCache) {
|
||||
const maxAge =
|
||||
resolverOptions.cacheMaxAge || defaultOptions.cacheMaxAge;
|
||||
cacheControl.setCacheHint({maxAge});
|
||||
const maxAge = resolverOptions.cacheMaxAge || defaultOptions.cacheMaxAge;
|
||||
cacheControl.setCacheHint({ maxAge });
|
||||
}
|
||||
|
||||
// get currentUser and Users collection from context
|
||||
const {currentUser, Users} = context;
|
||||
const { currentUser, Users } = context;
|
||||
|
||||
// get collection based on collectionName argument
|
||||
const collection = context[collectionName];
|
||||
|
||||
// get selector and options from terms and perform Mongo query
|
||||
|
||||
let {selector, options} = await collection.getParameters(
|
||||
terms,
|
||||
{},
|
||||
context
|
||||
);
|
||||
let { selector, options } = await collection.getParameters(terms, {}, context);
|
||||
options.skip = terms.offset;
|
||||
|
||||
debug({selector, options});
|
||||
debug({ selector, options });
|
||||
|
||||
const docs = await Connectors.find(collection, selector, options);
|
||||
|
||||
|
@ -81,23 +76,17 @@ export function getDefaultResolvers(options) {
|
|||
: docs;
|
||||
|
||||
// take the remaining documents and remove any fields that shouldn't be accessible
|
||||
const restrictedDocs = Users.restrictViewableFields(
|
||||
currentUser,
|
||||
collection,
|
||||
viewableDocs
|
||||
);
|
||||
const restrictedDocs = Users.restrictViewableFields(currentUser, collection, viewableDocs);
|
||||
|
||||
// prime the cache
|
||||
restrictedDocs.forEach(doc => collection.loader.prime(doc._id, doc));
|
||||
|
||||
debug(`\x1b[33m=> ${restrictedDocs.length} documents returned\x1b[0m`);
|
||||
debugGroupEnd();
|
||||
debug(
|
||||
`--------------- end \x1b[35m${typeName} Multi Resolver\x1b[0m ---------------`
|
||||
);
|
||||
debug(`--------------- end \x1b[35m${typeName} Multi Resolver\x1b[0m ---------------`);
|
||||
debug('');
|
||||
|
||||
const data = {results: restrictedDocs};
|
||||
const data = { results: restrictedDocs };
|
||||
|
||||
if (enableTotal) {
|
||||
// get total count of documents matching the selector
|
||||
|
@ -114,8 +103,8 @@ export function getDefaultResolvers(options) {
|
|||
single: {
|
||||
description: `A single ${typeName} document fetched by ID or slug`,
|
||||
|
||||
async resolver(root, {input = {}}, context, {cacheControl}) {
|
||||
const {selector = {}, enableCache = false, allowNull = false} = input;
|
||||
async resolver(root, { input = {} }, context, { cacheControl }) {
|
||||
const { selector = {}, enableCache = false, allowNull = false } = input;
|
||||
|
||||
debug('');
|
||||
debugGroup(
|
||||
|
@ -125,12 +114,11 @@ export function getDefaultResolvers(options) {
|
|||
debug(`Selector: ${JSON.stringify(selector)}`);
|
||||
|
||||
if (cacheControl && enableCache) {
|
||||
const maxAge =
|
||||
resolverOptions.cacheMaxAge || defaultOptions.cacheMaxAge;
|
||||
cacheControl.setCacheHint({maxAge});
|
||||
const maxAge = resolverOptions.cacheMaxAge || defaultOptions.cacheMaxAge;
|
||||
cacheControl.setCacheHint({ maxAge });
|
||||
}
|
||||
|
||||
const {currentUser, Users} = context;
|
||||
const { currentUser, Users } = context;
|
||||
const collection = context[collectionName];
|
||||
|
||||
// use Dataloader if doc is selected by documentId/_id
|
||||
|
@ -141,11 +129,11 @@ export function getDefaultResolvers(options) {
|
|||
|
||||
if (!doc) {
|
||||
if (allowNull) {
|
||||
return {result: null};
|
||||
return { result: null };
|
||||
} else {
|
||||
throwError({
|
||||
id: 'app.missing_document',
|
||||
data: {documentId, selector},
|
||||
data: { documentId, selector },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -164,20 +152,14 @@ export function getDefaultResolvers(options) {
|
|||
);
|
||||
}
|
||||
|
||||
const restrictedDoc = Users.restrictViewableFields(
|
||||
currentUser,
|
||||
collection,
|
||||
doc
|
||||
);
|
||||
const restrictedDoc = Users.restrictViewableFields(currentUser, collection, doc);
|
||||
|
||||
debugGroupEnd();
|
||||
debug(
|
||||
`--------------- end \x1b[35m${typeName} Single Resolver\x1b[0m ---------------`
|
||||
);
|
||||
debug(`--------------- end \x1b[35m${typeName} Single Resolver\x1b[0m ---------------`);
|
||||
debug('');
|
||||
|
||||
// filter out disallowed properties and return resulting document
|
||||
return {result: restrictedDoc};
|
||||
return { result: restrictedDoc };
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -39,11 +39,11 @@ export { default as withMutation } from './containers/withMutation.js';
|
|||
export { default as withUpsert } from './containers/withUpsert.js';
|
||||
export { default as withSiteData } from './containers/withSiteData.js';
|
||||
|
||||
export {default as withComponents} from './containers/withComponents';
|
||||
export { default as withComponents } from './containers/withComponents';
|
||||
|
||||
// OpenCRUD backwards compatibility
|
||||
export {default as withNew} from './containers/withCreate.js';
|
||||
export {default as withEdit} from './containers/withUpdate.js';
|
||||
export {default as withRemove} from './containers/withDelete.js';
|
||||
export {default as withList} from './containers/withMulti.js';
|
||||
export {default as withDocument} from './containers/withSingle.js';
|
||||
export { default as withNew } from './containers/withCreate.js';
|
||||
export { default as withEdit } from './containers/withUpdate.js';
|
||||
export { default as withRemove } from './containers/withDelete.js';
|
||||
export { default as withList } from './containers/withMulti.js';
|
||||
export { default as withDocument } from './containers/withSingle.js';
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
import 'jsdom-global/register';
|
||||
import React from 'react';
|
||||
import expect from 'expect';
|
||||
import {shallow} from 'enzyme';
|
||||
import {Components} from 'meteor/vulcan:core';
|
||||
import {initComponentTest} from 'meteor/vulcan:test';
|
||||
import {withComponents} from '../lib/modules';
|
||||
import { shallow } from 'enzyme';
|
||||
import { Components } from 'meteor/vulcan:core';
|
||||
import { initComponentTest } from 'meteor/vulcan:test';
|
||||
import { withComponents } from '../lib/modules';
|
||||
|
||||
// we must import all the other components, so that "registerComponent" is called
|
||||
import '../lib/modules';
|
||||
|
@ -18,60 +18,51 @@ describe('vulcan-core/containers', function() {
|
|||
// replace any component for testing purpose
|
||||
const firstComponentName = Components[Object.keys(Components)[0]];
|
||||
const FooComponent = () => 'FOO';
|
||||
const components = {[firstComponentName]: FooComponent};
|
||||
const MyComponent = withComponents(({Components}) =>
|
||||
Components[firstComponentName]()
|
||||
);
|
||||
const components = { [firstComponentName]: FooComponent };
|
||||
const MyComponent = withComponents(({ Components }) => Components[firstComponentName]());
|
||||
const wrapper = shallow(<MyComponent components={components} />);
|
||||
expect(wrapper.prop('Components')).toBeDefined();
|
||||
expect(wrapper.prop('Components')[firstComponentName]).toEqual(
|
||||
FooComponent
|
||||
);
|
||||
expect(wrapper.prop('Components')[firstComponentName]).toEqual(FooComponent);
|
||||
expect(wrapper.html()).toEqual('FOO');
|
||||
});
|
||||
});
|
||||
describe('handleOptions', function() {
|
||||
const expectedCollectionName = 'COLLECTION_NAME';
|
||||
const collectionNameOptions = {collectionName: expectedCollectionName};
|
||||
const expectedCollection = {options: collectionNameOptions};
|
||||
const collectionNameOptions = { collectionName: expectedCollectionName };
|
||||
const expectedCollection = { options: collectionNameOptions };
|
||||
it('get collectionName from collection', function() {
|
||||
const options = {collection: expectedCollection};
|
||||
const {collection, collectionName} = extractCollectionInfo(options);
|
||||
const options = { collection: expectedCollection };
|
||||
const { collection, collectionName } = extractCollectionInfo(options);
|
||||
expect(collection).toEqual(expectedCollection);
|
||||
expect(collectionName).toEqual(expectedCollectionName);
|
||||
});
|
||||
it('get collection from collectioName', function() {
|
||||
// MOCK getCollection
|
||||
const {collection, collectionName} = extractCollectionInfo(
|
||||
collectionNameOptions
|
||||
);
|
||||
const { collection, collectionName } = extractCollectionInfo(collectionNameOptions);
|
||||
expect(collection).toEqual(expectedCollection);
|
||||
expect(collectionName).toEqual(expectedCollectionName);
|
||||
});
|
||||
const expectedFragmentName = 'FRAGMENT_NAME';
|
||||
const expectedFragment = {
|
||||
definitions: [{name: {value: expectedFragmentName}}],
|
||||
definitions: [{ name: { value: expectedFragmentName } }],
|
||||
};
|
||||
it('get fragment from fragmentName', function() {
|
||||
// MOCK getCollection
|
||||
const options = {fragmentName: expectedFragmentName};
|
||||
const {fragment, fragmentName} = extractFragmentInfo(options);
|
||||
const options = { fragmentName: expectedFragmentName };
|
||||
const { fragment, fragmentName } = extractFragmentInfo(options);
|
||||
expect(fragment).toEqual(expectedFragment);
|
||||
expect(fragmentName).toEqual(expectedFragmentName);
|
||||
});
|
||||
it('get fragmentName from fragment', function() {
|
||||
const options = {fragment: expectedFragment};
|
||||
const {fragment, fragmentName} = extractFragmentInfo(options);
|
||||
const options = { fragment: expectedFragment };
|
||||
const { fragment, fragmentName } = extractFragmentInfo(options);
|
||||
expect(fragment).toEqual(expectedFragment);
|
||||
expect(fragmentName).toEqual(expectedFragmentName);
|
||||
});
|
||||
it('get fragmentName and fragment from collectionName', function() {
|
||||
// if options does not contain fragment, we get the collection default fragment based on its name
|
||||
const options = {};
|
||||
const {fragment, fragmentName} = extractFragmentInfo(
|
||||
options,
|
||||
expectedCollectionName
|
||||
);
|
||||
const { fragment, fragmentName } = extractFragmentInfo(options, expectedCollectionName);
|
||||
expect(fragment).toEqual(expectedFragment);
|
||||
expect(fragmentName).toEqual(expectedFragmentName);
|
||||
});
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
import {addTrackFunction} from 'meteor/vulcan:events';
|
||||
import {
|
||||
getApolloClient,
|
||||
getFragment,
|
||||
createClientTemplate,
|
||||
} from 'meteor/vulcan:lib';
|
||||
import { addTrackFunction } from 'meteor/vulcan:events';
|
||||
import { getApolloClient, getFragment, createClientTemplate } from 'meteor/vulcan:lib';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
function trackInternal(eventName, eventProperties) {
|
||||
|
@ -13,7 +9,7 @@ function trackInternal(eventName, eventProperties) {
|
|||
const fragment = getFragment(fragmentName);
|
||||
|
||||
const mutation = gql`
|
||||
${createClientTemplate({typeName: 'AnalyticsEvent', fragmentName})}
|
||||
${createClientTemplate({ typeName: 'AnalyticsEvent', fragmentName })}
|
||||
${fragment}
|
||||
`;
|
||||
|
||||
|
@ -23,7 +19,7 @@ function trackInternal(eventName, eventProperties) {
|
|||
properties: eventProperties,
|
||||
},
|
||||
};
|
||||
apolloClient.mutate({mutation, variables});
|
||||
apolloClient.mutate({ mutation, variables });
|
||||
}
|
||||
|
||||
addTrackFunction(trackInternal);
|
||||
|
|
|
@ -1,23 +1,18 @@
|
|||
import {ApolloClient} from 'apollo-client';
|
||||
import {ApolloLink} from 'apollo-link';
|
||||
import { ApolloClient } from 'apollo-client';
|
||||
import { ApolloLink } from 'apollo-link';
|
||||
import watchedMutationLink from './links/watchedMutation';
|
||||
import httpLink from './links/http';
|
||||
import meteorAccountsLink from './links/meteor';
|
||||
import errorLink from './links/error';
|
||||
import {createStateLink} from '../../modules/apollo-common';
|
||||
import { createStateLink } from '../../modules/apollo-common';
|
||||
import cache from './cache';
|
||||
|
||||
// these links do not change once created
|
||||
const staticLinks = [
|
||||
watchedMutationLink,
|
||||
errorLink,
|
||||
meteorAccountsLink,
|
||||
httpLink,
|
||||
];
|
||||
const staticLinks = [watchedMutationLink, errorLink, meteorAccountsLink, httpLink];
|
||||
|
||||
let apolloClient;
|
||||
export const createApolloClient = () => {
|
||||
const stateLink = createStateLink({cache});
|
||||
const stateLink = createStateLink({ cache });
|
||||
const newClient = new ApolloClient({
|
||||
link: ApolloLink.from([stateLink, ...staticLinks]),
|
||||
cache,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {InMemoryCache} from 'apollo-cache-inmemory';
|
||||
import { InMemoryCache } from 'apollo-cache-inmemory';
|
||||
|
||||
const cache = new InMemoryCache()
|
||||
//ssr
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import {onError} from 'apollo-link-error';
|
||||
import { onError } from 'apollo-link-error';
|
||||
|
||||
const errorLink = onError(({graphQLErrors, networkError}) => {
|
||||
const errorLink = onError(({ graphQLErrors, networkError }) => {
|
||||
if (graphQLErrors)
|
||||
graphQLErrors.map(({message, locations, path}) =>
|
||||
console.log(
|
||||
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
|
||||
)
|
||||
graphQLErrors.map(({ message, locations, path }) =>
|
||||
console.log(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`)
|
||||
);
|
||||
if (networkError) console.log(`[Network error]: ${networkError}`);
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {HttpLink} from 'apollo-link-http';
|
||||
import { HttpLink } from 'apollo-link-http';
|
||||
|
||||
const httpLink = new HttpLink({
|
||||
uri: '/graphql',
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {MeteorAccountsLink} from 'meteor/apollo';
|
||||
import { MeteorAccountsLink } from 'meteor/apollo';
|
||||
|
||||
const meteorAccountsLink = new MeteorAccountsLink();
|
||||
export default meteorAccountsLink;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* @see https://github.com/haytko/apollo-link-watched-mutation
|
||||
*/
|
||||
import WatchedMutationLink from 'apollo-link-watched-mutation';
|
||||
import {WatchedMutations} from '../updates';
|
||||
import { WatchedMutations } from '../updates';
|
||||
import cache from '../cache';
|
||||
const watchedMutationLink = new WatchedMutationLink(cache, WatchedMutations);
|
||||
|
||||
|
|
|
@ -5,11 +5,7 @@ import Mingo from 'mingo';
|
|||
|
||||
export const WatchedMutations = {};
|
||||
|
||||
export const registerWatchedMutation = (
|
||||
mutationName,
|
||||
queryName,
|
||||
updateFunction
|
||||
) => {
|
||||
export const registerWatchedMutation = (mutationName, queryName, updateFunction) => {
|
||||
WatchedMutations[mutationName] = {
|
||||
[queryName]: updateFunction,
|
||||
};
|
||||
|
@ -30,8 +26,7 @@ export const belongsToSet = (document, selector) => {
|
|||
Test if a document is already in a result set
|
||||
|
||||
*/
|
||||
export const isInSet = (data, document) =>
|
||||
data.results.find(item => item._id === document._id);
|
||||
export const isInSet = (data, document) => data.results.find(item => item._id === document._id);
|
||||
|
||||
/*
|
||||
|
||||
|
@ -53,9 +48,9 @@ Update a document in a set of results
|
|||
*/
|
||||
export const updateInSet = (queryData, document) => {
|
||||
const oldDocument = queryData.results.find(item => item._id === document._id);
|
||||
const newDocument = {...oldDocument, ...document};
|
||||
const newDocument = { ...oldDocument, ...document };
|
||||
const index = queryData.results.findIndex(item => item._id === document._id);
|
||||
const newData = {results: [...queryData.results]}; // clone
|
||||
const newData = { results: [...queryData.results] }; // clone
|
||||
newData.results[index] = newDocument;
|
||||
return newData;
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
import Cookies from 'universal-cookie';
|
||||
|
||||
import {Meteor} from 'meteor/meteor';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
const cookie = new Cookies();
|
||||
|
||||
|
@ -26,9 +26,7 @@ function setToken(loginToken, expires) {
|
|||
|
||||
function initToken() {
|
||||
const loginToken = global.localStorage['Meteor.loginToken'];
|
||||
const loginTokenExpires = new Date(
|
||||
global.localStorage['Meteor.loginTokenExpires']
|
||||
);
|
||||
const loginTokenExpires = new Date(global.localStorage['Meteor.loginTokenExpires']);
|
||||
|
||||
if (loginToken) {
|
||||
setToken(loginToken, loginTokenExpires);
|
||||
|
|
|
@ -11,17 +11,12 @@
|
|||
* Example
|
||||
* @see https://hackernoon.com/storing-local-state-in-react-with-apollo-link-state-738f6ca45569
|
||||
*/
|
||||
import {withClientState} from 'apollo-link-state';
|
||||
import { withClientState } from 'apollo-link-state';
|
||||
|
||||
/**
|
||||
* Create a state link
|
||||
*/
|
||||
export const createStateLink = ({
|
||||
cache,
|
||||
resolvers,
|
||||
defaults,
|
||||
...otherOptions
|
||||
}) => {
|
||||
export const createStateLink = ({ cache, resolvers, defaults, ...otherOptions }) => {
|
||||
const stateLink = withClientState({
|
||||
cache,
|
||||
defaults: defaults || getStateLinkDefaults(),
|
||||
|
@ -36,11 +31,7 @@ const registeredDefaults = {};
|
|||
/**
|
||||
* Defaults are default response to queries
|
||||
*/
|
||||
export const registerStateLinkDefault = ({
|
||||
name,
|
||||
defaultValue,
|
||||
options = {},
|
||||
}) => {
|
||||
export const registerStateLinkDefault = ({ name, defaultValue, options = {} }) => {
|
||||
registeredDefaults[name] = defaultValue;
|
||||
return registeredDefaults;
|
||||
};
|
||||
|
@ -49,7 +40,7 @@ export const getStateLinkDefaults = () => registeredDefaults;
|
|||
// Mutation are equivalent to a Redux Action + Reducer
|
||||
// except it uses GraphQL to retrieve/update data in the cache
|
||||
const registeredMutations = {};
|
||||
export const registerStateLinkMutation = ({name, mutation, options = {}}) => {
|
||||
export const registerStateLinkMutation = ({ name, mutation, options = {} }) => {
|
||||
registeredMutations[name] = mutation;
|
||||
return registeredMutations;
|
||||
};
|
||||
|
|
|
@ -10,9 +10,9 @@ import deepmerge from 'deepmerge';
|
|||
import GraphQLJSON from 'graphql-type-json';
|
||||
import GraphQLDate from 'graphql-date';
|
||||
import Vulcan from './config.js'; // used for global export
|
||||
import {Utils} from './utils.js';
|
||||
import {disableFragmentWarnings} from 'graphql-tag';
|
||||
import {isIntlField} from './intl.js';
|
||||
import { Utils } from './utils.js';
|
||||
import { disableFragmentWarnings } from 'graphql-tag';
|
||||
import { isIntlField } from './intl.js';
|
||||
import {
|
||||
selectorInputTemplate,
|
||||
mainTypeTemplate,
|
||||
|
@ -44,11 +44,7 @@ const getGraphQLType = (schema, fieldName, isInput = false) => {
|
|||
const field = schema[fieldName];
|
||||
const type = field.type.singleType;
|
||||
const typeName =
|
||||
typeof type === 'object'
|
||||
? 'Object'
|
||||
: typeof type === 'function'
|
||||
? type.name
|
||||
: type;
|
||||
typeof type === 'object' ? 'Object' : typeof type === 'function' ? type.name : type;
|
||||
|
||||
if (field.isIntlData) {
|
||||
return isInput ? '[IntlValueInput]' : '[IntlValue]';
|
||||
|
@ -119,13 +115,13 @@ export const GraphQLSchema = {
|
|||
// queries
|
||||
queries: [],
|
||||
addQuery(query, description) {
|
||||
this.queries.push({query, description});
|
||||
this.queries.push({ query, description });
|
||||
},
|
||||
|
||||
// mutations
|
||||
mutations: [],
|
||||
addMutation(mutation, description) {
|
||||
this.mutations.push({mutation, description});
|
||||
this.mutations.push({ mutation, description });
|
||||
},
|
||||
|
||||
// add resolvers
|
||||
|
@ -182,9 +178,7 @@ export const GraphQLSchema = {
|
|||
) {
|
||||
const fieldDescription = field.description;
|
||||
const fieldDirective = isIntlField(field) ? '@intl' : '';
|
||||
const fieldArguments = isIntlField(field)
|
||||
? [{name: 'locale', type: 'String'}]
|
||||
: [];
|
||||
const fieldArguments = isIntlField(field) ? [{ name: 'locale', type: 'String' }] : [];
|
||||
|
||||
// if field has a resolveAs, push it to schema
|
||||
if (field.resolveAs) {
|
||||
|
@ -208,13 +202,9 @@ export const GraphQLSchema = {
|
|||
const resolver = {
|
||||
[typeName]: {
|
||||
[resolverName]: (document, args, context, info) => {
|
||||
const {Users, currentUser} = context;
|
||||
const { Users, currentUser } = context;
|
||||
// check that current user has permission to access the original non-resolved field
|
||||
const canReadField = Users.canReadField(
|
||||
currentUser,
|
||||
field,
|
||||
document
|
||||
);
|
||||
const canReadField = Users.canReadField(currentUser, field, document);
|
||||
return canReadField
|
||||
? field.resolveAs.resolver(document, args, context, info)
|
||||
: null;
|
||||
|
@ -316,80 +306,61 @@ export const GraphQLSchema = {
|
|||
|
||||
const fields = this.getFields(schema, typeName);
|
||||
|
||||
const {interfaces = [], resolvers, mutations} = collection.options;
|
||||
const { interfaces = [], resolvers, mutations } = collection.options;
|
||||
|
||||
const description = collection.options.description
|
||||
? collection.options.description
|
||||
: `Type for ${collectionName}`;
|
||||
|
||||
const {
|
||||
mainType,
|
||||
create,
|
||||
update,
|
||||
selector,
|
||||
selectorUnique,
|
||||
orderBy,
|
||||
} = fields;
|
||||
const { mainType, create, update, selector, selectorUnique, orderBy } = fields;
|
||||
|
||||
if (mainType.length) {
|
||||
schemaFragments.push(
|
||||
mainTypeTemplate({typeName, description, interfaces, fields: mainType})
|
||||
mainTypeTemplate({ typeName, description, interfaces, fields: mainType })
|
||||
);
|
||||
schemaFragments.push(deleteInputTemplate({typeName}));
|
||||
schemaFragments.push(singleInputTemplate({typeName}));
|
||||
schemaFragments.push(multiInputTemplate({typeName}));
|
||||
schemaFragments.push(singleOutputTemplate({typeName}));
|
||||
schemaFragments.push(multiOutputTemplate({typeName}));
|
||||
schemaFragments.push(mutationOutputTemplate({typeName}));
|
||||
schemaFragments.push(deleteInputTemplate({ typeName }));
|
||||
schemaFragments.push(singleInputTemplate({ typeName }));
|
||||
schemaFragments.push(multiInputTemplate({ typeName }));
|
||||
schemaFragments.push(singleOutputTemplate({ typeName }));
|
||||
schemaFragments.push(multiOutputTemplate({ typeName }));
|
||||
schemaFragments.push(mutationOutputTemplate({ typeName }));
|
||||
|
||||
if (create.length) {
|
||||
schemaFragments.push(createInputTemplate({typeName}));
|
||||
schemaFragments.push(
|
||||
createDataInputTemplate({typeName, fields: create})
|
||||
);
|
||||
schemaFragments.push(createInputTemplate({ typeName }));
|
||||
schemaFragments.push(createDataInputTemplate({ typeName, fields: create }));
|
||||
}
|
||||
|
||||
if (update.length) {
|
||||
schemaFragments.push(updateInputTemplate({typeName}));
|
||||
schemaFragments.push(upsertInputTemplate({typeName}));
|
||||
schemaFragments.push(
|
||||
updateDataInputTemplate({typeName, fields: update})
|
||||
);
|
||||
schemaFragments.push(updateInputTemplate({ typeName }));
|
||||
schemaFragments.push(upsertInputTemplate({ typeName }));
|
||||
schemaFragments.push(updateDataInputTemplate({ typeName, fields: update }));
|
||||
}
|
||||
|
||||
schemaFragments.push(selectorInputTemplate({typeName, fields: selector}));
|
||||
schemaFragments.push(selectorInputTemplate({ typeName, fields: selector }));
|
||||
|
||||
schemaFragments.push(
|
||||
selectorUniqueInputTemplate({typeName, fields: selectorUnique})
|
||||
);
|
||||
schemaFragments.push(selectorUniqueInputTemplate({ typeName, fields: selectorUnique }));
|
||||
|
||||
schemaFragments.push(orderByInputTemplate({typeName, fields: orderBy}));
|
||||
schemaFragments.push(orderByInputTemplate({ typeName, fields: orderBy }));
|
||||
|
||||
if (!_.isEmpty(resolvers)) {
|
||||
const queryResolvers = {};
|
||||
|
||||
// single
|
||||
if (resolvers.single) {
|
||||
addGraphQLQuery(
|
||||
singleQueryTemplate({typeName}),
|
||||
resolvers.single.description
|
||||
addGraphQLQuery(singleQueryTemplate({ typeName }), resolvers.single.description);
|
||||
queryResolvers[Utils.camelCaseify(typeName)] = resolvers.single.resolver.bind(
|
||||
resolvers.single
|
||||
);
|
||||
queryResolvers[
|
||||
Utils.camelCaseify(typeName)
|
||||
] = resolvers.single.resolver.bind(resolvers.single);
|
||||
}
|
||||
|
||||
// multi
|
||||
if (resolvers.multi) {
|
||||
addGraphQLQuery(
|
||||
multiQueryTemplate({typeName}),
|
||||
resolvers.multi.description
|
||||
);
|
||||
addGraphQLQuery(multiQueryTemplate({ typeName }), resolvers.multi.description);
|
||||
queryResolvers[
|
||||
Utils.camelCaseify(Utils.pluralize(typeName))
|
||||
] = resolvers.multi.resolver.bind(resolvers.multi);
|
||||
}
|
||||
addGraphQLResolvers({Query: {...queryResolvers}});
|
||||
addGraphQLResolvers({ Query: { ...queryResolvers } });
|
||||
}
|
||||
|
||||
if (!_.isEmpty(mutations)) {
|
||||
|
@ -403,13 +374,10 @@ export const GraphQLSchema = {
|
|||
`// Warning: you defined a "create" mutation for collection ${collectionName}, but it doesn't have any mutable fields, so no corresponding mutation types can be generated. Remove the "create" mutation or define a "canCreate" property on a field to disable this warning`
|
||||
);
|
||||
} else {
|
||||
addGraphQLMutation(
|
||||
createMutationTemplate({typeName}),
|
||||
mutations.create.description
|
||||
addGraphQLMutation(createMutationTemplate({ typeName }), mutations.create.description);
|
||||
mutationResolvers[`create${typeName}`] = mutations.create.mutation.bind(
|
||||
mutations.create
|
||||
);
|
||||
mutationResolvers[
|
||||
`create${typeName}`
|
||||
] = mutations.create.mutation.bind(mutations.create);
|
||||
}
|
||||
}
|
||||
// update
|
||||
|
@ -421,13 +389,10 @@ export const GraphQLSchema = {
|
|||
`// Warning: you defined an "update" mutation for collection ${collectionName}, but it doesn't have any mutable fields, so no corresponding mutation types can be generated. Remove the "update" mutation or define a "canUpdate" property on a field to disable this warning`
|
||||
);
|
||||
} else {
|
||||
addGraphQLMutation(
|
||||
updateMutationTemplate({typeName}),
|
||||
mutations.update.description
|
||||
addGraphQLMutation(updateMutationTemplate({ typeName }), mutations.update.description);
|
||||
mutationResolvers[`update${typeName}`] = mutations.update.mutation.bind(
|
||||
mutations.update
|
||||
);
|
||||
mutationResolvers[
|
||||
`update${typeName}`
|
||||
] = mutations.update.mutation.bind(mutations.update);
|
||||
}
|
||||
}
|
||||
// upsert
|
||||
|
@ -439,27 +404,19 @@ export const GraphQLSchema = {
|
|||
`// Warning: you defined an "upsert" mutation for collection ${collectionName}, but it doesn't have any mutable fields, so no corresponding mutation types can be generated. Remove the "upsert" mutation or define a "canUpdate" property on a field to disable this warning`
|
||||
);
|
||||
} else {
|
||||
addGraphQLMutation(
|
||||
upsertMutationTemplate({typeName}),
|
||||
mutations.upsert.description
|
||||
addGraphQLMutation(upsertMutationTemplate({ typeName }), mutations.upsert.description);
|
||||
mutationResolvers[`upsert${typeName}`] = mutations.upsert.mutation.bind(
|
||||
mutations.upsert
|
||||
);
|
||||
mutationResolvers[
|
||||
`upsert${typeName}`
|
||||
] = mutations.upsert.mutation.bind(mutations.upsert);
|
||||
}
|
||||
}
|
||||
// delete
|
||||
if (mutations.delete) {
|
||||
// e.g. "deleteMovie(input: DeleteMovieInput) : Movie"
|
||||
addGraphQLMutation(
|
||||
deleteMutationTemplate({typeName}),
|
||||
mutations.delete.description
|
||||
);
|
||||
mutationResolvers[
|
||||
`delete${typeName}`
|
||||
] = mutations.delete.mutation.bind(mutations.delete);
|
||||
addGraphQLMutation(deleteMutationTemplate({ typeName }), mutations.delete.description);
|
||||
mutationResolvers[`delete${typeName}`] = mutations.delete.mutation.bind(mutations.delete);
|
||||
}
|
||||
addGraphQLResolvers({Mutation: {...mutationResolvers}});
|
||||
addGraphQLResolvers({ Mutation: { ...mutationResolvers } });
|
||||
}
|
||||
graphQLSchema = schemaFragments.join('\n\n') + '\n\n\n';
|
||||
} else {
|
||||
|
@ -475,9 +432,7 @@ export const GraphQLSchema = {
|
|||
// getters
|
||||
getSchema() {
|
||||
if (!(this.finalSchema && this.finalSchema.length)) {
|
||||
throw new Error(
|
||||
'Warning: trying to access schema before it has been created by the server.'
|
||||
);
|
||||
throw new Error('Warning: trying to access schema before it has been created by the server.');
|
||||
}
|
||||
return this.finalSchema[0];
|
||||
},
|
||||
|
@ -503,21 +458,11 @@ Vulcan.getGraphQLSchema = () => {
|
|||
return schema;
|
||||
};
|
||||
|
||||
export const addGraphQLCollection = GraphQLSchema.addCollection.bind(
|
||||
GraphQLSchema
|
||||
);
|
||||
export const addGraphQLCollection = GraphQLSchema.addCollection.bind(GraphQLSchema);
|
||||
export const addGraphQLSchema = GraphQLSchema.addSchema.bind(GraphQLSchema);
|
||||
export const addGraphQLQuery = GraphQLSchema.addQuery.bind(GraphQLSchema);
|
||||
export const addGraphQLMutation = GraphQLSchema.addMutation.bind(GraphQLSchema);
|
||||
export const addGraphQLResolvers = GraphQLSchema.addResolvers.bind(
|
||||
GraphQLSchema
|
||||
);
|
||||
export const removeGraphQLResolver = GraphQLSchema.removeResolver.bind(
|
||||
GraphQLSchema
|
||||
);
|
||||
export const addToGraphQLContext = GraphQLSchema.addToContext.bind(
|
||||
GraphQLSchema
|
||||
);
|
||||
export const addGraphQLDirective = GraphQLSchema.addDirective.bind(
|
||||
GraphQLSchema
|
||||
);
|
||||
export const addGraphQLResolvers = GraphQLSchema.addResolvers.bind(GraphQLSchema);
|
||||
export const removeGraphQLResolver = GraphQLSchema.removeResolver.bind(GraphQLSchema);
|
||||
export const addToGraphQLContext = GraphQLSchema.addToContext.bind(GraphQLSchema);
|
||||
export const addGraphQLDirective = GraphQLSchema.addDirective.bind(GraphQLSchema);
|
||||
|
|
|
@ -6,44 +6,37 @@
|
|||
// Meteor WebApp use a Connect server, so we need to
|
||||
// use apollo-server-express integration
|
||||
//import express from 'express';
|
||||
import {ApolloServer} from 'apollo-server-express';
|
||||
import { ApolloServer } from 'apollo-server-express';
|
||||
|
||||
import {Meteor} from 'meteor/meteor';
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
|
||||
import {WebApp} from 'meteor/webapp';
|
||||
import { WebApp } from 'meteor/webapp';
|
||||
import bodyParser from 'body-parser';
|
||||
|
||||
// import cookiesMiddleware from 'universal-cookie-express';
|
||||
// import Cookies from 'universal-cookie';
|
||||
import voyagerMiddleware from 'graphql-voyager/middleware/express';
|
||||
import getVoyagerConfig from './voyager';
|
||||
import {graphiqlMiddleware, getGraphiqlConfig} from './graphiql';
|
||||
import { graphiqlMiddleware, getGraphiqlConfig } from './graphiql';
|
||||
import getPlaygroundConfig from './playground';
|
||||
|
||||
import initGraphQL from './initGraphQL';
|
||||
import './settings';
|
||||
import {engineConfig} from './engine';
|
||||
import {initContext, computeContextFromReq} from './context.js';
|
||||
import { engineConfig } from './engine';
|
||||
import { initContext, computeContextFromReq } from './context.js';
|
||||
|
||||
import {GraphQLSchema} from '../../modules/graphql.js';
|
||||
import { GraphQLSchema } from '../../modules/graphql.js';
|
||||
|
||||
import {enableSSR} from '../apollo-ssr';
|
||||
import { enableSSR } from '../apollo-ssr';
|
||||
|
||||
import universalCookiesMiddleware from 'universal-cookie-express';
|
||||
|
||||
import {
|
||||
getApolloApplyMiddlewareOptions,
|
||||
getApolloServerOptions,
|
||||
} from './settings';
|
||||
import { getApolloApplyMiddlewareOptions, getApolloServerOptions } from './settings';
|
||||
|
||||
import {getSetting} from '../../modules/settings.js';
|
||||
import {formatError} from 'apollo-errors';
|
||||
import { getSetting } from '../../modules/settings.js';
|
||||
import { formatError } from 'apollo-errors';
|
||||
|
||||
export const setupGraphQLMiddlewares = (
|
||||
apolloServer,
|
||||
config,
|
||||
apolloApplyMiddlewareOptions
|
||||
) => {
|
||||
export const setupGraphQLMiddlewares = (apolloServer, config, apolloApplyMiddlewareOptions) => {
|
||||
// IMPORTANT: order matters !
|
||||
// 1 - Add request parsing middleware
|
||||
// 2 - Add apollo specific middlewares
|
||||
|
@ -59,12 +52,9 @@ export const setupGraphQLMiddlewares = (
|
|||
// parse request (order matters)
|
||||
WebApp.connectHandlers.use(
|
||||
config.path,
|
||||
bodyParser.json({limit: getSetting('apolloServer.jsonParserOptions.limit')})
|
||||
);
|
||||
WebApp.connectHandlers.use(
|
||||
config.path,
|
||||
bodyParser.text({type: 'application/graphql'})
|
||||
bodyParser.json({ limit: getSetting('apolloServer.jsonParserOptions.limit') })
|
||||
);
|
||||
WebApp.connectHandlers.use(config.path, bodyParser.text({ type: 'application/graphql' }));
|
||||
|
||||
// Provide the Meteor WebApp Connect server instance to Apollo
|
||||
// Apollo will use it instead of its own HTTP server when handling requests
|
||||
|
@ -88,15 +78,9 @@ export const setupGraphQLMiddlewares = (
|
|||
export const setupToolsMiddlewares = config => {
|
||||
// Voyager is a GraphQL schema visual explorer
|
||||
// available on /voyager as a default
|
||||
WebApp.connectHandlers.use(
|
||||
config.voyagerPath,
|
||||
voyagerMiddleware(getVoyagerConfig(config))
|
||||
);
|
||||
WebApp.connectHandlers.use(config.voyagerPath, voyagerMiddleware(getVoyagerConfig(config)));
|
||||
// Setup GraphiQL
|
||||
WebApp.connectHandlers.use(
|
||||
config.graphiqlPath,
|
||||
graphiqlMiddleware(getGraphiqlConfig(config))
|
||||
);
|
||||
WebApp.connectHandlers.use(config.graphiqlPath, graphiqlMiddleware(getGraphiqlConfig(config)));
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -158,7 +142,7 @@ export const onStart = () => {
|
|||
formatError,
|
||||
tracing: getSetting('apolloTracing', Meteor.isDevelopment),
|
||||
cacheControl: true,
|
||||
context: ({req}) => context(req),
|
||||
context: ({ req }) => context(req),
|
||||
...getApolloServerOptions(),
|
||||
},
|
||||
});
|
||||
|
@ -170,7 +154,7 @@ export const onStart = () => {
|
|||
setupToolsMiddlewares(config);
|
||||
}
|
||||
// ssr
|
||||
enableSSR({computeContext: context});
|
||||
enableSSR({ computeContext: context });
|
||||
};
|
||||
// createApolloServer when server startup
|
||||
Meteor.startup(onStart);
|
||||
|
|
|
@ -12,14 +12,14 @@
|
|||
|
||||
//import deepmerge from 'deepmerge';
|
||||
import DataLoader from 'dataloader';
|
||||
import {Collections} from '../../modules/collections.js';
|
||||
import {runCallbacks} from '../../modules/callbacks.js';
|
||||
import { Collections } from '../../modules/collections.js';
|
||||
import { runCallbacks } from '../../modules/callbacks.js';
|
||||
import findByIds from '../../modules/findbyids.js';
|
||||
import {GraphQLSchema} from '../../modules/graphql.js';
|
||||
import { GraphQLSchema } from '../../modules/graphql.js';
|
||||
import _merge from 'lodash/merge';
|
||||
import {getUser} from 'meteor/apollo';
|
||||
import {getHeaderLocale} from '../intl.js';
|
||||
import {getSetting} from '../../modules/settings.js';
|
||||
import { getUser } from 'meteor/apollo';
|
||||
import { getHeaderLocale } from '../intl.js';
|
||||
import { getSetting } from '../../modules/settings.js';
|
||||
|
||||
/**
|
||||
* Called once on server creation
|
||||
|
@ -28,7 +28,7 @@ import {getSetting} from '../../modules/settings.js';
|
|||
export const initContext = currentContext => {
|
||||
let context;
|
||||
if (currentContext) {
|
||||
context = {...currentContext};
|
||||
context = { ...currentContext };
|
||||
} else {
|
||||
context = {};
|
||||
}
|
||||
|
@ -52,10 +52,7 @@ import Cookies from 'universal-cookie';
|
|||
// initial request will get the login token from a cookie, subsequent requests from
|
||||
// the header
|
||||
const getAuthToken = req => {
|
||||
return (
|
||||
req.headers.authorization ||
|
||||
new Cookies(req.cookies).get('meteor_login_token')
|
||||
);
|
||||
return req.headers.authorization || new Cookies(req.cookies).get('meteor_login_token');
|
||||
};
|
||||
// @see https://www.apollographql.com/docs/react/recipes/meteor#Server
|
||||
const setupAuthToken = async (context, req) => {
|
||||
|
@ -75,8 +72,8 @@ export const computeContextFromReq = (currentContext, customContextFromReq) => {
|
|||
// givenOptions can be either a function of the request or an object
|
||||
const getBaseContext = req =>
|
||||
customContextFromReq
|
||||
? {...currentContext, ...customContextFromReq(req)}
|
||||
: {...currentContext};
|
||||
? { ...currentContext, ...customContextFromReq(req) }
|
||||
: { ...currentContext };
|
||||
// Previous implementation
|
||||
// Now meteor/apollo already provide this
|
||||
// Get the token from the header
|
||||
|
@ -127,7 +124,7 @@ export const computeContextFromReq = (currentContext, customContextFromReq) => {
|
|||
// console.log('// apollo_server.js locale:', req.headers.locale);
|
||||
|
||||
// if apiKey is present, assign "fake" currentUser with admin rights
|
||||
if (headers.apikey && (headers.apikey === getSetting('vulcan.apiKey'))) {
|
||||
if (headers.apikey && headers.apikey === getSetting('vulcan.apiKey')) {
|
||||
context.currentUser = { isAdmin: true, isApiUser: true };
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import {getSetting} from '../../modules/settings.js';
|
||||
import { getSetting } from '../../modules/settings.js';
|
||||
// see https://github.com/apollographql/apollo-cache-control
|
||||
export const engineApiKey =
|
||||
process.env.ENGINE_API_KEY || getSetting('apolloEngine.apiKey');
|
||||
export const engineApiKey = process.env.ENGINE_API_KEY || getSetting('apolloEngine.apiKey');
|
||||
// options now available:
|
||||
// @see https://www.apollographql.com/docs/apollo-server/api/apollo-server.html#EngineReportingOptions
|
||||
export const engineConfig = engineApiKey
|
||||
|
|
|
@ -42,18 +42,14 @@ function safeSerialize(data) {
|
|||
|
||||
export function renderGraphiQL(data) {
|
||||
const endpointURL = data.endpointURL;
|
||||
const endpointWs =
|
||||
endpointURL.startsWith('ws://') || endpointURL.startsWith('wss://');
|
||||
const endpointWs = endpointURL.startsWith('ws://') || endpointURL.startsWith('wss://');
|
||||
const subscriptionsEndpoint = data.subscriptionsEndpoint;
|
||||
const usingHttp = !endpointWs;
|
||||
const usingWs = endpointWs || !!subscriptionsEndpoint;
|
||||
const endpointURLWs =
|
||||
usingWs && (endpointWs ? endpointURL : subscriptionsEndpoint);
|
||||
const endpointURLWs = usingWs && (endpointWs ? endpointURL : subscriptionsEndpoint);
|
||||
|
||||
const queryString = data.query;
|
||||
const variablesString = data.variables
|
||||
? JSON.stringify(data.variables, null, 2)
|
||||
: null;
|
||||
const variablesString = data.variables ? JSON.stringify(data.variables, null, 2) : null;
|
||||
const resultString = null;
|
||||
const operationName = data.operationName;
|
||||
const passHeader = data.passHeader ? data.passHeader : '';
|
||||
|
@ -87,11 +83,7 @@ export function renderGraphiQL(data) {
|
|||
? `<link href="//cdn.jsdelivr.net/npm/codemirror@5/theme/${editorTheme}.min.css" rel="stylesheet" />`
|
||||
: ''
|
||||
}
|
||||
${
|
||||
usingHttp
|
||||
? '<script src="//cdn.jsdelivr.net/fetch/2.0.1/fetch.min.js"></script>'
|
||||
: ''
|
||||
}
|
||||
${usingHttp ? '<script src="//cdn.jsdelivr.net/fetch/2.0.1/fetch.min.js"></script>' : ''}
|
||||
${
|
||||
usingWs
|
||||
? `<script src="//unpkg.com/subscriptions-transport-ws@${SUBSCRIPTIONS_TRANSPORT_VERSION}/browser/client.js"></script>`
|
||||
|
@ -263,8 +255,7 @@ function createGraphiQLData(params, options) {
|
|||
endpointURL: options.endpointURL,
|
||||
subscriptionsEndpoint: options.subscriptionsEndpoint,
|
||||
query: params.query || options.query,
|
||||
variables:
|
||||
(params.variables && JSON.parse(params.variables)) || options.variables,
|
||||
variables: (params.variables && JSON.parse(params.variables)) || options.variables,
|
||||
operationName: params.operationName || options.operationName,
|
||||
passHeader: options.passHeader,
|
||||
editorTheme: options.editorTheme,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export * from './apollo_server2';
|
||||
export * from './settings';
|
||||
|
||||
export {default as initGraphQL} from './initGraphQL';
|
||||
export { default as initGraphQL } from './initGraphQL';
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
* Init the graphQL schema
|
||||
*/
|
||||
|
||||
import {makeExecutableSchema} from 'apollo-server';
|
||||
import {GraphQLSchema} from '../../modules/graphql.js';
|
||||
import {runCallbacks} from '../../modules/callbacks.js';
|
||||
import { makeExecutableSchema } from 'apollo-server';
|
||||
import { GraphQLSchema } from '../../modules/graphql.js';
|
||||
import { runCallbacks } from '../../modules/callbacks.js';
|
||||
|
||||
const getQueries = () =>
|
||||
`type Query {
|
||||
|
|
|
@ -27,5 +27,4 @@ let apolloApplyMiddlewareOptions = {};
|
|||
export const registerApolloApplyMiddlewareOptions = options => {
|
||||
apolloApplyMiddlewareOptions = _merge(apolloApplyMiddlewareOptions, options);
|
||||
};
|
||||
export const getApolloApplyMiddlewareOptions = () =>
|
||||
apolloApplyMiddlewareOptions;
|
||||
export const getApolloApplyMiddlewareOptions = () => apolloApplyMiddlewareOptions;
|
||||
|
|
|
@ -5,29 +5,29 @@
|
|||
* /!\ It must be recreated on every request
|
||||
*/
|
||||
|
||||
import {ApolloClient} from 'apollo-client';
|
||||
import {InMemoryCache} from 'apollo-cache-inmemory';
|
||||
import { ApolloClient } from 'apollo-client';
|
||||
import { InMemoryCache } from 'apollo-cache-inmemory';
|
||||
|
||||
import {SchemaLink} from 'apollo-link-schema';
|
||||
import {GraphQLSchema} from '../../modules/graphql.js';
|
||||
import { SchemaLink } from 'apollo-link-schema';
|
||||
import { GraphQLSchema } from '../../modules/graphql.js';
|
||||
|
||||
import {createStateLink} from '../../modules/apollo-common';
|
||||
import {ApolloLink} from 'apollo-link';
|
||||
import { createStateLink } from '../../modules/apollo-common';
|
||||
import { ApolloLink } from 'apollo-link';
|
||||
|
||||
// @see https://www.apollographql.com/docs/react/features/server-side-rendering.html#local-queries
|
||||
// import { createHttpLink } from 'apollo-link-http';
|
||||
// import fetch from 'node-fetch'
|
||||
|
||||
export const createClient = async ({req, computeContext}) => {
|
||||
export const createClient = async ({ req, computeContext }) => {
|
||||
// init
|
||||
// stateLink will init the client internal state
|
||||
const cache = new InMemoryCache();
|
||||
const stateLink = createStateLink({cache});
|
||||
const stateLink = createStateLink({ cache });
|
||||
// schemaLink will fetch data directly based on the executable schema
|
||||
const schema = GraphQLSchema.getExecutableSchema();
|
||||
// this is the resolver context
|
||||
const context = await computeContext(req);
|
||||
const schemaLink = new SchemaLink({schema, context});
|
||||
const schemaLink = new SchemaLink({ schema, context });
|
||||
const client = new ApolloClient({
|
||||
ssrMode: true,
|
||||
link: ApolloLink.from([stateLink, schemaLink]),
|
||||
|
|
|
@ -5,7 +5,7 @@ import React from 'react';
|
|||
import { ApolloProvider } from 'react-apollo';
|
||||
import { StaticRouter } from 'react-router';
|
||||
|
||||
import { Components } from 'meteor/vulcan:lib'
|
||||
import { Components } from 'meteor/vulcan:lib';
|
||||
|
||||
import { CookiesProvider } from 'react-cookie';
|
||||
|
||||
|
@ -16,7 +16,7 @@ import Cookies from 'universal-cookie';
|
|||
const AppGenerator = ({ req, apolloClient, context }) => {
|
||||
// TODO: universalCookies should be defined here, but it isn't
|
||||
// @see https://github.com/meteor/meteor-feature-requests/issues/174#issuecomment-441047495
|
||||
const cookies = new Cookies(req.cookies) // req.universalCookies;
|
||||
const cookies = new Cookies(req.cookies); // req.universalCookies;
|
||||
const App = (
|
||||
<ApolloProvider client={apolloClient}>
|
||||
<StaticRouter location={req.url} context={context}>
|
||||
|
@ -26,6 +26,6 @@ const AppGenerator = ({ req, apolloClient, context }) => {
|
|||
</StaticRouter>
|
||||
</ApolloProvider>
|
||||
);
|
||||
return App
|
||||
return App;
|
||||
};
|
||||
export default AppGenerator;
|
||||
|
|
|
@ -2,25 +2,21 @@
|
|||
* Actually enable SSR
|
||||
*/
|
||||
|
||||
import {
|
||||
populateComponentsApp,
|
||||
populateRoutesApp,
|
||||
initializeFragments,
|
||||
} from 'meteor/vulcan:lib';
|
||||
import { populateComponentsApp, populateRoutesApp, initializeFragments } from 'meteor/vulcan:lib';
|
||||
// onPageLoad is mostly equivalent to an Express middleware
|
||||
// excepts it is tailored to handle Meteor server side rendering
|
||||
import {onPageLoad} from 'meteor/server-render';
|
||||
import { onPageLoad } from 'meteor/server-render';
|
||||
|
||||
import makePageRenderer from './renderPage';
|
||||
|
||||
const enableSSR = ({computeContext}) => {
|
||||
const enableSSR = ({ computeContext }) => {
|
||||
Meteor.startup(() => {
|
||||
// init the application components and routes, including components & routes from 3rd-party packages
|
||||
initializeFragments();
|
||||
populateComponentsApp();
|
||||
populateRoutesApp();
|
||||
// render the page
|
||||
onPageLoad(makePageRenderer({computeContext}));
|
||||
onPageLoad(makePageRenderer({ computeContext }));
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
export {default as enableSSR} from './enableSSR';
|
||||
export { default as enableSSR } from './enableSSR';
|
||||
|
|
|
@ -6,22 +6,22 @@
|
|||
*/
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/server';
|
||||
import {renderToStringWithData} from 'react-apollo';
|
||||
import { renderToStringWithData } from 'react-apollo';
|
||||
|
||||
import {runCallbacks} from '../../modules/callbacks';
|
||||
import {createClient} from './apolloClient';
|
||||
import { runCallbacks } from '../../modules/callbacks';
|
||||
import { createClient } from './apolloClient';
|
||||
|
||||
import Head from './components/Head';
|
||||
import ApolloState from './components/ApolloState';
|
||||
import AppGenerator from './components/AppGenerator';
|
||||
|
||||
const makePageRenderer = ({computeContext}) => {
|
||||
const makePageRenderer = ({ computeContext }) => {
|
||||
// onPageLoad callback
|
||||
const renderPage = async sink => {
|
||||
const req = sink.request;
|
||||
// according to the Apollo doc, client needs to be recreated on every request
|
||||
// this avoids caching server side
|
||||
const client = await createClient({req, computeContext});
|
||||
const client = await createClient({ req, computeContext });
|
||||
|
||||
// Used by callbacks to handle side effects
|
||||
// E.g storing the stylesheet generated by styled-components
|
||||
|
@ -31,15 +31,13 @@ const makePageRenderer = ({computeContext}) => {
|
|||
// middlewares at this point
|
||||
// @see https://github.com/meteor/meteor-feature-requests/issues/174#issuecomment-441047495
|
||||
|
||||
const App = (
|
||||
<AppGenerator req={req} apolloClient={client} context={context} />
|
||||
);
|
||||
const App = <AppGenerator req={req} apolloClient={client} context={context} />;
|
||||
|
||||
// run user registered callbacks that wraps the React app
|
||||
const WrappedApp = runCallbacks({
|
||||
name: 'router.server.wrapper',
|
||||
iterator: App,
|
||||
properties: {req, context, apolloClient: client},
|
||||
properties: { req, context, apolloClient: client },
|
||||
});
|
||||
|
||||
// equivalent to calling getDataFromTree and then renderToStringWithData
|
||||
|
@ -67,7 +65,7 @@ const makePageRenderer = ({computeContext}) => {
|
|||
runCallbacks({
|
||||
name: 'router.server.postRender',
|
||||
iterator: sink,
|
||||
properties: {context},
|
||||
properties: { context },
|
||||
});
|
||||
};
|
||||
return renderPage;
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
// see https://github.com/apollographql/graphql-tools/blob/master/docs/source/schema-directives.md#marking-strings-for-internationalization
|
||||
|
||||
import {addGraphQLDirective, addGraphQLSchema} from '../modules/graphql';
|
||||
import {SchemaDirectiveVisitor} from 'graphql-tools';
|
||||
import {defaultFieldResolver} from 'graphql';
|
||||
import {Collections} from '../modules/collections';
|
||||
import {getSetting} from '../modules/settings';
|
||||
import {debug} from '../modules/debug';
|
||||
import { addGraphQLDirective, addGraphQLSchema } from '../modules/graphql';
|
||||
import { SchemaDirectiveVisitor } from 'graphql-tools';
|
||||
import { defaultFieldResolver } from 'graphql';
|
||||
import { Collections } from '../modules/collections';
|
||||
import { getSetting } from '../modules/settings';
|
||||
import { debug } from '../modules/debug';
|
||||
import Vulcan from '../modules/config';
|
||||
import {isIntlField} from '../modules/intl';
|
||||
import {Connectors} from './connectors';
|
||||
import { isIntlField } from '../modules/intl';
|
||||
import { Connectors } from './connectors';
|
||||
import pickBy from 'lodash/pickBy';
|
||||
|
||||
/*
|
||||
|
@ -32,16 +32,11 @@ Take an array of translations, a locale, and a default locale, and return a matc
|
|||
|
||||
*/
|
||||
const getLocaleString = (translations, locale, defaultLocale) => {
|
||||
const localeObject = translations.find(
|
||||
translation => translation.locale === locale
|
||||
);
|
||||
const localeObject = translations.find(translation => translation.locale === locale);
|
||||
const defaultLocaleObject = translations.find(
|
||||
translation => translation.locale === defaultLocale
|
||||
);
|
||||
return (
|
||||
(localeObject && localeObject.value) ||
|
||||
(defaultLocaleObject && defaultLocaleObject.value)
|
||||
);
|
||||
return (localeObject && localeObject.value) || (defaultLocaleObject && defaultLocaleObject.value);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -59,15 +54,12 @@ class IntlDirective extends SchemaDirectiveVisitor {
|
|||
const defaultLocale = getSetting('locale');
|
||||
const intlField = doc[`${name}_intl`];
|
||||
// Return string in requested or default language, or else field's original value
|
||||
return (
|
||||
(intlField && getLocaleString(intlField, locale, defaultLocale)) ||
|
||||
fieldValue
|
||||
);
|
||||
return (intlField && getLocaleString(intlField, locale, defaultLocale)) || fieldValue;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
addGraphQLDirective({intl: IntlDirective});
|
||||
addGraphQLDirective({ intl: IntlDirective });
|
||||
|
||||
addGraphQLSchema('directive @intl on FIELD_DEFINITION');
|
||||
|
||||
|
@ -79,7 +71,7 @@ Migration function
|
|||
const migrateIntlFields = async defaultLocale => {
|
||||
if (!defaultLocale) {
|
||||
throw new Error(
|
||||
"Please pass the id of the locale to which to migrate your current content (e.g. migrateIntlFields('en'))"
|
||||
'Please pass the id of the locale to which to migrate your current content (e.g. migrateIntlFields(\'en\'))'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -101,31 +93,22 @@ const migrateIntlFields = async defaultLocale => {
|
|||
const selector = {
|
||||
$or: intlFieldsNames.map(f => {
|
||||
return {
|
||||
$and: [
|
||||
{[`${f}`]: {$exists: true}},
|
||||
{[`${f}_intl`]: {$exists: false}},
|
||||
],
|
||||
$and: [{ [`${f}`]: { $exists: true } }, { [`${f}_intl`]: { $exists: false } }],
|
||||
};
|
||||
}),
|
||||
};
|
||||
const documentsToMigrate = await Connectors.find(collection, selector);
|
||||
|
||||
if (documentsToMigrate.length) {
|
||||
console.log(
|
||||
`-> found ${documentsToMigrate.length} documents to migrate \n`
|
||||
); // eslint-disable-line no-console
|
||||
console.log(`-> found ${documentsToMigrate.length} documents to migrate \n`); // eslint-disable-line no-console
|
||||
for (const doc of documentsToMigrate) {
|
||||
console.log(`// Migrating document ${doc._id}`); // eslint-disable-line no-console
|
||||
const modifier = {$push: {}};
|
||||
const modifier = { $push: {} };
|
||||
|
||||
intlFieldsNames.forEach(f => {
|
||||
if (doc[f] && !doc[`${f}_intl`]) {
|
||||
const translationObject = {locale: defaultLocale, value: doc[f]};
|
||||
console.log(
|
||||
`-> Adding field ${f}_intl: ${JSON.stringify(
|
||||
translationObject
|
||||
)} `
|
||||
); // eslint-disable-line no-console
|
||||
const translationObject = { locale: defaultLocale, value: doc[f] };
|
||||
console.log(`-> Adding field ${f}_intl: ${JSON.stringify(translationObject)} `); // eslint-disable-line no-console
|
||||
modifier.$push[`${f}_intl`] = translationObject;
|
||||
}
|
||||
});
|
||||
|
@ -133,11 +116,7 @@ const migrateIntlFields = async defaultLocale => {
|
|||
if (!_.isEmpty(modifier.$push)) {
|
||||
// update document
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
const n = await Connectors.update(
|
||||
collection,
|
||||
{_id: doc._id},
|
||||
modifier
|
||||
);
|
||||
const n = await Connectors.update(collection, { _id: doc._id }, modifier);
|
||||
console.log(`-> migrated ${n} documents \n`); // eslint-disable-line no-console
|
||||
}
|
||||
console.log('\n'); // eslint-disable-line no-console
|
||||
|
@ -173,9 +152,7 @@ export const getHeaderLocale = (headers, userLocale) => {
|
|||
|
||||
// get locale from accepted-language header
|
||||
if (headers['accept-language']) {
|
||||
const acceptedLanguages = headers['accept-language']
|
||||
.split(',')
|
||||
.map(l => l.split(';')[0]);
|
||||
const acceptedLanguages = headers['accept-language'].split(',').map(l => l.split(';')[0]);
|
||||
acceptedLocale = acceptedLanguages[0]; // for now only use the highest-priority accepted language
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
Run a GraphQL request from the server with the proper context
|
||||
|
||||
*/
|
||||
import {graphql} from 'graphql';
|
||||
import {Collections} from '../modules/collections.js';
|
||||
import { graphql } from 'graphql';
|
||||
import { Collections } from '../modules/collections.js';
|
||||
import DataLoader from 'dataloader';
|
||||
import findByIds from '../modules/findbyids.js';
|
||||
import {
|
||||
|
@ -12,16 +12,16 @@ import {
|
|||
extractFragmentName,
|
||||
getFragmentText,
|
||||
} from '../modules/fragments.js';
|
||||
import {getSetting} from '../modules/settings';
|
||||
import { getSetting } from '../modules/settings';
|
||||
import merge from 'lodash/merge';
|
||||
import {singleClientTemplate} from '../modules/graphql_templates';
|
||||
import {Utils} from './utils';
|
||||
import {GraphQLSchema} from '../modules/graphql';
|
||||
import { singleClientTemplate } from '../modules/graphql_templates';
|
||||
import { Utils } from './utils';
|
||||
import { GraphQLSchema } from '../modules/graphql';
|
||||
|
||||
// note: if no context is passed, default to running requests with full admin privileges
|
||||
export const runGraphQL = async (query, variables = {}, context) => {
|
||||
const defaultContext = {
|
||||
currentUser: {isAdmin: true},
|
||||
currentUser: { isAdmin: true },
|
||||
locale: getSetting('locale'),
|
||||
};
|
||||
const queryContext = merge(defaultContext, context);
|
||||
|
@ -30,21 +30,14 @@ export const runGraphQL = async (query, variables = {}, context) => {
|
|||
// within the scope of this specific request,
|
||||
// decorate each collection with a new Dataloader object and add it to context
|
||||
Collections.forEach(collection => {
|
||||
collection.loader = new DataLoader(
|
||||
ids => findByIds(collection, ids, queryContext),
|
||||
{cache: true}
|
||||
);
|
||||
collection.loader = new DataLoader(ids => findByIds(collection, ids, queryContext), {
|
||||
cache: true,
|
||||
});
|
||||
queryContext[collection.options.collectionName] = collection;
|
||||
});
|
||||
|
||||
// see http://graphql.org/graphql-js/graphql/#graphql
|
||||
const result = await graphql(
|
||||
executableSchema,
|
||||
query,
|
||||
{},
|
||||
queryContext,
|
||||
variables
|
||||
);
|
||||
const result = await graphql(executableSchema, query, {}, queryContext, variables);
|
||||
|
||||
if (result.errors) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
@ -65,7 +58,7 @@ 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}) => {
|
||||
export const buildQuery = (collection, { fragmentName, fragmentText }) => {
|
||||
const collectionName = collection.options.collectionName;
|
||||
const typeName = collection.options.typeName;
|
||||
|
||||
|
@ -100,16 +93,9 @@ Meteor.startup(() => {
|
|||
Collections.forEach(collection => {
|
||||
const typeName = collection.options.typeName;
|
||||
|
||||
collection.queryOne = async (
|
||||
documentId,
|
||||
{fragmentName, fragmentText, context}
|
||||
) => {
|
||||
const query = buildQuery(collection, {fragmentName, fragmentText});
|
||||
const result = await runQuery(
|
||||
query,
|
||||
{input: {selector: {documentId}}},
|
||||
context
|
||||
);
|
||||
collection.queryOne = async (documentId, { fragmentName, fragmentText, context }) => {
|
||||
const query = buildQuery(collection, { fragmentName, fragmentText });
|
||||
const result = await runQuery(query, { input: { selector: { documentId } } }, context);
|
||||
return result.data[Utils.camelCaseify(typeName)].result;
|
||||
};
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@ Package.onUse(function(api) {
|
|||
api.versionsFrom('1.6.1');
|
||||
|
||||
// note: if used, accounts-base should be loaded before vulcan:lib
|
||||
api.use('accounts-base', {weak: true});
|
||||
api.use('accounts-base', { weak: true });
|
||||
|
||||
var packages = [
|
||||
'buffer@0.0.0', // see https://github.com/meteor/meteor/issues/8645
|
||||
|
|
|
@ -6,9 +6,9 @@ import {
|
|||
initContext,
|
||||
computeContextFromReq,
|
||||
} from '../../lib/server/apollo-server';
|
||||
import {GraphQLSchema} from '../../lib/modules/graphql';
|
||||
import { GraphQLSchema } from '../../lib/modules/graphql';
|
||||
import expect from 'expect';
|
||||
import {executableSchema} from './fixtures/minimalSchema';
|
||||
import { executableSchema } from './fixtures/minimalSchema';
|
||||
|
||||
const test = it; // TODO: just before we switch to jest
|
||||
// @see https://www.apollographql.com/docs/apollo-server/features/testing.html
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// blatantly stolen from https://www.apollographql.com/docs/graphql-tools/generate-schema.html
|
||||
import find from 'lodash/find';
|
||||
import filter from 'lodash/filter';
|
||||
import {makeExecutableSchema} from 'graphql-tools';
|
||||
import { makeExecutableSchema } from 'graphql-tools';
|
||||
|
||||
const typeDefs = `
|
||||
type Author {
|
||||
|
@ -34,27 +34,27 @@ const typeDefs = `
|
|||
|
||||
// example data
|
||||
const authors = [
|
||||
{id: 1, firstName: 'Tom', lastName: 'Coleman'},
|
||||
{id: 2, firstName: 'Sashko', lastName: 'Stubailo'},
|
||||
{id: 3, firstName: 'Mikhail', lastName: 'Novikov'},
|
||||
{ id: 1, firstName: 'Tom', lastName: 'Coleman' },
|
||||
{ id: 2, firstName: 'Sashko', lastName: 'Stubailo' },
|
||||
{ id: 3, firstName: 'Mikhail', lastName: 'Novikov' },
|
||||
];
|
||||
|
||||
const posts = [
|
||||
{id: 1, authorId: 1, title: 'Introduction to GraphQL', votes: 2},
|
||||
{id: 2, authorId: 2, title: 'Welcome to Meteor', votes: 3},
|
||||
{id: 3, authorId: 2, title: 'Advanced GraphQL', votes: 1},
|
||||
{id: 4, authorId: 3, title: 'Launchpad is Cool', votes: 7},
|
||||
{ id: 1, authorId: 1, title: 'Introduction to GraphQL', votes: 2 },
|
||||
{ id: 2, authorId: 2, title: 'Welcome to Meteor', votes: 3 },
|
||||
{ id: 3, authorId: 2, title: 'Advanced GraphQL', votes: 1 },
|
||||
{ id: 4, authorId: 3, title: 'Launchpad is Cool', votes: 7 },
|
||||
];
|
||||
|
||||
const resolvers = {
|
||||
Query: {
|
||||
posts: () => posts,
|
||||
author: (_, {id}) => find(authors, {id}),
|
||||
author: (_, { id }) => find(authors, { id }),
|
||||
},
|
||||
|
||||
Mutation: {
|
||||
upvotePost: (_, {postId}) => {
|
||||
const post = find(posts, {id: postId});
|
||||
upvotePost: (_, { postId }) => {
|
||||
const post = find(posts, { id: postId });
|
||||
if (!post) {
|
||||
throw new Error(`Couldn't find post with id ${postId}`);
|
||||
}
|
||||
|
@ -64,11 +64,11 @@ const resolvers = {
|
|||
},
|
||||
|
||||
Author: {
|
||||
posts: author => filter(posts, {authorId: author.id}),
|
||||
posts: author => filter(posts, { authorId: author.id }),
|
||||
},
|
||||
|
||||
Post: {
|
||||
author: post => find(authors, {id: post.authorId}),
|
||||
author: post => find(authors, { id: post.authorId }),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import expect from 'expect';
|
||||
|
||||
import {GraphQLSchema} from '../../lib/modules/graphql';
|
||||
import { GraphQLSchema } from '../../lib/modules/graphql';
|
||||
import initGraphQL from '../../lib/server/apollo-server/initGraphQL';
|
||||
|
||||
describe('vulcan:lib/graphql', function() {
|
||||
|
|
|
@ -55,26 +55,16 @@ import express from 'express';
|
|||
import Stripe from 'stripe';
|
||||
import Charges from '../../modules/charges/collection.js';
|
||||
import Users from 'meteor/vulcan:users';
|
||||
import {Products} from '../../modules/products.js';
|
||||
import {Promise} from 'meteor/promise';
|
||||
import { Products } from '../../modules/products.js';
|
||||
import { Promise } from 'meteor/promise';
|
||||
|
||||
registerSetting('stripe', null, 'Stripe settings');
|
||||
registerSetting('stripe.publishableKey', null, 'Publishable key', true);
|
||||
registerSetting(
|
||||
'stripe.publishableKeyTest',
|
||||
null,
|
||||
'Publishable key (test)',
|
||||
true
|
||||
);
|
||||
registerSetting('stripe.publishableKeyTest', null, 'Publishable key (test)', true);
|
||||
registerSetting('stripe.secretKey', null, 'Secret key');
|
||||
registerSetting('stripe.secretKeyTest', null, 'Secret key (test)');
|
||||
registerSetting('stripe.endpointSecret', null, 'Endpoint secret for webhook');
|
||||
registerSetting(
|
||||
'stripe.alwaysUseTest',
|
||||
false,
|
||||
'Always use test keys in all environments',
|
||||
true
|
||||
);
|
||||
registerSetting('stripe.alwaysUseTest', false, 'Always use test keys in all environments', true);
|
||||
|
||||
const stripeSettings = getSetting('stripe');
|
||||
|
||||
|
@ -102,13 +92,7 @@ export const receiveAction = async args => {
|
|||
document,
|
||||
returnDocument = {};
|
||||
|
||||
const {
|
||||
userId,
|
||||
productKey,
|
||||
associatedCollection,
|
||||
associatedId,
|
||||
properties,
|
||||
} = args;
|
||||
const { userId, productKey, associatedCollection, associatedId, properties } = args;
|
||||
|
||||
if (!stripeSettings) {
|
||||
throw new Error('Please fill in your Stripe settings');
|
||||
|
@ -117,7 +101,7 @@ export const receiveAction = async args => {
|
|||
// if an associated collection name and document id have been provided,
|
||||
// get the associated collection and document
|
||||
if (associatedCollection && associatedId) {
|
||||
collection = _.findWhere(Collections, {_name: associatedCollection});
|
||||
collection = _.findWhere(Collections, { _name: associatedCollection });
|
||||
document = await Connectors.get(collection, associatedId);
|
||||
}
|
||||
|
||||
|
@ -193,7 +177,7 @@ Retrieve or create a Stripe customer
|
|||
|
||||
*/
|
||||
export const getCustomer = async (user, token) => {
|
||||
const {id} = token;
|
||||
const { id } = token;
|
||||
|
||||
let customer;
|
||||
|
||||
|
@ -203,7 +187,7 @@ export const getCustomer = async (user, token) => {
|
|||
} catch (error) {
|
||||
// if user doesn't have a stripeCustomerId; or if id doesn't match up with Stripe
|
||||
// create new customer object
|
||||
const customerOptions = {email: user.email};
|
||||
const customerOptions = { email: user.email };
|
||||
if (id) {
|
||||
customerOptions.source = id;
|
||||
}
|
||||
|
@ -213,7 +197,7 @@ export const getCustomer = async (user, token) => {
|
|||
await updateMutator({
|
||||
collection: Users,
|
||||
documentId: user._id,
|
||||
data: {stripeCustomerId: customer.id},
|
||||
data: { stripeCustomerId: customer.id },
|
||||
validate: false,
|
||||
});
|
||||
}
|
||||
|
@ -226,18 +210,8 @@ export const getCustomer = async (user, token) => {
|
|||
Create one-time charge.
|
||||
|
||||
*/
|
||||
export const createCharge = async ({
|
||||
user,
|
||||
product,
|
||||
collection,
|
||||
document,
|
||||
metadata,
|
||||
args,
|
||||
}) => {
|
||||
const {
|
||||
token,
|
||||
/* userId, productKey, associatedId, properties, */ coupon,
|
||||
} = args;
|
||||
export const createCharge = async ({ user, product, collection, document, metadata, args }) => {
|
||||
const { token, /* userId, productKey, associatedId, properties, */ coupon } = args;
|
||||
|
||||
const customer = await getCustomer(user, token);
|
||||
|
||||
|
@ -314,7 +288,7 @@ export const createSubscription = async ({
|
|||
// eslint-disable-next-line no-unused-vars
|
||||
const subscription = await stripe.subscriptions.create({
|
||||
customer: customer.id,
|
||||
items: [{plan: product.plan}],
|
||||
items: [{ plan: product.plan }],
|
||||
metadata,
|
||||
...product.subscriptionProperties,
|
||||
});
|
||||
|
@ -410,9 +384,7 @@ const createOrRetrievePlan = async planObject => {
|
|||
if (error.statusCode === 404) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
`Creating subscription plan ${
|
||||
planObject.plan
|
||||
} for ${(planObject.amount &&
|
||||
`Creating subscription plan ${planObject.plan} for ${(planObject.amount &&
|
||||
(planObject.amount / 100).toLocaleString('en-US', {
|
||||
style: 'currency',
|
||||
currency: planObject.currency,
|
||||
|
@ -435,17 +407,9 @@ export const createOrRetrieveSubscriptionPlan = async maybePlanObject =>
|
|||
Process charges, subscriptions, etc. on Vulcan's side
|
||||
|
||||
*/
|
||||
export const processAction = async ({
|
||||
collection,
|
||||
document,
|
||||
stripeObject,
|
||||
args,
|
||||
user,
|
||||
}) => {
|
||||
export const processAction = async ({ collection, document, stripeObject, args, user }) => {
|
||||
debug('');
|
||||
debugGroup(
|
||||
'--------------- start\x1b[35m processAction \x1b[0m ---------------'
|
||||
);
|
||||
debugGroup('--------------- start\x1b[35m processAction \x1b[0m ---------------');
|
||||
debug(`Collection: ${collection.options.collectionName}`);
|
||||
debug(`documentId: ${document._id}`);
|
||||
debug(`Charge: ${stripeObject}`);
|
||||
|
@ -462,9 +426,7 @@ export const processAction = async ({
|
|||
if (existingCharge) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
`// Charge with Stripe id ${
|
||||
stripeObject.id
|
||||
} already exists in db; aborting processAction`
|
||||
`// Charge with Stripe id ${stripeObject.id} already exists in db; aborting processAction`
|
||||
);
|
||||
return collection && document ? document : {};
|
||||
}
|
||||
|
@ -514,13 +476,13 @@ export const processAction = async ({
|
|||
? [...document.chargeIds, chargeSaved._id]
|
||||
: [chargeSaved._id];
|
||||
|
||||
let data = {chargeIds};
|
||||
let data = { chargeIds };
|
||||
|
||||
// run collection.charge.sync callbacks
|
||||
data = await runCallbacks({
|
||||
name: 'stripe.process.sync',
|
||||
iterator: data,
|
||||
properties: {collection, document, chargeDoc, user},
|
||||
properties: { collection, document, chargeDoc, user },
|
||||
});
|
||||
|
||||
const updateResult = await updateMutator({
|
||||
|
@ -583,11 +545,7 @@ app.post('/stripe', addRawBody, async function(req, res) {
|
|||
const sig = req.headers['stripe-signature'];
|
||||
|
||||
try {
|
||||
const event = stripe.webhooks.constructEvent(
|
||||
req.rawBody,
|
||||
sig,
|
||||
stripeSettings.endpointSecret
|
||||
);
|
||||
const event = stripe.webhooks.constructEvent(req.rawBody, sig, stripeSettings.endpointSecret);
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('event ///////////////////');
|
||||
|
@ -615,20 +573,13 @@ app.post('/stripe', addRawBody, async function(req, res) {
|
|||
console.log(invoice);
|
||||
|
||||
// look up corresponding subscription
|
||||
const subscription = await stripe.subscriptions.retrieve(
|
||||
invoice.subscription
|
||||
);
|
||||
const subscription = await stripe.subscriptions.retrieve(invoice.subscription);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('////// subscription');
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(subscription);
|
||||
|
||||
const {
|
||||
userId,
|
||||
productKey,
|
||||
associatedCollection,
|
||||
associatedId,
|
||||
} = subscription.metadata;
|
||||
const { userId, productKey, associatedCollection, associatedId } = subscription.metadata;
|
||||
|
||||
if (associatedCollection && associatedId) {
|
||||
const collection = _.findWhere(Collections, {
|
||||
|
@ -653,7 +604,7 @@ app.post('/stripe', addRawBody, async function(req, res) {
|
|||
livemode: subscription.livemode,
|
||||
};
|
||||
|
||||
processAction({collection, document, stripeObject: charge, args});
|
||||
processAction({ collection, document, stripeObject: charge, args });
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
|
@ -762,14 +713,14 @@ webAppConnectHandlersUse(Meteor.bindEnvironment(app), {
|
|||
Meteor.startup(() => {
|
||||
registerCallback({
|
||||
name: 'stripe.receive.sync',
|
||||
description: "Modify any metadata before calling Stripe's API",
|
||||
description: 'Modify any metadata before calling Stripe\'s API',
|
||||
arguments: [
|
||||
{metadata: 'Metadata about the action'},
|
||||
{user: 'The user'},
|
||||
{product: 'Product created with addProduct'},
|
||||
{collection: 'Associated collection of the charge'},
|
||||
{document: 'Associated document in collection to the charge'},
|
||||
{args: 'Original mutation arguments'},
|
||||
{ metadata: 'Metadata about the action' },
|
||||
{ user: 'The user' },
|
||||
{ product: 'Product created with addProduct' },
|
||||
{ collection: 'Associated collection of the charge' },
|
||||
{ document: 'Associated document in collection to the charge' },
|
||||
{ args: 'Original mutation arguments' },
|
||||
],
|
||||
runs: 'sync',
|
||||
newSyntax: true,
|
||||
|
@ -778,14 +729,14 @@ Meteor.startup(() => {
|
|||
|
||||
registerCallback({
|
||||
name: 'stripe.receive.async',
|
||||
description: "Run after calling Stripe's API",
|
||||
description: 'Run after calling Stripe\'s API',
|
||||
arguments: [
|
||||
{metadata: 'Metadata about the charge'},
|
||||
{user: 'The user'},
|
||||
{product: 'Product created with addProduct'},
|
||||
{collection: 'Associated collection of the charge'},
|
||||
{document: 'Associated document in collection to the charge'},
|
||||
{args: 'Original mutation arguments'},
|
||||
{ metadata: 'Metadata about the charge' },
|
||||
{ user: 'The user' },
|
||||
{ product: 'Product created with addProduct' },
|
||||
{ collection: 'Associated collection of the charge' },
|
||||
{ document: 'Associated document in collection to the charge' },
|
||||
{ args: 'Original mutation arguments' },
|
||||
],
|
||||
runs: 'sync',
|
||||
newSyntax: true,
|
||||
|
@ -793,14 +744,13 @@ Meteor.startup(() => {
|
|||
|
||||
registerCallback({
|
||||
name: 'stripe.charge.async',
|
||||
description:
|
||||
'Perform operations immediately after the stripe subscription has completed',
|
||||
description: 'Perform operations immediately after the stripe subscription has completed',
|
||||
arguments: [
|
||||
{charge: 'The charge'},
|
||||
{collection: 'Associated collection of the subscription'},
|
||||
{document: 'Associated document in collection to the charge'},
|
||||
{args: 'Original mutation arguments'},
|
||||
{user: 'The user'},
|
||||
{ charge: 'The charge' },
|
||||
{ collection: 'Associated collection of the subscription' },
|
||||
{ document: 'Associated document in collection to the charge' },
|
||||
{ args: 'Original mutation arguments' },
|
||||
{ user: 'The user' },
|
||||
],
|
||||
runs: 'async',
|
||||
newSyntax: true,
|
||||
|
@ -808,14 +758,13 @@ Meteor.startup(() => {
|
|||
|
||||
registerCallback({
|
||||
name: 'stripe.subscribe.async',
|
||||
description:
|
||||
'Perform operations immediately after the stripe subscription has completed',
|
||||
description: 'Perform operations immediately after the stripe subscription has completed',
|
||||
arguments: [
|
||||
{subscription: 'The subscription'},
|
||||
{collection: 'Associated collection of the subscription'},
|
||||
{document: 'Associated document in collection to the charge'},
|
||||
{args: 'Original mutation arguments'},
|
||||
{user: 'The user'},
|
||||
{ subscription: 'The subscription' },
|
||||
{ collection: 'Associated collection of the subscription' },
|
||||
{ document: 'Associated document in collection to the charge' },
|
||||
{ args: 'Original mutation arguments' },
|
||||
{ user: 'The user' },
|
||||
],
|
||||
runs: 'async',
|
||||
newSyntax: true,
|
||||
|
@ -826,13 +775,12 @@ Meteor.startup(() => {
|
|||
description: 'Modify any metadata before sending the charge to stripe',
|
||||
arguments: [
|
||||
{
|
||||
modifier:
|
||||
'The modifier object used to update the associated collection',
|
||||
modifier: 'The modifier object used to update the associated collection',
|
||||
},
|
||||
{collection: 'Collection associated to the product'},
|
||||
{document: 'Associated document'},
|
||||
{chargeDoc: "Charge document returned by Stripe's API"},
|
||||
{user: 'The user'},
|
||||
{ collection: 'Collection associated to the product' },
|
||||
{ document: 'Associated document' },
|
||||
{ chargeDoc: 'Charge document returned by Stripe\'s API' },
|
||||
{ user: 'The user' },
|
||||
],
|
||||
runs: 'sync',
|
||||
returns: 'The modified arguments to be sent to stripe',
|
||||
|
@ -842,10 +790,10 @@ Meteor.startup(() => {
|
|||
name: 'stripe.process.async',
|
||||
description: 'Modify any metadata before sending the charge to stripe',
|
||||
arguments: [
|
||||
{collection: 'Collection associated to the product'},
|
||||
{document: 'Associated document'},
|
||||
{chargeDoc: "Charge document returned by Stripe's API"},
|
||||
{user: 'The user'},
|
||||
{ collection: 'Collection associated to the product' },
|
||||
{ document: 'Associated document' },
|
||||
{ chargeDoc: 'Charge document returned by Stripe\'s API' },
|
||||
{ user: 'The user' },
|
||||
],
|
||||
runs: 'async',
|
||||
returns: 'The modified arguments to be sent to stripe',
|
||||
|
@ -858,11 +806,7 @@ Meteor.startup(() => {
|
|||
Promise.awaitAll(
|
||||
Object.keys(Products)
|
||||
// Filter out function type products and those without a plan defined (non-subscription)
|
||||
.filter(
|
||||
productKey =>
|
||||
typeof Products[productKey] === 'object' &&
|
||||
Products[productKey].plan
|
||||
)
|
||||
.filter(productKey => typeof Products[productKey] === 'object' && Products[productKey].plan)
|
||||
.map(productKey => createOrRetrievePlan(Products[productKey]))
|
||||
);
|
||||
// eslint-disable-next-line no-console
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import {Provider} from 'react-redux';
|
||||
import {addCallback} from 'meteor/vulcan:core';
|
||||
import {initStore} from '../modules/redux';
|
||||
import { Provider } from 'react-redux';
|
||||
import { addCallback } from 'meteor/vulcan:core';
|
||||
import { initStore } from '../modules/redux';
|
||||
|
||||
const setupRedux = () => {
|
||||
const store = initStore();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {createStore, applyMiddleware, compose, combineReducers} from 'redux';
|
||||
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
|
||||
import _isEmpty from 'lodash/isEmpty';
|
||||
// TODO: now we should add some callback call to add the store to
|
||||
// Apollo SSR + client side too
|
||||
|
@ -48,9 +48,7 @@ export const configureStore = (
|
|||
}
|
||||
if (options.reducers) {
|
||||
reducers =
|
||||
typeof options.reducers === 'object'
|
||||
? combineReducers(options.reducers)
|
||||
: options.reducers;
|
||||
typeof options.reducers === 'object' ? combineReducers(options.reducers) : options.reducers;
|
||||
}
|
||||
this.replaceReducer(reducers);
|
||||
return store;
|
||||
|
@ -63,7 +61,7 @@ export const configureStore = (
|
|||
// **Notes: server side, addAction to server share with every req**
|
||||
let actions = {};
|
||||
export const addAction = addedAction => {
|
||||
actions = {...actions, ...addedAction};
|
||||
actions = { ...actions, ...addedAction };
|
||||
return actions;
|
||||
};
|
||||
export const getActions = () => actions;
|
||||
|
@ -73,7 +71,7 @@ export const getActions = () => actions;
|
|||
let reducers = {};
|
||||
|
||||
export const addReducer = addedReducer => {
|
||||
reducers = {...reducers, ...addedReducer};
|
||||
reducers = { ...reducers, ...addedReducer };
|
||||
return reducers;
|
||||
};
|
||||
export const getReducers = () => reducers;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import {Provider} from 'react-redux';
|
||||
import {addCallback} from 'meteor/vulcan:core';
|
||||
import {initStore} from '../modules/redux';
|
||||
import { Provider } from 'react-redux';
|
||||
import { addCallback } from 'meteor/vulcan:core';
|
||||
import { initStore } from '../modules/redux';
|
||||
|
||||
const setupRedux = () => {
|
||||
const store = initStore();
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// Setup SSR
|
||||
import {ServerStyleSheet} from 'styled-components';
|
||||
import {addCallback} from 'meteor/vulcan:core';
|
||||
import { ServerStyleSheet } from 'styled-components';
|
||||
import { addCallback } from 'meteor/vulcan:core';
|
||||
|
||||
const setupStyledComponents = () => {
|
||||
addCallback('router.server.wrapper', function collectStyles(app, {context}) {
|
||||
addCallback('router.server.wrapper', function collectStyles(app, { context }) {
|
||||
const stylesheet = new ServerStyleSheet();
|
||||
// @see https://www.styled-components.com/docs/advanced/#example
|
||||
const wrappedApp = stylesheet.collectStyles(app);
|
||||
|
@ -12,10 +12,7 @@ const setupStyledComponents = () => {
|
|||
return wrappedApp;
|
||||
});
|
||||
|
||||
addCallback('router.server.postRender', function appendStyleTags(
|
||||
sink,
|
||||
{context}
|
||||
) {
|
||||
addCallback('router.server.postRender', function appendStyleTags(sink, { context }) {
|
||||
sink.appendToHead(context.stylesheet.getStyleTags());
|
||||
return sink;
|
||||
});
|
||||
|
|
|
@ -9,9 +9,9 @@ import Adapter from 'enzyme-adapter-react-16.3';
|
|||
|
||||
const initComponentTest = () => {
|
||||
// setup enzyme
|
||||
Enzyme.configure({adapter: new Adapter()});
|
||||
Enzyme.configure({ adapter: new Adapter() });
|
||||
// and then load them in the app so that <Component.Whatever /> is defined
|
||||
import {populateComponentsApp, initializeFragments} from 'meteor/vulcan:lib';
|
||||
import { populateComponentsApp, initializeFragments } from 'meteor/vulcan:lib';
|
||||
// we need registered fragments to be initialized because populateComponentsApp will run
|
||||
// hocs, like withUpdate, that rely on fragments
|
||||
initializeFragments();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Utils} from 'meteor/vulcan:lib';
|
||||
import { Utils } from 'meteor/vulcan:lib';
|
||||
import Users from './collection.js';
|
||||
import moment from 'moment';
|
||||
import _ from 'underscore';
|
||||
|
@ -32,12 +32,7 @@ Users.getUser = function(userOrUserId) {
|
|||
Users.getUserName = function(user) {
|
||||
try {
|
||||
if (user.username) return user.username;
|
||||
if (
|
||||
user &&
|
||||
user.services &&
|
||||
user.services.twitter &&
|
||||
user.services.twitter.screenName
|
||||
)
|
||||
if (user && user.services && user.services.twitter && user.services.twitter.screenName)
|
||||
return user.services.twitter.screenName;
|
||||
} catch (error) {
|
||||
console.log(error); // eslint-disable-line
|
||||
|
@ -189,7 +184,7 @@ Users.hasCompletedProfile = function(user) {
|
|||
///////////////////
|
||||
|
||||
Users.findLast = function(user, collection) {
|
||||
return collection.findOne({userId: user._id}, {sort: {createdAt: -1}});
|
||||
return collection.findOne({ userId: user._id }, { sort: { createdAt: -1 } });
|
||||
};
|
||||
|
||||
Users.timeSinceLast = function(user, collection) {
|
||||
|
@ -263,5 +258,5 @@ Users.getRequiredFields = function() {
|
|||
// };
|
||||
|
||||
Users.findByEmail = function(email) {
|
||||
return Users.findOne({email: email});
|
||||
return Users.findOne({ email: email });
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {addGraphQLResolvers, Connectors} from 'meteor/vulcan:lib';
|
||||
import { addGraphQLResolvers, Connectors } from 'meteor/vulcan:lib';
|
||||
|
||||
const specificResolvers = {
|
||||
Query: {
|
||||
|
@ -26,30 +26,26 @@ const defaultOptions = {
|
|||
|
||||
const resolvers = {
|
||||
multi: {
|
||||
async resolver(root, {input = {}}, {currentUser, Users}, {cacheControl}) {
|
||||
const {terms = {}, enableCache = false, enableTotal = true} = input;
|
||||
async resolver(root, { input = {} }, { currentUser, Users }, { cacheControl }) {
|
||||
const { terms = {}, enableCache = false, enableTotal = true } = input;
|
||||
|
||||
if (cacheControl && enableCache) {
|
||||
const maxAge = defaultOptions.cacheMaxAge;
|
||||
cacheControl.setCacheHint({maxAge});
|
||||
cacheControl.setCacheHint({ maxAge });
|
||||
}
|
||||
|
||||
// get selector and options from terms and perform Mongo query
|
||||
let {selector, options} = await Users.getParameters(terms);
|
||||
let { selector, options } = await Users.getParameters(terms);
|
||||
options.skip = terms.offset;
|
||||
const users = await Connectors.find(Users, selector, options);
|
||||
|
||||
// restrict documents fields
|
||||
const restrictedUsers = Users.restrictViewableFields(
|
||||
currentUser,
|
||||
Users,
|
||||
users
|
||||
);
|
||||
const restrictedUsers = Users.restrictViewableFields(currentUser, Users, users);
|
||||
|
||||
// prime the cache
|
||||
restrictedUsers.forEach(user => Users.loader.prime(user._id, user));
|
||||
|
||||
const data = {results: restrictedUsers};
|
||||
const data = { results: restrictedUsers };
|
||||
|
||||
if (enableTotal) {
|
||||
// get total count of documents matching the selector
|
||||
|
@ -61,22 +57,22 @@ const resolvers = {
|
|||
},
|
||||
|
||||
single: {
|
||||
async resolver(root, {input = {}}, {currentUser, Users}, {cacheControl}) {
|
||||
const {selector = {}, enableCache = false} = input;
|
||||
const {documentId, slug} = selector;
|
||||
async resolver(root, { input = {} }, { currentUser, Users }, { cacheControl }) {
|
||||
const { selector = {}, enableCache = false } = input;
|
||||
const { documentId, slug } = selector;
|
||||
|
||||
if (cacheControl && enableCache) {
|
||||
const maxAge = defaultOptions.cacheMaxAge;
|
||||
cacheControl.setCacheHint({maxAge});
|
||||
cacheControl.setCacheHint({ maxAge });
|
||||
}
|
||||
|
||||
// don't use Dataloader if user is selected by slug
|
||||
const user = documentId
|
||||
? await Users.loader.load(documentId)
|
||||
: slug
|
||||
? await Connectors.get(Users, {slug})
|
||||
? await Connectors.get(Users, { slug })
|
||||
: await Connectors.get(Users);
|
||||
return {result: Users.restrictViewableFields(currentUser, Users, user)};
|
||||
return { result: Users.restrictViewableFields(currentUser, Users, user) };
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue