├── README.md ├── bubble-stack_postgres_create.sql ├── server ├── controllers │ ├── stackConCopy.js │ └── stackControllers.js ├── models │ └── stackModels.js ├── server.js └── routes │ └── api.js ├── .gitignore ├── client ├── variables.scss ├── .DS_Store ├── index.tsx ├── index.html ├── components │ ├── data.ts │ ├── App.tsx │ ├── containers │ │ ├── staticContainer.tsx │ │ └── mainContainer.tsx │ ├── map.js │ ├── Bubble.tsx │ └── MakeMap.js └── style.scss ├── .vscode └── settings.json ├── .DS_Store ├── tsconfig.json ├── webpack.config.js └── package.json /README.md: -------------------------------------------------------------------------------- 1 | # Scratch -------------------------------------------------------------------------------- /bubble-stack_postgres_create.sql: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /server/controllers/stackConCopy.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .vscode 3 | .env -------------------------------------------------------------------------------- /client/variables.scss: -------------------------------------------------------------------------------- 1 | $primary: rgb(70, 87, 78); -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.enable": false 3 | } -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Star-Nosed-Mole/Bubble-Stack/HEAD/.DS_Store -------------------------------------------------------------------------------- /client/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Star-Nosed-Mole/Bubble-Stack/HEAD/client/.DS_Store -------------------------------------------------------------------------------- /client/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import App from './components/App'; 4 | 5 | const style = require('./style.scss'); 6 | render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "sourceMap": true, 6 | "noImplicitAny": false, 7 | "jsx" :"react", 8 | "esModuleInterop": true, 9 | }, 10 | "exclude": [ 11 | "node_modules" 12 | ] 13 | } -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /client/components/data.ts: -------------------------------------------------------------------------------- 1 | const testData = { 2 | name: 'nivo', 3 | color: 'hsl(0, 100%, 50%)', 4 | children: [ 5 | { 6 | name: 'React', 7 | color: 'hsl(191, 70%, 50%)', 8 | loc: 13285 9 | }, 10 | { 11 | name: 'Redux', 12 | color: 'hsl(191, 70%, 50%)', 13 | loc: 13285 14 | } 15 | ] 16 | }; 17 | 18 | export default testData; 19 | -------------------------------------------------------------------------------- /client/components/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import MainContainer from './containers/mainContainer'; 3 | 4 | // import MainContainer from './containers/MainContainer'; 5 | class App extends Component { 6 | constructor(props) { 7 | super(props); 8 | } 9 | render() { 10 | return ( 11 |
12 | 13 |
14 | ); 15 | } 16 | } 17 | export default App; 18 | -------------------------------------------------------------------------------- /client/components/containers/staticContainer.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | import Map from '../map'; 4 | 5 | // need the div classname because the Map componenet needs to be nested in a div that has height stlying for nivo bubble to work 6 | class StaticContainer extends Component { 7 | constructor(props) { 8 | super(props); 9 | } 10 | 11 | render() { 12 | return ( 13 |
14 | 15 |
16 | ); 17 | } 18 | } 19 | 20 | export default StaticContainer; 21 | -------------------------------------------------------------------------------- /server/models/stackModels.js: -------------------------------------------------------------------------------- 1 | // URI to the postgres database is in .env file which is not commited to github. Please request URI from previous team 2 | // Database contains 3 tables for technology Libraries, Framework, and Types 3 | 4 | // import PG_URI from '../../.env'; 5 | const { Pool } = require('pg'); 6 | 7 | require("dotenv").config(); 8 | 9 | // create a new pool here using the connection string above 10 | const pool = new Pool({ 11 | connectionString: process.env.PG_URI 12 | }); 13 | 14 | // We export an object that contains a property called query, 15 | // which is a function that returns the invocation of pool.query() after logging the query 16 | // This will be required in the controllers to be the access point to the database 17 | module.exports = { 18 | query: (text, params, callback) => { 19 | console.log('executed query', text); 20 | return pool.query(text, params, callback); 21 | } 22 | }; -------------------------------------------------------------------------------- /client/components/containers/mainContainer.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import StaticContainer from './staticContainer'; 3 | import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom'; 4 | import MakeMap from '../MakeMap'; 5 | 6 | // import MainContainer from './containers/MainContainer'; 7 | class MainContainer extends Component { 8 | constructor(props) { 9 | super(props); 10 | } 11 | // router set up to render make map on clicking the button 12 | render() { 13 | return ( 14 |
15 | 16 |
17 |
18 |

