Preoptimize mutation, fragments and batched subscription documents

This commit is contained in:
Chi Vinh Le
2017-12-01 15:42:13 +01:00
parent 5c1314165c
commit 2c72975137
7 changed files with 61 additions and 37 deletions
@@ -9,7 +9,7 @@ class TalkProvider extends React.Component {
pym: this.props.pym,
plugins: this.props.plugins,
rest: this.props.rest,
graphqlRegistry: this.props.graphqlRegistry,
graphql: this.props.graphql,
notification: this.props.notification,
storage: this.props.storage,
history: this.props.history,
@@ -32,7 +32,7 @@ TalkProvider.childContextTypes = {
eventEmitter: PropTypes.object,
plugins: PropTypes.object,
rest: PropTypes.func,
graphqlRegistry: PropTypes.object,
graphql: PropTypes.object,
notification: PropTypes.object,
storage: PropTypes.object,
history: PropTypes.object,
@@ -179,7 +179,9 @@ export default function reduceDocument(document, options = {}) {
const mainDefinition = getMainDefinition(document);
const fragments = getFragmentDefinitions(document);
const operationDefinition = getOperationDefinition(document);
const path = operationDefinition.operation;
const path = operationDefinition
? operationDefinition.operation
: `type.${mainDefinition.typeCondition.name.value}`;
const execContext = {
fragmentMap: createFragmentMap(fragments),
@@ -224,6 +226,17 @@ export function createTypeGetter(introspectionData) {
const parts = path.split('.');
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
// Handle special path e.g. 'type.ROOT_QUERY.fieldName'
if (part === 'type') {
const type = parts[i + 1];
const nextPath = `type.${type}`;
result[nextPath] = type;
currentPath = nextPath;
i++;
continue;
}
const nextPath = currentPath ? `${currentPath}.${part}` : part;
if (nextPath in result) {
currentPath = nextPath;
+3 -6
View File
@@ -64,18 +64,15 @@ function hasEqualLeaves(a, b, path = '') {
export default (fragments) => hoistStatics((BaseComponent) => {
class WithFragments extends React.Component {
static contextTypes = {
graphqlRegistry: PropTypes.object,
graphql: PropTypes.object,
};
get graphqlRegistry() {
return this.context.graphqlRegistry;
return this.context.graphql.registry;
}
resolveDocument(documentOrCallback) {
const document = typeof documentOrCallback === 'function'
? documentOrCallback(this.props, this.context)
: documentOrCallback;
return this.graphqlRegistry.resolveFragments(document);
return this.context.graphql.resolveDocument(documentOrCallback, this.props, this.context);
}
fragments = mapValues(fragments, (val) => this.resolveDocument(val));
+3 -6
View File
@@ -42,18 +42,15 @@ export default (document, config = {}) => hoistStatics((WrappedComponent) => {
static contextTypes = {
eventEmitter: PropTypes.object,
store: PropTypes.object,
graphqlRegistry: PropTypes.object,
graphql: PropTypes.object,
};
get graphqlRegistry() {
return this.context.graphqlRegistry;
return this.context.graphql.registry;
}
resolveDocument(documentOrCallback) {
const document = typeof documentOrCallback === 'function'
? documentOrCallback(this.props, this.context)
: documentOrCallback;
return this.graphqlRegistry.resolveFragments(document);
return this.context.graphql.resolveDocument(documentOrCallback, this.props, this.context);
}
// Lazily resolve fragments from graphRegistry to support circular dependencies.
+4 -17
View File
@@ -4,12 +4,7 @@ import {getDefinitionName, separateDataAndRoot, getResponseErrors} from '../util
import PropTypes from 'prop-types';
import hoistStatics from 'recompose/hoistStatics';
import {getOperationName} from 'apollo-client/queries/getFromAST';
import {addTypenameToDocument} from 'apollo-client/queries/queryTransform';
import throttle from 'lodash/throttle';
import reduceDocument, {createTypeGetter} from '../graphql/reduceDocument';
import introspectionData from '../graphql/introspection.json';
const typeGetter = createTypeGetter(introspectionData);
const withSkipOnErrors = (reducer) => (prev, action, ...rest) => {
if (action.type === 'APOLLO_MUTATION_RESULT' && getResponseErrors(action.result)) {
@@ -46,7 +41,7 @@ export default (document, config = {}) => hoistStatics((WrappedComponent) => {
return class WithQuery extends React.Component {
static contextTypes = {
eventEmitter: PropTypes.object,
graphqlRegistry: PropTypes.object,
graphql: PropTypes.object,
client: PropTypes.object,
};
@@ -61,7 +56,7 @@ export default (document, config = {}) => hoistStatics((WrappedComponent) => {
subscriptionQueue = [];
get graphqlRegistry() {
return this.context.graphqlRegistry;
return this.context.graphql.registry;
}
get client() {
@@ -69,15 +64,7 @@ export default (document, config = {}) => hoistStatics((WrappedComponent) => {
}
resolveDocument(documentOrCallback) {
let document = typeof documentOrCallback === 'function'
? documentOrCallback(this.props, this.context)
: documentOrCallback;
document = reduceDocument(this.graphqlRegistry.resolveFragments(document), {typeGetter});
// We also add typenames to the document which apollo would usually do,
// but we also use the network interface in subscriptions directly
// which require the resolved typenames.
return addTypenameToDocument(document);
return this.context.graphql.resolveDocument(documentOrCallback, this.props, this.context);
}
emitWhenNeeded(data) {
@@ -124,7 +111,7 @@ export default (document, config = {}) => hoistStatics((WrappedComponent) => {
subscribeToMoreThrottled = ({document, variables, updateQuery}) => {
// We need to add the typenames and resolve fragments.
const query = addTypenameToDocument(this.graphqlRegistry.resolveFragments(document));
const query = this.resolveDocument(document);
const handler = (error, data) => {
if (error) {
+9 -5
View File
@@ -12,6 +12,7 @@ import {BASE_PATH} from 'coral-framework/constants/url';
import {createPluginsService} from './plugins';
import {createNotificationService} from './notification';
import {createGraphQLRegistry} from './graphqlRegistry';
import {createGraphQLService} from './graphql';
import globalFragments from 'coral-framework/graphql/fragments';
import {createStorage, createPymStorage} from 'coral-framework/services/storage';
import {createHistory} from 'coral-framework/services/history';
@@ -121,7 +122,10 @@ export async function createContext({
introspectionData,
});
const plugins = createPluginsService(pluginsConfig);
const graphqlRegistry = createGraphQLRegistry(plugins.getSlotFragments.bind(plugins));
const graphql = createGraphQLService(
createGraphQLRegistry(plugins.getSlotFragments.bind(plugins)),
introspectionData,
);
if (!notification) {
// Use default notification service (pym based)
@@ -134,7 +138,7 @@ export async function createContext({
plugins,
eventEmitter,
rest,
graphqlRegistry,
graphql,
notification,
storage,
history,
@@ -143,13 +147,13 @@ export async function createContext({
};
// Load framework fragments.
Object.keys(globalFragments).forEach((key) => graphqlRegistry.addFragment(key, globalFragments[key]));
Object.keys(globalFragments).forEach((key) => graphql.registry.addFragment(key, globalFragments[key]));
// Register graphql extension
graphqlRegistry.add(graphqlExtension);
graphql.registry.add(graphqlExtension);
// Register plugin graphql extensions.
plugins.getGraphQLExtensions().forEach((ext) => graphqlRegistry.add(ext));
plugins.getGraphQLExtensions().forEach((ext) => graphql.registry.add(ext));
// Load plugin translations.
plugins.getTranslations().forEach((t) => loadTranslations(t));
@@ -0,0 +1,26 @@
import reduceDocument, {createTypeGetter} from '../graphql/reduceDocument';
import {addTypenameToDocument} from 'apollo-client/queries/queryTransform';
/**
* createHistory returns the history service for react router
* @param {string} basename base path of the url
* @return {Object} histor service
*/
export function createGraphQLService(registry, introspectionData) {
const typeGetter = createTypeGetter(introspectionData);
return {
registry,
resolveDocument(documentOrCallback, props, context) {
let document = typeof documentOrCallback === 'function'
? documentOrCallback(props, context)
: documentOrCallback;
document = reduceDocument(registry.resolveFragments(document), {typeGetter});
// We also add typenames to the document which apollo would usually do,
// but we also use the network interface in subscriptions directly
// which require the resolved typenames.
return addTypenameToDocument(document);
},
};
}