├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── bin └── neo4j-graphql ├── img └── neo4j-graphql-cli.gif ├── movieSchema.graphql ├── package.json └── src ├── config.js ├── helpers.js ├── index.js └── strings.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Mac stuff 9 | .DS_Store 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (http://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # Typescript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # images 9 | img 10 | 11 | # Mac stuff 12 | .DS_Store 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (http://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Typescript v1 declaration files 46 | typings/ 47 | 48 | # Optional npm cache directory 49 | .npm 50 | 51 | # Optional eslint cache 52 | .eslintcache 53 | 54 | # Optional REPL history 55 | .node_repl_history 56 | 57 | # Output of 'npm pack' 58 | *.tgz 59 | 60 | # Yarn Integrity file 61 | .yarn-integrity 62 | 63 | # dotenv environment variables file 64 | .env 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 2 | 3 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Neo4j GraphQL CLI 2 | 3 | ## NOTE: This project is deprecated in favor of [the official Neo4j GraphQL Library](https://neo4j.com/product/graphql-library/) 4 | 5 | Deploy Neo4j backed GraphQL APIs based on your custom GraphQL schema. 6 | 7 | **This is a very early project, under active development. Use for prototyping and demo projects only** 8 | 9 | ![](img/neo4j-graphql-cli.gif) 10 | 11 | **What does it do?** 12 | 13 | *`neo4j-graphql-cli` allows you to deploy a Neo4j GraphQL instance on Neo4j Sandbox. This Neo4j GraphQL instance will serve a GraphQL endpoint based on a user-defined GraphQL schema.* 14 | 15 | ## Steps 16 | 17 | 1. `npm install -g neo4j-graphql-cli` 18 | 1. Define your GraphQL schema using GraphQL schema syntax, *myschema.graphql* 19 | 1. `neo4j-graphql myschema.graphql` - if you do not specify a schema a [default movies schema]() will be used. 20 | 1. When prompted sign into Neo4j Sandbox using the URL provided. This URL will include a token to associate your `neo4j-graphql-cli` session with your sandbox instance. 21 | 1. Once your Neo4j GraphQL instance is deployed, you'll be presented with the credentials for your GraphQL instance, including a Graphiql URL. 22 | 23 | ## Schema First Development 24 | 25 | ### IDL / Schema Syntax 26 | 27 | ### Neo4j GraphQL Schema 28 | 29 | Neo4j GraphQL supports the basic schema syntax, with the addition of directives that expose the power of a graph database when combined with GraphQL: 30 | 31 | * `@cypher` 32 | 33 | **@cypher directive** 34 | 35 | The `@cypher` directive exposes the power of a full graph query language, Cypher, through GraphQL. 36 | 37 | ### Example movies schema 38 | 39 | ~~~graphql 40 | type User { 41 | id: Int 42 | name: String! 43 | movies: [Movie] @relation(name: "RATED", direction: "out") 44 | } 45 | 46 | type Movie { 47 | id: Int 48 | title: String! 49 | year: Int 50 | plot: String 51 | poster: String 52 | imdbRating: Float 53 | genres: [Genre] @relation(name: "IN_GENRE", direction: "out") 54 | actors: [Actor] @relation(name: "ACTED_IN", direction: "in") 55 | directors: [Director] @relation(name: "DIRECTED", direction: "in") 56 | similar: [Movie] @cypher(statement: "WITH {this} AS this MATCH (this)-[:IN_GENRE]->(:Genre)<-[:IN_GENRE]-(rec:Movie) WITH rec, COUNT(*) AS num ORDER BY num DESC RETURN rec LIMIT 10") 57 | } 58 | 59 | type Genre { 60 | id: Int 61 | name: String! 62 | movies: [Movie] @relation(name: "IN_GENRE", direction: "in") 63 | } 64 | 65 | type Director { 66 | id: Int 67 | name: String! 68 | movies: [Movie] @relation(name: "DIRECTED", direction: "out") 69 | } 70 | 71 | type Actor { 72 | id: Int 73 | name: String! 74 | movies: [Movie] @relation(name: "ACTED_IN", direction: "out") 75 | } 76 | ~~~ 77 | 78 | 79 | 80 | ## .graphqlconfig 81 | 82 | `neo4j-graphql-cli` supports [.graphqlconfig](https://github.com/graphcool/graphql-config) and will create a .graphqlconfig file that contains the path to the schema file, endpoints and authorization header. For example: 83 | 84 | ``` 85 | { 86 | "schemaPath":"personSchema.graphql", 87 | "extensions": 88 | { 89 | "endpoints": 90 | { 91 | "dev": 92 | { 93 | "url": "10-0-1-70-34285.neo4jsandbox.com/graphql/", 94 | "headers": 95 | { 96 | "Authorization":"Basic ${env:NEO4J_GRAPHQL_TOKEN}" 97 | } 98 | } 99 | } 100 | } 101 | } 102 | ``` 103 | 104 | # Features 105 | 106 | - [x] deploy Neo4j GraphQL Sandbox instance 107 | - [x] support user defined GraphQL schema 108 | - [x] support @cypher GraphQL schema directives 109 | - [x] .graphqlconfig support 110 | - [ ] support self-hosted Neo4j instances. NOTE: see 111 | - [ ] Docker support 112 | - [ ] support schema updates 113 | - [ ] client app scaffolding 114 | 115 | 116 | # Feedback 117 | 118 | This project is in active development and user validation. Have a use case you'd like to see supported? We'd love to hear your feedback, please email devrel@neo4j.com 119 | -------------------------------------------------------------------------------- /bin/neo4j-graphql: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('../src/index'); 3 | -------------------------------------------------------------------------------- /img/neo4j-graphql-cli.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/neo4j-graphql/neo4j-graphql-cli/7c33e3d1887e3600047fbd8c401e71a484436248/img/neo4j-graphql-cli.gif -------------------------------------------------------------------------------- /movieSchema.graphql: -------------------------------------------------------------------------------- 1 | type User { 2 | name: ID! 3 | seen: [Movie] @relation(name: "RATED") 4 | recommended(first:Int = 5): [Movie] @cypher(statement:"WITH $this as u MATCH (u)-->(:Movie)<--(:User)-->(reco:Movie) WHERE NOT (u)-[:RATED]->(reco) RETURN reco, count(*) as score ORDER BY score DESC LIMIT $first") 5 | } 6 | 7 | enum Genre { 8 | SciFi, Drama, Horror, Family, Comedy 9 | } 10 | 11 | type Movie { 12 | title: ID! 13 | year: Int 14 | plot: String 15 | imdbRating: Float 16 | poster: String 17 | genre: [Genre] 18 | actors: [Actor] @relation(name: "ACTED_IN", direction: "in") 19 | directors: [Director] @relation(name: "DIRECTED", direction: "in") 20 | similar(first:Int=5): [Movie] @cypher(statement: 21 | "WITH $this AS this MATCH (this)<-[:DIRECTED|ACTED_IN]-(:Person)-[:DIRECTED|ACTED_IN]->(sim:Movie) RETURN sim, COUNT(*) AS freq ORDER BY freq DESC LIMIT $first") 22 | } 23 | 24 | interface Person { 25 | name: ID! 26 | born: Int 27 | movies: [Movie] 28 | } 29 | 30 | type Director implements Person { 31 | name: ID! 32 | born: Int 33 | movies: [Movie] @relation(name: "DIRECTED") 34 | } 35 | 36 | type Actor implements Person { 37 | name: ID! 38 | born: Int 39 | movies: [Movie] @relation(name: "ACTED_IN") 40 | totalMovies:Int @cypher(statement: "WITH $this as this RETURN size( (this)-[:ACTED_IN]->(:Movie) ) as total") 41 | } 42 | 43 | schema { 44 | mutation: MutationType 45 | query: QueryType 46 | } 47 | 48 | type QueryType { 49 | topRatedMovies(rating:Int): [Movie] @cypher(statement: "MATCH (m:Movie)<-[r:RATED]-(:User) WHERE r.score > $score RETURN m, avg(r.rating) as score ORDER BY score DESC LIMIT 10") 50 | moviesByName(substring:String, first: Int): [Movie] @cypher(statement: "MATCH (m:Movie) WHERE toLower(m.title) CONTAINS toLower($substring) RETURN m LIMIT $first") 51 | } 52 | 53 | type MutationType { 54 | rateMovie(user:ID!,movie:ID!,score:Float=5) : String @cypher(statement:"MATCH (u:User {name:$user}), (m:Movie {title:$movie}) MERGE (u)-[r:RATED]->(m) SET r.score = $score") 55 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "neo4j-graphql-cli", 3 | "version": "0.0.10", 4 | "description": "Deploy a Neo4j backed GraphQL API based on your GraphQL schema", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "./bin/neo4j-graphql" 9 | }, 10 | "bin": { 11 | "neo4j-graphql": "./bin/neo4j-graphql" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/neo4j-graphql/neo4j-graphql-cli.git" 16 | }, 17 | "keywords": [ 18 | "GraphQL", 19 | "Neo4j" 20 | ], 21 | "author": "William Lyon", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/neo4j-graphql/neo4j-graphql-cli/issues" 25 | }, 26 | "homepage": "https://github.com/neo4j-graphql/neo4j-graphql-cli#readme", 27 | "dependencies": { 28 | "async": "^2.4.0", 29 | "chalk": "^1.1.3", 30 | "follow-redirects": "^1.2.3", 31 | "opn": "^5.0.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | API_KEY: "hafNGT5kK76l11tjtq9Q18swzs7frVPG3NlUrZc3", 3 | API_URL: "ppriuj7e7i.execute-api.us-east-1.amazonaws.com", 4 | API_ENV: "/prod/", 5 | GET_AUTH_TOKEN_ENDPOINT: "SandboxGetAuthToken", 6 | SANDBOX_REDIRECT_URL: "https://neo4j.com/sandbox-v2/graphql?action=graphql-run&auth_token=", 7 | SANDBOX_GET_INFO_ENDPOINT : "SandboxGetAuthTokenInfo/?token=", 8 | BASE_GRAPHIQL_URL: "https://neo4j-graphql.github.io/graphiql4all/index.html", 9 | DEFAULT_SCHEMA_FILENAME: "movieSchema.graphql", 10 | DEFAULT_SCHEMA: `type User { 11 | name: ID! 12 | seen: [Movie] @relation(name: "RATED") 13 | recommended(first:Int = 5): [Movie] @cypher(statement:"WITH $this as u MATCH (u)-->(:Movie)<--(:User)-->(reco:Movie) WHERE NOT (u)-[:RATED]->(reco) RETURN reco, count(*) as score ORDER BY score DESC LIMIT $first") 14 | } 15 | enum Genre { 16 | SciFi, Drama, Horror, Family, Comedy 17 | } 18 | type Movie { 19 | title: ID! 20 | year: Int 21 | plot: String 22 | imdbRating: Float 23 | poster: String 24 | genre: [Genre] 25 | actors: [Actor] @relation(name: "ACTED_IN", direction: "in") 26 | directors: [Director] @relation(name: "DIRECTED", direction: "in") 27 | similar(first:Int=5): [Movie] @cypher(statement: 28 | "WITH $this AS this MATCH (this)<-[:DIRECTED|ACTED_IN]-(:Person)-[:DIRECTED|ACTED_IN]->(sim:Movie) RETURN sim, COUNT(*) AS freq ORDER BY freq DESC LIMIT $first") 29 | } 30 | interface Person { 31 | name: ID! 32 | born: Int 33 | movies: [Movie] 34 | } 35 | type Director implements Person { 36 | name: ID! 37 | born: Int 38 | movies: [Movie] @relation(name: "DIRECTED") 39 | } 40 | type Actor implements Person { 41 | name: ID! 42 | born: Int 43 | movies: [Movie] @relation(name: "ACTED_IN") 44 | totalMovies:Int @cypher(statement: 45 | "WITH $this as this RETURN size( (this)-[:ACTED_IN]->(:Movie) ) as total") 46 | } 47 | schema { 48 | mutation: MutationType 49 | query: QueryType 50 | } 51 | type QueryType { 52 | topRatedMovies(rating:Int): [Movie] @cypher(statement: 53 | "MATCH (m:Movie)<-[r:RATED]-(:User) WHERE r.score > $score RETURN m, avg(r.rating) as score ORDER BY score DESC LIMIT 10") 54 | moviesByName(substring:String, first: Int): [Movie] @cypher(statement: 55 | "MATCH (m:Movie) WHERE toLower(m.title) CONTAINS toLower($substring) RETURN m LIMIT $first") 56 | } 57 | type MutationType { 58 | rateMovie(user:ID!,movie:ID!,score:Float=5) : String 59 | @cypher(statement:"MATCH (u:User {name:$user}), (m:Movie {title:$movie}) MERGE (u)-[r:RATED]->(m) SET r.score = $score") 60 | }` 61 | } 62 | -------------------------------------------------------------------------------- /src/helpers.js: -------------------------------------------------------------------------------- 1 | var config = require("./config"); 2 | 3 | /* 4 | * Builds the proxy ip from the private ip for the sandbox instance 5 | */ 6 | function constructProxyIp(creds) { 7 | var privip = creds['privip'], 8 | port = creds['port']; 9 | 10 | var proxyIp = privip.replace(/\./gi, "-") + "-" + port.toString() + "." + "neo4jsandbox.com"; 11 | return proxyIp; 12 | } 13 | 14 | function constructGraphiql(creds) { 15 | var graphqlEndpoint = "https://" + constructProxyIp(creds) + "/graphql/"; 16 | var graphiqlURL = config.BASE_GRAPHIQL_URL + "?graphqlEndpoint=" + graphqlEndpoint + "&graphqlUser=neo4j" + "&graphqlPassword=" + creds.password; 17 | 18 | return graphiqlURL; 19 | } 20 | 21 | module.exports = { 22 | constructProxyIp: constructProxyIp, 23 | constructGraphiql: constructGraphiql 24 | } 25 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | var async = require('async'), 2 | https = require('follow-redirects').https, 3 | http = require('http'), 4 | fs = require('fs'), 5 | strings = require('./strings'), 6 | config = require('./config'), 7 | chalk = require('chalk'), 8 | helpers = require('./helpers'), 9 | opn = require('opn'); 10 | 11 | // Chalk styles 12 | 13 | var chalkHighlightURL = chalk.bgBlue.bold.underline, 14 | chalkBanner = chalk.bgBlue.green, 15 | chalkURL = chalk.inverse; 16 | 17 | function presentBanner(callback) { 18 | console.log(chalkBanner(strings.banner)); 19 | console.log(chalk.blue(strings.boxTopText)); 20 | console.log(strings.introText); 21 | return callback(null); 22 | } 23 | 24 | function getAuthToken(callback) { 25 | console.log("Requesting token...."); 26 | 27 | var options = { 28 | headers: { 29 | 'x-api-key': config.API_KEY 30 | }, 31 | host: config.API_URL, 32 | path: config.API_ENV + config.GET_AUTH_TOKEN_ENDPOINT, 33 | method: 'POST' 34 | } 35 | 36 | var req = https.request(options, (res) => { 37 | 38 | res.on('data', (d) => { 39 | var obj = JSON.parse(d); 40 | if (obj.status && obj.token) { 41 | callback(null, obj.token) 42 | } else { 43 | callback("Error, no auth token received"); 44 | } 45 | }); 46 | 47 | }).on('error', (e) => { 48 | callback(e); 49 | }) 50 | 51 | req.write('{}'); 52 | req.end(); 53 | 54 | 55 | } 56 | 57 | function redirectUser(token, callback) { 58 | 59 | console.log("Token retrieved..."); 60 | console.log(strings.redirectForSandboxText); 61 | console.log(strings.boxTopText) 62 | console.log(chalkURL(config.SANDBOX_REDIRECT_URL + token)); 63 | console.log(strings.boxTopText); 64 | console.log(strings.sandboxExplainerText); 65 | 66 | opn(config.SANDBOX_REDIRECT_URL + token); 67 | 68 | callback(null, token); 69 | } 70 | 71 | 72 | 73 | 74 | function pollSandboxInfo(token, callback) { 75 | // is passed token 76 | // polls SANDBOX_GET_INFO_ENDPOINT until 200 77 | // calls callback with dictionary of credential info 78 | 79 | process.stdout.write("Waiting for you to sign in..."); 80 | 81 | var options = { 82 | headers: { 83 | 'x-api-key': config.API_KEY 84 | }, 85 | host: config.API_URL, 86 | path: config.API_ENV + config.SANDBOX_GET_INFO_ENDPOINT + token, 87 | } 88 | 89 | //console.log(options); 90 | 91 | async.doUntil(function(innerCallback) { 92 | https.get(options, (res) => { 93 | 94 | res.on('data', (d) => { 95 | var obj = JSON.parse(d); 96 | process.stdout.write("."); 97 | setTimeout(function() {innerCallback(null, obj)}, 5000) ; 98 | }); 99 | 100 | }).on('error', (e) => { 101 | return callback(e); 102 | }) 103 | }, 104 | function(obj) { 105 | 106 | // FIXME: better check here 107 | if (obj instanceof Array) { 108 | return true; 109 | } else { 110 | return false; 111 | } 112 | }, 113 | function(error, creds) { 114 | 115 | if (creds instanceof Array) { 116 | console.log("Neo4j GraphQL sandbox created!"); 117 | callback(error, creds[0]); 118 | } else { 119 | callback(error, creds); 120 | } 121 | } 122 | ) 123 | } 124 | 125 | function presentInfoSuccess(creds, callback) { 126 | console.log("Your GraphQL endpoint is available here:") 127 | console.log(chalkURL("https://" + helpers.constructProxyIp(creds) + "/graphql/\n")); 128 | console.log("Be sure to set Basic Auth header in your requests:"); 129 | console.log(chalk.bold("Authorization: Basic " + new Buffer("neo4j:" + creds.password).toString('base64'))); 130 | console.log("\n"); 131 | 132 | console.log("Explore your Neo4j GraphQL API in Graphiql here:") 133 | console.log(chalkURL(helpers.constructGraphiql(creds))); 134 | 135 | return callback(null, creds); 136 | } 137 | 138 | /* STEP 4 - post schema to https://$ip:$port/graphql/idl 139 | */ 140 | 141 | function postSchema(creds, callback) { 142 | 143 | var schemaFilename = process.argv[2], 144 | schema; 145 | 146 | if (!schemaFilename) { 147 | console.log("No schema specified, using default movieSchema.graphql"); 148 | schema = config.DEFAULT_SCHEMA; 149 | } else { 150 | try { 151 | schema = fs.readFileSync(schemaFilename); 152 | } catch (e) { 153 | console.log(chalk.red.bold("Unable to read " + schemaFilename + " - falling back to default movieSchema.graphlql")); 154 | schema = config.DEFAULT_SCHEMA; 155 | } 156 | } 157 | 158 | var options = { 159 | headers: { 160 | 'Authorization': "Basic " + new Buffer("neo4j:" + creds.password).toString('base64') 161 | }, 162 | host: helpers.constructProxyIp(creds), 163 | path: '/graphql/idl', 164 | method: 'POST' 165 | } 166 | 167 | var req = https.request(options, (res) => { 168 | 169 | res.on('data', (chunk) => { 170 | // got some data 171 | }); 172 | 173 | res.on('end', (d) => { 174 | //process.stdout.write(d); 175 | 176 | if (res.statusCode == 200) { 177 | return callback(null, creds) 178 | } else { 179 | return callback("Error, posting the schema IDL failed. Please ensure your schema format is valid."); 180 | } 181 | 182 | }); 183 | 184 | }).on('error', (e) => { 185 | return callback(e); 186 | }) 187 | 188 | req.write(schema); 189 | req.end(); 190 | } 191 | 192 | function writeGraphqlConfig(creds, callback) { 193 | 194 | process.env['NEO4J_GRAPHQL_ENDPOINT'] = helpers.constructProxyIp(creds) + "/graphql/"; 195 | process.env['NEO4J_GRAPHQL_TOKEN'] = new Buffer("neo4j:" + creds.password).toString('base64'); 196 | 197 | let graphqlConfig = { 198 | "schemaPath": process.argv[2] || config.DEFAULT_SCHEMA_FILENAME, 199 | "extensions": { 200 | "endpoints": { 201 | "dev": { 202 | "url": process.env['NEO4J_GRAPHQL_ENDPOINT'], 203 | "headers": { 204 | "Authorization": "Basic ${env:NEO4J_GRAPHQL_TOKEN}" 205 | } 206 | } 207 | } 208 | } 209 | }; 210 | 211 | try { 212 | fs.writeFileSync(".graphqlconfig", JSON.stringify(graphqlConfig)); 213 | } catch (e) { 214 | callback(e, creds); 215 | } 216 | 217 | console.log("Writing config to .graphqlconfig...."); 218 | // TODO: log environment variable export command given shell 219 | console.log("Note: .graphqlconfig references an environment variable for the authorization token."); 220 | console.log("Run this command to export NEO4J_GRAPHQ_TOKEN as an environment variable:"); 221 | console.log("export NEO4J_GRAPHQL_TOKEN=" + process.env['NEO4J_GRAPHQL_TOKEN']); 222 | 223 | callback(null, creds); 224 | } 225 | 226 | // MAIN 227 | async.waterfall([ 228 | presentBanner, 229 | getAuthToken, 230 | redirectUser, 231 | pollSandboxInfo, 232 | presentInfoSuccess, 233 | writeGraphqlConfig, 234 | postSchema 235 | ], function (err, result) { 236 | if (err) { 237 | console.log("ERROR - exiting"); 238 | console.log(err); 239 | process.exit(1); 240 | } else { 241 | if (result) { 242 | var name = result.name || ""; 243 | console.log("\nThanks " + name + "! Please email " + chalk.underline("devrel@neo4j.com") + " with any questions or feedback."); 244 | process.exit(0); 245 | } 246 | } 247 | }); -------------------------------------------------------------------------------- /src/strings.js: -------------------------------------------------------------------------------- 1 | var banner = ` 2 | ███╗ ██╗███████╗ ██████╗ ██╗ ██╗ ██╗ ██████╗ ██████╗ █████╗ ██████╗ ██╗ ██╗ ██████╗ ██╗ 3 | ████╗ ██║██╔════╝██╔═══██╗██║ ██║ ██║ ██╔════╝ ██╔══██╗██╔══██╗██╔══██╗██║ ██║██╔═══██╗██║ 4 | ██╔██╗ ██║█████╗ ██║ ██║███████║ ██║ ██║ ███╗██████╔╝███████║██████╔╝███████║██║ ██║██║ 5 | ██║╚██╗██║██╔══╝ ██║ ██║╚════██║██ ██║ ██║ ██║██╔══██╗██╔══██║██╔═══╝ ██╔══██║██║▄▄ ██║██║ 6 | ██║ ╚████║███████╗╚██████╔╝ ██║╚█████╔╝ ╚██████╔╝██║ ██║██║ ██║██║ ██║ ██║╚██████╔╝███████╗ 7 | ╚═╝ ╚═══╝╚══════╝ ╚═════╝ ╚═╝ ╚════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚══▀▀═╝ ╚══════╝ 8 | ` 9 | var introText = "Welcome to Neo4j-GraphQL. We will now deploy a Neo4j backed GraphQL API based on your custom defined schema"; 10 | 11 | var noSchemaText = "No schema specified. Using default movie schema."; 12 | 13 | var redirectForSandboxText = "You may now sign in to Neo4j Sandbox to create a Neo4j GraphQL instance. Please visit this URL: "; 14 | 15 | var sandboxExplainerText = "Once you sign into Neo4j Sandbox your Neo4j GraphQL instance will be deployed and a GraphQL endpoint will be created based on your schema"; 16 | 17 | var boxTopText = "--------------------------------------------------------------------------------"; 18 | 19 | module.exports = { 20 | banner: banner, 21 | introText: introText, 22 | noSchemaText: noSchemaText, 23 | redirectForSandboxText: redirectForSandboxText, 24 | boxTopText: boxTopText, 25 | sandboxExplainerText: sandboxExplainerText 26 | } 27 | --------------------------------------------------------------------------------