From 04ae844a1a23d1a82cf4305611c6d1ba53b31b79 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 5 Dec 2017 19:25:32 +0100 Subject: [PATCH 1/4] Use graphql-ast-tools --- client/coral-framework/services/graphql.js | 6 +++--- package.json | 1 + yarn.lock | 6 ++++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/client/coral-framework/services/graphql.js b/client/coral-framework/services/graphql.js index 859d57d8a..fb40ecf71 100644 --- a/client/coral-framework/services/graphql.js +++ b/client/coral-framework/services/graphql.js @@ -1,4 +1,4 @@ -import reduceDocument, {createTypeGetter} from '../graphql/reduceDocument'; +import {transformDocument, createTypeGetter} from 'graphql-ast-tools'; import {addTypenameToDocument} from 'apollo-client/queries/queryTransform'; /** @@ -10,7 +10,7 @@ export function createGraphQLService(registry, { introspectionData, optimize = false, }) { - const reduceOptions = { + const transformOptions = { typeGetter: optimize && introspectionData ? createTypeGetter(introspectionData) : null, // Use shared fragment map. @@ -27,7 +27,7 @@ export function createGraphQLService(registry, { document = registry.resolveFragments(document); if (optimize) { - document = reduceDocument(document, reduceOptions); + document = transformDocument(document, transformOptions); } // We also add typenames to the document which apollo would usually do, diff --git a/package.json b/package.json index 5ac69b6e6..7876c8636 100644 --- a/package.json +++ b/package.json @@ -107,6 +107,7 @@ "fs-extra": "^4.0.1", "gql-merge": "^0.0.4", "graphql": "^0.9.1", + "graphql-ast-tools": "^0.1.5", "graphql-docs": "0.2.0", "graphql-errors": "^2.1.0", "graphql-redis-subscriptions": "1.3.0", diff --git a/yarn.lock b/yarn.lock index 78ded3707..a536670b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3519,6 +3519,12 @@ graphql-anywhere@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/graphql-anywhere/-/graphql-anywhere-3.1.0.tgz#3ea0d8e8646b5cee68035016a9a7557c15c21e96" +graphql-ast-tools@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/graphql-ast-tools/-/graphql-ast-tools-0.1.5.tgz#840c8027b71d60b560482d08e70988e5bc91da90" + dependencies: + apollo-utilities "^1.0.3" + graphql-docs@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/graphql-docs/-/graphql-docs-0.2.0.tgz#cf803f9c9d354fa03e89073d74e419261a5bfa74" From d3040804a23eb8695c377b615295a0c90b8836dc Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 5 Dec 2017 19:39:14 +0100 Subject: [PATCH 2/4] Upgrade graphql-anywhere-optimized and graphql-ast-tools --- yarn.lock | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index a536670b9..c01e2e85a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12,8 +12,10 @@ eslint-plugin-react "^7.3.0" "@coralproject/graphql-anywhere-optimized@^0.1.0": - version "0.1.0" - resolved "https://registry.yarnpkg.com/@coralproject/graphql-anywhere-optimized/-/graphql-anywhere-optimized-0.1.0.tgz#3456f16f790d8593b0ca4355910578ab1c6edab5" + version "0.1.3" + resolved "https://registry.yarnpkg.com/@coralproject/graphql-anywhere-optimized/-/graphql-anywhere-optimized-0.1.3.tgz#f92f3906bb04f001aef725697237786752c49bd6" + dependencies: + graphql-ast-tools "^0.1.6" "@kadira/storybook-deployer@^1.1.0": version "1.2.0" @@ -3519,9 +3521,9 @@ graphql-anywhere@^3.0.1: version "3.1.0" resolved "https://registry.yarnpkg.com/graphql-anywhere/-/graphql-anywhere-3.1.0.tgz#3ea0d8e8646b5cee68035016a9a7557c15c21e96" -graphql-ast-tools@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/graphql-ast-tools/-/graphql-ast-tools-0.1.5.tgz#840c8027b71d60b560482d08e70988e5bc91da90" +graphql-ast-tools@^0.1.5, graphql-ast-tools@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/graphql-ast-tools/-/graphql-ast-tools-0.1.6.tgz#48eb656434bf4c7dba2a0d4784f1fdb988ab70ed" dependencies: apollo-utilities "^1.0.3" From 6a00d3d2497c66e5f9bd40b19d6f683b77f02033 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Tue, 5 Dec 2017 19:40:53 +0100 Subject: [PATCH 3/4] Remove unused code --- .../coral-framework/graphql/reduceDocument.js | 293 ------------------ 1 file changed, 293 deletions(-) delete mode 100644 client/coral-framework/graphql/reduceDocument.js diff --git a/client/coral-framework/graphql/reduceDocument.js b/client/coral-framework/graphql/reduceDocument.js deleted file mode 100644 index 12e0954d7..000000000 --- a/client/coral-framework/graphql/reduceDocument.js +++ /dev/null @@ -1,293 +0,0 @@ -import { - getMainDefinition, - getFragmentDefinitions, - createFragmentMap, - shouldInclude, - getOperationDefinition, -} from 'apollo-utilities'; - -function getDirectivesID(directives) { - let id = ''; - directives.forEach((directive) => { - id += `@${directive.name.value}(`; - let first = true; - directive.arguments.forEach((arg) => { - if (!first) { - id += ','; - } - first = false; - const value = arg.value.kind === 'Variable' - ? `$${arg.value.name.value}` - : arg.value.value; - id += `${arg.name.value}:${value}`; - }); - id += ')'; - }); - return id; -} - -// If two definitions have the same id, they can be merged. -function getDefinitionID(definition) { - - // Only merge when directives are exactly the same. - const trailing = definition.directives.length - ? `_${getDirectivesID(definition.directives)}` - : ''; - - switch (definition.kind) { - case 'FragmentSpread': - return `FragmentSpread_${definition.name.value}`; - case 'Field': - return `Field_${definition.alias ? definition.alias.value : definition.name.value}${trailing}`; - case 'InlineFragment': - return `InlineFragment_${definition.typeCondition.name.value}${trailing}`; - default: - throw new Error(`unknown definition kind ${definition.kind}`); - } -} - -/** - * Merge selections of 2 definitions. - */ -export function mergeDefinitions(a, b) { - const name = getDefinitionID(a); - - if (!!a.selectionSet !== !!b.selectionSet) { - throw Error(`incompatible field definition for ${name}`); - } - - if (!a.selectionSet) { - return b; - } - - const selectionSet = mergeSelectionSets(a.selectionSet, b.selectionSet); - - return { - ...b, - selectionSet, - }; -} - -/** - * Merge selectionSets - */ -export function mergeSelectionSets(a, b) { - const selectionsMap = [...a.selections, ...b.selections].reduce((o, sel) => { - const selName = getDefinitionID(sel); - if (!(selName in o)) { - o[selName] = sel; - return o; - } - o[selName] = mergeDefinitions(o[selName], sel); - return o; - }, {}); - - const selections = Object.keys(selectionsMap).map((key) => selectionsMap[key]); - - return { - ...b, - selections, - }; -} - -function getFragmentOrDie(name, execContext) { - const { - rawFragmentMap, - fragmentMap, - } = execContext; - - if (!(name in fragmentMap)) { - const fragment = rawFragmentMap[name]; - - if (!fragment) { - throw new Error(`fragment ${fragment.name.value} does not exist`); - } - - const typeCondition = fragment.typeCondition.name.value; - const transformed = transformDefinition(fragment, execContext, `type.${typeCondition}`, typeCondition); - fragmentMap[name] = transformed; - } - - return fragmentMap[name]; -} - -/** - * Return selections with resolved named fragments and directives. - */ -function getTransformedSelections(definition, path, gqlType, execContext) { - const { - variables, - } = execContext; - - const selectionsMap = definition.selectionSet.selections.reduce((o, sel) => { - if (variables && !shouldInclude(sel, variables)) { - - // Skip this entirely - return o; - } - if (sel.kind !== 'FragmentSpread') { - const transformed = transformDefinition(sel, execContext, path, gqlType); - const name = getDefinitionID(sel); - - // Merge existing value. - if (name in o) { - o[name] = mergeDefinitions(o[name], transformed); - return o; - } - - o[name] = transformed; - return o; - } - - const fragment = getFragmentOrDie(sel.name.value, execContext); - const typeCondition = fragment.typeCondition.name.value; - - // Turn NamedFragment into an InlineFragment. - if (gqlType !== typeCondition || fragment.directives.length) { - const node = { - ...fragment, - kind: 'InlineFragment', - }; - const name = getDefinitionID(node); - - // Merge existing value. - if (name in o) { - o[name] = mergeDefinitions(o[name], node); - return o; - } - - o[name] = node; - return o; - } - - // Merge NamedFragment directly into selections. - const fragmentSelections = fragment.selectionSet.selections; - fragmentSelections.forEach((s) => { - - if (variables && !shouldInclude(s, variables)) { - - // Skip this entirely - return; - } - - const selName = getDefinitionID(s); - if (!(selName in o)) { - o[selName] = s; - return; - } - - o[selName] = mergeDefinitions(o[selName], s); - }); - return o; - }, {}); - - const selections = Object.keys(selectionsMap).map((key) => selectionsMap[key]); - return selections; -} - -/** - * Resolve named fragments and directives in a definition. - */ -function transformDefinition(definition, execContext, path = '', type = null) { - if (!definition.selectionSet) { - return definition; - } - - const {typeGetter} = execContext; - - if (definition.kind === 'Field') { - const fieldName = definition.name.value; - path = `${path}.${fieldName}`; - - if (typeGetter) { - type = typeGetter(path); - } - } - - // InlineFragments - else if(!type && typeGetter) { - type = typeGetter(path); - } - - return { - ...definition, - selectionSet: { - ...definition.selectionSet, - selections: getTransformedSelections(definition, path, type, execContext), - }, - }; -} - -export default function reduceDocument(document, options = {}) { - const mainDefinition = getMainDefinition(document); - const fragments = getFragmentDefinitions(document); - const operationDefinition = getOperationDefinition(document); - const path = operationDefinition - ? operationDefinition.operation - : `type.${mainDefinition.typeCondition.name.value}`; - - const execContext = { - rawFragmentMap: createFragmentMap(fragments), - fragmentMap: options.fragmentMap || {}, - variables: options.variables, - typeGetter: options.typeGetter || (() => null), - }; - - return { - kind: 'Document', - definitions: [transformDefinition(mainDefinition, execContext, path)], - }; -} - -function getObjectType(fieldType) { - if (['NON_NULL', 'LIST'].indexOf(fieldType.kind) > -1) { - return getObjectType(fieldType.ofType); - } - return fieldType.name; -} - -function getFieldType(parentType, fieldName) { - const field = parentType.fields.find((f) => f.name === fieldName); - return getObjectType(field.type); -} - -export function createTypeGetter(introspectionData) { - const types = {}; - introspectionData.__schema.types.forEach((type) => types[type.name] = type); - - const result = { - 'query': introspectionData.__schema.queryType.name, - 'mutation': introspectionData.__schema.mutationType.name, - 'subscription': introspectionData.__schema.subscriptionType.name, - }; - - return (path) => { - if (result[path]) { - return result[path]; - } - let currentPath = ''; - 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; - continue; - } - result[nextPath] = getFieldType(types[result[currentPath]], part); - currentPath = nextPath; - } - return result[path]; - }; -} From aabd40d53a13c5ff07a8dd9675b9d5a59c1057e6 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Wed, 6 Dec 2017 11:45:16 +0100 Subject: [PATCH 4/4] Remove apollo-utilities --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 7876c8636..4712a75c4 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,6 @@ "@coralproject/graphql-anywhere-optimized": "^0.1.0", "accepts": "^1.3.4", "apollo-client": "^1.9.1", - "apollo-utilities": "^1.0.3", "app-module-path": "^2.2.0", "autoprefixer": "^6.5.2", "babel-cli": "6.26.0",