├── .babelrc ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .travis.yml ├── README.md ├── __test__ ├── index.test.js ├── schema.js └── server.js ├── database ├── pgClient.js └── queryString.js ├── dist ├── bundle.js └── c022c33dc0ce3c799f135353a8ab8365.png ├── package-lock.json ├── package.json ├── public ├── bluegenesis.jpg ├── genQLdemo.mp4 ├── genQLdemo_FINAL.gif ├── genesis.png ├── genesisCrop.png ├── genesisql.PNG ├── genesiswhite.png ├── index.html └── searchicon.png ├── server ├── app.js ├── generatedApolloServer.js ├── server.js └── utils │ ├── create_templates │ └── schema.js │ ├── models │ └── mongodb.js │ ├── searchController.js │ └── userController.js ├── src ├── App.jsx ├── components │ ├── codeOutput.jsx │ ├── dataView.jsx │ ├── form.jsx │ ├── inputField.jsx │ ├── navBar.jsx │ └── search.jsx ├── containers │ ├── mainContainer.jsx │ ├── productionContainer.jsx │ └── schemaBuilderContainer.jsx ├── index.js └── style.css └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/env", "@babel/preset-react"] 3 | } -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | !.eslintrc.js 2 | /node_modules -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "commonjs": true, 5 | "es6": true 6 | }, 7 | "extends": [ 8 | "airbnb" 9 | ], 10 | "globals": { 11 | "Atomics": "readonly", 12 | "SharedArrayBuffer": "readonly" 13 | }, 14 | "parserOptions": { 15 | "ecmaFeatures": { 16 | "jsx": true 17 | }, 18 | "ecmaVersion": 2018 19 | }, 20 | "plugins": [ 21 | "react" 22 | ], 23 | "rules": { 24 | } 25 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | 84 | # Gatsby files 85 | .cache/ 86 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 87 | # https://nextjs.org/blog/next-9-1#public-directory-support 88 | # public 89 | 90 | # vuepress build output 91 | .vuepress/dist 92 | 93 | # Serverless directories 94 | .serverless/ 95 | 96 | # FuseBox cache 97 | .fusebox/ 98 | 99 | # DynamoDB Local files 100 | .dynamodb/ 101 | 102 | # TernJS port file 103 | .tern-port 104 | 105 | # courtesy of https://github.com/github/gitignore/blob/master/Node.gitignore -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | env: 5 | - PG_URI=postgres://juqdonnj:URk1eALCbIPa5tgs29o2Tzs9YkvgL_yx@salt.db.elephantsql.com:5432/juqdonnj 6 | - MONGO_URI=mongodb+srv://fake:pass@cluster0-8hzwr.mongodb.net/test 7 | # secure: "e0NIYT4SkfIaprrl6OXwtjD8LVsyvYUQIY1+YJebqio1guXnYrNXLK9O2etTDpnv4BBcivsuH/f8d1W/4Q8kfT4M9i8kwfhpbpczlqJ5E2ww8z0F22yXiqt5OgMQAHmObc7zEwCOXBUYX9t0wy/uv4zqU6As5eGBq3tP2FQcuvII4NXvVtfVuv09i2QoWZxrCNiX09ZxyLqhbA1rpZcGq9iDgFMySk4mYlTTXKoJsjNoQkyXmu6ToRUHr0J9ixSYIjx9KDes6ZMDhckQDCK1ncJa0Tru6XetUhXrjQxtZ77RJA44QT1uJOmCfIF4xK5JV8Rl8JK9Bx800Rrs3C+IqYCr99Xa6v7f/PJN39wFGzchiwIOa+s+WpJ5Kbjrh6cIheAKIAnV6oF1HcwNE7vIJ2u5upmK1dqGx19AYM1e935CtEwKjGajrv5prSCVcSeXslSCyDOoNr2rvawZUd/8H/N06DIdjRUOVoEbRxjakqj5rhYlJm7UV/3++ngOdsVV61YY18+dePDPOILfeV3/pPtC2QA/TJ9GPRH18ydR6c0ciL6fS24Sz7TTFJffkEv0nqXMPlf77T9BTBVCMWr5shLjnkioAZc+iFgpDoo+KULvwLZwmCikF8amqHX9h3QYDJCcSviw+idkXDnZ5YZ1GQmMue/M33oeCjl+f3sNYw4=" 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![genesisQL Logo](https://github.com/oslabs-beta/genesisQL/blob/dev/public/genesisCrop.png?raw=true) 2 | 3 | # 4 | [![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://lbesson.mit-license.org/) 5 | [![Build Status](http://img.shields.io/travis/badges/badgerbadgerbadger.svg?style=flat-square)](https://travis-ci.org/badges/badgerbadgerbadger) 6 | [![GitHub release](https://img.shields.io/github/release/Naereen/StrapDown.js.svg)](https://GitHub.com/Naereen/StrapDown.js/releases/) 7 | [![Open Source Love svg2](https://badges.frapsoft.com/os/v2/open-source.svg?v=103)](https://github.com/ellerbrock/open-source-badges/) 8 | 9 | # genesisQL 10 | [genesisQL](http://www.genesisql.com/) is **an open-source web-application that enables rapid schema-prototyping of GraphQL applications**, making your development process easier & faster. Made for developers, by developers. 11 | 12 | Created by [Adam Goren](https://github.com/adamgoren), [Tom Herrmann](https://github.com/TomHerrmann), [Xose Manolo](https://github.com/xosemanolo), and [Andrew Paisner](https://github.com/apaisner). 13 | 14 | 15 | 16 | See the full demo video here: [genesisQL Walkthrough](https://vimeo.com/374757326) 17 | 18 | ## Features 19 | - Generate graphQL schemas, types, and queries, through an intuitive graphical user interface (GUI) 20 | - Easily select which fields you'd like to use in your schema, from an automatically generated dropdown menu 21 | - Ability to specify whether fields are required or not 22 | - View data retrieved from API urls with a single search, in app 23 | - Copy generated schema code to clipboard with a single button click 24 | - Outputted code can be used to set-up/run a functional apollo-server, and access the graphQL playground 25 | 26 | ## Usage 27 | 1) Visit our website: [genesisQL Beta](http://www.genesisql.com/) 28 | 2) Enter an endpoint to retrieve your data from (e.g. https://swapi.co/api/people/2/) 29 | 3) Enter a name for your object type, and select input field names & types from the dropdown menu. Toggle if needed 30 | 4) Use the '+' button to add more fields as needed 31 | 5) Press submit to generate your schema output 32 | 6) Use the copy button to quickly select the outputted code, and paste it into your project folder as a new file 33 | 7) Install apollo server using a package manager of your choice: e.g. 'npm i apollo-server --save' 34 | 8) Simply run the new file, and the graphQL playground will appear 35 | 36 | ## Example Code Output 37 | ```javascript 38 | const { ApolloServer, gql } = require('apollo-server'); 39 | 40 | const typeDefs = gql` 41 | 42 | type starwars_traits { 43 | name: String! 44 | mass: Int 45 | homeworld: String 46 | } 47 | 48 | type Query { 49 | getAllstarwars_traits: [starwars_traits] 50 | getstarwars_traitsById(id: ID!): starwars_traits 51 | } 52 | 53 | type Mutation { 54 | addstarwars_traits(name: String!, mass: Int, homeworld: String, ): Boolean 55 | updatestarwars_traits(name: String!, mass: Int, homeworld: String, ): Boolean 56 | deletestarwars_traits(id: ID): Boolean 57 | } 58 | 59 | `; 60 | 61 | const resolvers = { 62 | }; 63 | 64 | const port = process.env.PORT || 4000; // defaults to port 4000 65 | const server = new ApolloServer({ typeDefs, resolvers }); 66 | 67 | server.listen({ port }).then(({ url }) => { 68 | console.log(`🚀 Server listening at ${url}`); 69 | }); 70 | ``` 71 | 72 | ## Contributing 73 | 74 | > To get started... 75 | 76 | ### Step 1 77 | 78 | - **Option 1** 79 | - 🍴 Fork this repo! 80 | 81 | - **Option 2** 82 | - 👯 Clone this repo to your local machine using `https://github.com/joanaz/HireDot2.git` 83 | 84 | ### Step 2 85 | 86 | - **HACK AWAY!** 🔨🔨🔨 87 | 88 | ### Step 3 89 | 90 | - 🔃 Create a new pull request using `https://github.com/oslabs-beta/genesisQL/compare`. 91 | -------------------------------------------------------------------------------- /__test__/index.test.js: -------------------------------------------------------------------------------- 1 | require('./schema.js'); 2 | require('./server.js'); 3 | -------------------------------------------------------------------------------- /__test__/schema.js: -------------------------------------------------------------------------------- 1 | let schemaGen = require('../server/utils/create_templates/schema.js'); 2 | 3 | function sum(a, b) { 4 | return a + b; 5 | } 6 | // console.log(schemaGen); 7 | 8 | // for testing purposes 9 | 10 | 11 | // const objectTypes; 12 | 13 | // const objectType1 = { 14 | // objTypeName: 'User', 15 | // fieldNames: ['id', 'name'], 16 | // fieldTypes: ['Int', 'String'], 17 | // }; 18 | // const objectType2 = { 19 | // objTypeName: 'Pet', 20 | // fieldNames: ['id', 'name'], 21 | // fieldTypes: ['Int', 'String'], 22 | // }; 23 | 24 | // objectTypes = [objectType1, objectType2]; 25 | describe('Schema Generator Unit Tests', () => { 26 | let objectTypes; 27 | beforeEach( ()=> { 28 | const objectType1 = { 29 | objTypeName: 'User', 30 | fieldNames: ['id', 'name'], 31 | fieldTypes: ['Int', 'String'], 32 | }; 33 | const objectType2 = { 34 | objTypeName: 'Pet', 35 | fieldNames: ['id', 'name'], 36 | fieldTypes: ['Int', 'String'], 37 | }; 38 | 39 | objectTypes = [objectType1, objectType2]; 40 | }) 41 | 42 | it('Testing type output of schema gen function', () => { 43 | // console.log(schemaGen); 44 | expect(schemaGen(objectTypes)).toEqual(expect.stringMatching(/.*type.*/)); 45 | }); 46 | }) -------------------------------------------------------------------------------- /__test__/server.js: -------------------------------------------------------------------------------- 1 | 2 | const request = require("supertest"); 3 | const assert = require("assert"); 4 | const app = require("../server/server.js"); 5 | // const request = supertest(app); 6 | 7 | describe('Test the root path', () => { 8 | it('It should response the GET method', () => { 9 | return request(app).get('/').expect(200); 10 | }); 11 | }) -------------------------------------------------------------------------------- /database/pgClient.js: -------------------------------------------------------------------------------- 1 | const { Pool } = require('pg'); 2 | const queryString = require('./queryString.js') 3 | 4 | const myURI = 'postgres://juqdonnj:URk1eALCbIPa5tgs29o2Tzs9YkvgL_yx@salt.db.elephantsql.com:5432/juqdonnj'; 5 | 6 | const URI = process.env.PG_URI || myURI; 7 | 8 | const pool = new Pool({ connectionString: URI }); 9 | 10 | // creates users table 11 | pool.query(queryString.createUserTable, (err, result) => { 12 | 13 | if(err) console.error('FIRST error', err); 14 | else { 15 | // console.log('TABLE users EXISTS') 16 | } 17 | }); 18 | 19 | module.exports = pool; 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /database/queryString.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | createUserTable: `CREATE TABLE IF NOT EXISTS users (_id SERIAL PRIMARY KEY, username VARCHAR, password VARCHAR)`, 3 | createSearchTable: `CREATE TABLE IF NOT EXISTS searches (_id SERIAL PRIMARY KEY, url VARCHAR, result VARCHAR)`, 4 | postSearchTable: `INSERT INTO searches (url, result) VALUES($1, $2)`, 5 | queryLogging: function (){ 6 | console.log('\n*********** visitsController.createVisit ****************', `\nMETHOD: ${req.method} \nENDPOINT: '${req.url}' \nBODY: ${JSON.stringify(req.body)} \nLOCALS: ${JSON.stringify(res.locals)} `); 7 | } 8 | 9 | } -------------------------------------------------------------------------------- /dist/c022c33dc0ce3c799f135353a8ab8365.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/genesisQL/141351af4caaf730c6111e3c66a25d1676c22893/dist/c022c33dc0ce3c799f135353a8ab8365.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "genesisql", 3 | "version": "1.0.0", 4 | "description": "Generates GraphQL schema WITH EASE", 5 | "main": "index.js", 6 | "scripts": { 7 | "serve": "nodemon server/server.js", 8 | "dev": "npm run serve & webpack-dev-server --mode development --open", 9 | "start": "node server/server.js", 10 | "build": "webpack --mode production", 11 | "test": "mocha __test__/server.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/oslabs-beta/GenesisQL.git" 16 | }, 17 | "keywords": [ 18 | "GraphQL", 19 | "schema" 20 | ], 21 | "author": "", 22 | "license": "ISC", 23 | "bugs": { 24 | "url": "https://github.com/oslabs-beta/GenesisQL/issues" 25 | }, 26 | "homepage": "https://github.com/oslabs-beta/GenesisQL#readme", 27 | "devDependencies": { 28 | "@babel/cli": "^7.10.3", 29 | "@babel/core": "^7.10.3", 30 | "@babel/preset-env": "^7.10.3", 31 | "@babel/preset-react": "^7.10.1", 32 | "babel-cli": "^6.26.0", 33 | "babel-loader": "^8.1.0", 34 | "babel-preset-env": "^1.7.0", 35 | "css-loader": "^3.6.0", 36 | "eslint": "^6.6.0", 37 | "eslint-config-airbnb": "^18.2.0", 38 | "eslint-plugin-import": "^2.21.2", 39 | "eslint-plugin-jsx-a11y": "^6.3.1", 40 | "eslint-plugin-react": "^7.20.0", 41 | "eslint-plugin-react-hooks": "^1.7.0", 42 | "file-loader": "^4.2.0", 43 | "jest": "^24.9.0", 44 | "jshint": "^2.11.1", 45 | "mocha": "^6.2.3", 46 | "nodemon": "^1.19.4", 47 | "style-loader": "^1.2.1", 48 | "superagent": "^5.2.2", 49 | "supertest": "^4.0.2", 50 | "webpack": "^4.43.0", 51 | "webpack-cli": "^3.3.12", 52 | "webpack-dev-server": "^3.11.0" 53 | }, 54 | "dependencies": { 55 | "@material-ui/core": "^4.10.2", 56 | "@material-ui/icons": "^4.9.1", 57 | "@material-ui/lab": "^4.0.0-alpha.56", 58 | "apollo-server": "^2.15.0", 59 | "body-parser": "^1.19.0", 60 | "cookie-parser": "^1.4.5", 61 | "dotenv": "^8.2.0", 62 | "express": "^4.17.1", 63 | "express-graphql": "^0.9.0", 64 | "graphql": "^14.6.0", 65 | "jest": "^24.9.0", 66 | "mongodb": "^3.5.9", 67 | "mongoose": "^5.9.20", 68 | "node-fetch": "^2.6.0", 69 | "notistack": "^0.9.17", 70 | "pg": "^7.18.2", 71 | "react": "^16.13.1", 72 | "react-copy-to-clipboard": "^5.0.2", 73 | "react-dom": "^16.13.1", 74 | "react-hot-loader": "^4.12.21", 75 | "react-toasts": "^3.0.6", 76 | "typeface-roboto": "0.0.75" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /public/bluegenesis.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/genesisQL/141351af4caaf730c6111e3c66a25d1676c22893/public/bluegenesis.jpg -------------------------------------------------------------------------------- /public/genQLdemo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/genesisQL/141351af4caaf730c6111e3c66a25d1676c22893/public/genQLdemo.mp4 -------------------------------------------------------------------------------- /public/genQLdemo_FINAL.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/genesisQL/141351af4caaf730c6111e3c66a25d1676c22893/public/genQLdemo_FINAL.gif -------------------------------------------------------------------------------- /public/genesis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/genesisQL/141351af4caaf730c6111e3c66a25d1676c22893/public/genesis.png -------------------------------------------------------------------------------- /public/genesisCrop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/genesisQL/141351af4caaf730c6111e3c66a25d1676c22893/public/genesisCrop.png -------------------------------------------------------------------------------- /public/genesisql.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/genesisQL/141351af4caaf730c6111e3c66a25d1676c22893/public/genesisql.PNG -------------------------------------------------------------------------------- /public/genesiswhite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/genesisQL/141351af4caaf730c6111e3c66a25d1676c22893/public/genesiswhite.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GraphQL Schema Generator - Data Graph Prototyping - genesisQL 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /public/searchicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/genesisQL/141351af4caaf730c6111e3c66a25d1676c22893/public/searchicon.png -------------------------------------------------------------------------------- /server/app.js: -------------------------------------------------------------------------------- 1 | // require in libraries 2 | const express = require('express'); 3 | const { ApolloServer } = require('apollo-server'); 4 | const path = require('path'); 5 | const fetch = require('node-fetch'); 6 | const bodyParser = require('body-parser'); 7 | const cookieParser = require('cookie-parser'); 8 | const searchController = require('./utils/searchController.js'); 9 | const schemaGen = require('./utils/create_templates/schema.js'); 10 | 11 | const graphQLschema = require('./utils/create_templates/schema.js'); 12 | 13 | require('dotenv').config(); 14 | 15 | // console.log('PROCESS ENV', process.env); 16 | 17 | // create our server 18 | const app = express(); 19 | const port = process.env.PORT || 3000; 20 | 21 | // handle incoming objects 22 | app.use(bodyParser()); 23 | app.use(cookieParser()); 24 | app.use(express.json()); 25 | // handles post data 26 | app.use(bodyParser.urlencoded({ extended: true })); 27 | 28 | /* Handles getting data and send it back to clients */ 29 | 30 | app.post( 31 | '/search', 32 | searchController.fetch, 33 | /* searchController.post, */ (req, res) => { 34 | // console.log('RESPONSE LOCALS FETCHED DATA', res.locals.fetch); 35 | // const { fetch } = res.locals 36 | res.status(200).send(res.locals.fetch); 37 | } 38 | ); 39 | 40 | // TO GENERATE CODE app.post('/code', ) 41 | app.post('/code', (req, res) => { 42 | const { objectTypes } = req.body; 43 | 44 | // console.log(req.body); 45 | 46 | // console.log(req.body); 47 | 48 | res.set('Content-Type', 'application/json'); 49 | res.send(JSON.stringify(schemaGen(objectTypes))); 50 | }); 51 | 52 | // serves PRODUCTION bundle 53 | app.use('/dist', express.static(path.resolve(__dirname, '../dist'))); 54 | 55 | // serves static developemnt files (/public) - DEV 56 | app.use('/', (req, res, next) => { 57 | res.sendFile(path.resolve(__dirname, '../public/index.html')); 58 | }); 59 | 60 | module.exports = { 61 | app, 62 | port, 63 | }; 64 | -------------------------------------------------------------------------------- /server/generatedApolloServer.js: -------------------------------------------------------------------------------- 1 | const { ApolloServer, gql } = require('apollo-server'); 2 | 3 | const typeDefs = gql` 4 | 5 | type people { 6 | name: String 7 | mass: Int 8 | } 9 | 10 | type Query { 11 | getAllpeople: [people] 12 | getpeopleById(id: ID!): people 13 | } 14 | 15 | type Mutation { 16 | addpeople(name: String, mass: Int, ): Boolean 17 | updatepeople(name: String, mass: Int, ): Boolean 18 | deletepeople(id: ID): Boolean 19 | } 20 | 21 | `; 22 | 23 | const resolvers = { 24 | }; 25 | 26 | const port = process.env.PORT || 4000; // defaults to port 4000 27 | const server = new ApolloServer({ typeDefs, resolvers }); 28 | 29 | server.listen({ port }).then(({ url }) => { 30 | console.log(`🚀 Server listening at ${url}`); 31 | }); 32 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | const { app, port } = require('./app'); 2 | // sets development port 3 | const server = app.listen(port, () => console.log(`listening on port ${port}`)); 4 | 5 | module.exports = server; 6 | -------------------------------------------------------------------------------- /server/utils/create_templates/schema.js: -------------------------------------------------------------------------------- 1 | const tab = ' '; 2 | 3 | // may need to sanitize user inputs for types (e.g. string -> String, number -> Int) 4 | function createSchema(objectTypes) { 5 | let result = ''; 6 | 7 | // create import string, start type def with backtick (`) 8 | result += importApolloServer(); 9 | 10 | // create object type strings 11 | for (const objectType of objectTypes) { 12 | result += createObjType(objectType); 13 | } 14 | 15 | // create query strings 16 | result += `${tab}type Query {\n`; 17 | for (const objectType of objectTypes) { 18 | result += createRootQuery(objectType); 19 | } 20 | result += `${tab}}\n\n`; 21 | 22 | // create mutation strings 23 | result += `${tab}type Mutation {\n`; 24 | for (const objectType of objectTypes) { 25 | result += createMutation(objectType); 26 | } 27 | result += `${tab}}\n\n`; 28 | 29 | // create resolver string 30 | result += createResolvers(); 31 | 32 | // close typedef with backtick (`), create server string 33 | result += createApolloServer(); 34 | 35 | // return final fully concatenated string 36 | return result; 37 | } 38 | // console.log(createSchema(objectTypes)); 39 | 40 | function createRootQuery(objectType) { 41 | const { objTypeName, fieldNames, fieldTypes } = objectType; 42 | let result = ''; 43 | result += `${tab}${tab}getAll${objTypeName}: [${objTypeName}]\n`; 44 | result += `${tab}${tab}get${objTypeName}ById(id: ID!): ${objTypeName}\n`; 45 | return result; 46 | } 47 | // console.log(createRootQuery(objectType)); 48 | 49 | function createMutation(objectType) { 50 | const { objTypeName, fieldNames, fieldTypes } = objectType; 51 | let result = ''; 52 | let args = ''; 53 | for (let i = 0; i < fieldNames.length; i++) { 54 | args += `${fieldNames[i]}: ${fieldTypes[i]}, `; 55 | } 56 | result += `${tab}${tab}add${objTypeName}(${args}): Boolean\n`; 57 | result += `${tab}${tab}update${objTypeName}(${args}): Boolean\n`; 58 | result += `${tab}${tab}delete${objTypeName}(id: ID): Boolean\n`; 59 | return result; 60 | } 61 | // console.log(createMutation(objectType)); 62 | 63 | function createObjType(objectType) { 64 | let result = ''; 65 | const { objTypeName, fieldNames, fieldTypes } = objectType; 66 | result += `${tab}type ${objTypeName} {\n`; 67 | for (let i = 0; i < fieldNames.length; i++) { 68 | result += `${tab}${tab}${fieldNames[i]}: ${fieldTypes[i]}\n`; 69 | } 70 | result += `${tab}}\n\n`; 71 | return result; 72 | } 73 | 74 | function createResolvers() { 75 | let result = ''; 76 | result += `${tab}\`; \n\n`; 77 | result += `${tab}const resolvers = {\n`; 78 | // result += `${tab}${tab}fieldName: (parent, args, context, info) => data;\n`; 79 | result += `${tab}};\n\n`; 80 | return result; 81 | } 82 | 83 | // importing our apollo server 84 | function importApolloServer() { 85 | let result = ''; 86 | result += `${tab}const { ApolloServer, gql } = require('apollo-server');\n\n`; 87 | result += `${tab}const typeDefs = gql\` \n\n`; 88 | return result; 89 | } 90 | 91 | // creating our apollo server 92 | function createApolloServer() { 93 | let result = ''; 94 | result += `${tab}const port = process.env.PORT || 4000; // defaults to port 4000 \n`; 95 | result += `${tab}const server = new ApolloServer({ typeDefs, resolvers });\n\n`; 96 | result += `${tab}server.listen({ port }).then(({ url }) => {\n`; 97 | result += `${tab}${tab}console.log(\`🚀 Server listening at \${url}\`);\n`; 98 | result += `${tab}});`; 99 | return result; 100 | } 101 | 102 | // console.log(createObjType(objectType)); 103 | 104 | module.exports = createSchema; 105 | -------------------------------------------------------------------------------- /server/utils/models/mongodb.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const myURI = 'mongodb+srv://fake:pass@cluster0-8hzwr.mongodb.net/test'; 4 | 5 | mongoose.connect(myURI, { useNewUrlParser: true, useUnifiedTopology: true }); 6 | 7 | mongoose.connection.once('open', () => { 8 | console.log('Connected to DB'); 9 | }) 10 | 11 | // const testSchema = new mongoose.Schema({ test: { type: 'String' } }); 12 | // const testModel = mongoose.model('Test', testSchema); 13 | // new testModel({ test: '1' }).save() -------------------------------------------------------------------------------- /server/utils/searchController.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | const pgClient = require('../../database/pgClient.js'); 3 | const queryString = require('../../database/queryString.js'); 4 | 5 | const searchController = {}; 6 | // searchController is the express middleware that handles the req 7 | // from the searchbar 8 | 9 | pgClient.query(queryString.createSearchTable, (err, result) => { 10 | if (err) console.error('FIRST error', err); 11 | else { 12 | // console.log('TABLE searches EXISTS'); 13 | } 14 | }); 15 | 16 | searchController.fetch = (req, res, next) => { 17 | const { url } = req.body; 18 | // console.log(url); 19 | fetch(url) 20 | .then((data) => 21 | // console.log(data); 22 | data.json()) 23 | .then((result) => { 24 | // console.log('RESULT IN THE SERVER CONTROLLER', result); 25 | res.locals.fetch = result; 26 | return next(); 27 | }) 28 | .catch((err) => next(err)); 29 | }; 30 | // Unit test for fetch func 31 | // TODO: move to test file 32 | // let req = {} 33 | // req.body = {url: 'https://swapi.co/api/people/1/'} 34 | // let res = {}; 35 | // res.locals = {}; 36 | // searchController.fetch(req, res, () => {}); 37 | 38 | searchController.post = (req, res, next) => { 39 | const { url } = req.body; 40 | const values = [`${url}`, `${res.locals.fetch}`]; 41 | pgClient.query(queryString.postSearchTable, values, (err, result) => { 42 | console.log(values); 43 | if (err) next(err); 44 | else { 45 | // console.log(`Cached Response from URL: ${url}`); 46 | res.send('Complete'); 47 | return next(); 48 | } 49 | }); 50 | }; 51 | 52 | // searchController.post(req, res, () => {}); 53 | module.exports = searchController; 54 | -------------------------------------------------------------------------------- /server/utils/userController.js: -------------------------------------------------------------------------------- 1 | const pgClient = require('../../database/pgClient.js'); 2 | // import queryString from '../../database/queryString.js'; 3 | 4 | function createNewUser (req, res, next) { 5 | const { username, password } = req.body; 6 | const text = `INSERT INTO users (username, password) VALUES($1, $2)`; 7 | const values = [`${username}`, `${password}`]; 8 | pgClient.query(text, values, (err, result) => { 9 | if(err) next(err); 10 | next(); 11 | console.log('users', result.rows[0]); 12 | }) 13 | } 14 | 15 | let req = {}; 16 | req.body = { 17 | username: 'bob', 18 | password: 'frank' 19 | } 20 | 21 | createNewUser(req, undefined ,() => {}) 22 | 23 | function checkPassword (req, res, next){ 24 | const { username, password } = req.body; 25 | // const values = []; 26 | const text = `SELECT * FROM users WHERE username='${username}' AND password='${password}'`; 27 | pgClient.query(text, [], (err, result) => { 28 | if(err) next(err); 29 | if(result.rowCount === 0){ 30 | //send message when we have wrong username or password 31 | res.send('wrong username or password') 32 | }else{ 33 | next(); 34 | } 35 | }) 36 | } 37 | // let req = {}; 38 | // req.body = { 39 | // username: 'bobs', 40 | // password: 'frank' 41 | // } 42 | 43 | // checkPassword(req, undefined ,() => {}) 44 | 45 | function updateUser (req, res, next) { 46 | const { username, password } = req.body; 47 | const text = `UPDATE users SET password= $2 WHERE username= $1`; 48 | const values = [`${username}`, `${password}`]; 49 | pgClient.query(text, values, (err, result) => { 50 | if(err) next(err); 51 | // console.log('inside and deep') 52 | next(); 53 | }) 54 | } 55 | // let req = {}; 56 | // req.body = { 57 | // username: 'bob', 58 | // password: 'franks' 59 | // } 60 | 61 | // updateUser(req, undefined,() => {}) 62 | 63 | function deleteUser (req, res, next) { 64 | const { username } = req.body; 65 | const text = `DELETE FROM users WHERE username=$1`; 66 | const value = [`${username}`]; 67 | pgClient.query(text, value, (err, result) => { 68 | if(err) next(err); 69 | console.log('deleted') 70 | }) 71 | } 72 | // let req = {}; 73 | // req.body = { 74 | // username: 'bob', 75 | // password: 'frank' 76 | // } 77 | 78 | // deleteUser(req, undefined ,() => {}) 79 | 80 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * *********************************** 3 | * 4 | * @module App 5 | * @author Tom Herrmann and Adam Goren 6 | * @date 10/29/2019 7 | * @description Top-level app component that renders Search and MainContainer Components 8 | * 9 | * *********************************** 10 | */ 11 | 12 | import React, { Component } from 'react'; 13 | import { hot } from 'react-hot-loader'; 14 | import './style.css'; 15 | import Icon from '@material-ui/core/Icon'; 16 | 17 | // component imports 18 | import { json } from 'body-parser'; 19 | import Search from './components/search'; 20 | import MainContainer from './containers/mainContainer'; 21 | import InputField from './components/inputField'; 22 | 23 | 24 | class App extends Component { 25 | constructor(props) { 26 | super(props); 27 | // defines App state 28 | this.state = { 29 | dataViewContent: '', 30 | currentTab: 'schemaBuilderTab', 31 | loading: false, 32 | inputFields: [], 33 | formSwitches: [false], 34 | codeGeneratedString: '', 35 | }; 36 | // binding methods to constructor 37 | this.dataPOSTRequest = this.dataPOSTRequest.bind(this); 38 | this.changeCurrentTab = this.changeCurrentTab.bind(this); 39 | this.handleNewFields = this.handleNewFields.bind(this); 40 | this.handleSwitchChange = this.handleSwitchChange.bind(this); 41 | this.handleFormSubmitButton = this.handleFormSubmitButton.bind(this); 42 | } 43 | 44 | changeCurrentTab(event, value) { 45 | switch (value) { 46 | case 'schemaBuilderTab': 47 | this.setState({ currentTab: 'schemaBuilderTab' }); 48 | // console.log('CHANGING CURRENT TAB TO SCB'); 49 | break; 50 | case 'codeOutputTab': 51 | this.setState({ currentTab: 'codeOutputTab' }); 52 | // console.log('CHANGING CURRENT TAB TO CO'); 53 | break; 54 | default: 55 | this.setState({ currentTab: 'schemaBuilderTab' }); 56 | } 57 | } 58 | 59 | // methods to pass as props 60 | dataPOSTRequest(data) { 61 | if (!data) return; 62 | this.setState({ loading: true }); 63 | // console.log('dataPOSTRequest INPUT FIELD', data); 64 | // console.log('JSON DATA', JSON.stringify(data)) 65 | fetch('/search', { 66 | method: 'POST', 67 | headers: { 68 | 'Content-Type': 'application/json', 69 | }, 70 | body: JSON.stringify({ url: data }), 71 | }) 72 | .then((data) => 73 | // console.log('data', data) 74 | data.json()) 75 | .then((result) => { 76 | this.setState({ dataViewContent: result, loading: false }); 77 | // console.log(result) 78 | }) 79 | .catch((err) => (console.log('ERROR', err))); 80 | } 81 | 82 | handleNewFields() { 83 | const newFieldIndex = this.state.inputFields.length + 1; 84 | 85 | const inputFieldsCopy = this.state.inputFields.slice(0); 86 | inputFieldsCopy.push(); 87 | 88 | const formSwitchesCopy = this.state.formSwitches.slice(0); 89 | formSwitchesCopy.push(false); 90 | 91 | this.setState({ inputFields: inputFieldsCopy, formSwitches: formSwitchesCopy }); 92 | } 93 | 94 | handleSwitchChange(event, value) { 95 | const switchIndex = Number(event.target.name.split('-')[1]); 96 | const formSwitchesCopy = this.state.formSwitches.slice(0); 97 | formSwitchesCopy[switchIndex] = value; 98 | this.setState({ formSwitches: formSwitchesCopy }); 99 | } 100 | 101 | handleFormSubmitButton() { 102 | const objectType = document.querySelector('.objectType').value; 103 | 104 | const fieldNames = []; 105 | document.querySelectorAll('.fieldNames').forEach( 106 | (el) => fieldNames.push(el.value), 107 | ); 108 | const fieldTypes = []; 109 | document.querySelectorAll('.fieldTypes').forEach( 110 | (el, index) => { 111 | // console.log('ELEMENT IN FIELD TYPE LOOP -->', el.value, index) 112 | if (this.state.formSwitches[index] === true) fieldTypes.push(`${el.value}!`); 113 | else fieldTypes.push(el.value); 114 | }, 115 | ); 116 | 117 | // CREATE PAYLOAD OBJECT TO SEND TO CODE-GENERATOR SERVER-SIDE 118 | const codeGenPayload = { 119 | objectTypes: [ 120 | { 121 | objTypeName: objectType, 122 | fieldNames, 123 | fieldTypes, 124 | }, 125 | ], 126 | }; 127 | // console.log('codeGenPayload:', codeGenPayload); 128 | 129 | // SEND FETCH REQUEST TO CODE-GEN ENDPOINT, WITH PAYLOAD 130 | fetch('/code', { 131 | method: 'POST', 132 | headers: { 133 | 'Content-Type': 'application/json', 134 | Accept: 'application/json', 135 | }, 136 | body: JSON.stringify(codeGenPayload), 137 | }) 138 | .then((data) => data.json()) 139 | .then((data) => { 140 | // console.log('data', data); 141 | // SETTING STATE 142 | this.setState({ codeGeneratedString: data, currentTab: 'codeOutputTab' }); 143 | // console.log('state is:', this.state); 144 | }); 145 | } 146 | 147 | render() { 148 | console.log('FORM SWITHC ARRAY IN APP -->', this.state.formSwitches); 149 | return ( 150 |
151 | 152 | 153 | 165 |
166 | ); 167 | } 168 | } 169 | 170 | export default hot(module)(App); 171 | -------------------------------------------------------------------------------- /src/components/codeOutput.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * *********************************** 3 | * 4 | * @module CodeOutput 5 | * @author Tom Herrmann and Adam Goren 6 | * @date 10/29/2019 7 | * @description Schema code output based on form inputs 8 | * 9 | * *********************************** 10 | */ 11 | 12 | import React, { Component } from 'react'; 13 | import { CopyToClipboard } from 'react-copy-to-clipboard'; 14 | 15 | import FileCopyIcon from '@material-ui/icons/FileCopy'; 16 | import Fab from '@material-ui/core/Fab'; 17 | 18 | import { withSnackbar } from 'notistack'; 19 | 20 | class CodeOutput extends Component { 21 | render() { 22 | return ( 23 |
24 |
25 |
{this.props.codeGeneratedString}
26 |
27 |
28 | 29 | { this.props.enqueueSnackbar('Schema copied!'); }} 35 | > 36 | 37 | 38 | 39 |
40 |
41 | ); 42 | } 43 | } 44 | 45 | export default withSnackbar(CodeOutput); 46 | -------------------------------------------------------------------------------- /src/components/dataView.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * *********************************** 3 | * 4 | * @module DataView 5 | * @author Tom Herrmann and Adam Goren 6 | * @date 10/29/2019 7 | * @description Display of data fetched from data collection 8 | * 9 | * *********************************** 10 | */ 11 | 12 | import React, { Component } from 'react'; 13 | import CircularProgress from '@material-ui/core/CircularProgress'; 14 | 15 | class DataView extends Component { 16 | constructor(props) { 17 | super(props); 18 | } 19 | 20 | render() { 21 | // console.log('loading exists', this.props.loading); 22 | // console.log('DISPLAY CONTENT', this.props.displayContent); 23 | return ( 24 |
25 |
26 |

Data View

27 |
28 |
29 |
{this.props.loading ?  : this.props.dataViewContent ? JSON.stringify(this.props.dataViewContent, null, 2) : null}
30 |
31 |
32 | ); 33 | } 34 | } 35 | 36 | export default DataView; 37 | -------------------------------------------------------------------------------- /src/components/form.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * *********************************** 3 | * 4 | * @module Form 5 | * @author Tom Herrmann and Adam Goren 6 | * @date 10/29/2019 7 | * @description Form for selecting which data is submitted in schema 8 | * @dataListSource https://blog.teamtreehouse.com/creating-autocomplete-dropdowns-datalist-element 9 | * *********************************** 10 | */ 11 | 12 | import React, { Component } from 'react'; 13 | import InputField from './inputField'; 14 | import Fab from '@material-ui/core/Fab'; 15 | import AddIcon from '@material-ui/icons/Add'; 16 | import Button from '@material-ui/core/Button'; 17 | 18 | class Form extends Component { 19 | constructor(props) { 20 | super(props); 21 | } 22 | 23 | render() { 24 | return ( 25 |
26 |
27 |
28 |

