├── .gitignore ├── .travis.yml ├── Dockerfile ├── Dockerfile-dev ├── Dockerrun.aws.json ├── LICENSE ├── README.md ├── __tests__ ├── CodeOutput.test.js ├── jestTest.js └── route.test.js ├── assets ├── -icon.png ├── Adam.png ├── ApolloBoilerplate.png ├── Brian.jpeg ├── Eric.jpeg ├── Mark.JPG ├── getstarted.gif ├── logo.png └── zoombeamcandid.png ├── client ├── App.jsx ├── Components │ ├── CodeOutput.jsx │ ├── CodeOutputButtons.jsx │ ├── Diagram.jsx │ ├── Docs.jsx │ ├── Team.jsx │ └── UriEntry.jsx ├── application.scss ├── index.js ├── logo.png ├── logo_transparent_background.png:Zone.Identifier └── theme.js ├── docker-compose-dev.yml ├── docker-compose-test.yml ├── index.html ├── package-lock.json ├── package.json ├── scripts └── deploy.sh ├── server ├── controllers │ ├── dataOutput 2.js │ ├── dataOutput.js │ ├── dbmodel.js │ ├── gqlController.js │ └── sqlController.js ├── gqlcorp │ ├── mutation.js │ ├── query.js │ ├── referenceData.js │ ├── type.js │ └── typeMain.js ├── router.js ├── server.js └── sqlQuery.sql └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | #See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | 8 | # production 9 | /dist 10 | 11 | # misc 12 | .DS_Store 13 | .env 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | config.env 19 | 20 | 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | services: 2 | - docker 3 | dist: focal 4 | script: 5 | # - docker-compose -f docker-compose-test.yml up --abort-on-container-exit 6 | - python3 -VV 7 | - pip -V 8 | before_deploy: 9 | - pip install --upgrade pip 10 | - python3 -m pip install --user awscli 11 | - python3 -m pip install --user awsebcli 12 | env: 13 | global: export PATH=$PATH:$HOME/.local/bin 14 | deploy: 15 | provider: script 16 | cleanup: true #skip_cleanup is deprecated, use cleanup instead 17 | on: 18 | branch: dev 19 | script: sh $TRAVIS_BUILD_DIR/scripts/deploy.sh -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14.17.6 2 | # Set up a WORKDIR for application in the container and set it to /usr/src/app. 3 | WORKDIR /usr/src/app 4 | COPY . /usr/src/app 5 | #COPY . /usr/src/app 6 | # RUN a command to npm install your node_modules in the container 7 | RUN npm i 8 | RUN npm run build 9 | # RUN a command to build your application in the container 10 | # EXPOSE your server port (3000) 11 | EXPOSE 3000 12 | # Create an ENTRYPOINT where you'll run node ./server/server.js 13 | CMD ["npm", "start"] 14 | -------------------------------------------------------------------------------- /Dockerfile-dev: -------------------------------------------------------------------------------- 1 | FROM node:14.17.6 2 | RUN npm i -g webpack 3 | WORKDIR /usr/src/app 4 | COPY package*.json ./ 5 | RUN npm i 6 | EXPOSE 8080 7 | -------------------------------------------------------------------------------- /Dockerrun.aws.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSEBDockerrunVersion": "1", 3 | "Image": { 4 | "Name": "746748738687.dkr.ecr.us-east-1.amazonaws.com/bql:", 5 | "Update": "true" 6 | }, 7 | "Ports": [{ 8 | "ContainerPort": "3000" 9 | }] 10 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 codesmith27 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 | # BeamQL 2 | Seamlessly visualize your PostgreSQL database and transform your REST API into GraphQL in less than 15 minutes. 3 | Live demo, docs, and more info [_here_](https://www.beamql.com). 4 | 5 | ## Table of Contents 6 | * [General Info](#general-information) 7 | * [Technologies Used](#technologies-used) 8 | * [Features](#features) 9 | * [Screenshots](#screenshots) 10 | * [Usage](#usage) 11 | * [To Do](#to-do) 12 | * [Acknowledgements](#acknowledgements) 13 | * [Contributors](#contact) 14 | 15 | 16 | 17 | ## General Information 18 | - BeamQL is on a mission to improve developers' workflow by helping them visualize their databases and transform their REST APIs to GraphQL. 19 | 20 | 21 | 22 | ## Technologies Used 23 | - React.js (React Hooks) - v17.0.2 24 | - React-flow-renderer - v5.2.0 25 | - Node.js - v14.17.6 26 | - Express - v4.17.1 27 | - jest - v27.1.0 28 | - supertest - v6.1.6 29 | - Docker 30 | - AWS 31 | - Travis.ci 32 | 33 | 34 | 35 | ## Features 36 | - Visualize user's PostgreSQL database by instantly creating an entity relationship diagram 37 | - Generate GraphQL complete type schema 38 | - Create GraphQL all resolvers 39 | 40 | 41 | ## Screenshots 42 | ![Example screenshot](./assets/getstarted.gif) 43 | 44 | 45 | 46 | 47 | ## Usage 48 | 1. Copy your PostgreSQL database URI into [beamql.com](www.beamql.com). 49 | 2. View and interact with the graphical visualization of the database and use it in API documentation to give users a better understanding of what they are working with. 50 | 3. Open NodeJS backend codebase in a code editor. 51 | 4. Use the schema and resolvers that BeamQL produces with [Apollo Server](https://www.npmjs.com/package/apollo-server-express) or a similar GraphQL server that suits your Node.js server. 52 | 5. Start server and navigate to localhost:X000/graphql 53 | 6. Behold the glory of your database accessible through the query language of the future! 54 | 55 | 56 | ## To Do 57 | - Migrate codebase to TypeScript! 58 | - Add support for every single SQL data type, options for custom types in GraphQL 59 | - Integrate [GraphiQL](https://github.com/graphql/graphiql) so users can test their endpoints with the generated schema and resolvers 60 | 61 | 62 | ## Acknowledgements 63 | - Many thanks to the tech accelerator [Open Source Labs](https://opensourcelabs.io/) for their continued support and sponsorship throughout this whole process. 64 | 65 | ## Contributors 66 | - [Brian Grosso](https://github.com/modelB) | [LinkedIn](https://www.linkedin.com/in/newarkbg/) 67 | - [Eric Askew](https://github.com/moonwalker5823) | [LinkedIn](https://www.linkedin.com/in/eric-askew-8a91714a/) 68 | - [Adam Goodman](https://github.com/AdamrG1) | [LinkedIn](https://www.linkedin.com/in/adam-goodman1/) 69 | - [Mark Liu](https://github.com/markyliu) | [LinkedIn](https://www.linkedin.com/in/markyliu1/) 70 | 71 | -------------------------------------------------------------------------------- /__tests__/CodeOutput.test.js: -------------------------------------------------------------------------------- 1 | import '@testing-library/react'; 2 | import CodeOutput from '../client/Components/CodeOutput'; 3 | 4 | // test('CodeOutput', () => { 5 | // expect(CodeOutput()).toBe(); 6 | // }); 7 | 8 | 9 | 10 | test('loads and displays codebox', async () => { 11 | render(CodeOutput) 12 | 13 | fireEvent.click(screen.getByText('Load Greeting')) 14 | 15 | await waitFor(() => screen.getByRole('heading')) 16 | 17 | expect(screen.getByRole('heading')).toHaveTextContent('hello there') 18 | expect(screen.getByRole('button')).toBeDisabled() 19 | }) 20 | 21 | test('handles server error', async () => { 22 | server.use( 23 | rest.get('/greeting', (req, res, ctx) => { 24 | return res(ctx.status(500)) 25 | }), 26 | ) 27 | -------------------------------------------------------------------------------- /__tests__/jestTest.js: -------------------------------------------------------------------------------- 1 | //require & install Jest? 2 | import capFirstLet from ('./server/gqlcorp/typeMain.js') 3 | import queryCreator from ('./server/gqlcorp/query.js'); 4 | 5 | 6 | 7 | test("cap first Letter", () => { 8 | expect(capFirstLet("eric")).toBe("Eric"); 9 | }); 10 | 11 | test("Returns correct Obj structure", () => { 12 | expect(queryCreator({item:'key', value: "value"})).toBe({item:Key,value:Value}); 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /__tests__/route.test.js: -------------------------------------------------------------------------------- 1 | //install SuperTest 2 | const { json } = require('express'); 3 | const supertest = require('supertest'); 4 | const app = require('../server/server.js'); 5 | const server = 'http://localhost:3000'; 6 | const request = supertest(app); 7 | 8 | 9 | 10 | it('Testing to see if Jest works', () => { 11 | expect(1).toBe(1); 12 | }) 13 | 14 | describe('Route integration', () => { 15 | describe('Post api/uri status test', function() { 16 | it('responds with 200 status', async function() { 17 | const response = await request.post('/api/uri') 18 | expect(response.status).toBe(200); 19 | }); 20 | }); 21 | describe('Post api/uri json test', function() { 22 | it('responds with json content', async function() { 23 | return request.post('/api/uri') 24 | expect('Content-Type').toBe(/application\/json/); 25 | }); 26 | }); 27 | describe('Post api/uri schema Types for StarWars DB', function() { 28 | it('responds with correct schema types for StarWars PSQL DB', async function() { 29 | return request.post('/api/uri') 30 | expect(res.locals.schemaTypes).toBe(`type Planet { 31 | _id: ID! 32 | name: String 33 | rotation_period: Int 34 | orbital_period: Int 35 | diameter: Int 36 | climate: String 37 | gravity: String 38 | terrain: String 39 | surface_water: String 40 | population: Int 41 | species: [Species] 42 | films: [Film] 43 | people: [Person] 44 | } 45 | 46 | type Film { 47 | _id: ID! 48 | title: String! 49 | episode_id: Int! 50 | opening_crawl: String! 51 | director: String! 52 | producer: String! 53 | release_date: String! 54 | people: [Person] 55 | planets: [Planet] 56 | species: [Species] 57 | vessels: [Vessel] 58 | } 59 | 60 | type Species { 61 | _id: ID! 62 | name: String! 63 | classification: String 64 | average_height: String 65 | average_lifespan: String 66 | hair_colors: String 67 | skin_colors: String 68 | eye_colors: String 69 | language: String 70 | planets: [Planet] 71 | films: [Film] 72 | people: [Person] 73 | } 74 | 75 | type Vessel { 76 | _id: ID! 77 | name: String! 78 | manufacturer: String 79 | model: String 80 | vessel_type: String! 81 | vessel_class: String! 82 | cost_in_credits: Int 83 | length: String 84 | max_atmosphering_speed: String 85 | crew: Int 86 | passengers: Int 87 | cargo_capacity: String 88 | consumables: String 89 | people: [Person] 90 | films: [Film] 91 | starshipSpecs: [StarshipSpec] 92 | } 93 | 94 | type Person { 95 | _id: ID! 96 | name: String! 97 | mass: String 98 | hair_color: String 99 | skin_color: String 100 | eye_color: String 101 | birth_year: String 102 | gender: String 103 | species: [Species] 104 | planets: [Planet] 105 | height: Int 106 | films: [Film] 107 | vessels: [Vessel] 108 | } 109 | 110 | type StarshipSpec { 111 | _id: ID! 112 | hyperdrive_rating: String 113 | MGLT: String 114 | vessels: [Vessel] 115 | } 116 | }`); 117 | }); 118 | }); 119 | describe('Post api/uri schema Mutations for StarWars DB', function() { 120 | it('responds with correct schema mutations for StarWars PSQL DB', async function() { 121 | return request.post('/api/uri') 122 | expect(res.locals.schemaMutations).toBe(`{addPlanet( 123 | name: String, 124 | rotation_period: Int, 125 | orbital_period: Int, 126 | diameter: Int, 127 | climate: String, 128 | gravity: String, 129 | terrain: String, 130 | surface_water: String, 131 | population: Int 132 | ): Planet! 133 | 134 | updatePlanet( 135 | _id: ID!, 136 | name: String, 137 | rotation_period: Int, 138 | orbital_period: Int, 139 | diameter: Int, 140 | climate: String, 141 | gravity: String, 142 | terrain: String, 143 | surface_water: String, 144 | population: Int 145 | ): Planet! 146 | 147 | deletePlanet(_id: ID!): Planet! 148 | 149 | addFilm( 150 | title: String!, 151 | episode_id: Int!, 152 | opening_crawl: String!, 153 | director: String!, 154 | producer: String!, 155 | release_date: String! 156 | ): Film! 157 | 158 | updateFilm( 159 | _id: ID!, 160 | title: String!, 161 | episode_id: Int!, 162 | opening_crawl: String!, 163 | director: String!, 164 | producer: String!, 165 | release_date: String! 166 | ): Film! 167 | 168 | deleteFilm(_id: ID!): Film! 169 | 170 | addSpecies( 171 | name: String!, 172 | classification: String, 173 | average_height: String, 174 | average_lifespan: String, 175 | hair_colors: String, 176 | skin_colors: String, 177 | eye_colors: String, 178 | language: String, 179 | homeworld_id: Int 180 | ): Species! 181 | 182 | updateSpecies( 183 | _id: ID!, 184 | name: String!, 185 | classification: String, 186 | average_height: String, 187 | average_lifespan: String, 188 | hair_colors: String, 189 | skin_colors: String, 190 | eye_colors: String, 191 | language: String, 192 | homeworld_id: Int 193 | ): Species! 194 | 195 | deleteSpecies(_id: ID!): Species! 196 | 197 | addVessel( 198 | name: String!, 199 | manufacturer: String, 200 | model: String, 201 | vessel_type: String!, 202 | vessel_class: String!, 203 | cost_in_credits: Int, 204 | length: String, 205 | max_atmosphering_speed: String, 206 | crew: Int, 207 | passengers: Int, 208 | cargo_capacity: String, 209 | consumables: String 210 | ): Vessel! 211 | 212 | updateVessel( 213 | _id: ID!, 214 | name: String!, 215 | manufacturer: String, 216 | model: String, 217 | vessel_type: String!, 218 | vessel_class: String!, 219 | cost_in_credits: Int, 220 | length: String, 221 | max_atmosphering_speed: String, 222 | crew: Int, 223 | passengers: Int, 224 | cargo_capacity: String, 225 | consumables: String 226 | ): Vessel! 227 | 228 | deleteVessel(_id: ID!): Vessel! 229 | 230 | addPerson( 231 | name: String!, 232 | mass: String, 233 | hair_color: String, 234 | skin_color: String, 235 | eye_color: String, 236 | birth_year: String, 237 | gender: String, 238 | species_id: Int, 239 | homeworld_id: Int, 240 | height: Int 241 | ): Person! 242 | 243 | updatePerson( 244 | _id: ID!, 245 | name: String!, 246 | mass: String, 247 | hair_color: String, 248 | skin_color: String, 249 | eye_color: String, 250 | birth_year: String, 251 | gender: String, 252 | species_id: Int, 253 | homeworld_id: Int, 254 | height: Int 255 | ): Person! 256 | 257 | deletePerson(_id: ID!): Person! 258 | 259 | addStarshipSpec( 260 | hyperdrive_rating: String, 261 | MGLT: String, 262 | vessel_id: Int! 263 | ): StarshipSpec! 264 | 265 | updateStarshipSpec( 266 | _id: ID!, 267 | hyperdrive_rating: String, 268 | MGLT: String, 269 | vessel_id: Int! 270 | ): StarshipSpec! 271 | 272 | deleteStarshipSpec(_id: ID!): StarshipSpec! 273 | 274 | }`) 275 | }); 276 | }); 277 | describe('Post api/uri schema Queries for StarWars DB', function() { 278 | it('responds with correct schema queries for StarWars PSQL DB', async function() { 279 | return request.post('/api/uri') 280 | expect(res.locals.schemaQueries).toBe(`type Query { 281 | planets: [Planet!]! 282 | planet(_id: ID!): Planet! 283 | films: [Film!]! 284 | film(_id: ID!): Film! 285 | species: [Species!]! 286 | speciesById(_id: ID!): Species! 287 | vessels: [Vessel!]! 288 | vessel(_id: ID!): Vessel! 289 | people: [Person!]! 290 | person(_id: ID!): Person! 291 | starshipSpecs: [StarshipSpec!]! 292 | starshipSpec(_id: ID!): StarshipSpec! 293 | }`) 294 | }); 295 | }); 296 | describe('Post api/uri complete schema for StarWars DB', function() { 297 | it('responds with correct complete schema string for StarWars PSQL DB', async function() { 298 | return request.post('/api/uri') 299 | // remove opening str after = on 310 300 | expect(res.locals.completeSchemaString).toBe(`const typeDefs = 301 | type Query { 302 | planets: [Planet!]! 303 | planet(_id: ID!): Planet! 304 | films: [Film!]! 305 | film(_id: ID!): Film! 306 | species: [Species!]! 307 | speciesById(_id: ID!): Species! 308 | vessels: [Vessel!]! 309 | vessel(_id: ID!): Vessel! 310 | people: [Person!]! 311 | person(_id: ID!): Person! 312 | starshipSpecs: [StarshipSpec!]! 313 | starshipSpec(_id: ID!): StarshipSpec! 314 | } 315 | 316 | type Mutation { 317 | addPlanet( 318 | name: String, 319 | rotation_period: Int, 320 | orbital_period: Int, 321 | diameter: Int, 322 | climate: String, 323 | gravity: String, 324 | terrain: String, 325 | surface_water: String, 326 | population: Int 327 | ): Planet! 328 | 329 | updatePlanet( 330 | _id: ID!, 331 | name: String, 332 | rotation_period: Int, 333 | orbital_period: Int, 334 | diameter: Int, 335 | climate: String, 336 | gravity: String, 337 | terrain: String, 338 | surface_water: String, 339 | population: Int 340 | ): Planet! 341 | 342 | deletePlanet(_id: ID!): Planet! 343 | 344 | addFilm( 345 | title: String!, 346 | episode_id: Int!, 347 | opening_crawl: String!, 348 | director: String!, 349 | producer: String!, 350 | release_date: String! 351 | ): Film! 352 | 353 | updateFilm( 354 | _id: ID!, 355 | title: String!, 356 | episode_id: Int!, 357 | opening_crawl: String!, 358 | director: String!, 359 | producer: String!, 360 | release_date: String! 361 | ): Film! 362 | 363 | deleteFilm(_id: ID!): Film! 364 | 365 | addSpecies( 366 | name: String!, 367 | classification: String, 368 | average_height: String, 369 | average_lifespan: String, 370 | hair_colors: String, 371 | skin_colors: String, 372 | eye_colors: String, 373 | language: String, 374 | homeworld_id: Int 375 | ): Species! 376 | 377 | updateSpecies( 378 | _id: ID!, 379 | name: String!, 380 | classification: String, 381 | average_height: String, 382 | average_lifespan: String, 383 | hair_colors: String, 384 | skin_colors: String, 385 | eye_colors: String, 386 | language: String, 387 | homeworld_id: Int 388 | ): Species! 389 | 390 | deleteSpecies(_id: ID!): Species! 391 | 392 | addVessel( 393 | name: String!, 394 | manufacturer: String, 395 | model: String, 396 | vessel_type: String!, 397 | vessel_class: String!, 398 | cost_in_credits: Int, 399 | length: String, 400 | max_atmosphering_speed: String, 401 | crew: Int, 402 | passengers: Int, 403 | cargo_capacity: String, 404 | consumables: String 405 | ): Vessel! 406 | 407 | updateVessel( 408 | _id: ID!, 409 | name: String!, 410 | manufacturer: String, 411 | model: String, 412 | vessel_type: String!, 413 | vessel_class: String!, 414 | cost_in_credits: Int, 415 | length: String, 416 | max_atmosphering_speed: String, 417 | crew: Int, 418 | passengers: Int, 419 | cargo_capacity: String, 420 | consumables: String 421 | ): Vessel! 422 | 423 | deleteVessel(_id: ID!): Vessel! 424 | 425 | addPerson( 426 | name: String!, 427 | mass: String, 428 | hair_color: String, 429 | skin_color: String, 430 | eye_color: String, 431 | birth_year: String, 432 | gender: String, 433 | species_id: Int, 434 | homeworld_id: Int, 435 | height: Int 436 | ): Person! 437 | 438 | updatePerson( 439 | _id: ID!, 440 | name: String!, 441 | mass: String, 442 | hair_color: String, 443 | skin_color: String, 444 | eye_color: String, 445 | birth_year: String, 446 | gender: String, 447 | species_id: Int, 448 | homeworld_id: Int, 449 | height: Int 450 | ): Person! 451 | 452 | deletePerson(_id: ID!): Person! 453 | 454 | addStarshipSpec( 455 | hyperdrive_rating: String, 456 | MGLT: String, 457 | vessel_id: Int! 458 | ): StarshipSpec! 459 | 460 | updateStarshipSpec( 461 | _id: ID!, 462 | hyperdrive_rating: String, 463 | MGLT: String, 464 | vessel_id: Int! 465 | ): StarshipSpec! 466 | 467 | deleteStarshipSpec(_id: ID!): StarshipSpec! 468 | 469 | } 470 | 471 | 472 | type Planet { 473 | _id: ID! 474 | name: String 475 | rotation_period: Int 476 | orbital_period: Int 477 | diameter: Int 478 | climate: String 479 | gravity: String 480 | terrain: String 481 | surface_water: String 482 | population: Int 483 | species: [Species] 484 | films: [Film] 485 | people: [Person] 486 | } 487 | 488 | type Film { 489 | _id: ID! 490 | title: String! 491 | episode_id: Int! 492 | opening_crawl: String! 493 | director: String! 494 | producer: String! 495 | release_date: String! 496 | people: [Person] 497 | planets: [Planet] 498 | species: [Species] 499 | vessels: [Vessel] 500 | } 501 | 502 | type Species { 503 | _id: ID! 504 | name: String! 505 | classification: String 506 | average_height: String 507 | average_lifespan: String 508 | hair_colors: String 509 | skin_colors: String 510 | eye_colors: String 511 | language: String 512 | planets: [Planet] 513 | films: [Film] 514 | people: [Person] 515 | } 516 | 517 | type Vessel { 518 | _id: ID! 519 | name: String! 520 | manufacturer: String 521 | model: String 522 | vessel_type: String! 523 | vessel_class: String! 524 | cost_in_credits: Int 525 | length: String 526 | max_atmosphering_speed: String 527 | crew: Int 528 | passengers: Int 529 | cargo_capacity: String 530 | consumables: String 531 | people: [Person] 532 | films: [Film] 533 | starshipSpecs: [StarshipSpec] 534 | } 535 | 536 | type Person { 537 | _id: ID! 538 | name: String! 539 | mass: String 540 | hair_color: String 541 | skin_color: String 542 | eye_color: String 543 | birth_year: String 544 | gender: String 545 | species: [Species] 546 | planets: [Planet] 547 | height: Int 548 | films: [Film] 549 | vessels: [Vessel] 550 | } 551 | 552 | type StarshipSpec { 553 | _id: ID! 554 | hyperdrive_rating: String 555 | MGLT: String 556 | vessels: [Vessel] 557 | } 558 | 559 | `) 560 | }); 561 | }); 562 | describe('Post api/uri resolvers for StarWars DB', function() { 563 | it('responds with correct resolvers for StarWars PSQL DB', async function() { 564 | return request.post('/api/uri') 565 | expect(res.locals.resolvers).toBe(`const resolvers = { 566 | Query: { 567 | planet: (parent, args) => { 568 | const query = 'SELECT * FROM planets WHERE _id = $1'; 569 | const values = [args._id]; 570 | return db.query(query, values) 571 | .then(data => data.rows[0]) 572 | .catch(err => new Error(err)); 573 | }, 574 | planets: () => { 575 | const query = 'SELECT * FROM planets'; 576 | return db.query(query) 577 | .then(data => data.rows) 578 | .catch(err =>new Error(err)); 579 | }, 580 | film: (parent, args) => { 581 | const query = 'SELECT * FROM films WHERE _id = $1'; 582 | const values = [args._id]; 583 | return db.query(query, values) 584 | .then(data => data.rows[0]) 585 | .catch(err => new Error(err)); 586 | }, 587 | films: () => { 588 | const query = 'SELECT * FROM films'; 589 | return db.query(query) 590 | .then(data => data.rows) 591 | .catch(err =>new Error(err)); 592 | }, 593 | speciesById: (parent, args) => { 594 | const query = 'SELECT * FROM species WHERE _id = $1'; 595 | const values = [args._id]; 596 | return db.query(query, values) 597 | .then(data => data.rows[0]) 598 | .catch(err => new Error(err)); 599 | }, 600 | species: () => { 601 | const query = 'SELECT * FROM species'; 602 | return db.query(query) 603 | .then(data => data.rows) 604 | .catch(err =>new Error(err)); 605 | }, 606 | vessel: (parent, args) => { 607 | const query = 'SELECT * FROM vessels WHERE _id = $1'; 608 | const values = [args._id]; 609 | return db.query(query, values) 610 | .then(data => data.rows[0]) 611 | .catch(err => new Error(err)); 612 | }, 613 | vessels: () => { 614 | const query = 'SELECT * FROM vessels'; 615 | return db.query(query) 616 | .then(data => data.rows) 617 | .catch(err =>new Error(err)); 618 | }, 619 | person: (parent, args) => { 620 | const query = 'SELECT * FROM people WHERE _id = $1'; 621 | const values = [args._id]; 622 | return db.query(query, values) 623 | .then(data => data.rows[0]) 624 | .catch(err => new Error(err)); 625 | }, 626 | people: () => { 627 | const query = 'SELECT * FROM people'; 628 | return db.query(query) 629 | .then(data => data.rows) 630 | .catch(err =>new Error(err)); 631 | }, 632 | starshipSpec: (parent, args) => { 633 | const query = 'SELECT * FROM starship_specs WHERE _id = $1'; 634 | const values = [args._id]; 635 | return db.query(query, values) 636 | .then(data => data.rows[0]) 637 | .catch(err => new Error(err)); 638 | }, 639 | starshipSpecs: () => { 640 | const query = 'SELECT * FROM starship_specs'; 641 | return db.query(query) 642 | .then(data => data.rows) 643 | .catch(err =>new Error(err)); 644 | }, 645 | }, 646 | Mutation: { 647 | addPlanet: (parent, args) => { 648 | const query = 'INSERT INTO planets (_id, name, rotation_period, orbital_period, diameter, climate, gravity, terrain, surface_water, population) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *'; 649 | const values = (args._id,args.name,args.rotation_period,args.orbital_period,args.diameter,args.climate,args.gravity,args.terrain,args.surface_water,args.population); 650 | return db.query(query, values) 651 | .then(data => data.rows[0]) 652 | .catch(err => new Error(err)); 653 | }, 654 | updatePlanet: (parent, args) => { 655 | let valList = []; 656 | for (const updateKey of Object.keys(args)) { 657 | if (updateKey !== '_id') valList.push(args[updateKey]); 658 | } 659 | valList.push(args._id); 660 | const argsArray = Object.keys(args).filter((el) => el !== '_id'); 661 | let setString = argsArray.map((x, i) => x + ' = $' + (i + 1)).join(', '); 662 | const pKArg = '$'+ (argsArray.length + 1); 663 | const query = 'UPDATE planets SET '+ setString +' WHERE _id = '+pKArg+' RETURNING *'; 664 | const values = valList; 665 | return db.query(query, values) 666 | .then(data => data.rows[0]) 667 | .catch(err => new Error(err)); 668 | }, 669 | deletePlanet: (parent, args) => { 670 | const query = 'DELETE FROM planets WHERE _id = $1 RETURNING *'; 671 | const values = [args._id]; 672 | return db.query(query, values) 673 | .then(data => data.rows[0]) 674 | .catch(err => new Error(err)); 675 | }, 676 | addFilm: (parent, args) => { 677 | const query = 'INSERT INTO films (_id, title, episode_id, opening_crawl, director, producer, release_date) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING *'; 678 | const values = (args._id,args.title,args.episode_id,args.opening_crawl,args.director,args.producer,args.release_date); 679 | return db.query(query, values) 680 | .then(data => data.rows[0]) 681 | .catch(err => new Error(err)); 682 | }, 683 | updateFilm: (parent, args) => { 684 | let valList = []; 685 | for (const updateKey of Object.keys(args)) { 686 | if (updateKey !== '_id') valList.push(args[updateKey]); 687 | } 688 | valList.push(args._id); 689 | const argsArray = Object.keys(args).filter((el) => el !== '_id'); 690 | let setString = argsArray.map((x, i) => x + ' = $' + (i + 1)).join(', '); 691 | const pKArg = '$'+ (argsArray.length + 1); 692 | const query = 'UPDATE films SET '+ setString +' WHERE _id = '+pKArg+' RETURNING *'; 693 | const values = valList; 694 | return db.query(query, values) 695 | .then(data => data.rows[0]) 696 | .catch(err => new Error(err)); 697 | }, 698 | deleteFilm: (parent, args) => { 699 | const query = 'DELETE FROM films WHERE _id = $1 RETURNING *'; 700 | const values = [args._id]; 701 | return db.query(query, values) 702 | .then(data => data.rows[0]) 703 | .catch(err => new Error(err)); 704 | }, 705 | addSpecies: (parent, args) => { 706 | const query = 'INSERT INTO species (_id, name, classification, average_height, average_lifespan, hair_colors, skin_colors, eye_colors, language, homeworld_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *'; 707 | const values = (args._id,args.name,args.classification,args.average_height,args.average_lifespan,args.hair_colors,args.skin_colors,args.eye_colors,args.language,args.homeworld_id); 708 | return db.query(query, values) 709 | .then(data => data.rows[0]) 710 | .catch(err => new Error(err)); 711 | }, 712 | updateSpecies: (parent, args) => { 713 | let valList = []; 714 | for (const updateKey of Object.keys(args)) { 715 | if (updateKey !== '_id') valList.push(args[updateKey]); 716 | } 717 | valList.push(args._id); 718 | const argsArray = Object.keys(args).filter((el) => el !== '_id'); 719 | let setString = argsArray.map((x, i) => x + ' = $' + (i + 1)).join(', '); 720 | const pKArg = '$'+ (argsArray.length + 1); 721 | const query = 'UPDATE species SET '+ setString +' WHERE _id = '+pKArg+' RETURNING *'; 722 | const values = valList; 723 | return db.query(query, values) 724 | .then(data => data.rows[0]) 725 | .catch(err => new Error(err)); 726 | }, 727 | deleteSpecies: (parent, args) => { 728 | const query = 'DELETE FROM species WHERE _id = $1 RETURNING *'; 729 | const values = [args._id]; 730 | return db.query(query, values) 731 | .then(data => data.rows[0]) 732 | .catch(err => new Error(err)); 733 | }, 734 | addVessel: (parent, args) => { 735 | const query = 'INSERT INTO vessels (_id, name, manufacturer, model, vessel_type, vessel_class, cost_in_credits, length, max_atmosphering_speed, crew, passengers, cargo_capacity, consumables) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) RETURNING *'; 736 | const values = (args._id,args.name,args.manufacturer,args.model,args.vessel_type,args.vessel_class,args.cost_in_credits,args.length,args.max_atmosphering_speed,args.crew,args.passengers,args.cargo_capacity,args.consumables); 737 | return db.query(query, values) 738 | .then(data => data.rows[0]) 739 | .catch(err => new Error(err)); 740 | }, 741 | updateVessel: (parent, args) => { 742 | let valList = []; 743 | for (const updateKey of Object.keys(args)) { 744 | if (updateKey !== '_id') valList.push(args[updateKey]); 745 | } 746 | valList.push(args._id); 747 | const argsArray = Object.keys(args).filter((el) => el !== '_id'); 748 | let setString = argsArray.map((x, i) => x + ' = $' + (i + 1)).join(', '); 749 | const pKArg = '$'+ (argsArray.length + 1); 750 | const query = 'UPDATE vessels SET '+ setString +' WHERE _id = '+pKArg+' RETURNING *'; 751 | const values = valList; 752 | return db.query(query, values) 753 | .then(data => data.rows[0]) 754 | .catch(err => new Error(err)); 755 | }, 756 | deleteVessel: (parent, args) => { 757 | const query = 'DELETE FROM vessels WHERE _id = $1 RETURNING *'; 758 | const values = [args._id]; 759 | return db.query(query, values) 760 | .then(data => data.rows[0]) 761 | .catch(err => new Error(err)); 762 | }, 763 | addPerson: (parent, args) => { 764 | const query = 'INSERT INTO people (_id, name, mass, hair_color, skin_color, eye_color, birth_year, gender, species_id, homeworld_id, height) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING *'; 765 | const values = (args._id,args.name,args.mass,args.hair_color,args.skin_color,args.eye_color,args.birth_year,args.gender,args.species_id,args.homeworld_id,args.height); 766 | return db.query(query, values) 767 | .then(data => data.rows[0]) 768 | .catch(err => new Error(err)); 769 | }, 770 | updatePerson: (parent, args) => { 771 | let valList = []; 772 | for (const updateKey of Object.keys(args)) { 773 | if (updateKey !== '_id') valList.push(args[updateKey]); 774 | } 775 | valList.push(args._id); 776 | const argsArray = Object.keys(args).filter((el) => el !== '_id'); 777 | let setString = argsArray.map((x, i) => x + ' = $' + (i + 1)).join(', '); 778 | const pKArg = '$'+ (argsArray.length + 1); 779 | const query = 'UPDATE people SET '+ setString +' WHERE _id = '+pKArg+' RETURNING *'; 780 | const values = valList; 781 | return db.query(query, values) 782 | .then(data => data.rows[0]) 783 | .catch(err => new Error(err)); 784 | }, 785 | deletePerson: (parent, args) => { 786 | const query = 'DELETE FROM people WHERE _id = $1 RETURNING *'; 787 | const values = [args._id]; 788 | return db.query(query, values) 789 | .then(data => data.rows[0]) 790 | .catch(err => new Error(err)); 791 | }, 792 | addStarshipSpec: (parent, args) => { 793 | const query = 'INSERT INTO starship_specs (_id, hyperdrive_rating, MGLT, vessel_id) VALUES ($1, $2, $3, $4) RETURNING *'; 794 | const values = (args._id,args.hyperdrive_rating,args.MGLT,args.vessel_id); 795 | return db.query(query, values) 796 | .then(data => data.rows[0]) 797 | .catch(err => new Error(err)); 798 | }, 799 | updateStarshipSpec: (parent, args) => { 800 | let valList = []; 801 | for (const updateKey of Object.keys(args)) { 802 | if (updateKey !== '_id') valList.push(args[updateKey]); 803 | } 804 | valList.push(args._id); 805 | const argsArray = Object.keys(args).filter((el) => el !== '_id'); 806 | let setString = argsArray.map((x, i) => x + ' = $' + (i + 1)).join(', '); 807 | const pKArg = '$'+ (argsArray.length + 1); 808 | const query = 'UPDATE starship_specs SET '+ setString +' WHERE _id = '+pKArg+' RETURNING *'; 809 | const values = valList; 810 | return db.query(query, values) 811 | .then(data => data.rows[0]) 812 | .catch(err => new Error(err)); 813 | }, 814 | deleteStarshipSpec: (parent, args) => { 815 | const query = 'DELETE FROM starship_specs WHERE _id = $1 RETURNING *'; 816 | const values = [args._id]; 817 | return db.query(query, values) 818 | .then(data => data.rows[0]) 819 | .catch(err => new Error(err)); 820 | }, 821 | }, 822 | Planet: { 823 | people: (pTable) => { 824 | const query = 'SELECT * FROM people WHERE homeworld_id = $1'; 825 | const values = [pTable._id]; 826 | return db.query(query, values) 827 | .then(data => data.rows) 828 | .catch(err => new Error(err)); 829 | }, 830 | films: (pTable) => { 831 | const query = 'SELECT * FROM films LEFT OUTER JOIN planets_in_films ON films._id = planets_in_films.film_id WHERE planets_in_films.planet_id = $1'; 832 | const values = [pTable._id]; 833 | return db.query(query, values) 834 | .then(data => data.rows) 835 | .catch(err => new Error(err)); 836 | }, 837 | species: (pTable) => { 838 | const query = 'SELECT * FROM species WHERE homeworld_id = $1'; 839 | const values = [pTable._id]; 840 | return db.query(query, values) 841 | .then(data => data.rows) 842 | .catch(err => new Error(err)); 843 | }, 844 | }, 845 | Film: { 846 | people: (pTable) => { 847 | const query = 'SELECT * FROM people LEFT OUTER JOIN people_in_films ON people._id = people_in_films.person_id WHERE people_in_films.film_id = $1'; 848 | const values = [pTable._id]; 849 | return db.query(query, values) 850 | .then(data => data.rows) 851 | .catch(err => new Error(err)); 852 | }, 853 | planets: (pTable) => { 854 | const query = 'SELECT * FROM planets LEFT OUTER JOIN planets_in_films ON planets._id = planets_in_films.planet_id WHERE planets_in_films.film_id = $1'; 855 | const values = [pTable._id]; 856 | return db.query(query, values) 857 | .then(data => data.rows) 858 | .catch(err => new Error(err)); 859 | }, 860 | species: (pTable) => { 861 | const query = 'SELECT * FROM species LEFT OUTER JOIN species_in_films ON species._id = species_in_films.species_id WHERE species_in_films.film_id = $1'; 862 | const values = [pTable._id]; 863 | return db.query(query, values) 864 | .then(data => data.rows) 865 | .catch(err => new Error(err)); 866 | }, 867 | vessels: (pTable) => { 868 | const query = 'SELECT * FROM vessels LEFT OUTER JOIN vessels_in_films ON vessels._id = vessels_in_films.vessel_id WHERE vessels_in_films.film_id = $1'; 869 | const values = [pTable._id]; 870 | return db.query(query, values) 871 | .then(data => data.rows) 872 | .catch(err => new Error(err)); 873 | }, 874 | }, 875 | Species: { 876 | people: (pTable) => { 877 | const query = 'SELECT * FROM people WHERE species_id = $1'; 878 | const values = [pTable._id]; 879 | return db.query(query, values) 880 | .then(data => data.rows) 881 | .catch(err => new Error(err)); 882 | }, 883 | planets: (pTable) => { 884 | const query = 'SELECT planets.* FROM planets LEFT OUTER JOIN species ON planets._id = species.homeworld_id WHERE species._id = $1'; 885 | const values = [pTable._id]; 886 | return db.query(query, values) 887 | .then(data => data.rows) 888 | .catch(err => new Error(err)); 889 | }, 890 | films: (pTable) => { 891 | const query = 'SELECT * FROM films LEFT OUTER JOIN species_in_films ON films._id = species_in_films.film_id WHERE species_in_films.species_id = $1'; 892 | const values = [pTable._id]; 893 | return db.query(query, values) 894 | .then(data => data.rows) 895 | .catch(err => new Error(err)); 896 | }, 897 | }, 898 | Vessel: { 899 | people: (pTable) => { 900 | const query = 'SELECT * FROM people LEFT OUTER JOIN pilots ON people._id = pilots.person_id WHERE pilots.vessel_id = $1'; 901 | const values = [pTable._id]; 902 | return db.query(query, values) 903 | .then(data => data.rows) 904 | .catch(err => new Error(err)); 905 | }, 906 | starshipSpecs: (pTable) => { 907 | const query = 'SELECT * FROM starship_specs WHERE vessel_id = $1'; 908 | const values = [pTable._id]; 909 | return db.query(query, values) 910 | .then(data => data.rows) 911 | .catch(err => new Error(err)); 912 | }, 913 | films: (pTable) => { 914 | const query = 'SELECT * FROM films LEFT OUTER JOIN vessels_in_films ON films._id = vessels_in_films.film_id WHERE vessels_in_films.vessel_id = $1'; 915 | const values = [pTable._id]; 916 | return db.query(query, values) 917 | .then(data => data.rows) 918 | .catch(err => new Error(err)); 919 | }, 920 | }, 921 | Person: { 922 | planets: (pTable) => { 923 | const query = 'SELECT planets.* FROM planets LEFT OUTER JOIN people ON planets._id = people.homeworld_id WHERE people._id = $1'; 924 | const values = [pTable._id]; 925 | return db.query(query, values) 926 | .then(data => data.rows) 927 | .catch(err => new Error(err)); 928 | }, 929 | species: (pTable) => { 930 | const query = 'SELECT species.* FROM species LEFT OUTER JOIN people ON species._id = people.species_id WHERE people._id = $1'; 931 | const values = [pTable._id]; 932 | return db.query(query, values) 933 | .then(data => data.rows) 934 | .catch(err => new Error(err)); 935 | }, 936 | films: (pTable) => { 937 | const query = 'SELECT * FROM films LEFT OUTER JOIN people_in_films ON films._id = people_in_films.film_id WHERE people_in_films.person_id = $1'; 938 | const values = [pTable._id]; 939 | return db.query(query, values) 940 | .then(data => data.rows) 941 | .catch(err => new Error(err)); 942 | }, 943 | vessels: (pTable) => { 944 | const query = 'SELECT * FROM vessels LEFT OUTER JOIN pilots ON vessels._id = pilots.vessel_id WHERE pilots.person_id = $1'; 945 | const values = [pTable._id]; 946 | return db.query(query, values) 947 | .then(data => data.rows) 948 | .catch(err => new Error(err)); 949 | }, 950 | }, 951 | StarshipSpec: { 952 | vessels: (pTable) => { 953 | const query = 'SELECT vessels.* FROM vessels LEFT OUTER JOIN starship_specs ON vessels._id = starship_specs.vessel_id WHERE starship_specs._id = $1'; 954 | const values = [pTable._id]; 955 | return db.query(query, values) 956 | .then(data => data.rows) 957 | .catch(err => new Error(err)); 958 | }, 959 | }, 960 | }`) 961 | }); 962 | }); 963 | }) 964 | 965 | 966 | describe('Post request user input URL', () => { 967 | describe('responds to invalid PSQL URL request with 500 status message', function () { 968 | it('responds to invalid PSQL URL post with 500 status message', async function () { 969 | return request 970 | .post('/api/uri') 971 | .send([{ uri: 'postgres://a;sdkfjl;aksdjfl;kasdjf;lasdlfkj' }]) 972 | .expect(500); 973 | }) 974 | }) 975 | }) 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | -------------------------------------------------------------------------------- /assets/-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/beamql/0e04ac44cc7f1b23ca3753269bf1bde29cc18397/assets/-icon.png -------------------------------------------------------------------------------- /assets/Adam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/beamql/0e04ac44cc7f1b23ca3753269bf1bde29cc18397/assets/Adam.png -------------------------------------------------------------------------------- /assets/ApolloBoilerplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/beamql/0e04ac44cc7f1b23ca3753269bf1bde29cc18397/assets/ApolloBoilerplate.png -------------------------------------------------------------------------------- /assets/Brian.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/beamql/0e04ac44cc7f1b23ca3753269bf1bde29cc18397/assets/Brian.jpeg -------------------------------------------------------------------------------- /assets/Eric.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/beamql/0e04ac44cc7f1b23ca3753269bf1bde29cc18397/assets/Eric.jpeg -------------------------------------------------------------------------------- /assets/Mark.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/beamql/0e04ac44cc7f1b23ca3753269bf1bde29cc18397/assets/Mark.JPG -------------------------------------------------------------------------------- /assets/getstarted.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/beamql/0e04ac44cc7f1b23ca3753269bf1bde29cc18397/assets/getstarted.gif -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/beamql/0e04ac44cc7f1b23ca3753269bf1bde29cc18397/assets/logo.png -------------------------------------------------------------------------------- /assets/zoombeamcandid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/beamql/0e04ac44cc7f1b23ca3753269bf1bde29cc18397/assets/zoombeamcandid.png -------------------------------------------------------------------------------- /client/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component, Image, useState } from 'react'; 2 | import axios from 'axios'; 3 | import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom'; 4 | import UriEntry from './Components/UriEntry.jsx'; 5 | import theme from './theme'; 6 | import { ThemeProvider } from '@material-ui/core'; 7 | import CodeOutput from './Components/CodeOutput.jsx'; 8 | import CodeOutputButtons from './Components/CodeOutputButtons.jsx'; 9 | import Diagram from './Components/Diagram.jsx'; 10 | import Docs from './Components/Docs.jsx'; 11 | import Team from './Components/Team.jsx'; 12 | 13 | // | App (contains navbar) 14 | // |URI Entry 15 | // |Codebox 16 | // |Diagram 17 | 18 | class App extends Component { 19 | constructor(props) { 20 | super(props); 21 | this.state = { 22 | database: { 23 | allTables: {}, 24 | 25 | foreignKeys: [], 26 | primaryKeys: [], 27 | completeSchemaString: ' ', 28 | resolvers: ' ' 29 | }, 30 | renderSchema: true, 31 | }; 32 | this.gTD = this.gTD.bind(this); 33 | this.changeRender = this.changeRender.bind(this); 34 | } 35 | 36 | gTD() { 37 | axios 38 | .post('/api/uri', { 39 | uri: 40 | document.getElementById('filled-basic').value || 41 | 'postgres://vdnvhfkq:sYiMTdCmk1vs2br_eUrrmX1unPvfucdW@batyr.db.elephantsql.com/vdnvhfkq', 42 | }) 43 | .then((response) => { 44 | // handle success 45 | document.getElementById('filled-basic').value = ''; 46 | this.setState(state => { 47 | return {...state, database: response.data } 48 | }); 49 | document.getElementById('outlined-multiline-static').value = this.state.database.completeSchemaString; 50 | }) 51 | .catch(function (error) { 52 | // handle error 53 | console.log(error); 54 | }); 55 | } 56 | 57 | changeRender(schema) { 58 | if (schema === false) { 59 | this.setState(state => { 60 | return {...state, renderSchema: false} 61 | }) 62 | document.getElementById('outlined-multiline-static').value = this.state.database.resolvers; 63 | } else { 64 | this.setState(state => { 65 | return {...state, renderSchema: true} 66 | }) 67 | document.getElementById('outlined-multiline-static').value = this.state.database.completeSchemaString; 68 | } 69 | 70 | 71 | } 72 | 73 | render() { 74 | return ( 75 | 76 | 77 |
78 | 79 | 80 | 81 |
82 |
83 | {/* 84 | 94 | */} 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 |
107 | 108 | 109 | 110 |
111 |
112 | 113 | 114 | Get Started 115 | {/*

Getting Started

*/} 116 |
117 | 118 | 119 | 120 | 121 |
122 | 123 |
124 | 125 | 126 |
127 |
128 |
129 | 130 | 131 | 132 |
133 |
134 | ); 135 | } 136 | } 137 | 138 | export default App; 139 | -------------------------------------------------------------------------------- /client/Components/CodeOutput.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import TextField from '@material-ui/core/TextField'; 3 | import { withStyles } from '@material-ui/core/styles'; 4 | import { makeStyles } from '@material-ui/core/styles'; 5 | import ButtonGroup from '@material-ui/core/ButtonGroup'; 6 | import Button from '@material-ui/core/Button'; 7 | 8 | const styles = makeStyles({ 9 | root: { 10 | '& .MuiOutlinedInput-root .MuiOutlinedInput-notchedOutline': { 11 | borderColor: 'white', 12 | border: '2px solid white', 13 | width: '100%', 14 | position: 'absolute', 15 | right: '0', 16 | height: '100%', 17 | spellCheck: 'false', 18 | }, 19 | '&:hover .MuiOutlinedInput-root .MuiOutlinedInput-notchedOutline': { 20 | borderColor: 'white', 21 | border: '2px solid white', 22 | width: '100%', 23 | position: 'absolute', 24 | right: '0', 25 | height: '100%', 26 | spellCheck: 'false', 27 | }, 28 | '& .MuiOutlinedInput-root.Mui-focused .MuiOutlinedInput-notchedOutline': { 29 | borderColor: 'white', 30 | border: '2px solid white', 31 | width: '100%', 32 | position: 'absolute', 33 | right: '0', 34 | height: '100%', 35 | spellCheck: 'false', 36 | }, 37 | }, 38 | }); 39 | 40 | function CodeOutput({ database, renderSchema }) { 41 | const classes = styles(); 42 | return ( 43 |
44 | 58 |
59 | ); 60 | } 61 | 62 | export default CodeOutput; 63 | -------------------------------------------------------------------------------- /client/Components/CodeOutputButtons.jsx: -------------------------------------------------------------------------------- 1 | import ButtonGroup from '@material-ui/core/ButtonGroup'; 2 | import Button from '@material-ui/core/Button'; 3 | import React, { Component } from 'react'; 4 | 5 | function CodeOutputButtons({ database, renderSchema, changeRender }) { 6 | return ( 7 |
8 | 14 | 20 | 24 |
25 | ); 26 | } 27 | 28 | export default CodeOutputButtons; 29 | -------------------------------------------------------------------------------- /client/Components/Docs.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | function Docs() { 4 | 5 | 6 | return ( 7 |
8 |
Documentation
9 |
10 |
11 | BeamQL is an open source tool to make understanding and adding a 12 | GraphQL layer to any REST API as simple as possible. 13 |

14 |

15 | The animation below shows the essential features of this tool. Simply 16 | insert your PostgreSQL database URI or select from one of the sample 17 | databases. Click submit and behold the visualization of your database as 18 | a graph data structure. You are now looking at your data the way GraphQL 19 | sees it. 20 |

21 |

22 | In addition, BeamQL generates everything you will need (all schema and 23 | resolvers) to add a GraphQL controller layer to your existing REST API. 24 | Assuming you are using a common backend framework in Node.js, you simply 25 | need to copy these two bodies of text into your server/app.js code, and 26 | install and include the relevant boilerplate for Apollo Server. 27 |
28 |
29 |
30 | Get Started 35 |
36 |
37 | Step-By-Step Migration 🔢 38 |
    39 |
  1. Copy your PostgreSQL database URI into BeamQL.com
  2. 40 |
    41 |
  3. View and interact with the graphical visualization of the database and or use it in API documentation to give users a better understanding of what they are working with.
  4. 42 |
    43 |
  5. Open NodeJS backend codebase in a code editor.
  6. 44 |
    45 |
  7. Use the schema and resolvers that BeamQL produces with Apollo Server or a similar GraphQL server that suits you.
  8. 46 |
    47 |
  9. Start server and navigate to localhost:X000/graphql
  10. 48 |
    49 |
  11. Behold the glory of your database accessible through the query language of the future!
  12. 50 |
51 |
52 |
53 | 54 |
55 | ); 56 | } 57 | 58 | export default Docs; 59 | -------------------------------------------------------------------------------- /client/Components/Team.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | function Team() { 3 | return ( 4 |
5 |
6 | Brian 13 |
14 |

15 | Brian Grosso 16 |

17 |
18 | 23 | 24 | 25 |
26 | 31 | 32 | 33 |
34 |
35 |
36 |
37 | Eric 44 |

45 | Eric Askew 46 |

47 |
48 | 49 | 50 | 51 |
52 | 57 | 58 | 59 |
60 |
61 | 62 |
63 | Adam 70 |

71 | Adam Goodman 72 |

73 |
74 | 75 | 76 | 77 |
78 | 83 | 84 | 85 |
86 |
87 |
88 | Marc 95 |

96 | Mark Liu 97 |

98 |
99 | 100 | {/* Github */} 101 | 102 | 103 | 104 |
105 | 106 | 107 | 108 |
109 |
110 |
111 | ); 112 | } 113 | export default Team; 114 | -------------------------------------------------------------------------------- /client/Components/UriEntry.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import TextField from '@material-ui/core/TextField'; 3 | import Button from '@material-ui/core/Button'; 4 | import ButtonGroup from '@material-ui/core/Button'; 5 | import FormControl from '@material-ui/core/FormControl'; 6 | import Select from '@material-ui/core/Select'; 7 | import InputLabel from '@material-ui/core/InputLabel'; 8 | import MenuItem from '@material-ui/core/MenuItem'; 9 | import { makeStyles, withStyles } from '@material-ui/core/styles'; 10 | import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom'; 11 | 12 | const useStyles = makeStyles({ 13 | root: { 14 | margin: '20px', 15 | height: '55px', 16 | color: 'black', 17 | }, 18 | }); 19 | 20 | const styles = { 21 | input: { 22 | 'background-color': 'white', 23 | }, 24 | }; 25 | 26 | 27 | export default function UriEntry(props) { 28 | const classes = useStyles(); 29 | return ( 30 |
31 |
32 | props.gTD()} InputProps={{className: classes.input}} label={ window.innerWidth > 500 ? "Enter database URI" : "DB URI"} variant='filled'/> 33 | 34 | 37 | 38 | 39 | 40 | 41 | Sample DB 42 | 43 | 57 | 58 | 59 |
60 | {/* 61 | 62 | 63 | 64 | */} 65 |
66 |
67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /client/application.scss: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0px; 3 | background-color: rgb(56, 33, 158); 4 | // color: black; 5 | font-family: Noto Sans, 'sans-serif'; 6 | outline: none; 7 | } 8 | 9 | #gifheader { 10 | margin: 0px; 11 | 12 | color: white; 13 | font-size: 8em; 14 | text-align: center; 15 | position: absolute; 16 | top: 40%; 17 | left: 20%; 18 | font-family: Noto Sans, 'sans-serif'; 19 | outline: none; 20 | } 21 | 22 | button:hover { 23 | cursor: pointer; 24 | } 25 | 26 | .node-key { 27 | font-weight: bolder; 28 | text-align: center; 29 | padding: 3px; 30 | font-size: 1.3em; 31 | } 32 | 33 | hr { 34 | padding: 0px; 35 | margin: 0px; 36 | border-color: rgba(0, 0, 0, 0.75); 37 | } 38 | 39 | .topButtons { 40 | display: flex; 41 | height: 60px; 42 | align-items: flex-start; 43 | justify-content: flex-end; 44 | margin: 0; 45 | margin-right: 20px; 46 | background-color: rgb(56, 33, 158); 47 | } 48 | 49 | #docs { 50 | color: white; 51 | display: flex; 52 | flex-direction: column; 53 | padding: 30px; 54 | text-decoration: none; 55 | width: 70vw; 56 | margin-left: auto; 57 | margin-right: auto; 58 | } 59 | 60 | #topLeftDocs { 61 | width: 40vh; 62 | } 63 | 64 | #docsTop { 65 | color: white; 66 | display: flex; 67 | flex-direction: row; 68 | padding: 30px; 69 | text-decoration: none; 70 | width: 90vw; 71 | } 72 | 73 | #docsBot { 74 | color: white; 75 | display: flex; 76 | flex-direction: row; 77 | padding: 30px; 78 | text-decoration: none; 79 | width: 90vw; 80 | } 81 | 82 | #docsRight { 83 | position: absolute; 84 | color: white; 85 | padding: 30px; 86 | text-decoration: none; 87 | width: 46vw; 88 | top: 74vh; 89 | margin-left: auto; 90 | right: 20px; 91 | } 92 | 93 | #textlink { 94 | text-decoration: underline; 95 | color: white; 96 | display: inline-block; 97 | } 98 | #gif { 99 | display: flex; 100 | border: 2px solid white; 101 | width: 75vw; 102 | margin-left: auto; 103 | margin-right: auto; 104 | align-self: center; 105 | justify-self: center; 106 | } 107 | 108 | .topButtons button { 109 | // margin: 0 10px 10px 0px; 110 | // border: 3px solid silver; 111 | // border-radius: 15px; 112 | // background-color: #4c00ff; 113 | // color: white; 114 | // font-size: 20px; 115 | // padding: 10px; 116 | // width: 150px; 117 | display: inline-block; 118 | padding: 1em 1.2em; 119 | border: 0.1em solid #ffffff; 120 | background-color: rgb(56, 33, 158); 121 | margin: 0 1em 0.5em 1em; 122 | font-size: 1em; 123 | border-radius: 0.12em; 124 | box-sizing: border-box; 125 | text-decoration: none; 126 | font-family: 'Roboto', sans-serif; 127 | font-weight: 300; 128 | color: #ffffff; 129 | text-align: center; 130 | transition: all 0.2s; 131 | } 132 | #choiceButton { 133 | margin-right: 80%; 134 | } 135 | 136 | #Selection { 137 | display: flex; 138 | position: absolute; 139 | justify-content: center; 140 | width: 20%; 141 | left: 70px; 142 | top: 215px; 143 | } 144 | 145 | #Selection button { 146 | display: inline-block; 147 | border: 0.1em solid #ffffff; 148 | background-color: whitesmoke; 149 | margin: 0 0.2em 0.2em 0.2em; 150 | font-size: .7em; 151 | border-radius: 0.12em; 152 | width: 30%; 153 | // box-sizing: border-box; 154 | text-decoration: none; 155 | font-family: 'Roboto', sans-serif; 156 | font-weight: 300; 157 | color: rgb(56, 33, 158); 158 | text-align: center; 159 | transition: all 0.2s solid black; 160 | } 161 | #OutputBox { 162 | display: flex; 163 | } 164 | #outputRight { 165 | right: 30px; 166 | } 167 | #outputLeft { 168 | justify-self: flex-start; 169 | } 170 | #codebox { 171 | display: flex; 172 | position: absolute; 173 | top: 225px; 174 | width: 23%; 175 | height: auto; 176 | left: 10px; 177 | justify-content: flex-start; 178 | margin: 10px; 179 | // bottom: 50px 180 | } 181 | 182 | #outlined-multiline-static { 183 | color: whitesmoke; 184 | } 185 | #outlined-multiline-static-label { 186 | color: white; 187 | border-color: white; 188 | } 189 | 190 | .PrivateNotchedOutline-root-2 { 191 | color: white; 192 | border: 0.1em solid #ffffff; 193 | } 194 | 195 | #erdbackground { 196 | display: flex; 197 | position: absolute; 198 | border: 1px solid #ffffff; 199 | } 200 | 201 | .MuiOutlinedInput-notchedOutline { 202 | color: white; 203 | border: 0.1em solid #ffffff; 204 | } 205 | 206 | .MuiInputBase-root 207 | .MuiOutlinedInput-input 208 | .MuiInputBase-inputMultiline 209 | .MuiOutlinedInput-inputMultiline { 210 | color: white; 211 | border-color: white; 212 | border: 0.1em solid #ffffff; 213 | } 214 | 215 | legend, 216 | fieldset { 217 | color: white; 218 | } 219 | 220 | #uribox { 221 | display: flex; 222 | margin-left: auto; 223 | margin-right: auto; 224 | justify-content: center; 225 | border-radius: 1px; 226 | } 227 | 228 | #demo-simple-select-filled-label { 229 | color: whitesmoke; 230 | } 231 | 232 | input, 233 | select, 234 | textarea { 235 | color: grey; 236 | } 237 | 238 | #filled-basic { 239 | color: purple; 240 | background-color: lightgray; 241 | border-radius: 5px; 242 | } 243 | .MuiSelect-nativeInput { 244 | color: white; 245 | } 246 | 247 | .MuiFilledInput-input { 248 | color: white; 249 | background-color: white; 250 | } 251 | 252 | #logo { 253 | height: 150px; 254 | position: absolute; 255 | top: 20px; 256 | left: 40px; 257 | } 258 | 259 | .topButtons button:hover { 260 | color: #000000; 261 | background-color: #ffffff; 262 | } 263 | 264 | .leftButtons { 265 | display: flex; 266 | justify-content: flex-start; 267 | } 268 | 269 | .rightButtons { 270 | display: flex; 271 | justify-content: flex-end; 272 | } 273 | 274 | .header h1 { 275 | display: flex; 276 | justify-content: center; 277 | font-size: 40px; 278 | margin-bottom: 40px; 279 | margin-top: 15px; 280 | } 281 | .butt { 282 | display: flex; 283 | justify-content: center; 284 | } 285 | 286 | .butt button { 287 | margin: 0 30px 50px 0px; 288 | border: 3px solid #00d8ff; 289 | border-radius: 15px; 290 | background-color: #00d8ff; 291 | color: white; 292 | font-size: 20px; 293 | padding: 10px; 294 | width: 150px; 295 | } 296 | 297 | .button2 button { 298 | margin: 0 20px 20px 10px; 299 | border: 3px solid #00d8ff; 300 | border-radius: 15px; 301 | background-color: #00d8ff; 302 | color: white; 303 | font-size: 20px; 304 | padding: 5px; 305 | width: 150px; 306 | } 307 | 308 | .Title { 309 | justify-content: center; 310 | } 311 | #team { 312 | display: flex; 313 | flex-wrap: wrap; 314 | min-width: 500px; 315 | justify-content: space-evenly; 316 | margin-left: auto; 317 | margin-right: auto; 318 | margin-top: 18vh; 319 | } 320 | #Brian { 321 | font-size: large; 322 | margin-bottom: 30px; 323 | text-align: center; 324 | } 325 | #Eric { 326 | font-size: large; 327 | margin-bottom: 30px; 328 | text-align: center; 329 | } 330 | #Adam { 331 | font-size: large; 332 | margin-bottom: 30px; 333 | text-align: center; 334 | } 335 | #Marc { 336 | font-size: large; 337 | margin-bottom: 30px; 338 | text-align: center; 339 | } 340 | h2 { 341 | color: white; 342 | } 343 | p { 344 | color: white; 345 | font-family: 'Helvetica'; 346 | text-decoration: underline; 347 | } 348 | .teampic{ 349 | border-radius: 100%; 350 | border:2px solid white; 351 | } 352 | .teampic:hover{ 353 | border:2px solid #48BFE3; 354 | } 355 | 356 | .socials{ 357 | display: flex; 358 | justify-content: center; 359 | font-size: 2.0em; 360 | color: white; 361 | } 362 | a:hover{ 363 | color:#48BFE3; 364 | } 365 | .firstLetter{ 366 | color:#48BFE3; 367 | font-weight: bold; 368 | } 369 | .fa-github { 370 | color:whitesmoke; 371 | margin: 0 10px 0 10px; 372 | } 373 | .fa-linkedin { 374 | color:whitesmoke; 375 | margin: 0 10px 0 10px; 376 | } 377 | 378 | .step { 379 | font-size: 1.5em; 380 | text-decoration: underline; 381 | margin-left: auto; 382 | margin-right: auto; 383 | } 384 | 385 | #olCode { 386 | list-style-position: inside; 387 | padding-left: 0; 388 | max-width: 600px; 389 | margin-left: auto; 390 | margin-right: auto; 391 | } 392 | 393 | #codeOutputImg { 394 | border: 2px solid white; 395 | width: 40vw; 396 | margin-left: auto; 397 | margin-right: auto; 398 | align-self: center; 399 | justify-self: center; 400 | } 401 | 402 | a { 403 | display: inline-block; 404 | } 405 | 406 | @media (max-width: 870px) { 407 | #uri-entry { 408 | position: static; 409 | display: flex; 410 | flex-wrap: wrap; 411 | } 412 | .header { 413 | display: flex; 414 | flex-direction: column; 415 | } 416 | 417 | #logo { 418 | position: static; 419 | margin-left: auto; 420 | margin-right: auto; 421 | } 422 | 423 | .topButtons { 424 | position: static; 425 | display: flex; 426 | height: 60px; 427 | flex-wrap: wrap; 428 | margin-left: auto; 429 | margin-right: auto; 430 | } 431 | 432 | .topButtons button { 433 | min-width: 40px; 434 | } 435 | 436 | #smallWidthFlex { 437 | display: flex; 438 | flex-direction: column; 439 | } 440 | #codebox { 441 | top: 225px; 442 | display: none; 443 | } 444 | 445 | #Selection { 446 | top: 215px; 447 | display: none; 448 | } 449 | } 450 | 451 | @media (max-width: 500px) { 452 | #codebox { 453 | top: 225px; 454 | display: none; 455 | } 456 | 457 | #Selection { 458 | top: 215px; 459 | } 460 | } -------------------------------------------------------------------------------- /client/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import App from './App.jsx'; 4 | import styles from './application.scss'; 5 | 6 | render(, document.getElementById('root')); 7 | -------------------------------------------------------------------------------- /client/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/beamql/0e04ac44cc7f1b23ca3753269bf1bde29cc18397/client/logo.png -------------------------------------------------------------------------------- /client/logo_transparent_background.png:Zone.Identifier: -------------------------------------------------------------------------------- 1 | [ZoneTransfer] 2 | ZoneId=3 3 | ReferrerUrl=C:\Users\gross\Downloads\Logos_ByTailorBrands.zip 4 | -------------------------------------------------------------------------------- /client/theme.js: -------------------------------------------------------------------------------- 1 | import { createTheme } from '@material-ui/core/styles'; 2 | import { red } from '@material-ui/core/colors'; 3 | 4 | // Create a theme instance. 5 | const theme = createTheme({ 6 | palette: { 7 | primary: { 8 | main: '#FFF', 9 | }, 10 | secondary: { 11 | main: '#FFF', 12 | }, 13 | error: { 14 | main: red.A400, 15 | }, 16 | background: { 17 | default: 'white', 18 | }, 19 | }, 20 | }); 21 | 22 | theme.spacing(8) 23 | 24 | 25 | 26 | export default theme; -------------------------------------------------------------------------------- /docker-compose-dev.yml: -------------------------------------------------------------------------------- 1 | version: "3.0" # optional since v1.27.0 2 | services: 3 | dev: 4 | image: beamcorp/bql-dev 5 | container_name: bql-dev 6 | ports: 7 | - 8080:8080 8 | volumes: 9 | - .:/usr/src/app 10 | - ./node_modules:/usr/src/app/node_modules 11 | command: npm run dev -------------------------------------------------------------------------------- /docker-compose-test.yml: -------------------------------------------------------------------------------- 1 | version: "3.0" 2 | services: 3 | test: 4 | image: beamcorp/bql-dev 5 | container_name: bql-test 6 | ports: 7 | - 3000:3000 8 | volumes: 9 | - .:/usr/src/app 10 | - ./node_modules:/usr/src/app/node_modules 11 | command: npm run test 12 | 13 | # currently unused, waiting for test suites -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | BeamQL 7 | 8 | 11 | 12 | 13 | 14 | 15 | 16 |

