├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── package.json ├── scripts ├── mocha-bootload.js └── prepublish.sh └── src ├── __tests__ ├── context.js └── fields.js └── index.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | 4 | "plugins": [ 5 | "babel" 6 | ], 7 | 8 | "env": { 9 | "es6": true, 10 | "node": true 11 | }, 12 | 13 | "ecmaFeatures": { 14 | "arrowFunctions": true, 15 | "binaryLiterals": true, 16 | "blockBindings": true, 17 | "classes": true, 18 | "defaultParams": true, 19 | "destructuring": true, 20 | "experimentalObjectRestSpread": true, 21 | "forOf": true, 22 | "generators": true, 23 | "globalReturn": true, 24 | "jsx": true, 25 | "modules": true, 26 | "objectLiteralComputedProperties": true, 27 | "objectLiteralDuplicateProperties": true, 28 | "objectLiteralShorthandMethods": true, 29 | "objectLiteralShorthandProperties": true, 30 | "octalLiterals": true, 31 | "regexUFlag": true, 32 | "regexYFlag": true, 33 | "restParams": true, 34 | "spread": true, 35 | "superInFunctions": true, 36 | "templateStrings": true, 37 | "unicodeCodePointEscapes": true 38 | }, 39 | 40 | "rules": { 41 | "babel/arrow-parens": [2, "as-needed"], 42 | 43 | "array-bracket-spacing": [2, "always"], 44 | "arrow-spacing": 2, 45 | "block-scoped-var": 0, 46 | "brace-style": [2, "1tbs", {"allowSingleLine": true}], 47 | "callback-return": 2, 48 | "camelcase": [2, {"properties": "always"}], 49 | "comma-dangle": 0, 50 | "comma-spacing": 0, 51 | "comma-style": [2, "last"], 52 | "complexity": 0, 53 | "computed-property-spacing": [2, "never"], 54 | "consistent-return": 0, 55 | "consistent-this": 0, 56 | "curly": [2, "all"], 57 | "default-case": 0, 58 | "dot-location": [2, "property"], 59 | "dot-notation": 0, 60 | "eol-last": 2, 61 | "eqeqeq": 2, 62 | "func-names": 0, 63 | "func-style": 0, 64 | "generator-star-spacing": [0, {"before": true, "after": false}], 65 | "guard-for-in": 2, 66 | "handle-callback-err": [2, "error"], 67 | "id-length": 0, 68 | "id-match": [2, "^(?:_?[a-zA-Z0-9]*)|[_A-Z0-9]+$"], 69 | "indent": [2, 2, {"SwitchCase": 1}], 70 | "init-declarations": 0, 71 | "key-spacing": [2, {"beforeColon": false, "afterColon": true}], 72 | "linebreak-style": 2, 73 | "lines-around-comment": 0, 74 | "max-depth": 0, 75 | "max-len": [2, 80, 4], 76 | "max-nested-callbacks": 0, 77 | "max-params": 0, 78 | "max-statements": 0, 79 | "new-cap": 0, 80 | "new-parens": 2, 81 | "newline-after-var": 0, 82 | "no-alert": 2, 83 | "no-array-constructor": 2, 84 | "no-bitwise": 0, 85 | "no-caller": 2, 86 | "no-catch-shadow": 0, 87 | "no-class-assign": 2, 88 | "no-cond-assign": 2, 89 | "no-console": 1, 90 | "no-const-assign": 2, 91 | "no-constant-condition": 2, 92 | "no-continue": 0, 93 | "no-control-regex": 0, 94 | "no-debugger": 1, 95 | "no-delete-var": 2, 96 | "no-div-regex": 2, 97 | "no-dupe-args": 2, 98 | "no-dupe-keys": 2, 99 | "no-duplicate-case": 2, 100 | "no-else-return": 2, 101 | "no-empty": 2, 102 | "no-empty-character-class": 2, 103 | "no-empty-label": 2, 104 | "no-eq-null": 0, 105 | "no-eval": 2, 106 | "no-ex-assign": 2, 107 | "no-extend-native": 2, 108 | "no-extra-bind": 2, 109 | "no-extra-boolean-cast": 2, 110 | "no-extra-parens": 0, 111 | "no-extra-semi": 2, 112 | "no-fallthrough": 2, 113 | "no-floating-decimal": 2, 114 | "no-func-assign": 2, 115 | "no-implicit-coercion": 2, 116 | "no-implied-eval": 2, 117 | "no-inline-comments": 0, 118 | "no-inner-declarations": [2, "functions"], 119 | "no-invalid-regexp": 2, 120 | "no-invalid-this": 0, 121 | "no-irregular-whitespace": 2, 122 | "no-iterator": 2, 123 | "no-label-var": 2, 124 | "no-labels": 0, 125 | "no-lone-blocks": 2, 126 | "no-lonely-if": 2, 127 | "no-loop-func": 0, 128 | "no-mixed-requires": [2, true], 129 | "no-mixed-spaces-and-tabs": 2, 130 | "no-multi-spaces": 2, 131 | "no-multi-str": 2, 132 | "no-multiple-empty-lines": 0, 133 | "no-native-reassign": 0, 134 | "no-negated-in-lhs": 2, 135 | "no-nested-ternary": 0, 136 | "no-new": 2, 137 | "no-new-func": 0, 138 | "no-new-object": 2, 139 | "no-new-require": 2, 140 | "no-new-wrappers": 2, 141 | "no-obj-calls": 2, 142 | "no-octal": 2, 143 | "no-octal-escape": 2, 144 | "no-param-reassign": 2, 145 | "no-path-concat": 2, 146 | "no-plusplus": 0, 147 | "no-process-env": 0, 148 | "no-process-exit": 0, 149 | "no-proto": 2, 150 | "no-redeclare": 2, 151 | "no-regex-spaces": 2, 152 | "no-restricted-modules": 0, 153 | "no-return-assign": 2, 154 | "no-script-url": 2, 155 | "no-self-compare": 0, 156 | "no-sequences": 2, 157 | "no-shadow": 2, 158 | "no-shadow-restricted-names": 2, 159 | "no-spaced-func": 2, 160 | "no-sparse-arrays": 2, 161 | "no-sync": 2, 162 | "no-ternary": 0, 163 | "no-this-before-super": 2, 164 | "no-throw-literal": 2, 165 | "no-trailing-spaces": 2, 166 | "no-undef": 2, 167 | "no-undef-init": 2, 168 | "no-undefined": 0, 169 | "no-underscore-dangle": 0, 170 | "no-unexpected-multiline": 2, 171 | "no-unneeded-ternary": 2, 172 | "no-unreachable": 2, 173 | "no-unused-expressions": 2, 174 | "no-unused-vars": [2, {"vars": "all", "args": "after-used"}], 175 | "no-use-before-define": 0, 176 | "no-useless-call": 2, 177 | "no-var": 0, 178 | "no-void": 2, 179 | "no-warning-comments": 0, 180 | "no-with": 2, 181 | "object-curly-spacing": [0, "always"], 182 | "object-shorthand": [2, "always"], 183 | "one-var": [2, "never"], 184 | "operator-assignment": [2, "always"], 185 | "operator-linebreak": [2, "after"], 186 | "padded-blocks": 0, 187 | "prefer-const": 0, 188 | "prefer-reflect": 0, 189 | "prefer-spread": 0, 190 | "quote-props": [2, "as-needed"], 191 | "quotes": [2, "single"], 192 | "radix": 2, 193 | "require-yield": 2, 194 | "semi": [2, "always"], 195 | "semi-spacing": [2, {"before": false, "after": true}], 196 | "sort-vars": 0, 197 | "space-after-keywords": [2, "always"], 198 | "space-before-blocks": [2, "always"], 199 | "space-before-function-paren": [2, {"anonymous": "always", "named": "never"}], 200 | "space-in-parens": 0, 201 | "space-infix-ops": [2, {"int32Hint": false}], 202 | "space-return-throw-case": 2, 203 | "space-unary-ops": [2, {"words": true, "nonwords": false}], 204 | "spaced-comment": [2, "always"], 205 | "strict": 0, 206 | "use-isnan": 2, 207 | "valid-jsdoc": 0, 208 | "valid-typeof": 2, 209 | "vars-on-top": 0, 210 | "wrap-iife": 2, 211 | "wrap-regex": 0, 212 | "yoda": [2, "never", {"exceptRange": true}] 213 | } 214 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *~ 3 | *.iml 4 | .*.haste_cache.* 5 | .DS_Store 6 | .idea 7 | npm-debug.log 8 | 9 | node_modules 10 | build -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .* 2 | *.swp 3 | *~ 4 | *.iml 5 | .*.haste_cache.* 6 | .DS_Store 7 | .idea 8 | npm-debug.log 9 | 10 | node_modules 11 | scripts 12 | src -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Kadira Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GraphQL Utils 2 | 3 | This is a set of utilities for schemas building with [graphql-js](https://github.com/graphql/graphql-js). 4 | 5 | * [Usage](#usage) 6 | * [Picking Fields Before Resolve](#picking-fields-before-resolve) 7 | * [Picking Fields](#picking-fields) 8 | * [Picking Nested Fields](#picking-nested-fields) 9 | * [Maintaining Context](#maintaining-context) 10 | * [Setting a context to an object](#setting-a-context-to-an-object) 11 | * [Setting a context to an array](#setting-a-context-to-an-array) 12 | 13 | ### Usage 14 | 15 | ~~~graohq 16 | npm install graphql-utils 17 | ~~~ 18 | 19 | ### Picking Fields Before Resolve 20 | 21 | With this, you can get the field names of the result type based the query. With that, you can filter which fields you need to fetch from the DB. 22 | 23 | You can also check fields on nested fields as well. 24 | 25 | > First of all, have a look at our [schema](https://gist.github.com/arunoda/5c819dc4fc30f7792c68). 26 | 27 | #### Picking Fields 28 | 29 | Basically, we need to pick fields mentioned in the following query. 30 | 31 | Here's our query: 32 | 33 | ```js 34 | const query = ` 35 | { 36 | recentPost { 37 | id, 38 | title 39 | } 40 | } 41 | `; 42 | ``` 43 | 44 | We need to get them inside the resolve function of the `recentPost` field. This is how we to do it. 45 | 46 | ```js 47 | import {getFieldsFromInfo} from 'graphql-utils'; 48 | 49 | const RootQuery = new GraphQLObjectType({ 50 | ... 51 | recentPost: { 52 | type: Post, 53 | resolve(root, args, info) { 54 | const fieldsMap = getFieldsFromInfo(info); 55 | console.log(Object.keys(fieldsMap)); 56 | } 57 | } 58 | ... 59 | }); 60 | ``` 61 | 62 | Now you can see `[ "id", "title" ]` is printed on the screen. 63 | 64 | #### Picking Nested Fields 65 | 66 | Let's say, we've a query like this: 67 | 68 | ```js 69 | const query = ` 70 | { 71 | recentPost { 72 | id, 73 | title, 74 | author { 75 | name 76 | } 77 | } 78 | } 79 | `; 80 | ``` 81 | 82 | Then this is how we can get the nested fields of the field `author`. 83 | 84 | ```js 85 | import { 86 | getFieldsFromAst, 87 | getFieldsFromInfo 88 | } from 'graphql-utils'; 89 | 90 | const RootQuery = new GraphQLObjectType({ 91 | ... 92 | recentPost: { 93 | type: Post, 94 | resolve(root, args, info) { 95 | const fieldsMapOfPost = getFieldsFromInfo(info); 96 | const fieldsMapOfAuthor = getFieldsFromAst(info, fieldsMapOfPost['author']); 97 | console.log(Object.keys(fieldsMapOfAuthor)); 98 | } 99 | } 100 | ... 101 | }); 102 | ``` 103 | 104 | Now you can see `[ "name" ]` is printed on the screen. 105 | 106 | ### Maintaining Context 107 | 108 | Sometimes it's very important to pass a context down to child nodes in graph. There is no built in functionality in graphql-js for that. But we can add a context with the return value of the `resolve` function. Then we can grab it from the parent value(in resolve functions) of child nodes. 109 | 110 | For that we can use `withContext` and `getContext` functions of `graphql-utils.` 111 | 112 | **Read [this blog](#) post to see how this can be done in a real app.** 113 | 114 | #### Setting a context to an object 115 | 116 | This is how we can set a context to a plain object. 117 | 118 | ```js 119 | import { 120 | withContext, 121 | getContext 122 | } from 'graphql-utils'; 123 | 124 | const payload = {the: "payload"}; 125 | const context = {some: "data"}; 126 | const payloadWithContext = withContext(payload, context); 127 | 128 | // payloadWithContext has a field called __context with the context we provide 129 | // We can get it easily with the `getContext` method 130 | console.log(getContext(payloadWithContext)); 131 | ``` 132 | 133 | #### Setting a context to an array 134 | 135 | When setting a context to an array, we need to set it for all the items in the array. This is how to do it. 136 | 137 | ```js 138 | import { 139 | withContext, 140 | getContext 141 | } from 'graphql-utils' 142 | 143 | const payload = [{id: 10}, {id: 20}]; 144 | const rootContext = {some: 'context'} 145 | const payloadWithContext = withContext(payload, (item) => { 146 | // You can use an immutable data structure to prevent costly clones like this 147 | let newContext = JSON.parse(JSON.stringify(rootContext)); 148 | newContext.itemId = item.id; 149 | return newContext; 150 | }); 151 | 152 | // Now each of the items in the array has it's own context. 153 | // You can check it by printing the new payload 154 | payloadWithContext.forEach((item) => { 155 | const context = getContext(item) 156 | console.log(context); 157 | }); 158 | ``` 159 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphql-utils", 3 | "version": "1.0.0", 4 | "description": "A set of utilities for apps building with graphql-js", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/kadirahq/graphql-utils-js.git" 8 | }, 9 | "author": "Arunoda Susiripala (http://github.com/arunoda)", 10 | "license": "MIT", 11 | "main": "./build", 12 | "options": { 13 | "mocha": "--require scripts/mocha-bootload src/**/__tests__/**/*.js" 14 | }, 15 | "scripts": { 16 | "prepublish": "./scripts/prepublish.sh", 17 | "lint": "./node_modules/.bin/eslint ./src", 18 | "lintfix": "./node_modules/.bin/eslint ./src --fix", 19 | "testonly": "./node_modules/.bin/mocha $npm_package_options_mocha", 20 | "test": "npm run lint && npm run testonly" 21 | }, 22 | "devDependencies": { 23 | "mocha": "2.x.x", 24 | "chai": "3.x.x", 25 | "babel": "5.x.x", 26 | "babel-runtime": "5.x.x", 27 | "eslint": "1.7.x", 28 | "babel-eslint": "4.x.x", 29 | "eslint-plugin-babel": "2.x.x" 30 | }, 31 | "dependencies": { 32 | "lodash": "3.x.x" 33 | }, 34 | "peerDependencies": { 35 | "graphql": "0.4.x" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /scripts/mocha-bootload.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | require('babel/register')({ 11 | optional: ['runtime', 'es7.asyncFunctions'] 12 | }); 13 | 14 | process.on('unhandledRejection', function (error) { 15 | console.error('Unhandled Promise Rejection:'); 16 | console.error(error && error.stack || error); 17 | }); -------------------------------------------------------------------------------- /scripts/prepublish.sh: -------------------------------------------------------------------------------- 1 | # Because of a long-running npm issue (https://github.com/npm/npm/issues/3059) 2 | # prepublish runs after `npm install` and `npm pack`. 3 | # In order to only run prepublish before `npm publish`, we have to check argv. 4 | # if node -e "process.exit(($npm_config_argv).original[0].indexOf('pu') === 0)"; then 5 | # exit 0; 6 | # fi 7 | 8 | rm -rf ./build 9 | node_modules/.bin/babel src --ignore __tests__ --out-dir ./build -------------------------------------------------------------------------------- /src/__tests__/context.js: -------------------------------------------------------------------------------- 1 | import {describe, it} from 'mocha'; 2 | import {expect} from 'chai'; 3 | 4 | import { 5 | withContext, 6 | getContext 7 | } from '../'; 8 | 9 | describe('Context', () => { 10 | describe('withContext', () => { 11 | it('it should not mutate the input payload if it\'s an object', () => { 12 | const original = {}; 13 | const context = {the: 'context'}; 14 | withContext(original, context); 15 | 16 | expect(original.__context).to.be.equal(undefined); 17 | }); 18 | 19 | it('it should not mutate the input payload if it\'s an array', () => { 20 | const original = [ {}, {} ]; 21 | const context = {the: 'context'}; 22 | withContext(original, context); 23 | 24 | original.forEach(({__context}) => { 25 | expect(__context).to.be.equal(undefined); 26 | }); 27 | }); 28 | 29 | it('it should asign the context to an object', () => { 30 | const original = {the: 'data'}; 31 | const context = {the: 'context'}; 32 | const payload = withContext(original, context); 33 | 34 | expect(payload.__context).to.be.equal(context); 35 | delete payload.__context; 36 | expect(payload).to.be.deep.equal(original); 37 | }); 38 | 39 | it('it should asign the context to all the elements in the array', () => { 40 | const original = [ {a: 10}, [ {b: 20} ] ]; 41 | const context = {the: 'context'}; 42 | const payload = withContext(original, context); 43 | 44 | payload.forEach((o, index) => { 45 | expect(o.__context).to.be.equal(context); 46 | delete o.__context; 47 | expect(o).to.be.deep.equal(original[index]); 48 | }); 49 | }); 50 | }); 51 | 52 | describe('getContext', () => { 53 | it('should return the context if there is', () => { 54 | const context = {the: 'context'}; 55 | const payload = {__context: context}; 56 | 57 | expect(getContext(payload)).to.be.equal(context); 58 | }); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /src/__tests__/fields.js: -------------------------------------------------------------------------------- 1 | import {describe, it} from 'mocha'; 2 | import {expect} from 'chai'; 3 | import _ from 'lodash'; 4 | import { 5 | GraphQLObjectType, 6 | GraphQLSchema, 7 | GraphQLString, 8 | graphql 9 | } from 'graphql'; 10 | 11 | import { 12 | getFieldsFromAst, 13 | getFieldsFromInfo 14 | } from '../'; 15 | 16 | describe('Fields', () => { 17 | describe('getFieldsFromAst', () => { 18 | it('should give root level fields', async () => { 19 | const query = ` 20 | { 21 | recentPost { 22 | id, 23 | title 24 | } 25 | } 26 | `; 27 | 28 | const info = await getInfo(query); 29 | const fields = getFieldsFromAst(info, info.fieldASTs[0]); 30 | expect(_.keys(fields)).to.be.deep.equal([ 'id', 'title' ]); 31 | }); 32 | 33 | it('should give fields in fragments', async () => { 34 | const query = ` 35 | { 36 | recentPost { 37 | id, 38 | ...a 39 | } 40 | } 41 | 42 | fragment a on Post { 43 | title 44 | } 45 | `; 46 | 47 | const info = await getInfo(query); 48 | const fields = getFieldsFromAst(info, info.fieldASTs[0]); 49 | expect(_.keys(fields)).to.be.deep.equal([ 'id', 'title' ]); 50 | }); 51 | 52 | it('should give fields in nested fragments', async () => { 53 | const query = ` 54 | { 55 | recentPost { 56 | id, 57 | ...a 58 | } 59 | } 60 | 61 | fragment a on Post { 62 | title, 63 | ...b 64 | } 65 | 66 | fragment b on Post { 67 | author { 68 | name 69 | } 70 | } 71 | `; 72 | 73 | const info = await getInfo(query); 74 | const fields = getFieldsFromAst(info, info.fieldASTs[0]); 75 | expect(_.keys(fields)).to.be.deep.equal([ 'id', 'title', 'author' ]); 76 | }); 77 | 78 | it('should allow to get fields in nested types', async () => { 79 | const query = ` 80 | { 81 | recentPost { 82 | id, 83 | ...a 84 | } 85 | } 86 | 87 | fragment a on Post { 88 | title, 89 | ...b 90 | } 91 | 92 | fragment b on Post { 93 | author { 94 | name 95 | } 96 | } 97 | `; 98 | 99 | const info = await getInfo(query); 100 | const fieldsOnPost = getFieldsFromAst(info, info.fieldASTs[0]); 101 | const fieldsOnAuthor = getFieldsFromAst(info, fieldsOnPost['author']); 102 | expect(_.keys(fieldsOnAuthor)).to.be.deep.equal([ 'name' ]); 103 | }); 104 | }); 105 | 106 | describe('getFieldsFromInfo', () => { 107 | it('should give filed just passing the info', async () => { 108 | const query = ` 109 | { 110 | recentPost { 111 | id, 112 | title 113 | } 114 | } 115 | `; 116 | 117 | const info = await getInfo(query); 118 | const fields = getFieldsFromInfo(info); 119 | expect(_.keys(fields)).to.be.deep.equal([ 'id', 'title' ]); 120 | }); 121 | }); 122 | }); 123 | 124 | function getInfo(query) { 125 | let resolve = null; 126 | 127 | const Post = new GraphQLObjectType({ 128 | name: 'Post', 129 | fields: () => ({ 130 | id: {type: GraphQLString}, 131 | title: {type: GraphQLString}, 132 | author: {type: Author}, 133 | }) 134 | }); 135 | 136 | const Author = new GraphQLObjectType({ 137 | name: 'Author', 138 | fields: () => ({ 139 | id: {type: GraphQLString}, 140 | name: {type: GraphQLString}, 141 | }) 142 | }); 143 | 144 | const schema = new GraphQLSchema({ 145 | query: new GraphQLObjectType({ 146 | name: 'RootQuery', 147 | fields: () => ({ 148 | recentPost: { 149 | type: Post, 150 | resolve(root, args, info) { 151 | resolve(info); 152 | return {}; 153 | } 154 | } 155 | }) 156 | }) 157 | }); 158 | 159 | setTimeout(() => graphql(schema, query), 0); 160 | return new Promise(r => {resolve = r;}); 161 | } 162 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import {clone} from 'lodash'; 2 | 3 | export const getFieldsFromAst = function (info, ast, fields = {}) { 4 | ast.selectionSet.selections.map(function (selection) { 5 | if (selection.kind === 'Field') { 6 | fields[selection.name.value] = selection; 7 | } else if (selection.kind === 'FragmentSpread') { 8 | let fragment = info.fragments[selection.name.value]; 9 | getFieldsFromAst(info, fragment, fields); 10 | } 11 | }); 12 | 13 | return fields; 14 | }; 15 | 16 | export const getFieldsFromInfo = function (info) { 17 | return getFieldsFromAst(info, info.fieldASTs[0]); 18 | }; 19 | 20 | export const withContext = function (originalPayload, _contextFn) { 21 | let contextFn = _contextFn; 22 | if (typeof contextFn !== 'function') { 23 | let context = contextFn; 24 | contextFn = () => context; 25 | } 26 | 27 | var resultPayload; 28 | if (originalPayload instanceof Array) { 29 | resultPayload = []; 30 | originalPayload.forEach(_o => { 31 | let o = clone(_o); 32 | o.__context = contextFn(o); 33 | resultPayload.push(o); 34 | }); 35 | } else { 36 | resultPayload = clone(originalPayload); 37 | resultPayload.__context = contextFn(originalPayload); 38 | } 39 | 40 | return resultPayload; 41 | }; 42 | 43 | export const getContext = function (source) { 44 | return source.__context; 45 | }; 46 | --------------------------------------------------------------------------------