Bubble Stack

19 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 |
33 |
34 | ); 35 | } 36 | } 37 | export default MainContainer; 38 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const app = express(); 3 | 4 | const path = require('path'); 5 | const PORT = 3000; 6 | const apiRouter = require('./routes/api'); 7 | 8 | app.use(express.json()); 9 | 10 | // statically serve everything in the build folder on the route '/build 11 | if (process.env.NODE_ENV === 'production') { 12 | app.use('/', express.static('client')); 13 | app.use('/build', express.static(path.join(__dirname, '../build'))); 14 | } 15 | 16 | app.use('/api', apiRouter); 17 | 18 | app.get('/make', (req, res) => { 19 | res.sendFile(path.join(__dirname, '../client/index.html'), (error) => { 20 | if (error) res.status(400).send(error) 21 | }) 22 | }) 23 | 24 | // global error handler 25 | app.use('*', (req, res) => res.sendStatus(404)); 26 | 27 | app.use((err, req, res, next) => { 28 | const defaultErr = { 29 | log: 'Express error handler caught unknown middleware error', 30 | status: 400, 31 | message: { err: 'An error occurred' } 32 | }; 33 | const errorObj = { ...defaultErr, ...err }; 34 | return res.status(errorObj.status).json(errorObj.message); 35 | }); 36 | app.listen(PORT, () => { 37 | console.log(`Server listening on port: ${PORT}`); 38 | }); 39 | 40 | module.exports = app; 41 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './client/index.tsx', 5 | output: { 6 | path: path.resolve(__dirname, 'build'), 7 | filename: 'bundle.js' 8 | }, 9 | mode: process.env.NODE_ENV, 10 | devtool: 'source-map', 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.(js|jsx)$/, 15 | exclude: /node_modules/, 16 | use: { 17 | loader: 'babel-loader', 18 | options: { 19 | presets: ['@babel/preset-env', '@babel/preset-react'] 20 | } 21 | } 22 | }, 23 | { 24 | test: /.(css|scss)$/, 25 | exclude: /node_modules/, 26 | use: ['style-loader', 'css-loader', 'sass-loader'] 27 | }, 28 | { 29 | test: /\.tsx?$/, 30 | exclude: /node_modules/, 31 | loader: 'ts-loader' 32 | }, 33 | { 34 | enforce: 'pre', 35 | test: /\.js$/, 36 | loader: 'source-map-loader' 37 | } 38 | ] 39 | }, 40 | resolve: { 41 | extensions: ['.tsx', '.ts', '.js'] 42 | }, 43 | devServer: { 44 | contentBase: path.join(__dirname, '/client'), 45 | publicPath: 'http://localhost:8080/build/', 46 | proxy: { 47 | '/api': 'http://localhost:3000', 48 | '/make': 'http://localhost:3000' 49 | } 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /client/components/map.js: -------------------------------------------------------------------------------- 1 | import React, { Component, useState, useEffect } from 'react'; 2 | import Bubble from './Bubble'; 3 | 4 | function Map() { 5 | const [data, setData] = useState({ 6 | name: 'React', 7 | loc: 10000, 8 | children: [] 9 | }); 10 | 11 | useEffect(() => { 12 | fetch('/api/') 13 | .then((res) => res.json()) 14 | .then((data) => { 15 | console.log('PAGE RENDER: ', data); 16 | //let chilrenArray = [...this.state] 17 | const newArray = []; 18 | for (let i = 0; i < data.length; i++) { 19 | data[i].loc = 2000; 20 | 21 | if (data[i].type === 'state-management') { 22 | data[i].color = 'hsl(228, 70%, 50%)'; 23 | } else if (data[i].type === 'ui-components') { 24 | data[i].color = 'hsl(24, 70%, 50%)'; 25 | } else if (data[i].type === 'router') { 26 | data[i].color = 'hsl(156, 70%, 50%)'; 27 | } 28 | newArray.push(data[i]); 29 | } 30 | //console.log('NEW ARRAY: ', newArray); 31 | setData({ 32 | name: 'React', 33 | loc: 10000, 34 | children: newArray 35 | }); 36 | }) 37 | .catch((err) => console.log('Map.componentDidMount: ERROR: ', err)); 38 | }); 39 | 40 | return ( 41 |
42 | 43 |
44 | ); 45 | } 46 | 47 | export default Map; 48 | -------------------------------------------------------------------------------- /client/components/Bubble.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { ResponsiveBubbleHtml } from '@nivo/circle-packing'; 3 | 4 | // make sure parent container have a defined height when using 5 | // responsive component, otherwise height will be 0 and 6 | // no chart will be rendered. 7 | // website examples showcase many properties, 8 | // you'll often use just a few of them. 9 | // check nivo bubble chart documentation to see the boilerplate and properties 10 | 11 | const Bubble = (props) => { 12 | // opens reactjs website. planned to open each techs respective website onClick of bubble 13 | const openTech = () => { 14 | const url = 'https://reactjs.org/'; 15 | window.open(url, '_blank'); 16 | }; 17 | return ( 18 | 38 | ); 39 | }; 40 | 41 | export default Bubble; 42 | -------------------------------------------------------------------------------- /client/style.scss: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,600;0,700;0,800;1,300;1,400;1,600;1,700;1,800&display=swap'); 2 | @import 'variables'; 3 | 4 | body { 5 | font-family: 'Open Sans', sans-serif; 6 | } 7 | 8 | .content { 9 | text-align: center; 10 | } 11 | 12 | .bubbleContainer { 13 | height: 1000px; 14 | transition: margin-left 0.5s; 15 | padding: 20px; 16 | } 17 | 18 | .left-sidebar { 19 | height: 100%; /* 100% Full-height */ 20 | width: 250px; /* 0 width - change this with JavaScript */ 21 | position: fixed; /* Stay in place */ 22 | z-index: 1; /* Stay on top */ 23 | top: 0; /* Stay at the top */ 24 | left: 0; 25 | background-color: $primary; 26 | overflow-x: hidden; /* Disable horizontal scroll */ 27 | padding-top: 60px; /* Place content 60px from the top */ 28 | transition: 0.5s; /* 0.5 second transition effect to slide in the sidenav */ 29 | } 30 | 31 | .left-sidebar summary { 32 | padding: 8px 8px 8px 32px; 33 | text-decoration: none; 34 | font-size: 25px; 35 | color: #ffffff; 36 | display: block; 37 | transition: 0.3s; 38 | background-color: $primary; 39 | border: 1px solid $primary; 40 | outline: none; 41 | } 42 | 43 | .left-sidebar summary:hover { 44 | color: #b0a2d6; 45 | cursor: pointer; 46 | } 47 | 48 | .collapsible-content button { 49 | background-color: $primary; 50 | border: 1px solid $primary; 51 | outline: none; 52 | display: block; 53 | color: #ffffff; 54 | font-size: 17px; 55 | text-decoration: none; 56 | } 57 | 58 | .collapsible-content button:hover { 59 | color: #b0a2d6; 60 | cursor: pointer; 61 | } 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bubble-stack", 3 | "version": "1.0.0", 4 | "description": "bubble-stack", 5 | "main": "index.ts", 6 | "scripts": { 7 | "start": "cross-env NODE_ENV=production webpack & cross-env NODE_ENV=production nodemon server/server.js", 8 | "server": "cross-env NODE_ENV=production webpack", 9 | "dev": "concurrently \"cross-env NODE_ENV=development webpack-dev-server --open --hot\" \"cross-env NODE_ENV=development nodemon ./server/server.js\"", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/Star-Nosed-Mole/Scratch.git" 15 | }, 16 | "author": "", 17 | "license": "ISC", 18 | "bugs": { 19 | "url": "https://github.com/Star-Nosed-Mole/Scratch/issues" 20 | }, 21 | "homepage": "https://github.com/Star-Nosed-Mole/Scratch#readme", 22 | "dependencies": { 23 | "@nivo/circle-packing": "^0.62.0", 24 | "dotenv": "^8.2.0", 25 | "express": "^4.17.1", 26 | "npm-stats-api": "^1.1.3", 27 | "pg": "^8.3.0", 28 | "react": "^16.13.1", 29 | "react-dom": "^16.13.1", 30 | "react-hot-loader": "^4.12.21", 31 | "react-router": "^5.2.0", 32 | "react-router-dom": "^5.2.0", 33 | "typescript": "^3.9.7" 34 | }, 35 | "devDependencies": { 36 | "@babel/core": "^7.10.5", 37 | "@babel/preset-env": "^7.10.4", 38 | "@babel/preset-react": "^7.10.4", 39 | "babel-loader": "^8.1.0", 40 | "concurrently": "^5.2.0", 41 | "cross-env": "^7.0.2", 42 | "css-loader": "^3.6.0", 43 | "nodemon": "^2.0.4", 44 | "sass": "^1.26.10", 45 | "sass-loader": "^9.0.2", 46 | "source-map-loader": "^1.0.1", 47 | "style-loader": "^1.2.1", 48 | "ts-loader": "^8.0.1", 49 | "webpack": "^4.44.0", 50 | "webpack-cli": "^3.3.12", 51 | "webpack-dev-server": "^3.11.0" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /server/routes/api.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const modelsController = require('../controllers/stackControllers'); 3 | const router = express.Router(); 4 | 5 | // Backend API Requests 6 | // GET to '/' 7 | // Retrieve all libraries and properties(types and frameworks) 8 | 9 | // POST to '/getLibrary' { "library": value } 10 | // Retrieve ONE library and its properties 11 | 12 | // GET to '/getTypes' 13 | // Retrieve all types 14 | 15 | // POST to '/getFramework' { "framework": value } 16 | // Retrieve libraries and corresponding types for ONE framework 17 | 18 | // POST to '/addType' { "type": value } 19 | // Add new type to database 20 | 21 | // POST to '/deleteType' { "type": value } 22 | // Remove type from database 23 | 24 | // POST to '/addFramework' { "framework": value } 25 | // Add new framework to database 26 | 27 | // POST to '/deleteFramework' { "framework": value } 28 | // Remove framework from database 29 | 30 | // POST to '/addLibrary' { "library": value, "framework": value, "type": value } 31 | // Add new library to database 32 | 33 | // POST to '/deleteLibrary' { "library": value } 34 | // Remove library from database 35 | 36 | //route get all libraries of all types and frameworks 37 | router.get('/', modelsController.getAll, (req, res) => { 38 | res.status(200).json(res.locals.all); 39 | }); 40 | 41 | // route get ONE specific library's information 42 | router.post( 43 | '/getLibrary', 44 | modelsController.updateLoc, 45 | modelsController.getLibrary, 46 | (req, res) => { 47 | res.status(200).json(res.locals.one); 48 | } 49 | ); 50 | 51 | //route retrieve types 52 | router.get('/getTypes', modelsController.getTypes, (req, res) => { 53 | res.status(200).json(res.locals.types); 54 | }); 55 | 56 | //route retrieve all information for a specific framework 57 | router.post('/getFramework', modelsController.getFramework, (req, res) => { 58 | res.status(200).json(res.locals.framework); 59 | }); 60 | 61 | //REQUESTS TO CREATE/DELETE IN THE DATABASE 62 | //route add type 63 | router.post('/addType', modelsController.addType, (req, res) => { 64 | res.sendStatus(200); 65 | }); 66 | 67 | //route delete type 68 | router.post('/deleteType', modelsController.deleteType, (req, res) => { 69 | res.sendStatus(200); 70 | }); 71 | 72 | //route add framework 73 | router.post('/addFramework', modelsController.addFramework, (req, res) => { 74 | res.sendStatus(200); 75 | }); 76 | 77 | //route delete framework 78 | router.post( 79 | '/deleteFramework', 80 | modelsController.deleteFramework, 81 | (req, res) => { 82 | res.sendStatus(200); 83 | } 84 | ); 85 | 86 | //route add library 87 | router.post('/addLibrary', modelsController.addLibrary, (req, res) => { 88 | res.sendStatus(200); 89 | }); 90 | 91 | //route delete library 92 | router.post('/deleteLibrary', modelsController.deleteLibrary, (req, res) => { 93 | res.sendStatus(200); 94 | }); 95 | 96 | module.exports = router; 97 | -------------------------------------------------------------------------------- /client/components/MakeMap.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Bubble from './Bubble'; 3 | import { findConfigFile } from 'typescript'; 4 | 5 | // import MainContainer from './containers/MainContainer'; 6 | class MakeMap extends Component { 7 | constructor(props) { 8 | super(props); 9 | // bubble chart initial state. 10 | this.state = { 11 | name: 'React', 12 | color: 'hsl(191, 70%, 50%)', 13 | loc: 10000, 14 | children: [], 15 | show: false 16 | }; 17 | 18 | this.getData = this.getData.bind(this); 19 | this.hideBtn = this.hideBtn.bind(this); 20 | } 21 | 22 | // showModal = () => { 23 | // this.setState({ show: true }); 24 | // }; 25 | 26 | // hideModal = () => { 27 | // this.setState({ show: false }); 28 | // }; 29 | 30 | componentDidMount() { 31 | this.hideBtn(); 32 | } 33 | 34 | hideBtn() { 35 | let d = document.getElementById('stack-btn'); 36 | d.style.display = 'none'; 37 | } 38 | // post request passing string of text from onClick in navbar to backend for query 39 | getData(tech) { 40 | fetch('/api/getLibrary', { 41 | method: 'POST', 42 | headers: { 43 | 'Content-Type': 'application/json' 44 | }, 45 | body: JSON.stringify({ library: tech }) 46 | }) 47 | .then((res) => res.json()) 48 | .then((data) => { 49 | // create bubble object to add to bubble charts children array for rendering 50 | let innerBubble = {}; 51 | // assign aliases for name and loc from 52 | let name = data.name; 53 | let loc = data.loc; 54 | // create shallow copy of state's children array to manipulate 55 | let childrenArray = [...this.state.children]; 56 | // boolean flag to check if the tech alreay exists in the children array 57 | let nameExists = false; 58 | for (let i = 0; i < childrenArray.length; i++) { 59 | // if a bubble object already exists with name property (because the user has already clicked on that tech) 60 | if (childrenArray[i].name === data.name) { 61 | nameExists = true; 62 | // remove that element from the array 63 | childrenArray.splice(i, 1); 64 | } 65 | } 66 | // if know tech is found after iterating through children array we will build out the innerBubble object 67 | if (nameExists === false) { 68 | // add properties and/or values to bubble objects 69 | // conditional statements are to assign colors to bubbles 70 | innerBubble.name = name; 71 | innerBubble.description = 'DESCRIPTION'; 72 | innerBubble.loc = loc; 73 | if (data.type === 'state-management') { 74 | innerBubble.color = 'hsl(228, 70%, 50%)'; 75 | } else if (data.type === 'ui-components') { 76 | innerBubble.color = 'hsl(24, 70%, 50%)'; 77 | } else if (data.type === 'router') { 78 | innerBubble.color = 'hsl(156, 70%, 50%)'; 79 | } 80 | // push innerBubble object to childrenArray 81 | childrenArray.push(innerBubble); 82 | } 83 | // update state with childrenArray. 84 | this.setState({ 85 | ...this.state, 86 | children: childrenArray 87 | }); 88 | }); 89 | } 90 | 91 | render() { 92 | return ( 93 |
94 |
95 |
96 | State Management 97 |
    98 | {/* fire getData method onClick and pass in the tech they clicked on in the navbar */} 99 | 100 | 101 | 102 |
