├── .gitignore ├── .nvmrc ├── LICENSE ├── README.md ├── package.json ├── src ├── graphql-client.js └── index.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 8.4 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Preposterous 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GraphQL Batching Helpers 2 | 3 | ## Installation 4 | 5 | ``` 6 | yarn add graphql-batching-helpers 7 | 8 | or 9 | 10 | npm install --save graphql-batching-helpers 11 | ``` 12 | 13 | ## Setup 14 | - process.env.GRAPHQL_ENDPOINT 15 | - process.env.GRAPHQL_ACCESS_TOKEN 16 | 17 | ## Quick Start 18 | 19 | ```es6 20 | await BatchQueryHelpers.batchDeleteAllModels({ 21 | modelName: 'Contact', 22 | first: 100, 23 | concurrency: 4, 24 | }) 25 | ``` 26 | What happens in order: 27 | 1. generate generate batch query name: 28 | 29 | ```graphql 30 | query getContacts { 31 | contacts: allContacts(first: 100) { 32 | id 33 | } 34 | } 35 | ``` 36 | 37 | 2. Request first N `Contact`s 38 | 3. If there are more than 0: 39 | - Generate match query mutations 40 | - Delete first N with concurrency N 41 | 42 | ## API 43 | 44 | - `generateBatchQuery` 45 | - modelName: String ("Contact") 46 | - first: Int (100) 47 | - `generateBatchDeleteMutation` 48 | - modelName: String ("Contact") 49 | - id 50 | - `generateBatchDeleteMutations` 51 | - values 52 | - id: String ("xyz") 53 | - options 54 | - modelName: String ("Contact") 55 | - `runBatchQuery` 56 | - options 57 | - modelName: String ("Contact") 58 | - first: Int (100) 59 | - `runBatchDelete` 60 | - mutations 61 | - options 62 | - concurrency: Int (4) 63 | - `batchDeleteAllModels` 64 | - options 65 | - modelName: String ("Contact") 66 | - first: Int (100) 67 | - concurrency: Int (4) 68 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "graphql-batching-helpers", 3 | "version": "0.1.2", 4 | "description": "GraphQL Batching Helpers for Creating & Deleting", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/Preposterous/graphql-batching-helpers.git" 12 | }, 13 | "keywords": [ 14 | "graphql", 15 | "batching" 16 | ], 17 | "author": "Peter Piekarczyk ", 18 | "license": "MIT", 19 | "bugs": { 20 | "url": "https://github.com/Preposterous/graphql-batching-helpers/issues" 21 | }, 22 | "homepage": "https://github.com/Preposterous/graphql-batching-helpers#readme", 23 | "dependencies": { 24 | "graphql-request": "^1.3.4", 25 | "lodash": "^4.17.4" 26 | }, 27 | "devDependencies": { 28 | "babel-cli": "^6.24.1", 29 | "babel-eslint": "^7.2.3", 30 | "eslint": "^4.0.0", 31 | "eslint-config-standard": "^10.2.1", 32 | "eslint-plugin-import": "^2.3.0", 33 | "eslint-plugin-node": "^5.0.0", 34 | "eslint-plugin-promise": "^3.5.0", 35 | "eslint-plugin-standard": "^3.0.1", 36 | "eslint-to-editorconfig": "^1.2.0", 37 | "prettier-eslint": "^6.2.3", 38 | "standard": "^10.0.2" 39 | }, 40 | "eslintConfig": { 41 | "extends": [ 42 | "standard" 43 | ], 44 | "parser": "babel-eslint", 45 | "plugins": [], 46 | "rules": { 47 | "indent": [ 48 | "error", 49 | 2 50 | ], 51 | "no-debugger": 0 52 | } 53 | }, 54 | "standard": { 55 | "parser": "babel-eslint", 56 | "ignore": [ 57 | "/dist/" 58 | ] 59 | }, 60 | "engines": { 61 | "node": "8.4" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/graphql-client.js: -------------------------------------------------------------------------------- 1 | const { GraphQLClient } = require('graphql-request') 2 | 3 | const client = new GraphQLClient(process.env.GRAPHQL_ENDPOINT, { 4 | headers: { 5 | Authorization: `Bearer ${process.env.GRAPHQL_ACCESS_TOKEN}` 6 | } 7 | }) 8 | 9 | module.exports = client 10 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash') 2 | const Client = require('./graphql-client') 3 | 4 | const generateBatchQuery = (modelName, first = 100) => { 5 | const queryName = `get${modelName}s` 6 | const queryAlias = `${modelName}s` 7 | const queryAll = `all${modelName}s` 8 | 9 | return ` 10 | query ${queryName} { 11 | ${queryAlias}: ${queryAll} (first: ${first}) { 12 | id 13 | } 14 | } 15 | ` 16 | } 17 | 18 | const generateBatchDeleteMutation = (modelName, id) => { 19 | const mutationName = `delete${modelName}` 20 | return ` 21 | mutation { 22 | ${mutationName}(id: "${id}") { 23 | id 24 | } 25 | } 26 | ` 27 | } 28 | 29 | const generateBatchDeleteMutations = (values, options) => { 30 | const { modelName } = options 31 | const mutations = _.chain(values) 32 | .map(v => generateBatchDeleteMutation(modelName, v.id)) 33 | .value() 34 | 35 | return mutations 36 | } 37 | 38 | const runBatchQuery = async options => { 39 | const { modelName, first } = options 40 | const batchQuery = generateBatchQuery(modelName, first) 41 | const data = `${modelName}s` 42 | const values = (await Client.request(batchQuery))[data] 43 | return values 44 | } 45 | 46 | const runBatchDelete = async (mutations, options) => { 47 | const deleteValues = await Promise.map(mutations, m => Client.request(m), { 48 | concurrency: options.concurrency || 4 49 | }) 50 | 51 | return deleteValues 52 | } 53 | 54 | const batchDeleteAllModels = async options => { 55 | console.log('Fetching Model:', options.modelName) 56 | const queryValues = await runBatchQuery(options) 57 | 58 | console.log('# of Items:', queryValues.length) 59 | if (queryValues && queryValues.length > 0) { 60 | console.log('Deleting Items') 61 | const mutationValues = generateBatchDeleteMutations(queryValues, options) 62 | await runBatchDelete(mutationValues, { 63 | concurrency: options.concurrency 64 | }) 65 | 66 | await batchDeleteAllModels(options) 67 | } else { 68 | console.log('Done! No more models to delete') 69 | } 70 | } 71 | 72 | module.exports.generateBatchQuery = generateBatchQuery 73 | module.exports.runBatchQuery = runBatchQuery 74 | module.exports.generateBatchDeleteMutation = generateBatchDeleteMutation 75 | module.exports.generateBatchDeleteMutations = generateBatchDeleteMutations 76 | module.exports.runBatchDelete = runBatchDelete 77 | module.exports.batchDeleteAllModels = batchDeleteAllModels 78 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | encoding@^0.1.11: 6 | version "0.1.12" 7 | resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" 8 | dependencies: 9 | iconv-lite "~0.4.13" 10 | 11 | graphql-request@^1.3.4: 12 | version "1.3.4" 13 | resolved "https://registry.yarnpkg.com/graphql-request/-/graphql-request-1.3.4.tgz#94d5f2e6644d9d9cbce0d06953e100c30e5cb633" 14 | dependencies: 15 | isomorphic-fetch "^2.2.1" 16 | 17 | iconv-lite@~0.4.13: 18 | version "0.4.18" 19 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2" 20 | 21 | is-stream@^1.0.1: 22 | version "1.1.0" 23 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" 24 | 25 | isomorphic-fetch@^2.2.1: 26 | version "2.2.1" 27 | resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" 28 | dependencies: 29 | node-fetch "^1.0.1" 30 | whatwg-fetch ">=0.10.0" 31 | 32 | lodash@^4.17.4: 33 | version "4.17.4" 34 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" 35 | 36 | node-fetch@^1.0.1: 37 | version "1.7.2" 38 | resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.2.tgz#c54e9aac57e432875233525f3c891c4159ffefd7" 39 | dependencies: 40 | encoding "^0.1.11" 41 | is-stream "^1.0.1" 42 | 43 | whatwg-fetch@>=0.10.0: 44 | version "2.0.3" 45 | resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" 46 | --------------------------------------------------------------------------------