Field Editor

29 |
30 |
31 | 34 | 39 | {this.props.inputFields} 40 |
41 | 48 | 49 | 50 |
51 |
52 |
53 |
54 | 57 |
58 |
59 | ); 60 | } 61 | } 62 | 63 | export default Form; 64 | -------------------------------------------------------------------------------- /src/components/inputField.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Switch from '@material-ui/core/Switch'; 3 | 4 | class InputField extends Component { 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | render() { 10 | console.log('FIELD INDEX FROM INPUT FIELD COMPONENT -->', this.props.fieldIndex) 11 | const formDataTypes = {}; 12 | const { dataViewContent } = this.props; 13 | for (const key in dataViewContent) { 14 | let type = ''; 15 | switch (typeof dataViewContent[key]) { 16 | case 'string': 17 | type = 'String'; 18 | break; 19 | case 'number': 20 | type = 'Int'; 21 | break; 22 | case 'boolean': 23 | type = 'Boolean'; 24 | break; 25 | case 'object': 26 | type = 'Custom'; 27 | break; 28 | } 29 | formDataTypes[key] = type; 30 | } 31 | 32 | const formDataTypesKeys = Object.keys(formDataTypes); 33 | const graphQLTypes = ['String', 'Int', 'Boolean', 'Custom Type']; 34 | 35 | const formInputOptions = []; 36 | for (let i = 0; i < formDataTypesKeys.length; i++) { 37 | formInputOptions.push(