103 |
104 | 105 |
106 | UI Components 107 |
    108 | 111 | 114 | 115 |
116 |
117 |
118 | 119 |
120 | {/* pass state down to bubble chart */} 121 | 122 |
123 |
124 | ); 125 | } 126 | } 127 | 128 | export default MakeMap; 129 | -------------------------------------------------------------------------------- /server/controllers/stackControllers.js: -------------------------------------------------------------------------------- 1 | const db = require('../models/stackModels'); 2 | const { stack } = require('../routes/api'); 3 | const npm = require('npm-stats-api'); 4 | 5 | const stackController = {}; 6 | 7 | /* 8 | The npm.stats.api allows us to get the number of npm downloads each tech stack has. 9 | We didn't want to refresh the data and fetch from the api with every get or post request because the numbers are big enough that refreshing every 10 | 5 days would've sufficed. The below is us checking for the last updated day - if it's been 5 or more days since the last update, 11 | we will make a request to the api and store the information as one SQL query into a variable called passing which we will query 12 | in the middleware stockController.updateLoc 13 | */ 14 | // day is last updated day 15 | let day = 11; /* day is currently hard coded in, but need to find a way to update day every time we update, didn't have time to figure this part out */ 16 | // getting today's date 17 | let date = new Date(); 18 | // getting the day of today's date (e.g., if it's July 27, today = 27) 19 | let today = date.getDate(); 20 | // formatting the date to match the syntax required for API parameter 21 | let formattedDate = `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`; 22 | // creating an array to store all the queries we will be creating below 23 | let queryUpdateLibrary = []; 24 | // we will be using the join method to combine the queryUpdateLibrary array elements into one string, so we are declaring the variable of this string here 25 | let passing = ''; 26 | // if it has been 5 or more days since the last update 27 | if (today > day + 5) { 28 | // reassign last updated day to be today's date 29 | day = today; 30 | // SQL query to get all names from the libraries table 31 | const queryGetNames = `SELECT name FROM libraries`; 32 | db.query(queryGetNames) 33 | .then((data) => { 34 | // data.rows comes back as an array of objects (console.log to see what this looks like for reference) so we will iterate through this array 35 | data.rows.forEach((tech) => { 36 | // for each el in array (console.log for reference), we will use the below API method to get the number of downloads and store that as a column called 'loc' in the libraries table 37 | // the parameters required for npm.stat is npm.stat(name of tech - string - e.g., 'redux' , range from date, range to date, callback function) 38 | npm.stat(tech.name, '2020-01-01', `${formattedDate}`, (err, response) => { 39 | // push this query into queryUpdateLibrary - had to divide by 1000 because numbers were too large 40 | queryUpdateLibrary.push(`UPDATE libraries SET loc = ${Math.floor(response.downloads/1000)} WHERE name = '${tech.name}';`) 41 | // concat arr els to be one string 42 | passing = queryUpdateLibrary.join(' '); 43 | }) 44 | }); 45 | }) 46 | .catch((err) => { 47 | return err; 48 | }); 49 | } 50 | // retrieve info from all libraries 51 | stackController.getAll = (req, res, next) => { 52 | const queryAll = `SELECT libraries.name, types.name AS type, framework.name AS framework FROM libraries 53 | INNER JOIN types ON libraries.type_id = types.type_id 54 | INNER JOIN framework ON libraries.framework_id = framework.framework_id;`; 55 | 56 | db.query(queryAll) 57 | .then((data) => { 58 | res.locals.all = data.rows; 59 | return next(); 60 | }) 61 | .catch((err) => { 62 | return next(err); 63 | }); 64 | }; 65 | 66 | // retrieve information for a specific library 67 | stackController.getLibrary = (req, res, next) => { 68 | const name = req.body.library; 69 | const queryOne = `SELECT libraries.name, libraries.loc, types.name AS type, framework.name AS framework FROM libraries 70 | INNER JOIN types ON libraries.type_id = types.type_id 71 | INNER JOIN framework ON libraries.framework_id = framework.framework_id 72 | WHERE libraries.name = '${name}'; `; 73 | db.query(queryOne) 74 | .then((data) => { 75 | console.log(data.rows[0]); 76 | res.locals.one = data.rows[0]; 77 | console.log('RESLOCALSONE ', res.locals.one); 78 | return next(); 79 | }) 80 | .catch((err) => { 81 | return next(err); 82 | }); 83 | }; 84 | 85 | // update loc column in libraries table 86 | stackController.updateLoc = (req, res, next) => { 87 | // console.log('passing', passing); 88 | db.query(passing) 89 | .then(() => { 90 | // console.log('SUCCESS'); 91 | return next(); 92 | }) 93 | .catch((err) => { 94 | return next(err); 95 | }); 96 | }; 97 | 98 | //retrieve types 99 | stackController.getTypes = (req, res, next) => { 100 | const queryTypes = `SELECT * FROM types;`; 101 | 102 | db.query(queryTypes) 103 | .then((data) => { 104 | console.log(data.rows); 105 | res.locals.types = data.rows; 106 | return next(); 107 | }) 108 | .catch((err) => { 109 | return next(err); 110 | }); 111 | }; 112 | 113 | //retrieve all libraries for a specific framework 114 | stackController.getFramework = (req, res, next) => { 115 | const name = req.body.framework; 116 | const queryFramework = `SELECT libraries.name, types.name AS type FROM libraries 117 | INNER JOIN types ON libraries.type_id = types.type_id 118 | INNER JOIN framework ON libraries.framework_id = framework.framework_id 119 | WHERE framework.name = '${name}';`; 120 | 121 | db.query(queryFramework) 122 | .then((data) => { 123 | console.log(data.rows); 124 | res.locals.framework = data.rows; 125 | return next(); 126 | }) 127 | .catch((err) => { 128 | return next(err); 129 | }); 130 | }; 131 | 132 | //add type 133 | stackController.addType = (req, res, next) => { 134 | const type = req.body.type; 135 | const queryType = `INSERT INTO types VALUES ('${type}');`; 136 | 137 | db.query(queryType) 138 | .then((data) => { 139 | return next(); 140 | }) 141 | .catch((err) => { 142 | return next(err); 143 | }); 144 | }; 145 | 146 | //delete type 147 | stackController.deleteType = (req, res, next) => { 148 | const type = req.body.type; 149 | const queryType = `DELETE FROM types WHERE name = '${type}';`; 150 | 151 | db.query(queryType) 152 | .then((data) => { 153 | return next(); 154 | }) 155 | .catch((err) => { 156 | return next(err); 157 | }); 158 | }; 159 | 160 | //add framework 161 | stackController.addFramework = (req, res, next) => { 162 | const framework = req.body.framework; 163 | const queryFramework = `INSERT INTO framework (name) VALUES ('${framework}');`; 164 | 165 | db.query(queryFramework) 166 | .then((data) => { 167 | return next(); 168 | }) 169 | .catch((err) => { 170 | return next(err); 171 | }); 172 | }; 173 | 174 | //delete framework 175 | stackController.deleteFramework = (req, res, next) => { 176 | const framework = req.body.framework; 177 | const queryFramework = `DELETE FROM framework WHERE name = '${framework}';`; 178 | 179 | db.query(queryFramework) 180 | .then((data) => { 181 | return next(); 182 | }) 183 | .catch((err) => { 184 | return next(err); 185 | }); 186 | }; 187 | 188 | //add library 189 | stackController.addLibrary = (req, res, next) => { 190 | const library = req.body.library; 191 | const framework = req.body.framework; 192 | const type = req.body.type; 193 | const queryLibrary = `INSERT INTO libraries (name, framework_id, type_id) VALUES ('${library}', 194 | (SELECT framework_id FROM framework WHERE framework.name = '${framework}'), 195 | (SELECT type_id FROM types WHERE types.name = '${type}'));`; 196 | 197 | db.query(queryLibrary) 198 | .then((data) => { 199 | return next(); 200 | }) 201 | .catch((err) => { 202 | return next(err); 203 | }); 204 | }; 205 | 206 | //delete library 207 | stackController.deleteLibrary = (req, res, next) => { 208 | const library = req.body.library; 209 | const queryLibrary = `DELETE FROM libraries WHERE name = '${library}';`; 210 | 211 | db.query(queryLibrary) 212 | .then((data) => { 213 | return next(); 214 | }) 215 | .catch((err) => { 216 | return next(err); 217 | }); 218 | }; 219 | 220 | module.exports = stackController; 221 | --------------------------------------------------------------------------------