17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BeamQL", 3 | "jest": { 4 | "verbose": true 5 | }, 6 | "version": "1.0.0", 7 | "description": "a postgresql interactive visualizer and graphql schema & resolver generator", 8 | "main": "./client/index.js", 9 | "scripts": { 10 | "start": "NODE_ENV=production nodemon ./server/server.js", 11 | "build": "NODE_ENV=production webpack", 12 | "dev": "NODE_ENV=development nodemon ./server/server.js & NODE_ENV=development webpack serve", 13 | "docker-dev": "docker-compose -f docker-compose-dev.yml up", 14 | "docker-test": "docker-compose -f docker-compose-test.yml up", 15 | "ports": "npx kill-port 3000 5000 8000 8080", 16 | "test": "jest --verbose ./__tests__/route.test.js" 17 | }, 18 | "nodemonConfig": { 19 | "ignore": [ 20 | "build", 21 | "client" 22 | ] 23 | }, 24 | "author": "Adam Goodman, Brian Grosso, Eric Askew, Mark Liu", 25 | "license": "MIT", 26 | "dependencies": { 27 | "@fortawesome/fontawesome-free": "^5.15.4", 28 | "@material-ui/core": "^4.12.3", 29 | "axios": "^0.21.1", 30 | "camelcase": "^5.3.1", 31 | "copy-webpack-plugin": "^9.0.1", 32 | "css-loader": "^6.2.0", 33 | "express": "^4.17.1", 34 | "json-stringify-pretty-compact": "^3.0.0", 35 | "pg": "^8.7.1", 36 | "pluralize": "^8.0.0", 37 | "react": "^17.0.2", 38 | "react-dom": "^17.0.2", 39 | "react-flow-renderer": "^9.6.6", 40 | "react-router-dom": "^5.2.0", 41 | "regenerator-runtime": "^0.13.9" 42 | }, 43 | "devDependencies": { 44 | "@babel/core": "^7.14.8", 45 | "@babel/preset-env": "^7.14.8", 46 | "@babel/preset-react": "^7.14.5", 47 | "@testing-library/dom": "^8.2.0", 48 | "@testing-library/react": "^12.0.0", 49 | "@types/regenerator-runtime": "^0.13.1", 50 | "babel-loader": "^8.2.2", 51 | "concurrently": "^6.0.2", 52 | "css-minimizer-webpack-plugin": "^3.0.2", 53 | "html-webpack-plugin": "^5.3.2", 54 | "isomorphic-fetch": "^3.0.0", 55 | "jest": "^27.1.0", 56 | "mini-css-extract-plugin": "^2.1.0", 57 | "node-fetch": "^2.6.1", 58 | "nodemon": "^2.0.7", 59 | "sass": "^1.36.0", 60 | "sass-loader": "^12.1.0", 61 | "style-loader": "^3.2.1", 62 | "supertest": "^6.1.6", 63 | "webpack": "^5.46.0", 64 | "webpack-cli": "4.7.2", 65 | "webpack-dev-server": "^3.11.2" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | echo "Processing deploy.sh" 2 | # Set EB BUCKET as env variable 3 | EB_BUCKET=elasticbeanstalk-us-east-1-746748738687 4 | # Set the default region for aws cli 5 | aws configure set default.region us-east-1 6 | # Log in to ECR 7 | eval $(aws ecr get-login --no-include-email --region us-east-1) 8 | # Build docker image based on our production Dockerfile 9 | docker build -t beamcorp/bql . 10 | # tag the image with the Travis-CI SHA 11 | docker tag beamcorp/bql:latest 746748738687.dkr.ecr.us-east-1.amazonaws.com/bql:$TRAVIS_COMMIT 12 | # Push built image to ECS 13 | docker push 746748738687.dkr.ecr.us-east-1.amazonaws.com/bql:$TRAVIS_COMMIT 14 | # Use the linux sed command to replace the text '' in our Dockerrun file with the Travis-CI SHA key 15 | sed -i='' "s//$TRAVIS_COMMIT/" Dockerrun.aws.json 16 | # Zip up our codebase, along with modified Dockerrun 17 | zip -r bql-prod-deploy.zip Dockerrun.aws.json . 18 | # Upload zip file to s3 bucket 19 | aws s3 cp bql-prod-deploy.zip s3://$EB_BUCKET/bql-prod-deploy.zip 20 | # Create a new application version with new Dockerrun 21 | aws elasticbeanstalk create-application-version --application-name BeamQL --version-label $TRAVIS_COMMIT --source-bundle S3Bucket=$EB_BUCKET,S3Key=bql-prod-deploy.zip 22 | # Update environment to use new version number 23 | aws elasticbeanstalk update-environment --environment-name Beamql-env --version-label $TRAVIS_COMMIT -------------------------------------------------------------------------------- /server/controllers/dataOutput 2.js: -------------------------------------------------------------------------------- 1 | 2 | export defult const data = { 3 | "allTables": { 4 | "planets": [ 5 | { 6 | "table_name": "planets", 7 | "column_name": "_id", 8 | "ordinal_position": 1, 9 | "column_default": "nextval('planets__id_seq'::regclass)", 10 | "data_type": "integer", 11 | "udt_name": "int4" 12 | }, 13 | { 14 | "table_name": "planets", 15 | "column_name": "name", 16 | "ordinal_position": 2, 17 | "column_default": null, 18 | "data_type": "character varying", 19 | "udt_name": "varchar" 20 | }, 21 | { 22 | "table_name": "planets", 23 | "column_name": "rotation_period", 24 | "ordinal_position": 3, 25 | "column_default": null, 26 | "data_type": "integer", 27 | "udt_name": "int4" 28 | }, 29 | { 30 | "table_name": "planets", 31 | "column_name": "orbital_period", 32 | "ordinal_position": 4, 33 | "column_default": null, 34 | "data_type": "integer", 35 | "udt_name": "int4" 36 | }, 37 | { 38 | "table_name": "planets", 39 | "column_name": "diameter", 40 | "ordinal_position": 5, 41 | "column_default": null, 42 | "data_type": "integer", 43 | "udt_name": "int4" 44 | }, 45 | { 46 | "table_name": "planets", 47 | "column_name": "climate", 48 | "ordinal_position": 6, 49 | "column_default": null, 50 | "data_type": "character varying", 51 | "udt_name": "varchar" 52 | }, 53 | { 54 | "table_name": "planets", 55 | "column_name": "gravity", 56 | "ordinal_position": 7, 57 | "column_default": null, 58 | "data_type": "character varying", 59 | "udt_name": "varchar" 60 | }, 61 | { 62 | "table_name": "planets", 63 | "column_name": "terrain", 64 | "ordinal_position": 8, 65 | "column_default": null, 66 | "data_type": "character varying", 67 | "udt_name": "varchar" 68 | }, 69 | { 70 | "table_name": "planets", 71 | "column_name": "surface_water", 72 | "ordinal_position": 9, 73 | "column_default": null, 74 | "data_type": "character varying", 75 | "udt_name": "varchar" 76 | }, 77 | { 78 | "table_name": "planets", 79 | "column_name": "population", 80 | "ordinal_position": 10, 81 | "column_default": null, 82 | "data_type": "bigint", 83 | "udt_name": "int8" 84 | } 85 | ], 86 | "pilots": [ 87 | { 88 | "table_name": "pilots", 89 | "column_name": "_id", 90 | "ordinal_position": 1, 91 | "column_default": "nextval('pilots__id_seq'::regclass)", 92 | "data_type": "integer", 93 | "udt_name": "int4" 94 | }, 95 | { 96 | "table_name": "pilots", 97 | "column_name": "person_id", 98 | "ordinal_position": 2, 99 | "column_default": null, 100 | "data_type": "bigint", 101 | "udt_name": "int8" 102 | }, 103 | { 104 | "table_name": "pilots", 105 | "column_name": "vessel_id", 106 | "ordinal_position": 3, 107 | "column_default": null, 108 | "data_type": "bigint", 109 | "udt_name": "int8" 110 | } 111 | ], 112 | "people_in_films": [ 113 | { 114 | "table_name": "people_in_films", 115 | "column_name": "_id", 116 | "ordinal_position": 1, 117 | "column_default": "nextval('people_in_films__id_seq'::regclass)", 118 | "data_type": "integer", 119 | "udt_name": "int4" 120 | }, 121 | { 122 | "table_name": "people_in_films", 123 | "column_name": "person_id", 124 | "ordinal_position": 2, 125 | "column_default": null, 126 | "data_type": "bigint", 127 | "udt_name": "int8" 128 | }, 129 | { 130 | "table_name": "people_in_films", 131 | "column_name": "film_id", 132 | "ordinal_position": 3, 133 | "column_default": null, 134 | "data_type": "bigint", 135 | "udt_name": "int8" 136 | } 137 | ], 138 | "films": [ 139 | { 140 | "table_name": "films", 141 | "column_name": "_id", 142 | "ordinal_position": 1, 143 | "column_default": "nextval('films__id_seq'::regclass)", 144 | "data_type": "integer", 145 | "udt_name": "int4" 146 | }, 147 | { 148 | "table_name": "films", 149 | "column_name": "title", 150 | "ordinal_position": 2, 151 | "column_default": null, 152 | "data_type": "character varying", 153 | "udt_name": "varchar" 154 | }, 155 | { 156 | "table_name": "films", 157 | "column_name": "episode_id", 158 | "ordinal_position": 3, 159 | "column_default": null, 160 | "data_type": "integer", 161 | "udt_name": "int4" 162 | }, 163 | { 164 | "table_name": "films", 165 | "column_name": "opening_crawl", 166 | "ordinal_position": 4, 167 | "column_default": null, 168 | "data_type": "character varying", 169 | "udt_name": "varchar" 170 | }, 171 | { 172 | "table_name": "films", 173 | "column_name": "director", 174 | "ordinal_position": 5, 175 | "column_default": null, 176 | "data_type": "character varying", 177 | "udt_name": "varchar" 178 | }, 179 | { 180 | "table_name": "films", 181 | "column_name": "producer", 182 | "ordinal_position": 6, 183 | "column_default": null, 184 | "data_type": "character varying", 185 | "udt_name": "varchar" 186 | }, 187 | { 188 | "table_name": "films", 189 | "column_name": "release_date", 190 | "ordinal_position": 7, 191 | "column_default": null, 192 | "data_type": "date", 193 | "udt_name": "date" 194 | } 195 | ], 196 | "species": [ 197 | { 198 | "table_name": "species", 199 | "column_name": "_id", 200 | "ordinal_position": 1, 201 | "column_default": "nextval('species__id_seq'::regclass)", 202 | "data_type": "integer", 203 | "udt_name": "int4" 204 | }, 205 | { 206 | "table_name": "species", 207 | "column_name": "name", 208 | "ordinal_position": 2, 209 | "column_default": null, 210 | "data_type": "character varying", 211 | "udt_name": "varchar" 212 | }, 213 | { 214 | "table_name": "species", 215 | "column_name": "classification", 216 | "ordinal_position": 3, 217 | "column_default": null, 218 | "data_type": "character varying", 219 | "udt_name": "varchar" 220 | }, 221 | { 222 | "table_name": "species", 223 | "column_name": "average_height", 224 | "ordinal_position": 4, 225 | "column_default": null, 226 | "data_type": "character varying", 227 | "udt_name": "varchar" 228 | }, 229 | { 230 | "table_name": "species", 231 | "column_name": "average_lifespan", 232 | "ordinal_position": 5, 233 | "column_default": null, 234 | "data_type": "character varying", 235 | "udt_name": "varchar" 236 | }, 237 | { 238 | "table_name": "species", 239 | "column_name": "hair_colors", 240 | "ordinal_position": 6, 241 | "column_default": null, 242 | "data_type": "character varying", 243 | "udt_name": "varchar" 244 | }, 245 | { 246 | "table_name": "species", 247 | "column_name": "skin_colors", 248 | "ordinal_position": 7, 249 | "column_default": null, 250 | "data_type": "character varying", 251 | "udt_name": "varchar" 252 | }, 253 | { 254 | "table_name": "species", 255 | "column_name": "eye_colors", 256 | "ordinal_position": 8, 257 | "column_default": null, 258 | "data_type": "character varying", 259 | "udt_name": "varchar" 260 | }, 261 | { 262 | "table_name": "species", 263 | "column_name": "language", 264 | "ordinal_position": 9, 265 | "column_default": null, 266 | "data_type": "character varying", 267 | "udt_name": "varchar" 268 | }, 269 | { 270 | "table_name": "species", 271 | "column_name": "homeworld_id", 272 | "ordinal_position": 10, 273 | "column_default": null, 274 | "data_type": "bigint", 275 | "udt_name": "int8" 276 | } 277 | ], 278 | "species_in_films": [ 279 | { 280 | "table_name": "species_in_films", 281 | "column_name": "_id", 282 | "ordinal_position": 1, 283 | "column_default": "nextval('species_in_films__id_seq'::regclass)", 284 | "data_type": "integer", 285 | "udt_name": "int4" 286 | }, 287 | { 288 | "table_name": "species_in_films", 289 | "column_name": "film_id", 290 | "ordinal_position": 2, 291 | "column_default": null, 292 | "data_type": "bigint", 293 | "udt_name": "int8" 294 | }, 295 | { 296 | "table_name": "species_in_films", 297 | "column_name": "species_id", 298 | "ordinal_position": 3, 299 | "column_default": null, 300 | "data_type": "bigint", 301 | "udt_name": "int8" 302 | } 303 | ], 304 | "vessels": [ 305 | { 306 | "table_name": "vessels", 307 | "column_name": "_id", 308 | "ordinal_position": 1, 309 | "column_default": "nextval('vessels__id_seq'::regclass)", 310 | "data_type": "integer", 311 | "udt_name": "int4" 312 | }, 313 | { 314 | "table_name": "vessels", 315 | "column_name": "name", 316 | "ordinal_position": 2, 317 | "column_default": null, 318 | "data_type": "character varying", 319 | "udt_name": "varchar" 320 | }, 321 | { 322 | "table_name": "vessels", 323 | "column_name": "manufacturer", 324 | "ordinal_position": 3, 325 | "column_default": null, 326 | "data_type": "character varying", 327 | "udt_name": "varchar" 328 | }, 329 | { 330 | "table_name": "vessels", 331 | "column_name": "model", 332 | "ordinal_position": 4, 333 | "column_default": null, 334 | "data_type": "character varying", 335 | "udt_name": "varchar" 336 | }, 337 | { 338 | "table_name": "vessels", 339 | "column_name": "vessel_type", 340 | "ordinal_position": 5, 341 | "column_default": null, 342 | "data_type": "character varying", 343 | "udt_name": "varchar" 344 | }, 345 | { 346 | "table_name": "vessels", 347 | "column_name": "vessel_class", 348 | "ordinal_position": 6, 349 | "column_default": null, 350 | "data_type": "character varying", 351 | "udt_name": "varchar" 352 | }, 353 | { 354 | "table_name": "vessels", 355 | "column_name": "cost_in_credits", 356 | "ordinal_position": 7, 357 | "column_default": null, 358 | "data_type": "bigint", 359 | "udt_name": "int8" 360 | }, 361 | { 362 | "table_name": "vessels", 363 | "column_name": "length", 364 | "ordinal_position": 8, 365 | "column_default": null, 366 | "data_type": "character varying", 367 | "udt_name": "varchar" 368 | }, 369 | { 370 | "table_name": "vessels", 371 | "column_name": "max_atmosphering_speed", 372 | "ordinal_position": 9, 373 | "column_default": null, 374 | "data_type": "character varying", 375 | "udt_name": "varchar" 376 | }, 377 | { 378 | "table_name": "vessels", 379 | "column_name": "crew", 380 | "ordinal_position": 10, 381 | "column_default": null, 382 | "data_type": "integer", 383 | "udt_name": "int4" 384 | }, 385 | { 386 | "table_name": "vessels", 387 | "column_name": "passengers", 388 | "ordinal_position": 11, 389 | "column_default": null, 390 | "data_type": "integer", 391 | "udt_name": "int4" 392 | }, 393 | { 394 | "table_name": "vessels", 395 | "column_name": "cargo_capacity", 396 | "ordinal_position": 12, 397 | "column_default": null, 398 | "data_type": "character varying", 399 | "udt_name": "varchar" 400 | }, 401 | { 402 | "table_name": "vessels", 403 | "column_name": "consumables", 404 | "ordinal_position": 13, 405 | "column_default": null, 406 | "data_type": "character varying", 407 | "udt_name": "varchar" 408 | } 409 | ], 410 | "vessels_in_films": [ 411 | { 412 | "table_name": "vessels_in_films", 413 | "column_name": "_id", 414 | "ordinal_position": 1, 415 | "column_default": "nextval('vessels_in_films__id_seq'::regclass)", 416 | "data_type": "integer", 417 | "udt_name": "int4" 418 | }, 419 | { 420 | "table_name": "vessels_in_films", 421 | "column_name": "vessel_id", 422 | "ordinal_position": 2, 423 | "column_default": null, 424 | "data_type": "bigint", 425 | "udt_name": "int8" 426 | }, 427 | { 428 | "table_name": "vessels_in_films", 429 | "column_name": "film_id", 430 | "ordinal_position": 3, 431 | "column_default": null, 432 | "data_type": "bigint", 433 | "udt_name": "int8" 434 | } 435 | ], 436 | "people": [ 437 | { 438 | "table_name": "people", 439 | "column_name": "_id", 440 | "ordinal_position": 1, 441 | "column_default": "nextval('people__id_seq'::regclass)", 442 | "data_type": "integer", 443 | "udt_name": "int4" 444 | }, 445 | { 446 | "table_name": "people", 447 | "column_name": "name", 448 | "ordinal_position": 2, 449 | "column_default": null, 450 | "data_type": "character varying", 451 | "udt_name": "varchar" 452 | }, 453 | { 454 | "table_name": "people", 455 | "column_name": "mass", 456 | "ordinal_position": 3, 457 | "column_default": null, 458 | "data_type": "character varying", 459 | "udt_name": "varchar" 460 | }, 461 | { 462 | "table_name": "people", 463 | "column_name": "hair_color", 464 | "ordinal_position": 4, 465 | "column_default": null, 466 | "data_type": "character varying", 467 | "udt_name": "varchar" 468 | }, 469 | { 470 | "table_name": "people", 471 | "column_name": "skin_color", 472 | "ordinal_position": 5, 473 | "column_default": null, 474 | "data_type": "character varying", 475 | "udt_name": "varchar" 476 | }, 477 | { 478 | "table_name": "people", 479 | "column_name": "eye_color", 480 | "ordinal_position": 6, 481 | "column_default": null, 482 | "data_type": "character varying", 483 | "udt_name": "varchar" 484 | }, 485 | { 486 | "table_name": "people", 487 | "column_name": "birth_year", 488 | "ordinal_position": 7, 489 | "column_default": null, 490 | "data_type": "character varying", 491 | "udt_name": "varchar" 492 | }, 493 | { 494 | "table_name": "people", 495 | "column_name": "gender", 496 | "ordinal_position": 8, 497 | "column_default": null, 498 | "data_type": "character varying", 499 | "udt_name": "varchar" 500 | }, 501 | { 502 | "table_name": "people", 503 | "column_name": "species_id", 504 | "ordinal_position": 9, 505 | "column_default": null, 506 | "data_type": "bigint", 507 | "udt_name": "int8" 508 | }, 509 | { 510 | "table_name": "people", 511 | "column_name": "homeworld_id", 512 | "ordinal_position": 10, 513 | "column_default": null, 514 | "data_type": "bigint", 515 | "udt_name": "int8" 516 | }, 517 | { 518 | "table_name": "people", 519 | "column_name": "height", 520 | "ordinal_position": 11, 521 | "column_default": null, 522 | "data_type": "integer", 523 | "udt_name": "int4" 524 | } 525 | ], 526 | "planets_in_films": [ 527 | { 528 | "table_name": "planets_in_films", 529 | "column_name": "_id", 530 | "ordinal_position": 1, 531 | "column_default": "nextval('planets_in_films__id_seq'::regclass)", 532 | "data_type": "integer", 533 | "udt_name": "int4" 534 | }, 535 | { 536 | "table_name": "planets_in_films", 537 | "column_name": "film_id", 538 | "ordinal_position": 2, 539 | "column_default": null, 540 | "data_type": "bigint", 541 | "udt_name": "int8" 542 | }, 543 | { 544 | "table_name": "planets_in_films", 545 | "column_name": "planet_id", 546 | "ordinal_position": 3, 547 | "column_default": null, 548 | "data_type": "bigint", 549 | "udt_name": "int8" 550 | } 551 | ], 552 | "starship_specs": [ 553 | { 554 | "table_name": "starship_specs", 555 | "column_name": "_id", 556 | "ordinal_position": 1, 557 | "column_default": "nextval('starship_specs__id_seq'::regclass)", 558 | "data_type": "integer", 559 | "udt_name": "int4" 560 | }, 561 | { 562 | "table_name": "starship_specs", 563 | "column_name": "hyperdrive_rating", 564 | "ordinal_position": 2, 565 | "column_default": null, 566 | "data_type": "character varying", 567 | "udt_name": "varchar" 568 | }, 569 | { 570 | "table_name": "starship_specs", 571 | "column_name": "MGLT", 572 | "ordinal_position": 3, 573 | "column_default": null, 574 | "data_type": "character varying", 575 | "udt_name": "varchar" 576 | }, 577 | { 578 | "table_name": "starship_specs", 579 | "column_name": "vessel_id", 580 | "ordinal_position": 4, 581 | "column_default": null, 582 | "data_type": "bigint", 583 | "udt_name": "int8" 584 | } 585 | ] 586 | }, 587 | "foreignKeys": [ 588 | { 589 | "foreign_table": "people", 590 | "rel": ">-", 591 | "primary_table": "planets", 592 | "fk_columns": "homeworld_id", 593 | "constraint_name": "people_fk1", 594 | "constraint_type": "FOREIGN KEY" 595 | }, 596 | { 597 | "foreign_table": "people", 598 | "rel": ">-", 599 | "primary_table": "species", 600 | "fk_columns": "species_id", 601 | "constraint_name": "people_fk0", 602 | "constraint_type": "FOREIGN KEY" 603 | }, 604 | { 605 | "foreign_table": "people_in_films", 606 | "rel": ">-", 607 | "primary_table": "films", 608 | "fk_columns": "film_id", 609 | "constraint_name": "people_in_films_fk1", 610 | "constraint_type": "FOREIGN KEY" 611 | }, 612 | { 613 | "foreign_table": "people_in_films", 614 | "rel": ">-", 615 | "primary_table": "people", 616 | "fk_columns": "person_id", 617 | "constraint_name": "people_in_films_fk0", 618 | "constraint_type": "FOREIGN KEY" 619 | }, 620 | { 621 | "foreign_table": "pilots", 622 | "rel": ">-", 623 | "primary_table": "people", 624 | "fk_columns": "person_id", 625 | "constraint_name": "pilots_fk0", 626 | "constraint_type": "FOREIGN KEY" 627 | }, 628 | { 629 | "foreign_table": "pilots", 630 | "rel": ">-", 631 | "primary_table": "vessels", 632 | "fk_columns": "vessel_id", 633 | "constraint_name": "pilots_fk1", 634 | "constraint_type": "FOREIGN KEY" 635 | }, 636 | { 637 | "foreign_table": "planets_in_films", 638 | "rel": ">-", 639 | "primary_table": "films", 640 | "fk_columns": "film_id", 641 | "constraint_name": "planets_in_films_fk0", 642 | "constraint_type": "FOREIGN KEY" 643 | }, 644 | { 645 | "foreign_table": "planets_in_films", 646 | "rel": ">-", 647 | "primary_table": "planets", 648 | "fk_columns": "planet_id", 649 | "constraint_name": "planets_in_films_fk1", 650 | "constraint_type": "FOREIGN KEY" 651 | }, 652 | { 653 | "foreign_table": "species", 654 | "rel": ">-", 655 | "primary_table": "planets", 656 | "fk_columns": "homeworld_id", 657 | "constraint_name": "species_fk0", 658 | "constraint_type": "FOREIGN KEY" 659 | }, 660 | { 661 | "foreign_table": "species_in_films", 662 | "rel": ">-", 663 | "primary_table": "films", 664 | "fk_columns": "film_id", 665 | "constraint_name": "species_in_films_fk0", 666 | "constraint_type": "FOREIGN KEY" 667 | }, 668 | { 669 | "foreign_table": "species_in_films", 670 | "rel": ">-", 671 | "primary_table": "species", 672 | "fk_columns": "species_id", 673 | "constraint_name": "species_in_films_fk1", 674 | "constraint_type": "FOREIGN KEY" 675 | }, 676 | { 677 | "foreign_table": "starship_specs", 678 | "rel": ">-", 679 | "primary_table": "vessels", 680 | "fk_columns": "vessel_id", 681 | "constraint_name": "starship_specs_fk0", 682 | "constraint_type": "FOREIGN KEY" 683 | }, 684 | { 685 | "foreign_table": "vessels_in_films", 686 | "rel": ">-", 687 | "primary_table": "films", 688 | "fk_columns": "film_id", 689 | "constraint_name": "vessels_in_films_fk1", 690 | "constraint_type": "FOREIGN KEY" 691 | }, 692 | { 693 | "foreign_table": "vessels_in_films", 694 | "rel": ">-", 695 | "primary_table": "vessels", 696 | "fk_columns": "vessel_id", 697 | "constraint_name": "vessels_in_films_fk0", 698 | "constraint_type": "FOREIGN KEY" 699 | } 700 | ], 701 | "primaryKeys": [ 702 | { 703 | "table_name": "films", 704 | "constraint_name": "films_pk", 705 | "primary_key_columns": "_id" 706 | }, 707 | { 708 | "table_name": "people", 709 | "constraint_name": "people_pk", 710 | "primary_key_columns": "_id" 711 | }, 712 | { 713 | "table_name": "people_in_films", 714 | "constraint_name": "people_in_films_pk", 715 | "primary_key_columns": "_id" 716 | }, 717 | { 718 | "table_name": "pilots", 719 | "constraint_name": "pilots_pk", 720 | "primary_key_columns": "_id" 721 | }, 722 | { 723 | "table_name": "planets", 724 | "constraint_name": "planets_pk", 725 | "primary_key_columns": "_id" 726 | }, 727 | { 728 | "table_name": "planets_in_films", 729 | "constraint_name": "planets_in_films_pk", 730 | "primary_key_columns": "_id" 731 | }, 732 | { 733 | "table_name": "species", 734 | "constraint_name": "species_pk", 735 | "primary_key_columns": "_id" 736 | }, 737 | { 738 | "table_name": "species_in_films", 739 | "constraint_name": "species_in_films_pk", 740 | "primary_key_columns": "_id" 741 | }, 742 | { 743 | "table_name": "starship_specs", 744 | "constraint_name": "starship_specs_pk", 745 | "primary_key_columns": "_id" 746 | }, 747 | { 748 | "table_name": "vessels", 749 | "constraint_name": "vessels_pk", 750 | "primary_key_columns": "_id" 751 | }, 752 | { 753 | "table_name": "vessels_in_films", 754 | "constraint_name": "vessels_in_films_pk", 755 | "primary_key_columns": "_id" 756 | } 757 | ] 758 | } -------------------------------------------------------------------------------- /server/controllers/dataOutput.js: -------------------------------------------------------------------------------- 1 | 2 | export defult const data = { 3 | "allTables": { 4 | "planets": [ 5 | { 6 | "table_name": "planets", 7 | "column_name": "_id", 8 | "ordinal_position": 1, 9 | "column_default": "nextval('planets__id_seq'::regclass)", 10 | "data_type": "integer", 11 | "udt_name": "int4" 12 | }, 13 | { 14 | "table_name": "planets", 15 | "column_name": "name", 16 | "ordinal_position": 2, 17 | "column_default": null, 18 | "data_type": "character varying", 19 | "udt_name": "varchar" 20 | }, 21 | { 22 | "table_name": "planets", 23 | "column_name": "rotation_period", 24 | "ordinal_position": 3, 25 | "column_default": null, 26 | "data_type": "integer", 27 | "udt_name": "int4" 28 | }, 29 | { 30 | "table_name": "planets", 31 | "column_name": "orbital_period", 32 | "ordinal_position": 4, 33 | "column_default": null, 34 | "data_type": "integer", 35 | "udt_name": "int4" 36 | }, 37 | { 38 | "table_name": "planets", 39 | "column_name": "diameter", 40 | "ordinal_position": 5, 41 | "column_default": null, 42 | "data_type": "integer", 43 | "udt_name": "int4" 44 | }, 45 | { 46 | "table_name": "planets", 47 | "column_name": "climate", 48 | "ordinal_position": 6, 49 | "column_default": null, 50 | "data_type": "character varying", 51 | "udt_name": "varchar" 52 | }, 53 | { 54 | "table_name": "planets", 55 | "column_name": "gravity", 56 | "ordinal_position": 7, 57 | "column_default": null, 58 | "data_type": "character varying", 59 | "udt_name": "varchar" 60 | }, 61 | { 62 | "table_name": "planets", 63 | "column_name": "terrain", 64 | "ordinal_position": 8, 65 | "column_default": null, 66 | "data_type": "character varying", 67 | "udt_name": "varchar" 68 | }, 69 | { 70 | "table_name": "planets", 71 | "column_name": "surface_water", 72 | "ordinal_position": 9, 73 | "column_default": null, 74 | "data_type": "character varying", 75 | "udt_name": "varchar" 76 | }, 77 | { 78 | "table_name": "planets", 79 | "column_name": "population", 80 | "ordinal_position": 10, 81 | "column_default": null, 82 | "data_type": "bigint", 83 | "udt_name": "int8" 84 | } 85 | ], 86 | "pilots": [ 87 | { 88 | "table_name": "pilots", 89 | "column_name": "_id", 90 | "ordinal_position": 1, 91 | "column_default": "nextval('pilots__id_seq'::regclass)", 92 | "data_type": "integer", 93 | "udt_name": "int4" 94 | }, 95 | { 96 | "table_name": "pilots", 97 | "column_name": "person_id", 98 | "ordinal_position": 2, 99 | "column_default": null, 100 | "data_type": "bigint", 101 | "udt_name": "int8" 102 | }, 103 | { 104 | "table_name": "pilots", 105 | "column_name": "vessel_id", 106 | "ordinal_position": 3, 107 | "column_default": null, 108 | "data_type": "bigint", 109 | "udt_name": "int8" 110 | } 111 | ], 112 | "people_in_films": [ 113 | { 114 | "table_name": "people_in_films", 115 | "column_name": "_id", 116 | "ordinal_position": 1, 117 | "column_default": "nextval('people_in_films__id_seq'::regclass)", 118 | "data_type": "integer", 119 | "udt_name": "int4" 120 | }, 121 | { 122 | "table_name": "people_in_films", 123 | "column_name": "person_id", 124 | "ordinal_position": 2, 125 | "column_default": null, 126 | "data_type": "bigint", 127 | "udt_name": "int8" 128 | }, 129 | { 130 | "table_name": "people_in_films", 131 | "column_name": "film_id", 132 | "ordinal_position": 3, 133 | "column_default": null, 134 | "data_type": "bigint", 135 | "udt_name": "int8" 136 | } 137 | ], 138 | "films": [ 139 | { 140 | "table_name": "films", 141 | "column_name": "_id", 142 | "ordinal_position": 1, 143 | "column_default": "nextval('films__id_seq'::regclass)", 144 | "data_type": "integer", 145 | "udt_name": "int4" 146 | }, 147 | { 148 | "table_name": "films", 149 | "column_name": "title", 150 | "ordinal_position": 2, 151 | "column_default": null, 152 | "data_type": "character varying", 153 | "udt_name": "varchar" 154 | }, 155 | { 156 | "table_name": "films", 157 | "column_name": "episode_id", 158 | "ordinal_position": 3, 159 | "column_default": null, 160 | "data_type": "integer", 161 | "udt_name": "int4" 162 | }, 163 | { 164 | "table_name": "films", 165 | "column_name": "opening_crawl", 166 | "ordinal_position": 4, 167 | "column_default": null, 168 | "data_type": "character varying", 169 | "udt_name": "varchar" 170 | }, 171 | { 172 | "table_name": "films", 173 | "column_name": "director", 174 | "ordinal_position": 5, 175 | "column_default": null, 176 | "data_type": "character varying", 177 | "udt_name": "varchar" 178 | }, 179 | { 180 | "table_name": "films", 181 | "column_name": "producer", 182 | "ordinal_position": 6, 183 | "column_default": null, 184 | "data_type": "character varying", 185 | "udt_name": "varchar" 186 | }, 187 | { 188 | "table_name": "films", 189 | "column_name": "release_date", 190 | "ordinal_position": 7, 191 | "column_default": null, 192 | "data_type": "date", 193 | "udt_name": "date" 194 | } 195 | ], 196 | "species": [ 197 | { 198 | "table_name": "species", 199 | "column_name": "_id", 200 | "ordinal_position": 1, 201 | "column_default": "nextval('species__id_seq'::regclass)", 202 | "data_type": "integer", 203 | "udt_name": "int4" 204 | }, 205 | { 206 | "table_name": "species", 207 | "column_name": "name", 208 | "ordinal_position": 2, 209 | "column_default": null, 210 | "data_type": "character varying", 211 | "udt_name": "varchar" 212 | }, 213 | { 214 | "table_name": "species", 215 | "column_name": "classification", 216 | "ordinal_position": 3, 217 | "column_default": null, 218 | "data_type": "character varying", 219 | "udt_name": "varchar" 220 | }, 221 | { 222 | "table_name": "species", 223 | "column_name": "average_height", 224 | "ordinal_position": 4, 225 | "column_default": null, 226 | "data_type": "character varying", 227 | "udt_name": "varchar" 228 | }, 229 | { 230 | "table_name": "species", 231 | "column_name": "average_lifespan", 232 | "ordinal_position": 5, 233 | "column_default": null, 234 | "data_type": "character varying", 235 | "udt_name": "varchar" 236 | }, 237 | { 238 | "table_name": "species", 239 | "column_name": "hair_colors", 240 | "ordinal_position": 6, 241 | "column_default": null, 242 | "data_type": "character varying", 243 | "udt_name": "varchar" 244 | }, 245 | { 246 | "table_name": "species", 247 | "column_name": "skin_colors", 248 | "ordinal_position": 7, 249 | "column_default": null, 250 | "data_type": "character varying", 251 | "udt_name": "varchar" 252 | }, 253 | { 254 | "table_name": "species", 255 | "column_name": "eye_colors", 256 | "ordinal_position": 8, 257 | "column_default": null, 258 | "data_type": "character varying", 259 | "udt_name": "varchar" 260 | }, 261 | { 262 | "table_name": "species", 263 | "column_name": "language", 264 | "ordinal_position": 9, 265 | "column_default": null, 266 | "data_type": "character varying", 267 | "udt_name": "varchar" 268 | }, 269 | { 270 | "table_name": "species", 271 | "column_name": "homeworld_id", 272 | "ordinal_position": 10, 273 | "column_default": null, 274 | "data_type": "bigint", 275 | "udt_name": "int8" 276 | } 277 | ], 278 | "species_in_films": [ 279 | { 280 | "table_name": "species_in_films", 281 | "column_name": "_id", 282 | "ordinal_position": 1, 283 | "column_default": "nextval('species_in_films__id_seq'::regclass)", 284 | "data_type": "integer", 285 | "udt_name": "int4" 286 | }, 287 | { 288 | "table_name": "species_in_films", 289 | "column_name": "film_id", 290 | "ordinal_position": 2, 291 | "column_default": null, 292 | "data_type": "bigint", 293 | "udt_name": "int8" 294 | }, 295 | { 296 | "table_name": "species_in_films", 297 | "column_name": "species_id", 298 | "ordinal_position": 3, 299 | "column_default": null, 300 | "data_type": "bigint", 301 | "udt_name": "int8" 302 | } 303 | ], 304 | "vessels": [ 305 | { 306 | "table_name": "vessels", 307 | "column_name": "_id", 308 | "ordinal_position": 1, 309 | "column_default": "nextval('vessels__id_seq'::regclass)", 310 | "data_type": "integer", 311 | "udt_name": "int4" 312 | }, 313 | { 314 | "table_name": "vessels", 315 | "column_name": "name", 316 | "ordinal_position": 2, 317 | "column_default": null, 318 | "data_type": "character varying", 319 | "udt_name": "varchar" 320 | }, 321 | { 322 | "table_name": "vessels", 323 | "column_name": "manufacturer", 324 | "ordinal_position": 3, 325 | "column_default": null, 326 | "data_type": "character varying", 327 | "udt_name": "varchar" 328 | }, 329 | { 330 | "table_name": "vessels", 331 | "column_name": "model", 332 | "ordinal_position": 4, 333 | "column_default": null, 334 | "data_type": "character varying", 335 | "udt_name": "varchar" 336 | }, 337 | { 338 | "table_name": "vessels", 339 | "column_name": "vessel_type", 340 | "ordinal_position": 5, 341 | "column_default": null, 342 | "data_type": "character varying", 343 | "udt_name": "varchar" 344 | }, 345 | { 346 | "table_name": "vessels", 347 | "column_name": "vessel_class", 348 | "ordinal_position": 6, 349 | "column_default": null, 350 | "data_type": "character varying", 351 | "udt_name": "varchar" 352 | }, 353 | { 354 | "table_name": "vessels", 355 | "column_name": "cost_in_credits", 356 | "ordinal_position": 7, 357 | "column_default": null, 358 | "data_type": "bigint", 359 | "udt_name": "int8" 360 | }, 361 | { 362 | "table_name": "vessels", 363 | "column_name": "length", 364 | "ordinal_position": 8, 365 | "column_default": null, 366 | "data_type": "character varying", 367 | "udt_name": "varchar" 368 | }, 369 | { 370 | "table_name": "vessels", 371 | "column_name": "max_atmosphering_speed", 372 | "ordinal_position": 9, 373 | "column_default": null, 374 | "data_type": "character varying", 375 | "udt_name": "varchar" 376 | }, 377 | { 378 | "table_name": "vessels", 379 | "column_name": "crew", 380 | "ordinal_position": 10, 381 | "column_default": null, 382 | "data_type": "integer", 383 | "udt_name": "int4" 384 | }, 385 | { 386 | "table_name": "vessels", 387 | "column_name": "passengers", 388 | "ordinal_position": 11, 389 | "column_default": null, 390 | "data_type": "integer", 391 | "udt_name": "int4" 392 | }, 393 | { 394 | "table_name": "vessels", 395 | "column_name": "cargo_capacity", 396 | "ordinal_position": 12, 397 | "column_default": null, 398 | "data_type": "character varying", 399 | "udt_name": "varchar" 400 | }, 401 | { 402 | "table_name": "vessels", 403 | "column_name": "consumables", 404 | "ordinal_position": 13, 405 | "column_default": null, 406 | "data_type": "character varying", 407 | "udt_name": "varchar" 408 | } 409 | ], 410 | "vessels_in_films": [ 411 | { 412 | "table_name": "vessels_in_films", 413 | "column_name": "_id", 414 | "ordinal_position": 1, 415 | "column_default": "nextval('vessels_in_films__id_seq'::regclass)", 416 | "data_type": "integer", 417 | "udt_name": "int4" 418 | }, 419 | { 420 | "table_name": "vessels_in_films", 421 | "column_name": "vessel_id", 422 | "ordinal_position": 2, 423 | "column_default": null, 424 | "data_type": "bigint", 425 | "udt_name": "int8" 426 | }, 427 | { 428 | "table_name": "vessels_in_films", 429 | "column_name": "film_id", 430 | "ordinal_position": 3, 431 | "column_default": null, 432 | "data_type": "bigint", 433 | "udt_name": "int8" 434 | } 435 | ], 436 | "people": [ 437 | { 438 | "table_name": "people", 439 | "column_name": "_id", 440 | "ordinal_position": 1, 441 | "column_default": "nextval('people__id_seq'::regclass)", 442 | "data_type": "integer", 443 | "udt_name": "int4" 444 | }, 445 | { 446 | "table_name": "people", 447 | "column_name": "name", 448 | "ordinal_position": 2, 449 | "column_default": null, 450 | "data_type": "character varying", 451 | "udt_name": "varchar" 452 | }, 453 | { 454 | "table_name": "people", 455 | "column_name": "mass", 456 | "ordinal_position": 3, 457 | "column_default": null, 458 | "data_type": "character varying", 459 | "udt_name": "varchar" 460 | }, 461 | { 462 | "table_name": "people", 463 | "column_name": "hair_color", 464 | "ordinal_position": 4, 465 | "column_default": null, 466 | "data_type": "character varying", 467 | "udt_name": "varchar" 468 | }, 469 | { 470 | "table_name": "people", 471 | "column_name": "skin_color", 472 | "ordinal_position": 5, 473 | "column_default": null, 474 | "data_type": "character varying", 475 | "udt_name": "varchar" 476 | }, 477 | { 478 | "table_name": "people", 479 | "column_name": "eye_color", 480 | "ordinal_position": 6, 481 | "column_default": null, 482 | "data_type": "character varying", 483 | "udt_name": "varchar" 484 | }, 485 | { 486 | "table_name": "people", 487 | "column_name": "birth_year", 488 | "ordinal_position": 7, 489 | "column_default": null, 490 | "data_type": "character varying", 491 | "udt_name": "varchar" 492 | }, 493 | { 494 | "table_name": "people", 495 | "column_name": "gender", 496 | "ordinal_position": 8, 497 | "column_default": null, 498 | "data_type": "character varying", 499 | "udt_name": "varchar" 500 | }, 501 | { 502 | "table_name": "people", 503 | "column_name": "species_id", 504 | "ordinal_position": 9, 505 | "column_default": null, 506 | "data_type": "bigint", 507 | "udt_name": "int8" 508 | }, 509 | { 510 | "table_name": "people", 511 | "column_name": "homeworld_id", 512 | "ordinal_position": 10, 513 | "column_default": null, 514 | "data_type": "bigint", 515 | "udt_name": "int8" 516 | }, 517 | { 518 | "table_name": "people", 519 | "column_name": "height", 520 | "ordinal_position": 11, 521 | "column_default": null, 522 | "data_type": "integer", 523 | "udt_name": "int4" 524 | } 525 | ], 526 | "planets_in_films": [ 527 | { 528 | "table_name": "planets_in_films", 529 | "column_name": "_id", 530 | "ordinal_position": 1, 531 | "column_default": "nextval('planets_in_films__id_seq'::regclass)", 532 | "data_type": "integer", 533 | "udt_name": "int4" 534 | }, 535 | { 536 | "table_name": "planets_in_films", 537 | "column_name": "film_id", 538 | "ordinal_position": 2, 539 | "column_default": null, 540 | "data_type": "bigint", 541 | "udt_name": "int8" 542 | }, 543 | { 544 | "table_name": "planets_in_films", 545 | "column_name": "planet_id", 546 | "ordinal_position": 3, 547 | "column_default": null, 548 | "data_type": "bigint", 549 | "udt_name": "int8" 550 | } 551 | ], 552 | "starship_specs": [ 553 | { 554 | "table_name": "starship_specs", 555 | "column_name": "_id", 556 | "ordinal_position": 1, 557 | "column_default": "nextval('starship_specs__id_seq'::regclass)", 558 | "data_type": "integer", 559 | "udt_name": "int4" 560 | }, 561 | { 562 | "table_name": "starship_specs", 563 | "column_name": "hyperdrive_rating", 564 | "ordinal_position": 2, 565 | "column_default": null, 566 | "data_type": "character varying", 567 | "udt_name": "varchar" 568 | }, 569 | { 570 | "table_name": "starship_specs", 571 | "column_name": "MGLT", 572 | "ordinal_position": 3, 573 | "column_default": null, 574 | "data_type": "character varying", 575 | "udt_name": "varchar" 576 | }, 577 | { 578 | "table_name": "starship_specs", 579 | "column_name": "vessel_id", 580 | "ordinal_position": 4, 581 | "column_default": null, 582 | "data_type": "bigint", 583 | "udt_name": "int8" 584 | } 585 | ] 586 | }, 587 | "foreignKeys": [ 588 | { 589 | "foreign_table": "people", 590 | "rel": ">-", 591 | "primary_table": "planets", 592 | "fk_columns": "homeworld_id", 593 | "constraint_name": "people_fk1", 594 | "constraint_type": "FOREIGN KEY" 595 | }, 596 | { 597 | "foreign_table": "people", 598 | "rel": ">-", 599 | "primary_table": "species", 600 | "fk_columns": "species_id", 601 | "constraint_name": "people_fk0", 602 | "constraint_type": "FOREIGN KEY" 603 | }, 604 | { 605 | "foreign_table": "people_in_films", 606 | "rel": ">-", 607 | "primary_table": "films", 608 | "fk_columns": "film_id", 609 | "constraint_name": "people_in_films_fk1", 610 | "constraint_type": "FOREIGN KEY" 611 | }, 612 | { 613 | "foreign_table": "people_in_films", 614 | "rel": ">-", 615 | "primary_table": "people", 616 | "fk_columns": "person_id", 617 | "constraint_name": "people_in_films_fk0", 618 | "constraint_type": "FOREIGN KEY" 619 | }, 620 | { 621 | "foreign_table": "pilots", 622 | "rel": ">-", 623 | "primary_table": "people", 624 | "fk_columns": "person_id", 625 | "constraint_name": "pilots_fk0", 626 | "constraint_type": "FOREIGN KEY" 627 | }, 628 | { 629 | "foreign_table": "pilots", 630 | "rel": ">-", 631 | "primary_table": "vessels", 632 | "fk_columns": "vessel_id", 633 | "constraint_name": "pilots_fk1", 634 | "constraint_type": "FOREIGN KEY" 635 | }, 636 | { 637 | "foreign_table": "planets_in_films", 638 | "rel": ">-", 639 | "primary_table": "films", 640 | "fk_columns": "film_id", 641 | "constraint_name": "planets_in_films_fk0", 642 | "constraint_type": "FOREIGN KEY" 643 | }, 644 | { 645 | "foreign_table": "planets_in_films", 646 | "rel": ">-", 647 | "primary_table": "planets", 648 | "fk_columns": "planet_id", 649 | "constraint_name": "planets_in_films_fk1", 650 | "constraint_type": "FOREIGN KEY" 651 | }, 652 | { 653 | "foreign_table": "species", 654 | "rel": ">-", 655 | "primary_table": "planets", 656 | "fk_columns": "homeworld_id", 657 | "constraint_name": "species_fk0", 658 | "constraint_type": "FOREIGN KEY" 659 | }, 660 | { 661 | "foreign_table": "species_in_films", 662 | "rel": ">-", 663 | "primary_table": "films", 664 | "fk_columns": "film_id", 665 | "constraint_name": "species_in_films_fk0", 666 | "constraint_type": "FOREIGN KEY" 667 | }, 668 | { 669 | "foreign_table": "species_in_films", 670 | "rel": ">-", 671 | "primary_table": "species", 672 | "fk_columns": "species_id", 673 | "constraint_name": "species_in_films_fk1", 674 | "constraint_type": "FOREIGN KEY" 675 | }, 676 | { 677 | "foreign_table": "starship_specs", 678 | "rel": ">-", 679 | "primary_table": "vessels", 680 | "fk_columns": "vessel_id", 681 | "constraint_name": "starship_specs_fk0", 682 | "constraint_type": "FOREIGN KEY" 683 | }, 684 | { 685 | "foreign_table": "vessels_in_films", 686 | "rel": ">-", 687 | "primary_table": "films", 688 | "fk_columns": "film_id", 689 | "constraint_name": "vessels_in_films_fk1", 690 | "constraint_type": "FOREIGN KEY" 691 | }, 692 | { 693 | "foreign_table": "vessels_in_films", 694 | "rel": ">-", 695 | "primary_table": "vessels", 696 | "fk_columns": "vessel_id", 697 | "constraint_name": "vessels_in_films_fk0", 698 | "constraint_type": "FOREIGN KEY" 699 | } 700 | ], 701 | "primaryKeys": [ 702 | { 703 | "table_name": "films", 704 | "constraint_name": "films_pk", 705 | "primary_key_columns": "_id" 706 | }, 707 | { 708 | "table_name": "people", 709 | "constraint_name": "people_pk", 710 | "primary_key_columns": "_id" 711 | }, 712 | { 713 | "table_name": "people_in_films", 714 | "constraint_name": "people_in_films_pk", 715 | "primary_key_columns": "_id" 716 | }, 717 | { 718 | "table_name": "pilots", 719 | "constraint_name": "pilots_pk", 720 | "primary_key_columns": "_id" 721 | }, 722 | { 723 | "table_name": "planets", 724 | "constraint_name": "planets_pk", 725 | "primary_key_columns": "_id" 726 | }, 727 | { 728 | "table_name": "planets_in_films", 729 | "constraint_name": "planets_in_films_pk", 730 | "primary_key_columns": "_id" 731 | }, 732 | { 733 | "table_name": "species", 734 | "constraint_name": "species_pk", 735 | "primary_key_columns": "_id" 736 | }, 737 | { 738 | "table_name": "species_in_films", 739 | "constraint_name": "species_in_films_pk", 740 | "primary_key_columns": "_id" 741 | }, 742 | { 743 | "table_name": "starship_specs", 744 | "constraint_name": "starship_specs_pk", 745 | "primary_key_columns": "_id" 746 | }, 747 | { 748 | "table_name": "vessels", 749 | "constraint_name": "vessels_pk", 750 | "primary_key_columns": "_id" 751 | }, 752 | { 753 | "table_name": "vessels_in_films", 754 | "constraint_name": "vessels_in_films_pk", 755 | "primary_key_columns": "_id" 756 | } 757 | ] 758 | } -------------------------------------------------------------------------------- /server/controllers/dbmodel.js: -------------------------------------------------------------------------------- 1 | 2 | // import Pool from 'pg'; 3 | const { Pool } = require('pg'); 4 | let PG_URI; 5 | const pool = new Pool({ 6 | connectionString: "postgres://vdnvhfkq:sYiMTdCmk1vs2br_eUrrmX1unPvfucdW@batyr.db.elephantsql.com/vdnvhfkq" 7 | }); 8 | 9 | module.exports = { 10 | query: (text, params, callback) => { 11 | console.log('executed query', text); 12 | return pool.query(text, params, callback); 13 | } 14 | }; -------------------------------------------------------------------------------- /server/controllers/gqlController.js: -------------------------------------------------------------------------------- 1 | const { singular } = require('pluralize'); 2 | const camelCase = require('camelcase'); 3 | const gqlController = {} 4 | const { 5 | capFirstLet, 6 | snakeToTitle, 7 | isNullable, 8 | dataTupleMaker, 9 | fkTupleMaker, 10 | countTupleKeys, 11 | tuplesToObjects, 12 | nonAndJoinTables, 13 | fktNoJoins, 14 | typeCreator, 15 | brianFunction 16 | } = require("../gqlcorp/typeMain.js"); 17 | 18 | const { 19 | convertTypesforMutation, 20 | addNullableFields, 21 | mutation, 22 | replacerOne, 23 | } = require("../gqlcorp/mutation.js"); 24 | 25 | const { camelCaseIt, queryCreator } = require("../gqlcorp/query.js"); 26 | 27 | //GraphQL Schema Middleware 28 | gqlController.makeSchemaTypes = async function (req, res, next) { 29 | try { 30 | //object destructuring to retrieve all table data and foreign key data 31 | const { allTables, foreignKeys } = res.locals.data; 32 | 33 | //Returns all values that according to the PSQL DB 34 | const nullableObj = isNullable(allTables); 35 | //Add nullable object to res.locals.data 36 | res.locals.data.nullableObj = nullableObj; 37 | //Make tuples out of table data to store PSQL columns and types to their corresponding tables 38 | const tablesTuples = dataTupleMaker(allTables); 39 | //Make tuples out of foreign key data to store PSQL foreign keys to their table and column 40 | const fKeyTuples = fkTupleMaker(foreignKeys); 41 | //Convert fkeyTuples and tablesTuples to objects 42 | const [fKeysObj, tablesObj] = tuplesToObjects(fKeyTuples, tablesTuples); 43 | //Count foreign keys to use later to determine join and nonJoin tables 44 | const fKeyCounts = countTupleKeys(fKeyTuples); 45 | //Count all keys to use later to determine join and nonJoin tables 46 | const allKeyCounts = countTupleKeys(tablesTuples); 47 | //Create join and nonJoin table objects from the PSQL DB. PK FK logic to determine whether the table is join or nonJoin 48 | const [joinTable, nonJoinTable] = nonAndJoinTables(fKeyCounts, allKeyCounts, fKeysObj, tablesObj); 49 | //Add nonJoinTable to res.locals.data for later use 50 | res.locals.data.nonJoinTable = nonJoinTable; 51 | //Creates object with foreign keys from non join tables 52 | const fktObjNoJoins = fktNoJoins(fKeysObj, nonJoinTable); 53 | //Creates the GraphQL Schema Type displayed on the front end. 54 | const gqlTypes = typeCreator(nonJoinTable, fKeysObj, fktObjNoJoins, nullableObj); 55 | 56 | //Adds schema type to res.locals to sent to the front end 57 | // res.locals.schemaTypes = gqlTypes; // test 58 | ///////////////////////////////////////////////////////////////////// 59 | const finalfinal = brianFunction(gqlTypes) 60 | ///////////////////////////////////////////////////////////////////// 61 | res.locals.schemaTypes = finalfinal 62 | 63 | return next(); 64 | } catch (err) { 65 | console.log("Error in makeSchemaTypes is: ", err); 66 | return next(err); 67 | } 68 | }; 69 | 70 | gqlController.makeSchemaMutations = async function (req, res, next) { 71 | try { 72 | //Retrieve nullableObj and nonJoinTable from res.locals via object destructuring 73 | const { nullableObj, nonJoinTable } = res.locals.data; 74 | //Convert the nonJoinTable object values (column data type) to correct GraphQL data type i.e. 'character varying' to 'string' or 'integer' to 'int' 75 | const nonjoinTablewithCorrectTypes = convertTypesforMutation(nonJoinTable); 76 | //Add nonJoinTablewithCorrecTypes to res.locals 77 | res.locals.data.nonjoinTablewithCorrectTypes = nonjoinTablewithCorrectTypes; 78 | //addNullable fields by adding '!' to the appropriate values in the object 79 | const mutatableObject = addNullableFields( 80 | nonjoinTablewithCorrectTypes, 81 | nullableObj 82 | ); 83 | //Coverts mutableObject syntax to GraphQL readable syntax (still in object form) 84 | const regExFormat = mutation(mutatableObject); 85 | //Outputs object in string form, formatted with spacing for GQL 86 | const completeMutation = replacerOne(regExFormat); 87 | //add completeMutation to res.locals 88 | res.locals.schemaMutations = completeMutation; 89 | return next(); 90 | } catch (err) { 91 | console.log("Error in makeSchemaMutations is: ", err); 92 | return next(err); 93 | } 94 | }; 95 | 96 | gqlController.makeSchemaQueries = async function (req, res, next) { 97 | try { 98 | //Object destructuring to pull nonJoinTablewithCorrectTypes from res.locals 99 | const { nonjoinTablewithCorrectTypes } = res.locals.data; 100 | //Creates all queries for each nonJoinTable, outputting as string 101 | const completeQuery = queryCreator(nonjoinTablewithCorrectTypes); 102 | res.locals.schemaQueries = completeQuery; 103 | return next(); 104 | } catch (err) { 105 | console.log("Error in makeSchemaQueries is: ", err); 106 | return next(err); 107 | } 108 | }; 109 | 110 | // in type, we need a new line before each type ____ MUST BE DONE WITHIN TYPE 111 | // all of type is indented when it should not be MUST BE DONE WITHIN TYPE ? 112 | 113 | // last curly bracket of mutation should be on a new line MUST BE DONE WITHIN MUTATION 114 | 115 | // need \n before mutation starts DONE 116 | 117 | // need \n after typeDefs = ` DONE 118 | // 119 | 120 | gqlController.completeString = function (req, res, next) { 121 | try { 122 | const {schemaQueries, schemaMutations, schemaTypes} = res.locals 123 | const completeSchemaString = 'const typeDefs = `' + '\n' + 124 | `${(schemaQueries + '\n\n' + 'type Mutation {\n ' + schemaMutations.slice(1) + '\n' + schemaTypes.slice(1,-1))}` + '\n' + '`' 125 | 126 | 127 | res.locals.completeSchemaString = completeSchemaString 128 | return next(); 129 | } catch (err) { 130 | console.log("Error in completeString is: ", err); 131 | return next(err); 132 | } 133 | } 134 | // convert types for mutation is done in mutation as well as types. do in types, store on res.locals, doesn't need to be done in makeschemamutations again (convertTypesForMutation) 135 | //rename variables i.e. regExFormat/toReplace 136 | 137 | gqlController.makeResolvers = async function(req, res, next) { 138 | try { 139 | //first build resolvers as a js object, then convert to a string 140 | const resolvers = {}; 141 | //there are 3 resolver types: Query, Mutation, (and some categorized by the table they relate to) 142 | resolvers['Query'] = {}; 143 | resolvers['Mutation'] = {}; 144 | //Our logic in building resolvers depends heavily on if a table presents data itself, or is a derivation or "join" table that simply links info from other tables to communicate a higher order thing such as all the people who appear in a given film. The trademark of a join table is that it only has foreign keys 145 | const nonJoinTables = {}; 146 | const joinTables = {}; 147 | //loop thru all table data 148 | for (let [key,val] of Object.entries(res.locals.data.allTables)){ 149 | //count props in fks that match current key 150 | const fkCount = res.locals.data.foreignKeys.filter(el => el.foreign_table === key).length; 151 | //if not a join table then we will be doing lots of stuff for this table! 152 | if (val.length > fkCount+1) { 153 | nonJoinTables[key] = true; 154 | //query one 155 | //look for pk name 156 | let pkName; 157 | //find the name of the primary key of the current table 158 | for (let i = 0; i {\n const query = 'SELECT * FROM ${key} WHERE ${pkName} = $1';\n const values = [args.${pkName}];\n return db.query(query, values)\n .then(data => data.rows[0])\n .catch(err => new Error(err));\n },`; 165 | 166 | //add plural query to Query for current table 167 | resolvers.Query[camelCase(key)] = `() => {\n const query = `+`'SELECT * FROM ${key}'`+ `;\n return db.query(query)\n .then(data => data.rows)\n .catch(err =>new Error(err));\n },` 168 | 169 | //MUTATIONS 170 | 171 | //find all of the non primary key colums of the current table. 172 | const nonPkColumns = []; 173 | let nonPkColNums = []; 174 | let curColNum = 1; 175 | for(let i = 0; i < val.length; i++){ 176 | if(i.column_name !== pkName) { 177 | nonPkColNums.push(`$${curColNum++}`); 178 | nonPkColumns.push(val[i].column_name); 179 | } 180 | } 181 | 182 | //add mutation 183 | resolvers.Mutation[camelCase(`add_${singular(key)}`)] = `(parent, args) => {\n const query = `+`'INSERT INTO ${key} (${nonPkColumns.join(", ")}) VALUES (${nonPkColNums.join(', ')}) RETURNING *'` +`;\n const values = (${nonPkColumns.map(el => 'args.'+el)});\n return db.query(query, values)\n .then(data => data.rows[0])\n .catch(err => new Error(err));\n },`; 184 | 185 | 186 | //update mutation 187 | resolvers.Mutation[camelCase(`update_${singular(key)}`)] = `(parent, args) => {\n let valList = [];\n for (const updateKey of Object.keys(args)) {\n if (updateKey !== '${pkName}') valList.push(args[updateKey]);\n }\n valList.push(args.${pkName});\n const argsArray = Object.keys(args).filter((el) => el !== '${pkName}');\n let setString = argsArray.map((x, i) => x + ' = $' + (i + 1)).join(', ');\n const pKArg = '$'+ (argsArray.length + 1);\n const query = 'UPDATE ${key} SET '+ setString +' WHERE ${pkName} = '+pKArg+' RETURNING *';\n const values = valList; \n return db.query(query, values)\n .then(data => data.rows[0])\n .catch(err => new Error(err));\n },` 188 | 189 | //delete mutation 190 | resolvers.Mutation[camelCase(`delete_${singular(key)}`)] = `(parent, args) => {\n const query = 'DELETE FROM ${key} WHERE ${pkName} = $1 RETURNING *';\n const values = [args.${pkName}];\n return db.query(query, values)\n .then(data => data.rows[0])\n .catch(err => new Error(err));\n },` 191 | 192 | } else { 193 | //if its number of cols is indeed num of fks +1 then dont do any of the above and just add to join tables 194 | joinTables[key] = true; 195 | } 196 | } 197 | 198 | //loop through pks and add as values in joinTables and nonJoinTables for ref 199 | for (let k = 0; k< res.locals.data.primaryKeys.length; k++) { 200 | const pkObjRef = res.locals.data.primaryKeys[k]; 201 | if (pkObjRef.table_name in nonJoinTables) nonJoinTables[pkObjRef.table_name] = pkObjRef.primary_key_columns; 202 | else joinTables[pkObjRef.table_name] = pkObjRef.primary_key_columns; 203 | } 204 | 205 | //INDIVIDUAL TABLE RESOLVERS 206 | 207 | //loop through nonjointables 208 | for (let key of Object.keys(nonJoinTables)) { 209 | let pkName; 210 | for (let i = 0; i {\n const query = 'SELECT * FROM ${fkObj.foreign_table} WHERE ${fkObj.fk_columns} = $1';\n const values = [pTable.${pkName}];\n return db.query(query, values)\n .then(data => data.rows)\n .catch(err => new Error(err));\n },` 225 | } else { 226 | //if the refing table is join 227 | //look for all the other nonjoins that join references 228 | const otherJoinFkObjs = res.locals.data.foreignKeys.filter(el => el.foreign_table === fkObj.foreign_table && el.primary_table !== key); 229 | for (let j = 0; j< otherJoinFkObjs.length; j++) { 230 | resolvers[resolversTablePropKey][camelCase(otherJoinFkObjs[j].primary_table)] = `(pTable) => {\n const query = 'SELECT * FROM ${otherJoinFkObjs[j].primary_table} LEFT OUTER JOIN ${otherJoinFkObjs[j].foreign_table} ON ${otherJoinFkObjs[j].primary_table}.${nonJoinTables[otherJoinFkObjs[j].primary_table]} = ${otherJoinFkObjs[j].foreign_table}.${otherJoinFkObjs[j].fk_columns} WHERE ${otherJoinFkObjs[j].foreign_table}.${fkObj.fk_columns} = $1';\n const values = [pTable.${pkName}];\n return db.query(query, values)\n .then(data => data.rows)\n .catch(err => new Error(err));\n },` 231 | } 232 | } 233 | // CHANGED otherJoinFkObjs[j].fk_columns to fkObj.fk_columns 234 | } 235 | 236 | //if this table is referencing another nonjoin's PK 237 | if (fkObj.foreign_table === key) { 238 | resolvers[resolversTablePropKey][camelCase(fkObj.primary_table)] = `(pTable) => {\n const query = 'SELECT ${fkObj.primary_table}.* FROM ${fkObj.primary_table} LEFT OUTER JOIN ${key} ON ${fkObj.primary_table}.${nonJoinTables[fkObj.primary_table]} = ${key}.${fkObj.fk_columns} WHERE ${key}.${nonJoinTables[key]} = $1';\n const values = [pTable.${pkName}];\n return db.query(query, values)\n .then(data => data.rows)\n .catch(err => new Error(err));\n },` 239 | } 240 | } 241 | } 242 | let output = "const resolvers = {"; 243 | for(let [key,val] of Object.entries(resolvers)){ 244 | //add top level properties to output 245 | output += `\n ${key}: {`; 246 | //add tier2 props to output for current tier1 247 | for(let [innerKey,innerVal] of Object.entries(val)) { 248 | output += (`\n ${innerKey}: `+ innerVal); 249 | } 250 | output += `\n },`; 251 | } 252 | output += "\n}"; 253 | res.locals.resolvers = output; 254 | return next(); 255 | // return resolvers; 256 | } catch (err) { 257 | console.log('ERR IN MAKERESOLVERS', err); 258 | return next(err); 259 | } 260 | } 261 | 262 | 263 | module.exports = gqlController; 264 | -------------------------------------------------------------------------------- /server/controllers/sqlController.js: -------------------------------------------------------------------------------- 1 | // const { query } = require("./dbmodel"); 2 | const { Pool } = require("pg"); 3 | 4 | // const PG_URI = "fill in here"; 5 | // const example_URI = 6 | // "postgres://vdnvhfkq:sYiMTdCmk1vs2br_eUrrmX1unPvfucdW@batyr.db.elephantsql.com/vdnvhfkq"; 7 | const fs = require("fs"); 8 | const sqlQuery = fs.readFileSync("server/sqlQuery.sql", "utf8"); 9 | 10 | //need to connect to PSQL PG_URI | example_URI 11 | const sqlController = {}; 12 | /* 13 | const arrayTables = db.query('put in query here') 14 | arraytables = [planest, pilots, films, etc] 15 | -- query each table for columns, column types, primary key, foreign keys 16 | const allTables = [arrayTables[0], arrayTables[1], etc] 17 | for(let i = 0; i < arrayTables.length; i++) { 18 | const eachTable = await db.query() -- 'SELECT ${arraytables[i]} from ______' 19 | allTables.push(eachTable) -- [{name: planets},{},{}] 20 | } 21 | next() 22 | */ 23 | 24 | 25 | 26 | //coming from router get request to '/uri' 27 | sqlController.getTableData = async function (req, res, next) { 28 | try { 29 | let PG_URI = req.body.uri ?? 'postgres://vdnvhfkq:sYiMTdCmk1vs2br_eUrrmX1unPvfucdW@batyr.db.elephantsql.com/vdnvhfkq'; 30 | const pool = new Pool({ 31 | connectionString: PG_URI, 32 | }); 33 | const data = {} 34 | async function query(text, params, callback) { 35 | //console.log("Executed query", text); 36 | return pool.query(text, params, callback); 37 | } 38 | 39 | //what type are we getting here? 40 | const arrayTables = await query( 41 | `SELECT ARRAY(SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE')` 42 | ); 43 | 44 | //`SELECT array_agg(table_name) FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE'` 45 | const junk = arrayTables.rows[0].array; // string '{this is some bullshit we're going to convert}' 46 | const junkString = junk.slice(1, junk.length - 1); // cutting off brackets at the end 47 | const arrayString = junkString.split(","); // string[] 48 | 49 | 50 | const allTables = {}; 51 | for (let i = 0; i < arrayString.length; i++) { 52 | const columnQuery = `select table_name, column_name, ordinal_position, column_default, data_type, udt_name, is_nullable AS required from information_schema.columns where table_name = $1`; 53 | const { rows } = await query(columnQuery, [arrayString[i]]); //arrays 54 | allTables[arrayString[i]] = rows; 55 | } 56 | 57 | 58 | //add allTables to data object. Will send data object to front-end via data 59 | data.allTables = allTables; 60 | 61 | const foreignKeyQuery = 62 | await query(`select kcu.table_name as foreign_table, '>-' as rel, rel_tco.table_name as primary_table, string_agg(kcu.column_name, ', ') as fk_columns, kcu.constraint_name, tco.constraint_type from information_schema.table_constraints tco join information_schema.key_column_usage kcu on tco.constraint_schema = kcu.constraint_schema and tco.constraint_name = kcu.constraint_name join information_schema.referential_constraints rco on tco.constraint_schema = rco.constraint_schema 63 | and tco.constraint_name = rco.constraint_name 64 | join information_schema.table_constraints rel_tco 65 | on rco.unique_constraint_schema = rel_tco.constraint_schema 66 | and rco.unique_constraint_name = rel_tco.constraint_name 67 | where tco.constraint_type = 'FOREIGN KEY' 68 | group by kcu.table_schema, 69 | kcu.table_name, 70 | rel_tco.table_name, 71 | rel_tco.table_schema, 72 | kcu.constraint_name, 73 | tco.constraint_type 74 | order by kcu.table_schema, 75 | kcu.table_name`); 76 | 77 | data.foreignKeys = foreignKeyQuery.rows; 78 | 79 | //allTables will be an object, keys are table names & values are full arrays with each column & info as an object 80 | //console.log("Foreign Key", foreignKeyQuery.rows); // -> send foreignKeyQuery.rows to the front-end 81 | 82 | const primaryKeyQuery = await query(`select kcu.table_name, 83 | tco.constraint_name, 84 | string_agg(kcu.column_name,', ') as Primary_key_columns 85 | from information_schema.table_constraints tco 86 | join information_schema.key_column_usage kcu 87 | on kcu.constraint_name = tco.constraint_name 88 | and kcu.constraint_schema = tco.constraint_schema 89 | and kcu.constraint_name = tco.constraint_name 90 | where tco.constraint_type = 'PRIMARY KEY' 91 | group by tco.constraint_name, 92 | kcu.table_schema, 93 | kcu.table_name 94 | order by kcu.table_schema, 95 | kcu.table_name`); 96 | data.primaryKeys = primaryKeyQuery.rows; 97 | // -- 2. query each table for columns, column types, primary key, foreign keys 98 | // -- each query would return multiple columns, column types, ONE pk, multiple fks 99 | // to get all column names => SELECT column_name[, data_type] FROM information_schema.columns WHERE table_name = 100 | // infomation schema. columns column name, data_type 101 | 102 | //CHECK on information_schema: 103 | // element_types, foreign_data_wrappers, foreign_table_options, foreign_tables, 104 | // `SELECT junk1, junk2, junk3, junk4 FROM $1(template literal) ` 105 | // ($1) === arrayString[i] 106 | // tableData.push(eachTable); 107 | // } 108 | 109 | // res.locals.finalData = finalData; // will be an array of table objects 110 | res.locals.data = data 111 | return next(); 112 | } catch (err) { 113 | console.log("HIT CATCH ERROR IN sqlC.getTableData: ", err); 114 | return next({ 115 | log: `sqlController.getTableData: Error: ${err}`, 116 | message: { 117 | err: `Error occured in sqlController.getTableData. Check server logs for more details.`, 118 | }, 119 | }); 120 | } 121 | }; 122 | 123 | sqlController.visualize = function () { 124 | try { 125 | } catch (err) {} 126 | }; 127 | 128 | module.exports = sqlController; 129 | -------------------------------------------------------------------------------- /server/gqlcorp/mutation.js: -------------------------------------------------------------------------------- 1 | const { singular } = require("pluralize"); 2 | 3 | //Capitalizes first letter of any string 4 | function capFirstLet(string) { 5 | return string.charAt(0).toUpperCase() + string.slice(1); 6 | }; 7 | 8 | //Converts to PascalCaseFrom snake_case 9 | function snakeToTitle(str) { 10 | return str.split("_").map(capFirstLet).join(""); 11 | }; 12 | 13 | 14 | 15 | // Takes non join tables, and switches SQL types to GQL types. 16 | const convertTypesforMutation = object => { 17 | //console.log("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$CONVERTTYPESFORRMUTATIONS OBJECT ARG", object); 18 | for (const table in object) { 19 | 20 | for (const column in object[table]) { 21 | switch (object[table][column]) { 22 | case "bigint": 23 | object[table][column] = "Int"; 24 | break; 25 | case "integer": 26 | object[table][column] = "Int"; 27 | break; 28 | case "character varying": 29 | object[table][column] = "String"; 30 | break; 31 | case "date": 32 | object[table][column] = "String"; 33 | break; 34 | case 'numeric': 35 | object[table][column] = 'Int' 36 | break; 37 | case 'smallint': 38 | object[table][column] = 'Int' 39 | break; 40 | case 'ARRAY': 41 | object[table][column] = '|String|' 42 | break; 43 | 44 | } 45 | if (column === "_id") { 46 | object[table][column] = "ID"; 47 | } 48 | } 49 | } 50 | return object //quicfix 51 | }; 52 | 53 | 54 | 55 | // Takes GQL typed non Join table and adds ! after the type to show it is required in GQL formatting 56 | const addNullableFields = (dataWTypes, nullable) => { 57 | 58 | for (const tbl in dataWTypes) { 59 | 60 | for (const column in nullable[tbl]) { 61 | let temp = dataWTypes[tbl][column]; 62 | if (temp[temp.length-1] === "!") temp = temp.slice(0, -1); 63 | if (temp) dataWTypes[tbl][column] = temp + "!"; 64 | } 65 | } 66 | return dataWTypes; 67 | }; 68 | 69 | 70 | 71 | //Creates strings for all mutations in GQL 72 | const mutation = (obj) => { 73 | mutationObj = {}; 74 | //Creating add Mutation for each nonJoinTable 75 | for (const key in obj) { 76 | let keyName = snakeToTitle(key); 77 | let temp = "add" + capFirstLet(singular(keyName)); 78 | mutationObj[temp] = {}; 79 | 80 | for (const column in obj[key]) { 81 | if (column !== "_id") { 82 | mutationObj[temp][column] = obj[key][column]; 83 | } 84 | } 85 | //Creating update Mutation for each nonJoinTable 86 | let keyName1 = snakeToTitle(key); 87 | let temp1 = "update" + capFirstLet(singular(keyName1)); 88 | mutationObj[temp1] = {}; 89 | 90 | for (const col in obj[key]) { 91 | mutationObj[temp1][col] = obj[key][col]; 92 | } 93 | //Createing delete mutation for each nonJoinTable 94 | for (const prop in obj) { 95 | let keyName2 = snakeToTitle(key); 96 | let temp2 = 97 | "delete" + 98 | capFirstLet(singular(keyName2)) + 99 | "(_id: ID!): " + 100 | capFirstLet(singular(keyName2)) + 101 | "!"; 102 | mutationObj[temp2] = true; 103 | } 104 | } 105 | 106 | return mutationObj; 107 | }; 108 | 109 | 110 | 111 | //Regex for GraphQL syntax, spacing, and indentation 112 | const replacerOne = (str) => { 113 | str = JSON.stringify(str) 114 | //console.log('before regex:', str) 115 | str = 116 | "\n" + 117 | str 118 | .replace(/:{/g, "(") 119 | .replace(/"/g, "") 120 | .replace(/,/g, ",\n") 121 | .replace(/},/g, ")\n") 122 | .replace(/:/g, ": ") 123 | .replace(/: I/g, " :I") 124 | .replace(/[)]/g, `\n)`) 125 | .replace(/[(]/g, `(\n`) 126 | .replace(/: true,/g, "\n") 127 | .replace(/: true/g, "") 128 | .replace(/,\n/g, ',\n ') 129 | .replace(/\(\n/g, '(\n ') 130 | .replace(/\(\n _id :ID!\n\)/g, '(_id: ID!)');//creates new line for delete mutations 131 | let output = ""; 132 | 133 | //while str length 134 | while (str.indexOf('add') !== -1) { 135 | //look for text between add and { 136 | let txtToAdd = str.match(/(?<=add).*(?=\()/); 137 | //replace the following 2 closing commas with ): [that text]! 138 | str = str.replace(/\)\n/, `): ${txtToAdd[0]}!\n`); 139 | output+=str.slice(0, str.indexOf(`update`)); 140 | str = str.slice(str.indexOf(`update`)); 141 | str = str.replace(/\)/, `): ${txtToAdd[0]}!`); 142 | //slice that stuff off to output 143 | output+=str.slice(0, str.indexOf(`delete`)); 144 | str = str.slice(str.indexOf(`delete`)); 145 | //find the next add 146 | } 147 | if (str.length) output+=str; 148 | output = output.replace(/,\n.*\]/g,""); //removing the keys to foreign tables, adam check if this works so easily..? 149 | //replace the vertical lines we subed for brackets in [String] to protect it from replacerone func 150 | output = output.replace(/\|String\|/g, "[String]"); 151 | return output.slice(1, -1) + '\n\n}'; //////////////////////////////////////// 152 | 153 | } 154 | 155 | 156 | module.exports = { convertTypesforMutation, addNullableFields, mutation, replacerOne }; 157 | -------------------------------------------------------------------------------- /server/gqlcorp/query.js: -------------------------------------------------------------------------------- 1 | const { singular } = require('pluralize'); 2 | 3 | //capitalize first letter of any string 4 | function capFirstLet(string) { 5 | return string.charAt(0).toUpperCase() + string.slice(1); 6 | } 7 | 8 | //convert any_thing like-this or.this toCamelCase 9 | const camelCaseIt = (string) => 10 | string 11 | .toLowerCase() 12 | .trim() 13 | .split(/[.\-_\s]/g) 14 | .reduce((string, word) => string + word[0].toUpperCase() + word.slice(1)); 15 | 16 | //Create schema query via nonJoinTablewithCorrectTypes as passed in argument. 17 | const queryCreator = (object) => { 18 | const queryObject = {}; 19 | 20 | for (const table in object) { 21 | // creating keys 22 | const camelCaseName = camelCaseIt(table); //starshipSpecs 23 | const ccnWithID = singular(camelCaseName) === camelCaseName ? `${camelCaseName}ById(_id:ID!)` : singular(camelCaseName)+"(_id:ID!)"; 24 | // const ccnWithID = singular(camelCaseName) + "(_id:ID!)"; // starshipSpecs(_id: ID!) 25 | // creating values 26 | const titleCaseWithBang = capFirstLet(singular(camelCaseName)) + '!'; // StarshipSpec! 27 | const pluralValue = '[' + titleCaseWithBang + ']!'; // [StarshipSpec!]! 28 | //assign key value pairs in obj 29 | queryObject[camelCaseName] = pluralValue; 30 | queryObject[ccnWithID] = titleCaseWithBang; 31 | } 32 | let queryStr = JSON.stringify(queryObject); 33 | //regex to display correct spacing and indentation on the front-end 34 | queryStr = 35 | 'type Query ' + 36 | queryStr 37 | .replace(/"/g, "") 38 | .replace(/,/g, "\n ") 39 | .replace(/{/g, "{\n ") 40 | .replace(/}/g, "\n}") 41 | .replace(/:/g, ": "); 42 | 43 | return queryStr; 44 | }; 45 | 46 | module.exports = { camelCaseIt, queryCreator }; 47 | -------------------------------------------------------------------------------- /server/gqlcorp/referenceData.js: -------------------------------------------------------------------------------- 1 | TYPE.JS 2 | data 3 | allTables 4 | primaryKeys 5 | foreignKeys 6 | 7 | isNullable(allTables): nullableObj 8 | dataTupleMaker(allTables): tablesTuples 9 | fkTupleMaker(foreignKeys): fKeyTuples 10 | [ 11 | countTupleKeys(fKeyTuples): fKeyCounts 12 | countTupleKeys(tablesTuples): allKeyCounts 13 | Object.fromEntries(fKeyTuples): fKeysObj 14 | Object.fromEntries(tablesTuples): tablesObj 15 | ] 16 | 17 | nonAndJoinTables(fKeyCounts, allKeyCounts, fKeysObj, tablesObj): joinTable AND nonJoinTable 18 | 19 | typeCreator(nonJoinTable[16], fKeysObj[12], nullableObj[6]): "finalTypeObjectFinito" 20 | 21 | { 22 | planets: { 23 | _id: 'ID!', 24 | name: 'String', 25 | rotation_period: 'Int', 26 | orbital_period: 'Int', 27 | diameter: 'Int', 28 | climate: 'String', 29 | gravity: 'String', 30 | terrain: 'String', 31 | surface_water: 'String', 32 | population: 'Int', 33 | species: '[Species]', 34 | films: '[Film]' 35 | }, 36 | films: { 37 | _id: 'ID!', 38 | title: 'String!', 39 | episode_id: 'Int!', 40 | opening_crawl: 'String!', 41 | director: 'String!', 42 | producer: 'String!', 43 | release_date: 'String!', 44 | people: '[Person]', 45 | planets: '[Planet]', 46 | species: '[Species]', 47 | vessels: '[Vessel]' 48 | }, 49 | species: { 50 | _id: 'ID!', 51 | name: 'String!', 52 | classification: 'String', 53 | average_height: 'String', 54 | average_lifespan: 'String', 55 | hair_colors: 'String', 56 | skin_colors: 'String', 57 | eye_colors: 'String', 58 | language: 'String', 59 | planets: '[Planet]', 60 | films: '[Film]' 61 | }, 62 | vessels: { 63 | _id: 'ID!', 64 | name: 'String!', 65 | manufacturer: 'String', 66 | model: 'String', 67 | vessel_type: 'String!', 68 | vessel_class: 'String!', 69 | cost_in_credits: 'Int', 70 | length: 'String', 71 | max_atmosphering_speed: 'String', 72 | crew: 'Int', 73 | passengers: 'Int', 74 | cargo_capacity: 'String', 75 | consumables: 'String', 76 | people: '[Person]', 77 | films: '[Film]' 78 | }, 79 | people: { 80 | _id: 'ID!', 81 | name: 'String!', 82 | mass: 'String', 83 | hair_color: 'String', 84 | skin_color: 'String', 85 | eye_color: 'String', 86 | birth_year: 'String', 87 | gender: 'String', 88 | species: '[Species]', 89 | planets: '[Planet]', 90 | height: 'Int', 91 | films: '[Film]', 92 | vessels: '[Vessel]' 93 | }, 94 | starship_specs: { 95 | _id: 'ID!', 96 | hyperdrive_rating: 'String', 97 | MGLT: 'String', 98 | vessels: '[Vessel]' 99 | } 100 | } 101 | 102 | 103 | 104 | 105 | 106 | MUTATION.JS 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | const finalResultOfTypeWorkByFriday = { 118 | planets: { 119 | _id: 'integer', 120 | name: 'character varying', 121 | rotation_period: 'integer', 122 | orbital_period: 'integer', 123 | diameter: 'integer', 124 | climate: 'character varying', 125 | gravity: 'character varying', 126 | terrain: 'character varying', 127 | surface_water: 'character varying', 128 | population: 'bigint', 129 | species: '[Species]', 130 | films: '[Film]', 131 | people: '[Person]' 132 | }, 133 | films: { 134 | _id: 'integer', 135 | title: 'character varying', 136 | episode_id: 'integer', 137 | opening_crawl: 'character varying', 138 | director: 'character varying', 139 | producer: 'character varying', 140 | release_date: 'date', 141 | people: '[Person]', 142 | planets: '[Planet]', 143 | species: '[Species]', 144 | vessels: '[Vessel]' 145 | }, 146 | species: { 147 | _id: 'integer', 148 | name: 'character varying', 149 | classification: 'character varying', 150 | average_height: 'character varying', 151 | average_lifespan: 'character varying', 152 | hair_colors: 'character varying', 153 | skin_colors: 'character varying', 154 | eye_colors: 'character varying', 155 | language: 'character varying', 156 | planets: '[Planet]', 157 | films: '[Film]', 158 | people: '[Person]' 159 | }, 160 | vessels: { 161 | _id: 'integer', 162 | name: 'character varying', 163 | manufacturer: 'character varying', 164 | model: 'character varying', 165 | vessel_type: 'character varying', 166 | vessel_class: 'character varying', 167 | cost_in_credits: 'bigint', 168 | length: 'character varying', 169 | max_atmosphering_speed: 'character varying', 170 | crew: 'integer', 171 | passengers: 'integer', 172 | cargo_capacity: 'character varying', 173 | consumables: 'character varying', 174 | people: '[Person]', 175 | films: '[Film]', 176 | starship_specs: '[Starship_spec]' 177 | }, 178 | people: { 179 | _id: 'integer', 180 | name: 'character varying', 181 | mass: 'character varying', 182 | hair_color: 'character varying', 183 | skin_color: 'character varying', 184 | eye_color: 'character varying', 185 | birth_year: 'character varying', 186 | gender: 'character varying', 187 | species: '[Species]', 188 | planets: '[Planet]', 189 | height: 'integer', 190 | films: '[Film]', 191 | vessels: '[Vessel]' 192 | }, 193 | starship_specs: { 194 | _id: 'integer', 195 | hyperdrive_rating: 'character varying', 196 | MGLT: 'character varying', 197 | vessels: '[Vessel]' 198 | } 199 | } 200 | 201 | const tDRes = [ 202 | [ 203 | 'planets', 204 | { 205 | _id: 'integer', 206 | name: 'character varying', 207 | rotation_period: 'integer', 208 | orbital_period: 'integer', 209 | diameter: 'integer', 210 | climate: 'character varying', 211 | gravity: 'character varying', 212 | terrain: 'character varying', 213 | surface_water: 'character varying', 214 | population: 'bigint' 215 | } 216 | ], 217 | [ 218 | 'pilots', 219 | { _id: 'integer', person_id: 'bigint', vessel_id: 'bigint' } 220 | ], 221 | [ 222 | 'people_in_films', 223 | { _id: 'integer', person_id: 'bigint', film_id: 'bigint' } 224 | ], 225 | [ 226 | 'films', 227 | { 228 | _id: 'integer', 229 | title: 'character varying', 230 | episode_id: 'integer', 231 | opening_crawl: 'character varying', 232 | director: 'character varying', 233 | producer: 'character varying', 234 | release_date: 'date' 235 | } 236 | ], 237 | [ 238 | 'species', 239 | { 240 | _id: 'integer', 241 | name: 'character varying', 242 | classification: 'character varying', 243 | average_height: 'character varying', 244 | average_lifespan: 'character varying', 245 | hair_colors: 'character varying', 246 | skin_colors: 'character varying', 247 | eye_colors: 'character varying', 248 | language: 'character varying', 249 | homeworld_id: 'bigint' 250 | } 251 | ], 252 | [ 253 | 'species_in_films', 254 | { _id: 'integer', film_id: 'bigint', species_id: 'bigint' } 255 | ], 256 | [ 257 | 'vessels', 258 | { 259 | _id: 'integer', 260 | name: 'character varying', 261 | manufacturer: 'character varying', 262 | model: 'character varying', 263 | vessel_type: 'character varying', 264 | vessel_class: 'character varying', 265 | cost_in_credits: 'bigint', 266 | length: 'character varying', 267 | max_atmosphering_speed: 'character varying', 268 | crew: 'integer', 269 | passengers: 'integer', 270 | cargo_capacity: 'character varying', 271 | consumables: 'character varying' 272 | } 273 | ], 274 | [ 275 | 'vessels_in_films', 276 | { _id: 'integer', vessel_id: 'bigint', film_id: 'bigint' } 277 | ], 278 | [ 279 | 'people', 280 | { 281 | _id: 'integer', 282 | name: 'character varying', 283 | mass: 'character varying', 284 | hair_color: 'character varying', 285 | skin_color: 'character varying', 286 | eye_color: 'character varying', 287 | birth_year: 'character varying', 288 | gender: 'character varying', 289 | species_id: 'bigint', 290 | homeworld_id: 'bigint', 291 | height: 'integer' 292 | } 293 | ], 294 | [ 295 | 'planets_in_films', 296 | { _id: 'integer', film_id: 'bigint', planet_id: 'bigint' } 297 | ], 298 | [ 299 | 'starship_specs', 300 | { 301 | _id: 'integer', 302 | hyperdrive_rating: 'character varying', 303 | MGLT: 'character varying', 304 | vessel_id: 'bigint' 305 | } 306 | ] 307 | ] 308 | 309 | const outputOfIsNullable = { 310 | planets: { _id: true }, 311 | pilots: { _id: true, person_id: true, vessel_id: true }, 312 | people_in_films: { _id: true, person_id: true, film_id: true }, 313 | films: { 314 | _id: true, 315 | title: true, 316 | episode_id: true, 317 | opening_crawl: true, 318 | director: true, 319 | producer: true, 320 | release_date: true 321 | }, 322 | species: { _id: true, name: true }, 323 | species_in_films: { _id: true, film_id: true, species_id: true }, 324 | vessels: { _id: true, name: true, vessel_type: true, vessel_class: true }, 325 | vessels_in_films: { _id: true, vessel_id: true, film_id: true }, 326 | people: { _id: true, name: true }, 327 | planets_in_films: { _id: true, film_id: true, planet_id: true }, 328 | starship_specs: { _id: true, vessel_id: true } 329 | } 330 | 331 | 332 | 333 | const atd = [ 334 | [ 335 | 'planets', 336 | { 337 | _id: 'integer', 338 | name: 'character varying', 339 | rotation_period: 'integer', 340 | orbital_period: 'integer', 341 | diameter: 'integer', 342 | climate: 'character varying', 343 | gravity: 'character varying', 344 | terrain: 'character varying', 345 | surface_water: 'character varying', 346 | population: 'bigint' 347 | } 348 | ], 349 | [ 350 | 'pilots', 351 | { _id: 'integer', person_id: 'bigint', vessel_id: 'bigint' } 352 | ], 353 | [ 354 | 'people_in_films', 355 | { _id: 'integer', person_id: 'bigint', film_id: 'bigint' } 356 | ], 357 | [ 358 | 'films', 359 | { 360 | _id: 'integer', 361 | title: 'character varying', 362 | episode_id: 'integer', 363 | opening_crawl: 'character varying', 364 | director: 'character varying', 365 | producer: 'character varying', 366 | release_date: 'date' 367 | } 368 | ], 369 | [ 370 | 'species', 371 | { 372 | _id: 'integer', 373 | name: 'character varying', 374 | classification: 'character varying', 375 | average_height: 'character varying', 376 | average_lifespan: 'character varying', 377 | hair_colors: 'character varying', 378 | skin_colors: 'character varying', 379 | eye_colors: 'character varying', 380 | language: 'character varying', 381 | homeworld_id: 'bigint' 382 | } 383 | ], 384 | [ 385 | 'species_in_films', 386 | { _id: 'integer', film_id: 'bigint', species_id: 'bigint' } 387 | ], 388 | [ 389 | 'vessels', 390 | { 391 | _id: 'integer', 392 | name: 'character varying', 393 | manufacturer: 'character varying', 394 | model: 'character varying', 395 | vessel_type: 'character varying', 396 | vessel_class: 'character varying', 397 | cost_in_credits: 'bigint', 398 | length: 'character varying', 399 | max_atmosphering_speed: 'character varying', 400 | crew: 'integer', 401 | passengers: 'integer', 402 | cargo_capacity: 'character varying', 403 | consumables: 'character varying' 404 | } 405 | ], 406 | [ 407 | 'vessels_in_films', 408 | { _id: 'integer', vessel_id: 'bigint', film_id: 'bigint' } 409 | ], 410 | [ 411 | 'people', 412 | { 413 | _id: 'integer', 414 | name: 'character varying', 415 | mass: 'character varying', 416 | hair_color: 'character varying', 417 | skin_color: 'character varying', 418 | eye_color: 'character varying', 419 | birth_year: 'character varying', 420 | gender: 'character varying', 421 | species_id: 'bigint', 422 | homeworld_id: 'bigint', 423 | height: 'integer' 424 | } 425 | ], 426 | [ 427 | 'planets_in_films', 428 | { _id: 'integer', film_id: 'bigint', planet_id: 'bigint' } 429 | ], 430 | [ 431 | 'starship_specs', 432 | { 433 | _id: 'integer', 434 | hyperdrive_rating: 'character varying', 435 | MGLT: 'character varying', 436 | vessel_id: 'bigint' 437 | } 438 | ] 439 | ] 440 | const fkt = [ 441 | [ 'people', { homeworld_id: 'planets', species_id: 'species' } ], 442 | [ 'people_in_films', { film_id: 'films', person_id: 'people' } ], //join 443 | [ 'pilots', { person_id: 'people', vessel_id: 'vessels' } ], //join 444 | [ 'planets_in_films', { film_id: 'films', planet_id: 'planets' } ], //join 445 | [ 'species', { homeworld_id: 'planets' } ], 446 | [ 'species_in_films', { film_id: 'films', species_id: 'species' } ], //join 447 | [ 'starship_specs', { vessel_id: 'vessels' } ], // 448 | [ 'vessels_in_films', { film_id: 'films', vessel_id: 'vessels' } ] 449 | ] 450 | 451 | // const nonJoinTables = { 452 | // planets: { 453 | // _id: 'integer', 454 | // name: 'character varying', 455 | // rotation_period: 'integer', 456 | // orbital_period: 'integer', 457 | // diameter: 'integer', 458 | // climate: 'character varying', 459 | // gravity: 'character varying', 460 | // terrain: 'character varying', 461 | // surface_water: 'character varying', 462 | // population: 'bigint' 463 | // }, 464 | 465 | // FKT AS OBJECT { 466 | // people: { homeworld_id: 'planets', species_id: 'species' }, 467 | 468 | // typeObject///////////////////// 469 | // planets: { 470 | // _id: 'integer', 471 | // name: 'character varying', 472 | // rotation_period: 'integer', 473 | // orbital_period: 'integer', 474 | // diameter: 'integer', 475 | // climate: 'character varying', 476 | // gravity: 'character varying', 477 | // terrain: 'character varying', 478 | // surface_water: 'character varying', 479 | // population: 'bigint', 480 | // species: '[Species]', 481 | // films: '[Film]' 482 | // }, 483 | 484 | FKT AS OBJECT { 485 | people: { homeworld_id: 'planets', species_id: 'species' }, 486 | people_in_films: { film_id: 'films', person_id: 'people' },// 487 | pilots: { person_id: 'people', vessel_id: 'vessels' },// 488 | planets_in_films: { film_id: 'films', planet_id: 'planets' },// 489 | species: { homeworld_id: 'planets' }, 490 | species_in_films: { film_id: 'films', species_id: 'species' },// 491 | starship_specs: { vessel_id: 'vessels' }, 492 | vessels_in_films: { film_id: 'films', vessel_id: 'vessels' }// 493 | } 494 | 495 | fktNoJoins { 496 | people: { homeworld_id: 'planets', species_id: 'species' }, 497 | species: { homeworld_id: 'planets' }, 498 | starship_specs: { vessel_id: 'vessels' } 499 | } 500 | 501 | 502 | //types w/ missing foreign kkeys 503 | planets: { //person 504 | _id: 'integer', 505 | name: 'character varying', 506 | rotation_period: 'integer', 507 | orbital_period: 'integer', 508 | diameter: 'integer', 509 | climate: 'character varying', 510 | gravity: 'character varying', 511 | terrain: 'character varying', 512 | surface_water: 'character varying', 513 | population: 'bigint', 514 | species: '[Species]', 515 | films: '[Film]' 516 | }, 517 | 518 | species: { //person 519 | _id: 'integer', 520 | name: 'character varying', 521 | classification: 'character varying', 522 | average_height: 'character varying', 523 | average_lifespan: 'character varying', 524 | hair_colors: 'character varying', 525 | skin_colors: 'character varying', 526 | eye_colors: 'character varying', 527 | language: 'character varying', 528 | planets: '[Planet]', 529 | films: '[Film]' 530 | }, 531 | 532 | vessels: { //starship_spec 533 | _id: 'integer', 534 | name: 'character varying', 535 | manufacturer: 'character varying', 536 | model: 'character varying', 537 | vessel_type: 'character varying', 538 | vessel_class: 'character varying', 539 | cost_in_credits: 'bigint', 540 | length: 'character varying', 541 | max_atmosphering_speed: 'character varying', 542 | crew: 'integer', 543 | passengers: 'integer', 544 | cargo_capacity: 'character varying', 545 | consumables: 'character varying', 546 | people: '[Person]', 547 | films: '[Film]' 548 | }, 549 | 550 | const numForeignKeys = { 551 | people: 2, 552 | people_in_films: 2, 553 | pilots: 2, 554 | planets_in_films: 2, 555 | species: 1, 556 | species_in_films: 2, 557 | starship_specs: 1, 558 | vessels_in_films: 2 559 | } 560 | const numTotalKeys = { 561 | planets: 10, 562 | pilots: 3, 563 | people_in_films: 3, 564 | films: 7, 565 | species: 10, 566 | species_in_films: 3, 567 | vessels: 13, 568 | vessels_in_films: 3, 569 | people: 11, 570 | planets_in_films: 3, 571 | starship_specs: 4 572 | } 573 | const nonJoinTables = { 574 | planets: { 575 | _id: 'integer', 576 | name: 'character varying', 577 | rotation_period: 'integer', 578 | orbital_period: 'integer', 579 | diameter: 'integer', 580 | climate: 'character varying', 581 | gravity: 'character varying', 582 | terrain: 'character varying', 583 | surface_water: 'character varying', 584 | population: 'bigint' 585 | }, 586 | films: { 587 | _id: 'integer', 588 | title: 'character varying', 589 | episode_id: 'integer', 590 | opening_crawl: 'character varying', 591 | director: 'character varying', 592 | producer: 'character varying', 593 | release_date: 'date' 594 | }, 595 | species: { 596 | _id: 'integer', 597 | name: 'character varying', 598 | classification: 'character varying', 599 | average_height: 'character varying', 600 | average_lifespan: 'character varying', 601 | hair_colors: 'character varying', 602 | skin_colors: 'character varying', 603 | eye_colors: 'character varying', 604 | language: 'character varying', 605 | homeworld_id: 'bigint' 606 | }, 607 | vessels: { 608 | _id: 'integer', 609 | name: 'character varying', 610 | manufacturer: 'character varying', 611 | model: 'character varying', 612 | vessel_type: 'character varying', 613 | vessel_class: 'character varying', 614 | cost_in_credits: 'bigint', 615 | length: 'character varying', 616 | max_atmosphering_speed: 'character varying', 617 | crew: 'integer', 618 | passengers: 'integer', 619 | cargo_capacity: 'character varying', 620 | consumables: 'character varying' 621 | }, 622 | people: { 623 | _id: 'integer', 624 | name: 'character varying', 625 | mass: 'character varying', 626 | hair_color: 'character varying', 627 | skin_color: 'character varying', 628 | eye_color: 'character varying', 629 | birth_year: 'character varying', 630 | gender: 'character varying', 631 | species_id: 'bigint', 632 | homeworld_id: 'bigint', 633 | height: 'integer' 634 | }, 635 | starship_specs: { 636 | _id: 'integer', 637 | hyperdrive_rating: 'character varying', 638 | MGLT: 'character varying', 639 | vessel_id: 'bigint' 640 | } 641 | } 642 | const joinTables = { 643 | pilots: { _id: 'integer', person_id: 'bigint', vessel_id: 'bigint' }, 644 | people_in_films: { _id: 'integer', person_id: 'bigint', film_id: 'bigint' }, 645 | species_in_films: { _id: 'integer', film_id: 'bigint', species_id: 'bigint' }, 646 | vessels_in_films: { _id: 'integer', vessel_id: 'bigint', film_id: 'bigint' }, 647 | planets_in_films: { _id: 'integer', film_id: 'bigint', planet_id: 'bigint' } 648 | } 649 | const typeObj = { 650 | planets: { 651 | _id: 'integer', 652 | name: 'character varying', 653 | rotation_period: 'integer', 654 | orbital_period: 'integer', 655 | diameter: 'integer', 656 | climate: 'character varying', 657 | gravity: 'character varying', 658 | terrain: 'character varying', 659 | surface_water: 'character varying', 660 | population: 'bigint', 661 | species: '[Species]', 662 | films: '[Film]' 663 | }, 664 | films: { 665 | _id: 'integer', 666 | title: 'character varying', 667 | episode_id: 'integer', 668 | opening_crawl: 'character varying', 669 | director: 'character varying', 670 | producer: 'character varying', 671 | release_date: 'date', 672 | people: '[Person]', 673 | planets: '[Planet]', 674 | species: '[Species]', 675 | vessels: '[Vessel]' 676 | }, 677 | species: { 678 | _id: 'integer', 679 | name: 'character varying', 680 | classification: 'character varying', 681 | average_height: 'character varying', 682 | average_lifespan: 'character varying', 683 | hair_colors: 'character varying', 684 | skin_colors: 'character varying', 685 | eye_colors: 'character varying', 686 | language: 'character varying', 687 | planets: '[Planet]', 688 | films: '[Film]' 689 | }, 690 | vessels: { 691 | _id: 'integer', 692 | name: 'character varying', 693 | manufacturer: 'character varying', 694 | model: 'character varying', 695 | vessel_type: 'character varying', 696 | vessel_class: 'character varying', 697 | cost_in_credits: 'bigint', 698 | length: 'character varying', 699 | max_atmosphering_speed: 'character varying', 700 | crew: 'integer', 701 | passengers: 'integer', 702 | cargo_capacity: 'character varying', 703 | consumables: 'character varying', 704 | people: '[Person]', 705 | films: '[Film]' 706 | }, 707 | people: { 708 | _id: 'integer', 709 | name: 'character varying', 710 | mass: 'character varying', 711 | hair_color: 'character varying', 712 | skin_color: 'character varying', 713 | eye_color: 'character varying', 714 | birth_year: 'character varying', 715 | gender: 'character varying', 716 | species: '[Species]', 717 | planets: '[Planet]', 718 | height: 'integer', 719 | films: '[Film]', 720 | vessels: '[Vessel]' 721 | }, 722 | starship_specs: { 723 | _id: 'integer', 724 | hyperdrive_rating: 'character varying', 725 | MGLT: 'character varying' 726 | } 727 | } 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | OLD DATA 745 | ------------------------------------------- 746 | 747 | 748 | 749 | 750 | foreign key obj { 751 | people: 2, 752 | people_in_films: 2, // 753 | pilots: 2, // 754 | planets_in_films: 2, // 755 | species: 1, 756 | species_in_films: 2, // 757 | starship_specs: 1, 758 | vessels_in_films: 2 // 759 | } 760 | total keys obj { 761 | planets: 10, 762 | pilots: 3, // 763 | people_in_films: 3, // 764 | films: 7, 765 | species: 10, 766 | species_in_films: 3, // 767 | vessels: 13, 768 | vessels_in_films: 3,// 769 | people: 11, 770 | planets_in_films: 3, // 771 | starship_specs: 3 772 | } 773 | 774 | the only join tables above are pilots, peopleinfilms, vessels in films species in films, planetsinfilms 775 | atd as Obj { 776 | planets: { 777 | _id: 'integer', 778 | name: 'character varying', 779 | rotation_period: 'integer', 780 | orbital_period: 'integer', 781 | diameter: 'integer', 782 | climate: 'character varying', 783 | gravity: 'character varying', 784 | terrain: 'character varying', 785 | surface_water: 'character varying', 786 | population: 'bigint' 787 | }, 788 | pilots: { _id: 'integer', person_id: 'bigint', vessel_id: 'bigint' }, 789 | people_in_films: { _id: 'integer', person_id: 'bigint', film_id: 'bigint' }, 790 | films: { 791 | _id: 'integer', 792 | title: 'character varying', 793 | episode_id: 'integer', 794 | opening_crawl: 'character varying', 795 | director: 'character varying', 796 | producer: 'character varying', 797 | release_date: 'date' 798 | } 799 | } // etc... 800 | //join table keys 801 | FKT AS OBJECT { 802 | people: { homeworld_id: 'planets', species_id: 'species' }, 803 | people_in_films: { film_id: 'films', person_id: 'people' }, 804 | pilots: { person_id: 'people', vessel_id: 'vessels' }, 805 | planets_in_films: { film_id: 'films', planet_id: 'planets' }, 806 | species: { homeworld_id: 'planets' }, 807 | species_in_films: { film_id: 'films', species_id: 'species' }, 808 | starship_specs: { vessel_id: 'vessels' }, 809 | vessels_in_films: { film_id: 'films', vessel_id: 'vessels' } 810 | } 811 | 812 | starship_specs: { 813 | _id: 'integer', 814 | hyperdrive_rating: 'character varying', 815 | MGLT: 'character varying' 816 | 817 | //WHY DOES STARSHIP_SPECS NOT HAVE KEY VESSEL_ID? 818 | } 819 | //from NonJoinTable 820 | { people: { 821 | _id: 'integer', 822 | name: 'character varying', 823 | mass: 'character varying', 824 | hair_color: 'character varying', 825 | skin_color: 'character varying', 826 | eye_color: 'character varying', 827 | birth_year: 'character varying', 828 | gender: 'character varying', 829 | species_id: 'bigint', 830 | homeworld_id: 'bigint', 831 | height: 'integer' 832 | } 833 | } 834 | typeObj { 835 | people: { 836 | _id: 'integer', 837 | name: 'character varying', 838 | mass: 'character varying', 839 | hair_color: 'character varying', 840 | skin_color: 'character varying', 841 | eye_color: 'character varying', 842 | birth_year: 'character varying', 843 | gender: 'character varying', 844 | species: 'Species', 845 | planets: 'Planet', 846 | height: 'integer' 847 | } 848 | ///etc 849 | } 850 | 851 | // typeCreator(nonJoinTables, joinTables, fktAsObj); 852 | // console.log(`typeObj`, typeObj); 853 | 854 | 855 | // add join table keys to typeObjects 856 | // singularize all shit last 857 | 858 | NONJOINTABLESHERE { 859 | people: { 860 | _id: 'integer', 861 | name: 'character varying', 862 | mass: 'character varying', 863 | hair_color: 'character varying', 864 | skin_color: 'character varying', 865 | eye_color: 'character varying', 866 | birth_year: 'character varying', 867 | gender: 'character varying', 868 | species_id: 'bigint', 869 | homeworld_id: 'bigint', 870 | height: 'integer' 871 | }, 872 | planets: { 873 | _id: 'integer', 874 | name: 'character varying', 875 | rotation_period: 'integer', 876 | orbital_period: 'integer', 877 | diameter: 'integer', 878 | climate: 'character varying', 879 | gravity: 'character varying', 880 | terrain: 'character varying', 881 | surface_water: 'character varying', 882 | population: 'bigint' 883 | }, 884 | films: { 885 | _id: 'integer', 886 | title: 'character varying', 887 | episode_id: 'integer', 888 | opening_crawl: 'character varying', 889 | director: 'character varying', 890 | producer: 'character varying', 891 | release_date: 'date' 892 | }, 893 | species: { 894 | _id: 'integer', 895 | name: 'character varying', 896 | classification: 'character varying', 897 | average_height: 'character varying', 898 | average_lifespan: 'character varying', 899 | hair_colors: 'character varying', 900 | skin_colors: 'character varying', 901 | eye_colors: 'character varying', 902 | language: 'character varying', 903 | homeworld_id: 'bigint' 904 | }, 905 | vessels: { 906 | _id: 'integer', 907 | name: 'character varying', 908 | manufacturer: 'character varying', 909 | model: 'character varying', 910 | vessel_type: 'character varying', 911 | vessel_class: 'character varying', 912 | cost_in_credits: 'bigint', 913 | length: 'character varying', 914 | max_atmosphering_speed: 'character varying', 915 | crew: 'integer', 916 | passengers: 'integer', 917 | cargo_capacity: 'character varying', 918 | consumables: 'character varying' 919 | }, 920 | starship_specs: { 921 | _id: 'integer', 922 | hyperdrive_rating: 'character varying', 923 | MGLT: 'character varying' 924 | } 925 | } 926 | isJoinTable { 927 | pilots: { _id: 'integer', person_id: 'bigint', vessel_id: 'bigint' }, 928 | people_in_films: { _id: 'integer', person_id: 'bigint', film_id: 'bigint' }, 929 | species_in_films: { _id: 'integer', film_id: 'bigint', species_id: 'bigint' }, 930 | vessels_in_films: { _id: 'integer', vessel_id: 'bigint', film_id: 'bigint' }, 931 | planets_in_films: { _id: 'integer', film_id: 'bigint', planet_id: 'bigint' } 932 | } 933 | 934 | //non join table with correct types 8/30 935 | const nonjoinTablewithCorrectTypes = { 936 | planets: { 937 | _id: 'ID', 938 | name: 'String', 939 | rotation_period: 'Int', 940 | orbital_period: 'Int', 941 | diameter: 'Int', 942 | climate: 'String', 943 | gravity: 'String', 944 | terrain: 'String', 945 | surface_water: 'String', 946 | population: 'ID' 947 | }, 948 | films: { 949 | _id: 'ID', 950 | title: 'String', 951 | episode_id: 'Int', 952 | opening_crawl: 'String', 953 | director: 'String', 954 | producer: 'String', 955 | release_date: 'ID' 956 | }, 957 | species: { 958 | _id: 'ID', 959 | name: 'String', 960 | classification: 'String', 961 | average_height: 'String', 962 | average_lifespan: 'String', 963 | hair_colors: 'String', 964 | skin_colors: 'String', 965 | eye_colors: 'String', 966 | language: 'String', 967 | homeworld_id: 'ID' 968 | }, 969 | vessels: { 970 | _id: 'ID', 971 | name: 'String', 972 | manufacturer: 'String', 973 | model: 'String', 974 | vessel_type: 'String', 975 | vessel_class: 'String', 976 | cost_in_credits: 'ID', 977 | length: 'String', 978 | max_atmosphering_speed: 'String', 979 | crew: 'Int', 980 | passengers: 'Int', 981 | cargo_capacity: 'String', 982 | consumables: 'String' 983 | }, 984 | people: { 985 | _id: 'ID', 986 | name: 'String', 987 | mass: 'String', 988 | hair_color: 'String', 989 | skin_color: 'String', 990 | eye_color: 'String', 991 | birth_year: 'String', 992 | gender: 'String', 993 | species_id: 'ID', 994 | homeworld_id: 'ID', 995 | height: 'Int' 996 | }, 997 | starship_specs: { 998 | _id: 'ID', 999 | hyperdrive_rating: 'String', 1000 | MGLT: 'String', 1001 | vessel_id: 'ID' 1002 | } 1003 | } 1004 | 1005 | const outputOfaddNullableFields = { 1006 | planets: { 1007 | _id: 'ID!', 1008 | name: 'String', 1009 | rotation_period: 'Int', 1010 | orbital_period: 'Int', 1011 | diameter: 'Int', 1012 | climate: 'String', 1013 | gravity: 'String', 1014 | terrain: 'String', 1015 | surface_water: 'String', 1016 | population: 'ID' 1017 | }, 1018 | films: { 1019 | _id: 'ID!', 1020 | title: 'String!', 1021 | episode_id: 'Int!', 1022 | opening_crawl: 'String!', 1023 | director: 'String!', 1024 | producer: 'String!', 1025 | release_date: 'ID!' 1026 | }, 1027 | species: { 1028 | _id: 'ID!', 1029 | name: 'String!', 1030 | classification: 'String', 1031 | average_height: 'String', 1032 | average_lifespan: 'String', 1033 | hair_colors: 'String', 1034 | skin_colors: 'String', 1035 | eye_colors: 'String', 1036 | language: 'String', 1037 | homeworld_id: 'ID' 1038 | }, 1039 | vessels: { 1040 | _id: 'ID!', 1041 | name: 'String!', 1042 | manufacturer: 'String', 1043 | model: 'String', 1044 | vessel_type: 'String!', 1045 | vessel_class: 'String!', 1046 | cost_in_credits: 'ID', 1047 | length: 'String', 1048 | max_atmosphering_speed: 'String', 1049 | crew: 'Int', 1050 | passengers: 'Int', 1051 | cargo_capacity: 'String', 1052 | consumables: 'String' 1053 | }, 1054 | people: { 1055 | _id: 'ID!', 1056 | name: 'String!', 1057 | mass: 'String', 1058 | hair_color: 'String', 1059 | skin_color: 'String', 1060 | eye_color: 'String', 1061 | birth_year: 'String', 1062 | gender: 'String', 1063 | species_id: 'ID', 1064 | homeworld_id: 'ID', 1065 | height: 'Int' 1066 | }, 1067 | starship_specs: { 1068 | _id: 'ID!', 1069 | hyperdrive_rating: 'String', 1070 | MGLT: 'String', 1071 | vessel_id: 'ID!' 1072 | } 1073 | } -------------------------------------------------------------------------------- /server/gqlcorp/typeMain.js: -------------------------------------------------------------------------------- 1 | const { singular } = require('pluralize'); 2 | const jspc = require('json-stringify-pretty-compact') 3 | const camelCase = require('camelcase'); 4 | //Capitalizes first letter of any string 5 | function capFirstLet(string) { 6 | return string.charAt(0).toUpperCase() + string.slice(1); 7 | } 8 | 9 | //Converts to PascalCaseFrom snake_case 10 | function snakeToTitle(str) { 11 | return str.split("_").map(capFirstLet).join(""); 12 | } 13 | 14 | const camelCaseIt = (string) => 15 | string 16 | .toLowerCase() 17 | .trim() 18 | .split(/[.\-_\s]/g) 19 | .reduce((string, word) => string + word[0].toUpperCase() + word.slice(1)); 20 | 21 | //Easy mapper for the two tuple items 22 | const tuplesToObjects = (a,b) => [a,b].map(x=>Object.fromEntries(x)) 23 | 24 | 25 | //Determines if each column in the table is nullable, if no add to nullable obj 26 | //pass in all table data as argument 27 | const isNullable = (obj) => { 28 | const types = {}; 29 | 30 | for (const tbl in obj) { 31 | types[tbl] = {}; 32 | 33 | for (const arr of obj[tbl]) { 34 | 35 | for (const column in arr) { 36 | let temp = arr.column_name 37 | if (arr.required === 'NO') { 38 | types[tbl][temp] = true; 39 | } 40 | } 41 | } 42 | } 43 | return types 44 | } 45 | 46 | // Takes allTables, returns array, elements are tuples, 1st val 47 | const dataTupleMaker = (object) => { 48 | const types = []; // holds everything 49 | tupColNames = Object.entries(object) 50 | 51 | for (const [tableName, colData] of tupColNames) { // key is col name, val is array holding multiple objs of column data // reducing array of objs to obj of col names and types 52 | const tupTypes = [tableName, {}]; // sets up tuple 53 | colData.forEach(val => { 54 | tupTypes[1][val.column_name] = val.data_type; // puts column:type into object 55 | }); 56 | types.push(tupTypes); // places tuple into types 57 | } 58 | //value is an array of objs 59 | return types; 60 | }; 61 | 62 | //takes array of obj, spits out array of tuples [foreign keys, {foreign keys: primary tables}] 63 | const fkTupleMaker = array => { // array of Foreign Keys, elem is object 64 | const tupArr = []; 65 | array.forEach(fkObj => { 66 | if (!tupArr.length || tupArr[tupArr.length - 1][0] !== fkObj.foreign_table) { //array not empty, last tuple in array's name is not yet made: 67 | const tuple = [fkObj.foreign_table, {}]; //make tuple => fTableName, { columnName: primaryTable} 68 | tuple[1][fkObj.fk_columns] = fkObj.primary_table; // add to foreign table obj w/ all column names to its primary table 69 | tupArr.push(tuple); 70 | } else { // fTable already exists in fkArray 71 | tupArr[tupArr.length - 1][1][fkObj.fk_columns] = fkObj.primary_table; // add in more primary tables 72 | } 73 | }); 74 | return tupArr; 75 | }; 76 | 77 | //Takes tuple array and returns object of each tableName as key and value as number of foreign keys 78 | const countTupleKeys = (tuples) => { 79 | const keyCount = {} 80 | tuples.forEach(tuple => { 81 | //purple arr for each table 82 | const tableName = tuple[0] 83 | const count = Object.keys(tuple[1]).length; //counts foreign keys 84 | keyCount[tableName] = count; 85 | }) 86 | return keyCount 87 | } 88 | 89 | // Compares all tables & their # of keys to foreign key tables and gives join and nonJoin tables 90 | const nonAndJoinTables = (numFKeys, numAllKeys, fKeysObject, tablesObj) => { 91 | const jTable = {} 92 | const nJTable = {} 93 | 94 | for (const tableName in numAllKeys) { 95 | //if number of foreign keys + 1 equals all keys, it's a join table 96 | if(numFKeys[tableName] + 1 === numAllKeys[tableName]) jTable[tableName] = tablesObj[tableName] 97 | //if not, throw in non join table 98 | else nJTable[tableName] = tablesObj[tableName] 99 | } 100 | return [jTable, nJTable] 101 | } 102 | 103 | //Creates obj of all the foreign keys in non join tables 104 | const fktNoJoins = (fktAsObj, nonJT) => { 105 | const fktNoJoinsObj = {} 106 | 107 | for (const key in fktAsObj) { 108 | if (nonJT[key]) { 109 | fktNoJoinsObj[key] = fktAsObj[key]; 110 | } 111 | } 112 | return fktNoJoinsObj; 113 | } 114 | 115 | //From non join tables, creating GQL formatted string with all types 116 | const typeCreator = (nonJoinTables, fktObj, fktObjNoJoins, nullable) => { 117 | const typeObj = {}; 118 | 119 | //creates schema types for nonJoinTables with foreign keys and without foreign keys. 120 | //NJT's w/o FK's are added directly as key/value pairs 121 | //NJT w/ FK's are added with the foreign key singularized and capitalized 122 | for (const key in nonJoinTables) { // key: people / starship spec 123 | //add non foreign key value pairs to type obj 124 | if (!fktObj[key]) { // 125 | typeObj[key] = nonJoinTables[key]; //no touch after this step 126 | } else { 127 | // if foreign key, create an object, to be filled with its column names 128 | typeObj[key] = {}; 129 | 130 | for (const prop in nonJoinTables[key]) {//prop: key within the people obj / hyperdrive 131 | // loop through each table for column data and add column info 132 | if (prop in fktObj[key] === false) { 133 | typeObj[key][prop] = nonJoinTables[key][prop]; 134 | } else { 135 | // but if it's in the foreign key object, it adds the type as a connection to a different table. 136 | const temp = fktObj[key][prop] 137 | // const temp = camelCase(fktObj[key][prop]); 138 | // typeObj[key][temp] = '['+capFirstLet(singular(temp))+']'; 139 | typeObj[key][temp] = '['+temp.slice(0,1).toUpperCase()+camelCase(singular(temp)).slice(1)+']'; 140 | } 141 | } 142 | } 143 | } 144 | 145 | //Add corresponding foreign keys from join tables & non join tables to their appropriate type in GraphQL. This enables us to properly connect our types with relational data 146 | for(let table in typeObj) { // key is people 147 | // look through values OF values OF fktasObj 148 | for(let fktObjKey in fktObj) { 149 | const arrFK = Object.values(fktObj[fktObjKey]) //values of objects in fktasobject as an array 150 | if (arrFK.includes(table)) { 151 | const valsToInput = arrFK.filter(elem => elem !== table) //filter for all other tables 152 | valsToInput.forEach(val => { //Add to final table 153 | typeObj[table][val] = '['+val.slice(0,1).toUpperCase()+camelCase(singular(val)).slice(1)+']' 154 | }) 155 | } 156 | } 157 | } 158 | 159 | 160 | //Add Foreign Types to Type obj 161 | //Due to no join tables, adding corresponding foreign keys to each non join table so each table can be relationally linked 162 | // for (const njtCol in nonJoinTables) { //type object name i.e. planets, species, films 163 | 164 | // for (const fktCol in fktObjNoJoins) { //iterate through fktObj fktCol i.e. people, peope_in_films 165 | 166 | // for (const col in fktObjNoJoins[fktCol]) { //iterate through nested obj 167 | // if (fktObjNoJoins[fktCol][col] === njtCol) { 168 | // if (!(Object.keys(typeObj[njtCol]).includes(fktCol))) { 169 | // //console.log('missing fks', typeObj[njtCol],fktCol) 170 | // typeObj[njtCol][fktCol] = '['+snakeToTitle(singular(fktCol))+']' 171 | // } 172 | // } 173 | // } 174 | // } 175 | // } 176 | ////////////////////////////////////////////////////////////////TEST////////////////////////////////// 177 | for (const njtCol in nonJoinTables) { //type object name i.e. planets, species, films 178 | 179 | for (const fktCol in fktObjNoJoins) { //iterate through fktObj fktCol i.e. people, peope_in_films 180 | 181 | for (const col in fktObjNoJoins[fktCol]) { //iterate through nested obj 182 | if (fktObjNoJoins[fktCol][col] === njtCol) { 183 | if (!(Object.keys(typeObj[njtCol]).includes(fktCol))) { 184 | //console.log('missing fks', typeObj[njtCol],fktCol) 185 | let temp = camelCaseIt(fktCol); 186 | typeObj[njtCol][temp] = '['+fktCol.slice(0,1).toUpperCase()+camelCase(singular(fktCol)).slice(1)+']' 187 | } 188 | } 189 | } 190 | } 191 | } 192 | 193 | ////////////////////////////////////////////////////////////////TEST////////////////////////////////// 194 | 195 | 196 | //convert SQL types to GQL type 197 | for (const table in typeObj) { 198 | 199 | for (const column in typeObj[table]) { 200 | if (nonJoinTables.hasOwnProperty(column) && camelCase(column) !== column) { 201 | typeObj[table][camelCase(column)] = typeObj[table][column]; 202 | delete typeObj[table][column]; 203 | } 204 | switch (typeObj[table][column]) { 205 | case 'bigint': 206 | typeObj[table][column] = 'Int' 207 | break; 208 | case 'integer': 209 | typeObj[table][column] = 'Int' 210 | break; 211 | case 'character varying': 212 | typeObj[table][column] = 'String' 213 | break; 214 | case 'date': 215 | typeObj[table][column] = 'String' 216 | break; 217 | case 'numeric': 218 | typeObj[table][column] = 'Int' 219 | break; 220 | case 'ARRAY': 221 | typeObj[table][column] = '[String]' 222 | break; 223 | case 'character': 224 | typeObj[table][column] = 'String' 225 | break; 226 | case 'smallint': 227 | typeObj[table][column] = 'Int' 228 | break; 229 | } 230 | if (column === '_id') { 231 | typeObj[table][column] = 'ID' 232 | } 233 | } 234 | } 235 | 236 | //Signifies which fields are nullable and adds them to the final object with ! for GQL format 237 | for (const tbl in typeObj) { 238 | 239 | for (const column in nullable[tbl]) { 240 | let temp = typeObj[tbl][column] 241 | if(temp) typeObj[tbl][column] = temp + '!' 242 | } 243 | } 244 | 245 | // const toDisplay = \t \n .replace 246 | // const directToCopy straighup string 247 | for (const fktCol in fktObjNoJoins) { 248 | // console.log('VALPROP', fktCol, fktObjNoJoins[fktCol]) 249 | for (let [valpropkey, valpropval] of Object.entries(fktObjNoJoins[fktCol])) { 250 | // console.log('INTENDED INSERT', `${typeObj}[${snakeToTitle(singular(valpropval))}][${camelCase(fktCol)}]`)// = '['+snakeToTitle(singular(fktCol))+']'; 251 | 252 | typeObj[valpropval][camelCase(fktCol)] = '['+fktCol.slice(0,1).toUpperCase()+camelCase(singular(fktCol)).slice(1)+']'; 253 | } 254 | } 255 | return typeObj 256 | } 257 | // jspc takes object, jsons it, then prettifies it 258 | // within object, take out commas and quotation marks 259 | // for each object, add "type " singular(key) plus object with above stuff done 260 | 261 | // evening work 262 | // in type, we need a new line before each type ____ MUST BE DONE WITHIN TYPE 263 | // all of type is indented when it should not be MUST BE DONE WITHIN TYPE ? 264 | 265 | function brianFunction(object) { 266 | const brianObj = {} 267 | //loop through properties of object 268 | for(const table in object) { 269 | //for the current property 270 | const value = object[table] 271 | const newKey = `type ${snakeToTitle(singular(table))}` 272 | brianObj[newKey] = value; 273 | } 274 | let closeToDone = jspc(brianObj); 275 | closeToDone = closeToDone.replace(/"/g, '').replace(/: {/g,' {').replace(/,/g,'').replace(/type /g, '\ntype ') 276 | return closeToDone 277 | //store val in temp variable 278 | //insert new property, key of which is "type singular(oldkeyname)" with temp as value 279 | //delete original property 280 | //return modified object 281 | 282 | 283 | 284 | 285 | // let frontEndString = ''; 286 | 287 | 288 | // jspc() 289 | // for(const table in object) { 290 | // frontEndString += 'type ' + snakeToTitle(table) + JSON.stringify(object[table]).replace(/"/g,'').replace(/,/g,'') 291 | // } 292 | // return frontEndString 293 | } 294 | 295 | // console.log('aefhoiwhfoiqwehfalsdvnasdlkghalkdvhasldkghasdlkghsadlkfhakldvhaoidvhaoidgha',brianFunction(text)) 296 | 297 | 298 | 299 | const postjspc = "{\n \"planets\": {\n \"_id\": \"ID!\",\n \"name\": \"String\",\n \"rotation_period\": \"Int\",\n \"orbital_period\": \"Int\",\n \"diameter\": \"Int\",\n \"climate\": \"String\",\n \"gravity\": \"String\",\n \"terrain\": \"String\",\n \"surface_water\": \"String\",\n \"population\": \"Int\",\n \"species\": \"[Species]\",\n \"films\": \"[Film]\",\n \"people\": \"[Person]\",\n \"planets_in_films\": \"[PlanetsInFilm]\"\n },\n \"films\": {\n \"_id\": \"ID!\",\n \"title\": \"String!\",\n \"episode_id\": \"Int!\",\n \"opening_crawl\": \"String!\",\n \"director\": \"String!\",\n \"producer\": \"String!\",\n \"release_date\": \"String!\",\n \"people\": \"[Person]\",\n \"planets\": \"[Planet]\",\n \"species\": \"[Species]\",\n \"vessels\": \"[Vessel]\",\n \"people_in_films\": \"[PeopleInFilm]\",\n \"planets_in_films\": \"[PlanetsInFilm]\",\n \"species_in_films\": \"[SpeciesInFilm]\",\n \"vessels_in_films\": \"[VesselsInFilm]\"\n },\n \"species\": {\n \"_id\": \"ID!\",\n \"name\": \"String!\",\n \"classification\": \"String\",\n \"average_height\": \"String\",\n \"average_lifespan\": \"String\",\n \"hair_colors\": \"String\",\n \"skin_colors\": \"String\",\n \"eye_colors\": \"String\",\n \"language\": \"String\",\n \"planets\": \"[Planet]\",\n \"films\": \"[Film]\",\n \"people\": \"[Person]\",\n \"species_in_films\": \"[SpeciesInFilm]\"\n },\n \"vessels\": {\n \"_id\": \"ID!\",\n \"name\": \"String!\",\n \"manufacturer\": \"String\",\n \"model\": \"String\",\n \"vessel_type\": \"String!\",\n \"vessel_class\": \"String!\",\n \"cost_in_credits\": \"Int\",\n \"length\": \"String\",\n \"max_atmosphering_speed\": \"String\",\n \"crew\": \"Int\",\n \"passengers\": \"Int\",\n \"cargo_capacity\": \"String\",\n \"consumables\": \"String\",\n \"people\": \"[Person]\",\n \"films\": \"[Film]\",\n \"pilots\": \"[Pilot]\",\n \"starship_specs\": \"[StarshipSpec]\",\n \"vessels_in_films\": \"[VesselsInFilm]\"\n },\n \"people\": {\n \"_id\": \"ID!\",\n \"name\": \"String!\",\n \"mass\": \"String\",\n \"hair_color\": \"String\",\n \"skin_color\": \"String\",\n \"eye_color\": \"String\",\n \"birth_year\": \"String\",\n \"gender\": \"String\",\n \"species\": \"[Species]\",\n \"planets\": \"[Planet]\",\n \"height\": \"Int\",\n \"films\": \"[Film]\",\n \"vessels\": \"[Vessel]\",\n \"people_in_films\": \"[PeopleInFilm]\",\n \"pilots\": \"[Pilot]\"\n },\n \"starship_specs\": {\n \"_id\": \"ID!\",\n \"hyperdrive_rating\": \"String\",\n \"MGLT\": \"String\",\n \"vessels\": \"[Vessel]\"\n }\n}" 300 | 301 | // const {allTables, foreignKeys} = data 302 | 303 | // const nullableObj = isNullable(allTables) 304 | // const tablesTuples = dataTupleMaker(allTables) 305 | // const fKeyTuples = fkTupleMaker(foreignKeys) 306 | 307 | // const fKeyCounts = countTupleKeys(fKeyTuples) 308 | // const allKeyCounts = countTupleKeys(tablesTuples) 309 | 310 | // const [fKeysObj, tablesObj] = tuplesToObjects(fKeyTuples, tablesTuples) 311 | 312 | 313 | // const [joinTable, nonJoinTable] = nonAndJoinTables(fKeyCounts, allKeyCounts, fKeysObj, tablesObj) 314 | 315 | // const fktObjNoJoins = fktNoJoins(fKeysObj, nonJoinTable) 316 | 317 | // const finalResult = typeCreator(nonJoinTable, fKeysObj, nullableObj) 318 | 319 | 320 | 321 | module.exports = { capFirstLet, 322 | snakeToTitle, 323 | isNullable, 324 | dataTupleMaker, 325 | fkTupleMaker, 326 | countTupleKeys, 327 | tuplesToObjects, 328 | nonAndJoinTables, 329 | fktNoJoins, 330 | typeCreator, 331 | brianFunction } -------------------------------------------------------------------------------- /server/router.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | const sqlController = require("./controllers/sqlController.js"); 5 | const gqlController = require("./controllers/gqlController.js"); 6 | 7 | router.post( 8 | "/uri", 9 | sqlController.getTableData, 10 | // sqlController.visualize, 11 | gqlController.makeSchemaTypes, 12 | gqlController.makeSchemaMutations, 13 | gqlController.makeSchemaQueries, 14 | gqlController.makeResolvers, 15 | gqlController.completeString, 16 | (req, res) => 17 | res.status(200).json({ 18 | allTables: res.locals.data.allTables, 19 | foreignKeys: res.locals.data.foreignKeys, 20 | primaryKeys: res.locals.data.primaryKeys, 21 | schemaTypes: res.locals.schemaTypes, 22 | schemaMutations: res.locals.schemaMutations, 23 | schemaQueries: res.locals.schemaQueries, 24 | resolvers: res.locals.resolvers, 25 | completeSchemaString: res.locals.completeSchemaString 26 | }) 27 | ); 28 | 29 | 30 | module.exports = router; 31 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const express = require('express'); 3 | const app = express(); 4 | const router = require('./router.js'); 5 | const PORT = 3000; 6 | 7 | /** 8 | * handle parsing request body 9 | */ 10 | app.use(express.json()); 11 | app.use(express.urlencoded({ extended: true })); 12 | 13 | /** 14 | * handle requests for static files 15 | */ 16 | //app.use(express.static(path.resolve(__dirname, '../client'))); // look over this later 17 | if (process.env.NODE_ENV === 'production') app.use(express.static(path.resolve(__dirname, '../dist'))); 18 | else app.use(express.static(path.resolve(__dirname, '../'))); 19 | // console.log(path.resolve(__dirname, '../client/assets')); 20 | /** 21 | * define route handlers 22 | */ 23 | app.use('/api', router); 24 | 25 | app.get('/*', function (req, res) { 26 | res.redirect('/'); 27 | }); 28 | 29 | // catch-all route handler for any requests to an unknown route 30 | app.use((req, res) => 31 | res.status(404).send("This is not the page you're looking for...") 32 | ); 33 | 34 | //Error handling 35 | app.use((err, req, res, next) => { 36 | const defaultErr = { 37 | log: 'Express error handler caught unknown middleware error', 38 | status: 500, 39 | message: { err: 'An error occurred' }, 40 | }; 41 | const errorObj = Object.assign({}, defaultErr, err); 42 | console.log(errorObj.log); 43 | return res.status(errorObj.status).json(errorObj.message); 44 | }); 45 | 46 | /** 47 | * start server 48 | */ 49 | app.listen(PORT, () => { 50 | console.log(`Server listening on port: ${PORT}...`); 51 | }); 52 | 53 | module.exports = app; 54 | -------------------------------------------------------------------------------- /server/sqlQuery.sql: -------------------------------------------------------------------------------- 1 | -- //get all tables in the db 2 | -- //get the columns, column types, primary key, foreign key 3 | -- // 4 | 5 | -- Easiest way: one longass command that will provide all of above 6 | -- we cant do easiest way because there is no way of saving the separate info we need for use later 7 | 8 | -- More annoying: 9 | -- query every single table 10 | -- list out table keys?(lol) and types 11 | -- list out its pks and fks 12 | 13 | -- json it and save as below: 14 | 15 | -- for loop and set a variable for each link 16 | 17 | -- is info coming as JSON? then .json() it individually 18 | -- {} 19 | 20 | -- 1. query for all table names 21 | 22 | -- SELECT table_name 23 | -- FROM information_schema.tables 24 | -- WHERE table_schema='public' 25 | -- AND table_type='BASE TABLE'; 26 | 27 | 28 | -- 2. query each table for columns, column types, primary key, foreign keys 29 | -- each query would return multiple columns, column types, ONE pk, multiple fks 30 | -- { 31 | -- tableName: people 32 | -- columns: [[_id, integer], [gender, charvar]], 33 | -- pk: 3, 34 | -- fk: { 35 | -- fk0: { 36 | -- foreign-key: species_id, 37 | -- table: species, 38 | -- column_id: _id 39 | -- }, 40 | -- fk1: { 41 | -- } 42 | -- }, 43 | -- referenced_by: { 44 | -- rb0: { 45 | -- from_table: people_in_films, 46 | -- from_key: person_id, 47 | -- column_id: _id 48 | -- }, 49 | -- fk1: { 50 | -- } 51 | -- } 52 | -- } 53 | 54 | -- --find all tables in DB 55 | -- const arrayTables = db.query('put in query here') 56 | -- arraytables = [planest, pilots, films, etc] 57 | -- -- query each table for columns, column types, primary key, foreign keys 58 | -- const allTables = [arrayTables[0], arrayTables[1], etc] 59 | -- for(let i = 0; i < arrayTables.length; i++) { 60 | -- const eachTable = await db.query() -- 'SELECT ${arraytables[i]} from ______' 61 | -- allTables.push(eachTable) -- [{name: planets},{},{}] 62 | -- } 63 | -- next() 64 | -- -- 65 | -- -- 66 | -- -- 67 | -- -- 68 | -- -- 69 | -- -- 70 | -- -- 71 | -- -- WHERE $1 = 72 | await db.any('', [$1,$2]) 73 | await db.any('SELECT * FROM users WHERE name = $1', 'John') 74 | 75 | 76 | 77 | --column data 78 | const columnQuery = `select table_name, column_name, ordinal_position, column_default, data_type, udt_name 79 | from information_schema.columns 80 | where table_name = $1` 81 | db.any(columnQuery, array[i]) 82 | 83 | select table_name, column_name, ordinal_position, column_default, data_type, udt_name 84 | from information_schema.columns 85 | where table_name = 'films' 86 | 87 | --foreign keys on table 88 | --kcu: key column usage 89 | --rel_tco: table constraints 90 | --rco: referential_constraint 91 | select kcu.table_name as foreign_table, 92 | '>-' as rel, 93 | rel_tco.table_name as primary_table, 94 | string_agg(kcu.column_name, ', ') as fk_columns, 95 | kcu.constraint_name 96 | from information_schema.table_constraints tco 97 | join information_schema.key_column_usage kcu 98 | on tco.constraint_schema = kcu.constraint_schema 99 | and tco.constraint_name = kcu.constraint_name 100 | join information_schema.referential_constraints rco 101 | on tco.constraint_schema = rco.constraint_schema 102 | and tco.constraint_name = rco.constraint_name 103 | join information_schema.table_constraints rel_tco 104 | on rco.unique_constraint_schema = rel_tco.constraint_schema 105 | and rco.unique_constraint_name = rel_tco.constraint_name 106 | where tco.constraint_type = 'FOREIGN KEY' 107 | group by kcu.table_schema, 108 | kcu.table_name, 109 | rel_tco.table_name, 110 | rel_tco.table_schema, 111 | kcu.constraint_name 112 | order by kcu.table_schema, 113 | kcu.table_name 114 | 115 | 116 | --Primary key query 117 | select kcu.table_name, 118 | tco.constraint_name, 119 | string_agg(kcu.column_name,', ') as Primary_key_columns 120 | from information_schema.table_constraints tco 121 | join information_schema.key_column_usage kcu 122 | on kcu.constraint_name = tco.constraint_name 123 | and kcu.constraint_schema = tco.constraint_schema 124 | and kcu.constraint_name = tco.constraint_name 125 | --where tco.constraint_type = 'PRIMARY KEY' 126 | group by tco.constraint_name, 127 | kcu.table_schema, 128 | kcu.table_name 129 | order by kcu.table_schema, 130 | kcu.table_name; 131 | 132 | 133 | --Primary Key && Foreign Key 134 | select kcu.table_name as foreign_table, 135 | '>-' as rel, 136 | rel_tco.table_name as primary_table, 137 | string_agg(kcu.column_name, ', ') as fk_columns, 138 | kcu.constraint_name, 139 | tco.constraint_type 140 | from information_schema.table_constraints tco 141 | join information_schema.key_column_usage kcu 142 | on tco.constraint_schema = kcu.constraint_schema 143 | and tco.constraint_name = kcu.constraint_name 144 | join information_schema.referential_constraints rco 145 | on tco.constraint_schema = rco.constraint_schema 146 | and tco.constraint_name = rco.constraint_name 147 | join information_schema.table_constraints rel_tco 148 | on rco.unique_constraint_schema = rel_tco.constraint_schema 149 | and rco.unique_constraint_name = rel_tco.constraint_name 150 | where tco.constraint_type = 'FOREIGN KEY' 151 | group by kcu.table_schema, 152 | kcu.table_name, 153 | rel_tco.table_name, 154 | rel_tco.table_schema, 155 | kcu.constraint_name, 156 | tco.constraint_type 157 | order by kcu.table_schema, 158 | kcu.table_name 159 | 160 | select kcu.table_name, 161 | tco.constraint_name, 162 | string_agg(kcu.column_name,', ') as Primary_key_columns 163 | from information_schema.table_constraints tco 164 | join information_schema.key_column_usage kcu 165 | on kcu.constraint_name = tco.constraint_name 166 | and kcu.constraint_schema = tco.constraint_schema 167 | and kcu.constraint_name = tco.constraint_name 168 | where tco.constraint_type = 'PRIMARY KEY' 169 | group by tco.constraint_name, 170 | kcu.table_schema, 171 | kcu.table_name 172 | order by kcu.table_schema, 173 | kcu.table_name; -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const CopyPlugin = require("copy-webpack-plugin"); 4 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 5 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 6 | const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); 7 | 8 | module.exports = { 9 | entry: ['regenerator-runtime/runtime.js', './client/index.js'], 10 | output: { 11 | path: path.resolve(__dirname, 'dist'), //watch it. default is "dist" 12 | filename: 'bundle.js', 13 | }, 14 | mode: process.env.NODE_ENV, 15 | devServer: { 16 | historyApiFallback: true, 17 | host: '0.0.0.0', // '0.0.0.0' FOR DOCKER NOT localhost 18 | port: 8080, 19 | inline: true, 20 | compress: true, 21 | hot: true, 22 | publicPath: '/', 23 | proxy: { 24 | '/': 'http://localhost:3000' 25 | // '/api/**': { 26 | // target: 'http://localhost:3000', 27 | // secure: false, 28 | // changeOrigin: true, 29 | // onProxyReq: (proxyReq) => { 30 | // proxyReq.setHeader('Cookie', cookie); 31 | // }, 32 | // } 33 | 34 | }, 35 | }, 36 | plugins: [ 37 | new HtmlWebpackPlugin({ 38 | title: 'Production', 39 | template: 'index.html' 40 | }), 41 | new MiniCssExtractPlugin(), 42 | new CopyPlugin({ 43 | patterns: [ 44 | { from: "assets", to: "assets" }, 45 | ], 46 | }),], 47 | module: { 48 | rules: [ 49 | { 50 | test: /\.jsx?/, 51 | exclude: /(node_modules)/, 52 | use: { 53 | loader: 'babel-loader', 54 | options: { 55 | presets: ['@babel/preset-env', '@babel/preset-react'], 56 | }, 57 | }, 58 | }, 59 | { 60 | test: /\.s[ac]ss$/i, 61 | exclude: /(node_modules)/, 62 | use: [ 63 | process.env.NODE_ENV === 'production' ? MiniCssExtractPlugin.loader : 'style-loader', 64 | 'css-loader', 65 | 'sass-loader' 66 | ], 67 | }, 68 | ], 69 | }, 70 | optimization: { 71 | minimizer: [new CssMinimizerPlugin()], 72 | } 73 | }; 74 | --------------------------------------------------------------------------------