├── .babelrc
├── .editorconfig
├── .eslintrc.json
├── .gitignore
├── .npmignore
├── Contribution.md
├── LICENSE
├── README.md
├── main.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── server
├── plugins
│ └── tracker.js
├── server.js
└── utils
│ ├── injectPaths.js
│ └── updatePackage.js
├── src
├── assets
│ ├── bypass.gif
│ ├── history.png
│ ├── metrics2.png
│ ├── performance.png
│ ├── q-logo-blk.png
│ ├── q-logo.png
│ ├── qleo-lion.png
│ ├── qleo-logo.png
│ └── schema.png
├── components
│ ├── App.jsx
│ ├── FileUpload.jsx
│ ├── GraphData.jsx
│ ├── HistoryItem.jsx
│ ├── Navbar.jsx
│ ├── QueryInput.jsx
│ ├── ResolversPerformance.jsx
│ ├── Schema.jsx
│ ├── deprecated
│ │ └── deprecated.jsx
│ ├── resolverDetailsComponent
│ │ ├── Dropdown.jsx
│ │ └── ResolverDetails.jsx
│ └── schemaComponents
│ │ ├── FieldItem.jsx
│ │ └── Root.jsx
├── containers
│ ├── HistoryContainer.jsx
│ ├── MainContainer.jsx
│ ├── PerformanceContainer.jsx
│ └── UploadContainer.jsx
├── css
│ └── App.css
├── index.jsx
└── utils
│ └── links.js
├── tailwind.config.js
├── webpack.build.config.js
└── webpack.dev.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-react",
4 | "@babel/preset-env"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | end_of_line = crlf
8 | indent_size = 2
9 | indent_style = space
10 | insert_final_newline = true
11 | trim_trailing_whitespace = true
12 |
13 | [*.md]
14 | trim_trailing_whitespace = false
15 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "ignorePatterns": ["**/test", "**/__tests__"],
4 | "env": {
5 | "browser": true,
6 | "node": true,
7 | "es2021": true
8 | },
9 | "plugins": ["import", "react", "jsx-a11y"],
10 | "extends": [
11 | "eslint:recommended",
12 | "plugin:import/errors",
13 | "plugin:react/recommended",
14 | "plugin:jsx-a11y/recommended"
15 | ],
16 | "parserOptions": {
17 | "ecmaFeatures": {
18 | "jsx": true
19 | },
20 | "sourceType": "module"
21 | },
22 | "rules": {
23 | "indent": ["warn", 2, { "SwitchCase": 1 }],
24 | "no-unused-vars": ["off", { "vars": "local" }],
25 | "prefer-const": "warn",
26 | "quotes": ["warn", "single"],
27 | "semi": ["warn", "always"],
28 | "space-infix-ops": "warn",
29 | "react/prefer-stateless-function": "off",
30 | "react/prop-types": "off",
31 | "react/jsx-key": "warn"
32 | },
33 | "settings": {
34 | "react": {
35 | "version": "detect"
36 | },
37 | "import/resolver": {
38 | "node": {
39 | "paths": ["./src"],
40 | "extensions": [".js", ".jsx"]
41 | },
42 | "webpack": "webpack.dev.config.js"
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build folder and files #
2 | ##########################
3 | builds/
4 |
5 | # Development folders and files #
6 | #################################
7 | .tmp/
8 | dist/
9 | node_modules/
10 | *.compiled.*
11 |
12 | # Folder config file #
13 | ######################
14 | Desktop.ini
15 |
16 | # Folder notes #
17 | ################
18 | _ignore/
19 |
20 | # Log files & folders #
21 | #######################
22 | logs/
23 | *.log
24 | npm-debug.log*
25 | .npm
26 |
27 | # Packages #
28 | ############
29 | # it's better to unpack these files and commit the raw source
30 | # git has its own built in compression methods
31 | *.7z
32 | *.dmg
33 | *.gz
34 | *.iso
35 | *.jar
36 | *.rar
37 | *.tar
38 | *.zip
39 |
40 | # Photoshop & Illustrator files #
41 | #################################
42 | *.ai
43 | *.eps
44 | *.psd
45 |
46 | # Windows & Mac file caches #
47 | #############################
48 | .DS_Store
49 | Thumbs.db
50 | ehthumbs.db
51 |
52 | # Windows shortcuts #
53 | #####################
54 | *.lnk
55 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | _ignore/
2 | docs/
3 | builds/
4 | dist/
5 | .editorconfig
6 | code-of-conduct.md
7 |
--------------------------------------------------------------------------------
/Contribution.md:
--------------------------------------------------------------------------------
1 | ## Contributing
2 |
3 | Thank you for your interest and passion in contributing to QLeo.
4 |
5 |
6 |
7 | ## Reporting Bugs
8 |
9 | Bugs are tracked as GitHub issues. Create an issue on our repository and provide the following information:
10 |
11 | - Please provide a clear and descriptive title for your issue.
12 | - Describe the steps you took to handle the problem. If possible, provide specific code or screenshots of the problem.
13 | - Describe the expected behavior and why.
14 | - Details about your configuration and environment.
15 |
16 |
17 |
18 | ## Versioning:
19 |
20 | Any improvements are tracked as Pull Requests. Create a pull request on our repository following the guidelines:
21 |
22 | - Fork QLeo’s repository and create a new branch from Main.
23 | - Make your changes in the new branch.
24 | - Run your code through our linter and ensure it passes.
25 | - Issue a pull request!
26 |
27 | ## License
28 |
29 | By contribution, you agree that your contributions will be licensed under MIT License.
30 |
31 |
32 |
33 | ## Thank You
34 |
35 | Our team will be working closely together to continue to improve QLeo and are willing to receive any feedback and assistance :)
36 |
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 OSLabs Beta
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 |
2 |
3 |
4 | QLeo
5 |
6 |
7 |
A Visual Performance Tracking Tool for GraphQL
8 |
9 |
10 |
11 | ## What is QLeo?
12 |
13 | QLeo is designed to be used in a development environment. We provide a simple and intuitive interface with graphical representations of key metrics. This allows developers to debug and tune performance for inefficient GraphQL requests.
14 |
15 | QLeo is in active development. Please follow to the end of the repository for contribution guidelines.
16 |
17 |
18 |
19 | ## Features
20 |
21 | - Easy Configuration: With just a click of a button, QLeo will start to run and allow users to generate performance metrics according to the uploaded schema model.
22 | - Montior Queries at Resolver-Level: Understand and track GraphQL queries, mutations, response times, and function invocations for individual resolvers.
23 | - Visualize: Compare and reveal potential performance bottlenecks utilizing QLeo's illustration to enhance the efficiency of GraphQL API calls.
24 | - History: Keep record of GraphQL request's performance metrics and graphs in the current session.
25 |
26 |
27 |
28 |
29 |
30 |
31 | ## Installation
32 |
33 | QLeo runs on Electron as a native desktop application.
34 | The instructions for MacOS users (Intel & M1) are as follows:
35 |
36 | 1. Visit the QLeo Official [Website](https://qleo.app) or click [here](https://github.com//oslabs-beta/QLeo/releases/download/v1.0.0/QLeo.zip) to download!
37 | 2. Open the application and give access permissions.
38 |
39 |
40 |
41 | 3. And that’s it! QLeo is all set and ready to start collecting performance data for any GraphQL requests.
42 |
43 |
44 |
45 | ## How to Use
46 |
47 | 1. Open application and upload a schema file.
48 | 2. Navigate to the Dashboard tab - where a schema model, a code editor, and a Metrics directory will be displayed.
49 | 3. The schema model and types will be available to view on the left panel for easy reference.
50 |
51 |
52 |
53 | 4. Write up a GraphQL query and/or mutation in QLeo’s code editor, then press 'Submit'.
54 | 5. QLeo will start to gather and display the data on the Metrics directory on the right panel.
55 | - It will display the total query response time.
56 | - It will also display a breakdown of resolvers:
57 | - Toggle the 'Show Details' to view corresponding performance and metrics.
58 |
59 |
60 |
61 |
62 | 6. Navigate to the Performance tab - View your resolver's performance with graphical representation.
63 |
64 |
65 | 7. All previous requests will be saved in the History tab - Refer back to any of the request's performance metrics by simply selecting the corresponding query that was made.
66 |
67 |
68 |
69 |
70 |
71 | ## Technologies Involved
72 |
73 | - Electron
74 | - Javascript ES6+
75 | - Apollo GraphQL
76 | - Node.js
77 | - React
78 | - Webpack
79 | - Tailwind CSS
80 | - CodeMirror Code Editor
81 |
82 |
83 | ## Contributing
84 |
85 | Interested in making a contribution to QLeo? Click [here](https://github.com/oslabs-beta/QLeo/blob/main/Contribution.md) for our open-source contribution guidelines.
86 |
87 |
88 |
89 | Visit our [website](https://qleo.app) for more information.
90 |
91 |
92 |
93 | ## Our Team
94 |
95 | * Andrew Talle [Github](https://github.com/ogAndrew) || [LinkedIn](https://www.linkedin.com/in/andrewtalle/)
96 | * Chon Hou Ho [Github](https://github.com/chon-h) || [LinkedIn](https://www.linkedin.com/in/chon-hou-ho/)
97 | * Irine Kang [Github](https://github.com/irinekangg) || [LinkedIn](https://www.linkedin.com/in/irinekang/)
98 | * Jack Fitzgerald [Github](https://github.com/jcf7) || [LinkedIn](https://www.linkedin.com/in/jcf7/)
99 |
100 |
101 |
102 |
103 | ## Contact
104 |
105 |
106 | Email: TeamQLeo@gmail.com
107 |
108 | Twitter: [@QLeo.App](https://twitter.com/QLeo.App)
109 |
110 | Website: [qleo.app](https://qleo.app)
111 |
112 | GitHub: [https://github.com/oslabs-beta/QLeo/](https://github.com/oslabs-beta/QLeo)
113 |
114 |
115 |
116 |
117 | ## License
118 |
119 | Distributed under the MIT License. See [`LICENSE`](https://github.com/oslabs-beta/QLeo/blob/main/LICENSE) for more information.
120 |
121 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const { app, BrowserWindow } = require('electron');
4 | const path = require('path');
5 | const url = require('url');
6 | const { exec } = require('child_process');
7 | const { Server } = require('http');
8 | const server = require('./server/server');
9 |
10 | let mainWindow;
11 |
12 | // Keep a reference for dev mode
13 | let dev = false;
14 |
15 | // Broken:
16 | // if (process.defaultApp || /[\\/]electron-prebuilt[\\/]/.test(process.execPath) || /[\\/]electron[\\/]/.test(process.execPath)) {
17 | // dev = true
18 | // }
19 |
20 | if (process.env.NODE_ENV !== undefined && process.env.NODE_ENV === 'development') {
21 | dev = true;
22 | }
23 |
24 | // Temporary fix broken high-dpi scale factor on Windows (125% scaling)
25 | // info: https://github.com/electron/electron/issues/9691
26 | if (process.platform === 'win32') {
27 | app.commandLine.appendSwitch('high-dpi-support', 'true');
28 | app.commandLine.appendSwitch('force-device-scale-factor', '1');
29 | }
30 |
31 | function createWindow() {
32 | // Create the browser window.
33 | mainWindow = new BrowserWindow({
34 | width: 1024,
35 | height: 768,
36 | show: false,
37 | webPreferences: {
38 | nodeIntegration: true,
39 | contextIsolation: false
40 | }
41 | });
42 |
43 | // and load the index.html of the app.
44 | let indexPath;
45 |
46 | if (dev && process.argv.indexOf('--noDevServer') === -1) {
47 | indexPath = url.format({
48 | protocol: 'http:',
49 | host: 'localhost:8080',
50 | pathname: 'index.html',
51 | slashes: true
52 | });
53 | } else {
54 | indexPath = url.format({
55 | protocol: 'file:',
56 | pathname: path.join(__dirname, 'dist', 'index.html'),
57 | slashes: true
58 | });
59 | }
60 |
61 | mainWindow.loadURL(indexPath);
62 |
63 | // Don't show until we are ready and loaded
64 | mainWindow.once('ready-to-show', () => {
65 | mainWindow.show();
66 |
67 | // Open the DevTools automatically if developing
68 | if (dev) {
69 | const { default: installExtension, REACT_DEVELOPER_TOOLS } = require('electron-devtools-installer');
70 |
71 | installExtension(REACT_DEVELOPER_TOOLS)
72 | .catch(err => console.log('Error loading React DevTools: ', err));
73 | mainWindow.webContents.openDevTools();
74 | }
75 | });
76 |
77 |
78 | // Emitted when the window is closed.
79 | mainWindow.on('closed', function() {
80 | // Dereference the window object, usually you would store windows
81 | // in an array if your app supports multi windows, this is the time
82 | // when you should delete the corresponding element.
83 | mainWindow = null;
84 | });
85 | }
86 |
87 | // This method will be called when Electron has finished
88 | // initialization and is ready to create browser windows.
89 | // Some APIs can only be used after this event occurs.
90 | app.on('ready', ()=>{
91 | createWindow();
92 | });
93 |
94 | // Quit when all windows are closed.
95 | app.on('window-all-closed', () => {
96 | // On macOS it is common for applications and their menu bar
97 | // to stay active until the user quits explicitly with Cmd + Q
98 | if (process.platform !== 'darwin') {
99 | app.quit();
100 | server.close();
101 | }
102 | });
103 |
104 | app.on('activate', () => {
105 | // On macOS it's common to re-create a window in the app when the
106 | // dock icon is clicked and there are no other windows open.
107 | if (mainWindow === null) {
108 | createWindow();
109 | }
110 | });
111 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "qleo",
3 | "productName": "QLeo",
4 | "version": "1.0.0",
5 | "description": "Electron app for QLeo graphQL performance tool",
6 | "main": "main.js",
7 | "repository": {
8 | "type": "git",
9 | "url": "git+https://github.com/oslabs-beta/QLeo"
10 | },
11 | "author": "",
12 | "license": "ISC",
13 | "bugs": {
14 | "url": "https://github.com/oslabs-beta/QLeo/issues"
15 | },
16 | "homepage": "https://github.com/oslabs-beta/QLeo",
17 | "keywords": [
18 | "app",
19 | "electron",
20 | "open",
21 | "open-source",
22 | "react",
23 | "reactjs",
24 | "webpack"
25 | ],
26 | "engines": {
27 | "node": ">=9.0.0",
28 | "npm": ">=5.0.0"
29 | },
30 | "browserslist": [
31 | "last 4 versions"
32 | ],
33 | "scripts": {
34 | "prod": "cross-env NODE_ENV=production webpack --mode production --config webpack.build.config.js && electron --noDevServer .",
35 | "start": "cross-env NODE_ENV=development webpack serve --hot --host 0.0.0.0 --config=./webpack.dev.config.js --mode development",
36 | "build": "cross-env NODE_ENV=production webpack --config webpack.build.config.js --mode production",
37 | "package": "npm run build",
38 | "postpackage": "electron-packager ./ --out=./builds",
39 | "lint": "eslint --fix --ext .js,.jsx .",
40 | "server": "nodemon server/server.js",
41 | "package-linux": "electron-packager . qleo --overwrite --asar=true --platform=linux --arch=x64 --prune=true --out=release-builds",
42 | "package-win": "electron-packager . --overwrite --platform=win32 --arch=ia32 --prune=false --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName='QLeo'"
43 | },
44 | "dependencies": {
45 | "@apollo/client": "^3.5.8",
46 | "@codemirror/theme-one-dark": "^0.19.1",
47 | "@fortawesome/fontawesome-svg-core": "^1.3.0",
48 | "@fortawesome/free-solid-svg-icons": "^6.0.0",
49 | "@fortawesome/react-fontawesome": "^0.1.17",
50 | "@uiw/react-codemirror": "^4.3.3",
51 | "apollo-server-express": "^3.6.3",
52 | "chart.js": "^3.7.0",
53 | "codemirror": "^5.65.1",
54 | "codemirror-graphql": "^1.2.11",
55 | "cors": "^2.8.5",
56 | "express": "^4.17.3",
57 | "graphql": "^15.8.0",
58 | "graphql-tools": "^8.2.0",
59 | "lodash": "^4.17.21",
60 | "react": "^17.0.2",
61 | "react-chartjs-2": "^4.0.1",
62 | "react-dom": "^17.0.2",
63 | "react-router-dom": "^6.2.1"
64 | },
65 | "devDependencies": {
66 | "@babel/core": "^7.16.5",
67 | "@babel/preset-env": "^7.16.5",
68 | "@babel/preset-react": "^7.16.5",
69 | "autoprefixer": "^10.4.2",
70 | "babel-loader": "^8.2.3",
71 | "cross-env": "^7.0.3",
72 | "css-loader": "^6.5.1",
73 | "electron": "^16.0.8",
74 | "electron-devtools-installer": "^3.2.0",
75 | "electron-packager": "^15.4.0",
76 | "eslint": "^8.8.0",
77 | "eslint-config-airbnb": "^19.0.4",
78 | "eslint-import-resolver-webpack": "^0.13.2",
79 | "file-loader": "^6.2.0",
80 | "html-webpack-plugin": "^5.5.0",
81 | "mini-css-extract-plugin": "^2.4.5",
82 | "nodemon": "^2.0.15",
83 | "postcss-loader": "^6.2.1",
84 | "style-loader": "^3.3.1",
85 | "tailwindcss": "^3.0.19",
86 | "webpack": "^5.65.0",
87 | "webpack-cli": "^4.9.1",
88 | "webpack-dev-server": "^3.11.3"
89 | },
90 | "resolutions": {
91 | "graphql": "^15.6.1",
92 | "**/graphql": "^15.6.1"
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/server/plugins/tracker.js:
--------------------------------------------------------------------------------
1 | const _ = require('lodash');
2 |
3 | // a customize function that handle array and number for lodash deep merge
4 | function customizer(objValue, srcValue) {
5 | // concat the two array if it is an array
6 | if (_.isArray(objValue)) return objValue.concat(srcValue);
7 | // sum it up if it is a number
8 | else if (typeof objValue === 'number') return objValue + srcValue;
9 | }
10 |
11 | module.exports = {
12 | // When the quest is received
13 | requestDidStart(requestContext) {
14 | // store the start time of the request
15 | const requestStart = Date.now();
16 | // create a placeholder for the performance data
17 | const performanceData = {};
18 |
19 | return {
20 | // Extract the performance of each resolver using the even cycle
21 | executionDidStart(){
22 | return {
23 | willResolveField({info}) {
24 | // store the start time of the resolver
25 | const start = performance.now();
26 |
27 | return (error, result) => {
28 | // error handler
29 | if (error) return console.log(`It failed with ${error}`);
30 |
31 | // store the end time of the resolver
32 | const end = performance.now();
33 |
34 | // create a placeholder for the performance of the individual resolver
35 | let itemPerformance = {};
36 |
37 | // initialize the performance data of resolver
38 | itemPerformance[info.fieldName] = {'time': [], trips: 0};
39 | itemPerformance[info.fieldName].time.push(end - start);
40 | itemPerformance[info.fieldName].trips += 1;
41 |
42 | // create a placeholder that we can use to traverse over the info object
43 | let current = info.path;
44 |
45 | // create the resulting object for performance that we want to send back to our front end
46 | while (current !== undefined) {
47 | if (current.typename !== undefined){
48 | const temp = {};
49 | temp[current.typename] = itemPerformance;
50 | itemPerformance = temp;
51 | }
52 | current = current.prev;
53 | }
54 |
55 | // merge the individual resolver performance object with the exisiting performance object
56 | _.mergeWith(performanceData, itemPerformance, customizer);
57 | };
58 | },
59 | };
60 | },
61 | // Customize data before sending back to client
62 | willSendResponse (context) {
63 | // record the request completion time
64 | const stop = Date.now();
65 | // compute the time spent to resolve request
66 | const elapsed = stop - requestStart;
67 | // compute the size of the returned response data in bytes
68 | const size = JSON.stringify(context.response).length * 2;
69 | // store the time spent to resolve request in performance data
70 | performanceData.queryTime = elapsed;
71 | // attach performance data to the response that get sent back to client
72 | context.response.extensions = {performanceData};
73 | }
74 | };
75 | },
76 | };
--------------------------------------------------------------------------------
/server/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const { ApolloServer } = require('apollo-server-express');
3 | const plugins = require('./plugins/tracker');
4 | const { updatePackageJson } = require('./utils/updatePackage');
5 | const cors = require('cors');
6 | const { fstat } = require('fs');
7 | const fs = require('fs');
8 | const { injectRequire } = require('./utils/injectPaths');
9 |
10 | // Set port where we launch the server
11 | const PORT = 3000;
12 |
13 | // Create express app
14 | const app = express();
15 |
16 | // Allow cors
17 | app.use(cors());
18 |
19 | // Handle parsing request body
20 | app.use(express.json());
21 | app.use(express.urlencoded({ extended: true }));
22 |
23 |
24 | // Handler for upload request
25 | app.post('/upload', async (req, res) => {
26 | // extract the filepath
27 | const filePath = req.body.pathName;
28 |
29 | // if it is a package.json file
30 | if (filePath.includes('package')) {
31 | try {
32 | // update our own package.json file
33 | updatePackageJson(filePath);
34 | // send a 200 response if succeeds
35 | res.status(200).json('upload endpoint');
36 | } catch (e) {
37 | // send a 500 response if an error occur
38 | res.status(500).json(e);
39 | }
40 | }
41 | // else if it is a schema file
42 | else {
43 |
44 | try {
45 |
46 |
47 |
48 | const schema = injectRequire(filePath);
49 |
50 | // start the apollo server with the passed in schema
51 | startServer(require(filePath));
52 |
53 | // revert back the changes that we did earlier
54 | console.log('new server start');
55 |
56 |
57 | // send a response with 200 if succeeds
58 | res.status(200).json('upload endpoints');
59 | } catch (e) {
60 | // send a 500 response if an error occur
61 | res.status(500).json(e);
62 | }
63 | }
64 | });
65 |
66 | // helper function for starting the server
67 | async function startServer(schema) {
68 | // first clean up the previous apollo graphql router if it exists
69 | const routes = app._router.stack;
70 | routes.forEach((route, i) => {
71 | if (route.handle.name === 'router') {
72 | routes.splice(i, 1);
73 | }
74 | });
75 |
76 | // create a new apollo server with the given schema and the performance plugin we created
77 | const server = new ApolloServer({
78 | schema,
79 | plugins: [
80 | plugins,
81 | ],
82 | });
83 |
84 | // start the apollo server
85 | await server.start();
86 |
87 | // apply the graphql server a middleware to the express app
88 | server.applyMiddleware( {app, path:'/graphql'} );
89 | }
90 |
91 | // start the express app on the above port
92 | app.listen(PORT, ()=> {
93 | console.log(`Server listening on port: ${PORT}...`);
94 | });
95 |
96 | module.export = app;
97 |
--------------------------------------------------------------------------------
/server/utils/injectPaths.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 |
3 | // function closure() {
4 | // const underlyingPath = module.paths.slice(0,3);
5 | // return function customizedRequire(filePath) {
6 | // module.paths = underlyingPath.concat(module.paths);
7 | // console.log(module.paths);
8 | // console.log(filePath);
9 | // if (filePath[0] !== '.' && filePath[0] !== '/') {
10 | // console.log('i am here');
11 | // const requiredModule = require(filePath);
12 | // return requiredModule;
13 | // }
14 | // console.log('i am there');
15 | // const requiredModule = require(filePath);
16 | // return requiredModule;
17 | // };
18 | // }
19 |
20 | // const customizedRequire = closure();
21 |
22 | // function injectRequire(filePath) {
23 | // if (filePath[0] !== '.' && filePath[0] !== '/') return customizedRequire(filePath);
24 | // console.log(filePath);
25 | // const newPath = require.resolve(filePath);
26 |
27 | // const data = fs.readFileSync(newPath, 'utf8');
28 |
29 | // const thisPath = module.filename;
30 | // // console.log(thisPath);
31 | // const injection = `const injectRequire = require('${thisPath}');\n`;
32 | // const modifyData = data.replaceAll('require(', 'injectRequire(');
33 | // const newData = injection + modifyData;
34 | // fs.writeFileSync(newPath, newData);
35 | // const requiredModule = customizedRequire(newPath);
36 | // console.log('I am there twice');
37 | // fs.writeFileSync(newPath, data);
38 | // return requiredModule;
39 | // }
40 |
41 |
42 | const underlyingPath = module.paths.slice(0,3);
43 |
44 | function injectRequire(filePath) {
45 | // if it is not a relative path/absolute path, require and return the module
46 | if (filePath[0] !== '.' && filePath[0] !== '/') {
47 | console.log(filePath, require.resolve(filePath));
48 | const module = require(filePath);
49 | return module;
50 | }
51 |
52 | // get the absolute file path
53 | const newPath = require.resolve(filePath);
54 |
55 | // read the file and store it's content in data
56 | const data = fs.readFileSync(newPath, 'utf8');
57 |
58 | // create the injections lines
59 | const createPath = `const underlyingPath = ['${underlyingPath[0]}', '${underlyingPath[1]}', '${underlyingPath[2]}']; \n`;
60 | const addPathToModule = 'module.paths = underlyingPath.concat(module.paths); \n';
61 | const injectFunction = `const fs = require('fs'); \n${injectRequire} \n`;
62 | const modifyData = data.replaceAll('require(', 'injectRequire(');
63 |
64 | // update the file with the injection lines attach at the beginning
65 | const newData = createPath + addPathToModule + injectFunction + modifyData;
66 | fs.writeFileSync(newPath, newData);
67 |
68 | // require the updated file
69 | const requiredModule = require(newPath);
70 |
71 | // revert the changes made to the file
72 | fs.writeFileSync(newPath, data);
73 |
74 | // return the required file
75 | return requiredModule;
76 | }
77 |
78 | module.exports = {
79 | injectRequire
80 | };
--------------------------------------------------------------------------------
/server/utils/updatePackage.js:
--------------------------------------------------------------------------------
1 | const { exec } = require('child_process');
2 | const path = require('path');
3 | const fs = require('fs');
4 |
5 | // function for updating our npm packages according to the end user's need
6 | function updatePackageJson(filePath) {
7 | try {
8 | // read the passed in package.json file
9 | const data = fs.readFileSync(filePath, 'utf8');
10 | // get the dependencies and devdependencies object
11 | const { dependencies, devDependencies } = JSON.parse(data);
12 |
13 | // read our own package.json file
14 | const currFile = fs.readFileSync(path.resolve('./package.json'), 'utf-8');
15 | // turn it into an object
16 | const parsedCurrFile = JSON.parse(currFile);
17 |
18 | // get all the keys of the previously defined objects
19 | const dependenciesArr = Object.keys(dependencies);
20 | const devDependenciesArr = Object.keys(devDependencies);
21 | const currDependencies = Object.keys(parsedCurrFile.dependencies);
22 | const currDevDependencies = Object.keys(parsedCurrFile.devDependencies);
23 |
24 | // get all the npm packges under dependencies that are in user's package.json file but not in ours
25 | const newDependencies = dependenciesArr.filter(item => {
26 | if (!currDependencies.includes(item)) return item;
27 | });
28 |
29 | // get all the npm packges under devDependencies that are in user's package.json file but not in ours
30 | const newDevDependencies = devDependenciesArr.filter(item => {
31 | if (!currDevDependencies.includes(item)) return item;
32 | });
33 |
34 | // create the terminal command to install all of the missing dependencies/devDependencies
35 | const npmInstallStr = 'npm install ' + newDependencies.join(' ');
36 | const npmDevInstallStr = 'npm install -D ' + newDevDependencies.join(' ');
37 |
38 | // execute the commands
39 | exec(npmInstallStr);
40 | exec(npmDevInstallStr);
41 |
42 | } catch (err) {
43 | console.error(err);
44 | }
45 | return;
46 | }
47 |
48 | module.exports = {
49 | updatePackageJson
50 | };
--------------------------------------------------------------------------------
/src/assets/bypass.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/QLeo/29d447c885f0c0cd7d28ce67f28681af1c971143/src/assets/bypass.gif
--------------------------------------------------------------------------------
/src/assets/history.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/QLeo/29d447c885f0c0cd7d28ce67f28681af1c971143/src/assets/history.png
--------------------------------------------------------------------------------
/src/assets/metrics2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/QLeo/29d447c885f0c0cd7d28ce67f28681af1c971143/src/assets/metrics2.png
--------------------------------------------------------------------------------
/src/assets/performance.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/QLeo/29d447c885f0c0cd7d28ce67f28681af1c971143/src/assets/performance.png
--------------------------------------------------------------------------------
/src/assets/q-logo-blk.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/QLeo/29d447c885f0c0cd7d28ce67f28681af1c971143/src/assets/q-logo-blk.png
--------------------------------------------------------------------------------
/src/assets/q-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/QLeo/29d447c885f0c0cd7d28ce67f28681af1c971143/src/assets/q-logo.png
--------------------------------------------------------------------------------
/src/assets/qleo-lion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/QLeo/29d447c885f0c0cd7d28ce67f28681af1c971143/src/assets/qleo-lion.png
--------------------------------------------------------------------------------
/src/assets/qleo-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/QLeo/29d447c885f0c0cd7d28ce67f28681af1c971143/src/assets/qleo-logo.png
--------------------------------------------------------------------------------
/src/assets/schema.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oslabs-beta/QLeo/29d447c885f0c0cd7d28ce67f28681af1c971143/src/assets/schema.png
--------------------------------------------------------------------------------
/src/components/App.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react';
2 | import {
3 | Link,
4 | HashRouter as Router,
5 | Routes,
6 | Route
7 | } from 'react-router-dom';
8 | import NavBar from './Navbar';
9 | import MainContainer from '../containers/MainContainer';
10 | import UploadContainer from '../containers/UploadContainer';
11 | import PerformanceContainer from '../containers/PerformanceContainer';
12 | import HistoryContinaer from '../containers/HistoryContainer';
13 |
14 | import '../css/App.css';
15 |
16 | function App() {
17 | const [query, setQuery] = useState('');
18 | const [metrics, setMetrics] = useState({});
19 | const [schema, setSchema] = useState(false);
20 | const [history, setHistory] = useState([]);
21 |
22 | return (
23 |