├── .babelrc ├── .env ├── .gitignore ├── LICENSE ├── README.md ├── assets ├── PeachLogo.png ├── PeachQE-3.png ├── PeachQE.png ├── PeachQE_Install_Guide.gif ├── icons │ ├── mac │ │ └── icon.icns │ ├── png │ │ └── icon.png │ └── win │ │ └── icon.ico └── readme_imgs │ ├── Peach-Mode-Example.jpeg │ └── Relay-Proper-Example.jpeg ├── electron ├── menu.js └── newWindow.js ├── html ├── addWindow.html ├── peachWindow.html └── peachWindow2.html ├── main.js ├── package-lock.json ├── package.json ├── queryHistory.json ├── schema.graphql ├── src ├── App.js ├── __generated__ │ └── AppQuery.graphql.js ├── components │ ├── Downloader │ │ └── index.js │ ├── ErrorBoundary.js │ ├── Footer.js │ ├── ImportedMode │ │ ├── TestUpload.js │ │ ├── Uploader.js │ │ ├── index.js │ │ ├── renderer.js │ │ ├── styles │ │ │ └── uploader.css │ │ ├── upload-files.component.js │ │ └── watcher.js │ ├── Logo.js │ ├── Navbar.js │ ├── QueryContainer │ │ ├── History.js │ │ └── index.js │ ├── QueryEditor │ │ └── index.js │ ├── QuerySelector │ │ ├── QueryButton.js │ │ └── index.js │ ├── ResponseDisplay │ │ ├── ImportedResponseDisplay.js │ │ ├── Response.js │ │ └── WrittenResponseDisplay.js │ ├── SchemaDisplay │ │ ├── SchemaDisplayContainer.js │ │ ├── SchemaSearch.js │ │ └── index.js │ ├── SchemaDownload │ │ ├── FileDownloader.js │ │ ├── InputGqlSchema.js │ │ ├── Modal.js │ │ └── SchemaUrlInput.js │ ├── StoreDisplay │ │ └── StoreDisplay.js │ ├── VariableInput │ │ └── index.js │ ├── hooks │ │ ├── http-common.js │ │ ├── upload-files.service.js │ │ ├── uploaderHook.js │ │ └── useFileDownloader.js │ └── icon.png ├── database │ ├── db.js │ ├── importedHistory.json │ ├── queryHistory.json │ └── schemaHistory.json ├── index.js ├── relay │ ├── RelayEnvironment.js │ ├── __generated__ │ │ ├── importedLongMediaQuery.graphql.js │ │ ├── importedMediaQuery.graphql.js │ │ ├── importedThreadQuery.graphql.js │ │ └── writtenQuery.graphql.js │ ├── aliasID.js │ ├── fetchGraphQL.js │ ├── gqlEndpoint.js │ ├── imported.js │ ├── makeJsonSchema.js │ └── written.js └── styles │ ├── App.css │ ├── Modal.css │ ├── downloader.css │ └── styles.css ├── webpack.build.config.js └── webpack.dev.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-react", "@babel/preset-env"], 3 | "plugins": [ 4 | ["macros"], 5 | ["relay"], 6 | ["@babel/plugin-transform-runtime", { 7 | "regenerator": true 8 | }], 9 | ["wildcard"] 10 | ], 11 | "ignore": [ 12 | "_generated_" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | SKIP_PREFLIGHT_CHECK=true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build folder and files # 2 | ########################## 3 | release-builds/ 4 | 5 | # Development folders and files # 6 | ################################# 7 | .tmp/ 8 | dist/ 9 | node_modules/ 10 | package-lock.json 11 | *.compiled.* 12 | 13 | # Folder config file # 14 | ###################### 15 | Desktop.ini 16 | 17 | # Folder notes # 18 | ################ 19 | _ignore/ 20 | 21 | # Log files & folders # 22 | ####################### 23 | logs/ 24 | *.log 25 | npm-debug.log* 26 | .npm 27 | 28 | # Packages # 29 | ############ 30 | # it's better to unpack these files and commit the raw source 31 | # git has its own built in compression methods 32 | *.7z 33 | *.dmg 34 | *.gz 35 | *.iso 36 | *.jar 37 | *.rar 38 | *.tar 39 | *.zip 40 | 41 | # Photoshop & Illustrator files # 42 | ################################# 43 | *.ai 44 | *.eps 45 | *.psd 46 | 47 | # Windows & Mac file caches # 48 | ############################# 49 | .DS_Store 50 | Thumbs.db 51 | ehthumbs.db 52 | 53 | # Windows shortcuts # 54 | ##################### 55 | *.lnk 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 |

4 | 5 | Logo 6 | 7 | 8 |

README

9 | 10 |

11 | 12 | GitHub contributors 13 | license 14 | last commit 15 | Repo stars 16 |

17 | 18 |

19 |

Peach QE

20 |

21 | A Relay visualizer for all your GraphQL queries. 22 |
23 | Homepage →
24 | Product Hunt Page → 25 |

26 |
27 | Explore the docs » 28 |
29 | 35 |

36 |

37 | 38 |
39 |
40 | 41 |

42 | Logo 43 |

44 | 45 |
46 |
47 | 48 | 49 |
50 | Table of Contents 51 |
    52 |
  1. 53 | About The Project 54 | 57 |
  2. 58 |
  3. Deployment
  4. 59 |
  5. 60 | Getting Started 61 | 65 |
  6. 66 |
  7. Deployment
  8. 67 |
  9. License
  10. 68 | 69 |
  11. Acknowledgements
  12. 70 |
71 |
72 | 73 |
74 | 75 | 76 | ## About The Project 77 | 78 | Peach QE is the first Relay visualizer to handle all your GraphQL queries under one roof, directly on your desktop. 79 | 80 | Upload pre-written queries, live-edit the queries you want to explore, variable inputs, and even handle as many databases as required. 81 | 82 |
83 | 84 | ### Built With 85 | 86 | 87 | * [Relay](https://relay.dev/) 88 | * [GraphQL](https://graphql.org/) 89 | * [React](https://reactjs.org/) 90 | * [Electron](https://www.electronjs.org/) 91 | * [Webpack](https://webpack.js.org/) 92 | 93 | 94 | 95 |
96 | 97 | 98 | ## Getting Started 99 | 100 | * Fork and Clone the Repo: 101 | 102 | ```sh 103 | git clone https://github.com/oslabs-beta/peach.git 104 | ``` 105 | 106 |
107 | 108 | ### Prerequisites 109 | 110 | Install the dpendencies: 111 | 112 | * [ npm i ] 113 | 114 | ```sh 115 | npm install 116 | ``` 117 | * Run the Electron App locally 118 | 119 | ```sh 120 | npm start 121 | ``` 122 | 123 |
124 | 125 | 126 | ## Deployment 127 | 128 | 1. In order to build the executable file (aka, the desktop client) you will need to run specific commands for your OS: 129 | 130 | * Mac: 131 | 132 | ```sh 133 | npm run package-mac 134 | ``` 135 | 136 | * Windows: 137 | 138 | ```sh 139 | npm run package-win 140 | ``` 141 | 142 | * Linux: 143 | 144 | ```sh 145 | npm run package-linux 146 | ``` 147 | 148 | 149 | 2. Look for the directory **Release-builds** in the root folder where you cloned this Repo. (created automatically by the previous command) 150 | 151 | 3. Find the specific folder for your OS. (e.g., *peachQE-win*) 152 | 153 | 4. The file **peachQE.exe** (in Windows) will launch the app in your machine. 154 | 155 |
156 | 157 | In **every** case, you will need to add the file [*schema.graphql*](https://github.com/oslabs-beta/peach/blob/dev/schema.graphql) to the root directory and the file [*imported.js*](https://github.com/oslabs-beta/peach/blob/dev/src/relay/imported.js) to the directory src>relay. For example: 158 | 159 | . 160 | ├── (Root) # Installed Root folder 161 | ├── schema.graphql 162 | ├── src 163 | ├── relay # Create both folders 164 | ├── imported.js 165 | │ 166 | └── ... 167 | 168 |

169 | 170 | ## User Manual 171 | 172 | Once you have the application running, you will notice that PeachQE operates in two different "modes": 173 | 174 | ### Relay Proper 175 | 176 | When the application first opens, the window will by default display "Relay Proper" mode, which contains much of the core functionality of the application: 177 | 178 | Relay Proper Mode 179 | 180 | 1. The left section of the window is reserved for the **Schema Display**. Here, you can: 181 | * View your current schema and type into the searchbar to find specific fields. 182 | * Import a new Schema, updating the contents of the schema display to contain the newly imported schema. 183 | * Update the GraphQL API url endpoint. This determines what API PeachQE is interacting with. 184 | 185 | 2. Below the **Schema Display** you will find the **Variable Input**. Here you can: 186 | * Manually enter variables as JSON objects. 187 | * The **Variable Input** will interact with the **Query Input** to set the response in the **Reponse Display**. 188 | 189 | 3. The central section is reserved for the **Query Input**. Here you can: 190 | * Manually type in valid GraphQL queries. 191 | * Select previous queries from the **Query History** dropdown. Once clicked the selected query will populate the Query Input Field. 192 | * **Submit** your query. 193 | 194 | 3. The right section is the **Response Display** which, upon clicking the **Submit Query** button, will display the reponses to any submitted query. 195 | 196 | ### Peach Mode 197 | 198 | Peach Mode 199 | 200 | By clicking the yellow button titled **Peach Mode** at the top right corner of the window, or simply by scrolling down, you will find our designated UI for **Relay** specific functionality. 201 | 202 | While the **Response Display** functions entirely the same, the central and left sections have been altered to accomodate for Relay-specific functionality. 203 | 204 | 1. The left section still contains a **Variable Input** with two additional fields: 205 | 1. The **Query File Uploader** simply directs you to the "Peaches" dropdown from the menu bar, where you can "Start a New Peach" by uploading a file containing a list of GraphQL Queries. 206 | 2. The **Store Display** offers you a direct visual of the Relay Store, a Relay-specific functionality that stores all the data sent by your GraphQL Queries, and all the data sent back from the responding GraphQL API. It is effectively a log of your interactions with a given GraphQl API, and it is made available with PeachQE! 207 | 208 | 2. The central section contains 2 new components: 209 | 1. The **Query Editor** where you may copy/paste or manually type in multiple GraphQl Queries. Upon clicking **Save Edited Query**, the user inputted text will be saved and rendered as **Relay** queries, which will be displayed in the **New Query Selector**. 210 | 2. The **New Query Selector** will contain a list of queries matching the queries inputted in the **Query Editor**. You may then toggle between these queries, and the **Response** display will update with the currently selected query. 211 | 212 |

213 | Enjoy! 214 |

215 | 216 |
217 |
218 | 219 | _For more information, please visit our [website](https://www.peachqe.io/)_ 220 | 221 | Download App 222 | 223 | 224 |

225 | 226 | 227 | ## License 228 | 229 | Distributed under the [MIT](https://github.com/oslabs-beta/peach/blob/dev/LICENSE) License. See [`LICENSE`](https://github.com/oslabs-beta/peach/blob/dev/LICENSE) for more information. 230 | 231 |
232 | 233 | 234 | ## Acknowledgements to the PEach QE team: 235 | 236 | * [Alura Chung-Mehdi](https://github.com/aluracm) 237 | * [Roland Wynter](https://github.com/Rcwynter) 238 | * [Graham Albachten](https://github.com/albachteng) 239 | * [Nakami Hope-Felix](https://github.com/Nuckaahf) 240 | * [Carlos Botero-Vargas](https://github.com/Carlos-BoteroVargas) 241 | 242 |
243 | 244 | ------------- 245 | 246 |

247 | THANK YOU! 248 |

249 | -------------------------------------------------------------------------------- /assets/PeachLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/peach/e5c65cd4cc287844e6ab224d16c0484995eb7d34/assets/PeachLogo.png -------------------------------------------------------------------------------- /assets/PeachQE-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/peach/e5c65cd4cc287844e6ab224d16c0484995eb7d34/assets/PeachQE-3.png -------------------------------------------------------------------------------- /assets/PeachQE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/peach/e5c65cd4cc287844e6ab224d16c0484995eb7d34/assets/PeachQE.png -------------------------------------------------------------------------------- /assets/PeachQE_Install_Guide.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/peach/e5c65cd4cc287844e6ab224d16c0484995eb7d34/assets/PeachQE_Install_Guide.gif -------------------------------------------------------------------------------- /assets/icons/mac/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/peach/e5c65cd4cc287844e6ab224d16c0484995eb7d34/assets/icons/mac/icon.icns -------------------------------------------------------------------------------- /assets/icons/png/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/peach/e5c65cd4cc287844e6ab224d16c0484995eb7d34/assets/icons/png/icon.png -------------------------------------------------------------------------------- /assets/icons/win/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/peach/e5c65cd4cc287844e6ab224d16c0484995eb7d34/assets/icons/win/icon.ico -------------------------------------------------------------------------------- /assets/readme_imgs/Peach-Mode-Example.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/peach/e5c65cd4cc287844e6ab224d16c0484995eb7d34/assets/readme_imgs/Peach-Mode-Example.jpeg -------------------------------------------------------------------------------- /assets/readme_imgs/Relay-Proper-Example.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/peach/e5c65cd4cc287844e6ab224d16c0484995eb7d34/assets/readme_imgs/Relay-Proper-Example.jpeg -------------------------------------------------------------------------------- /electron/menu.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file holds the application's window menu structure 3 | */ 4 | 5 | const createAddWindow = require('./newWindow') 6 | const { app } = require('electron'); 7 | 8 | // evaluates to true if the platform is Mac 9 | const isMac = process.platform === 'darwin' ? true : false; 10 | 11 | // Create Menu template 12 | const mainMenuTemplate = [ 13 | ...(isMac ? [{ role: 'appMenu' }] : []), 14 | { 15 | role: 'fileMenu' 16 | }, 17 | { 18 | role: 'editMenu' 19 | }, 20 | { 21 | label: 'Peaches', 22 | submenu: [ 23 | { 24 | label: 'Start a New Peach', 25 | click(){ 26 | createAddWindow(); 27 | } 28 | }, 29 | { 30 | label: 'Continue working with a Peach' 31 | } 32 | ] 33 | }, 34 | ]; 35 | 36 | // If OSX, add empty object to the start of menu 37 | if (process.platform == 'darwin') { 38 | mainMenuTemplate.unshift({label: app.getName()}); 39 | } 40 | 41 | // * Add developer tools option if in dev 42 | if(process.env.NODE_ENV !== 'production'){ 43 | mainMenuTemplate.push({ 44 | label: 'Developer Tools', 45 | submenu:[ 46 | { role: 'reload' }, 47 | { role: 'forcereload' }, 48 | { type: 'separator' }, 49 | { 50 | label: 'Toggle DevTools', 51 | accelerator:process.platform == 'darwin' ? 'Command+I' : 'Ctrl+I', 52 | click(item, focusedWindow){ 53 | focusedWindow.toggleDevTools(); 54 | } 55 | } 56 | ] 57 | }); 58 | } 59 | 60 | module.exports = mainMenuTemplate -------------------------------------------------------------------------------- /electron/newWindow.js: -------------------------------------------------------------------------------- 1 | /* 2 | handles logic for generating a window that allows you to create a new project 3 | currently only functions as a text box 4 | */ 5 | 6 | const path = require('path'); 7 | const url = require('url'); 8 | const { BrowserWindow, dialog, app, IPCRenderer } = require('electron'); 9 | const mainMenuTemplate = require('./menu'); 10 | 11 | let fs = require('fs') 12 | 13 | // check for development environment 14 | let isDev = false 15 | 16 | if ( 17 | process.env.NODE_ENV !== undefined && 18 | process.env.NODE_ENV === 'development' 19 | ) { 20 | isDev = true 21 | } 22 | 23 | // Handle add item window 24 | const createAddWindow = () => { 25 | addWindow = new BrowserWindow({ 26 | width: 1155, 27 | height: 600, 28 | minHeight:400, 29 | minWidth:400, 30 | title: 'Upload your own Peach', 31 | backgroundcolor: 'white', 32 | // To have rounded corners, although it doesn't work for Mac 33 | // transparent: true, 34 | // The compiler is not recognizing the icon image. Keep commented out. 35 | // icon: `../assets/icons/png/icon.png`, 36 | // To remove the entire frame from Popup 37 | // frame: false, 38 | webPreferences: { 39 | nodeIntegration: true, 40 | contextIsolation: false, 41 | enableRemoteModule:true, 42 | worldSafeExecuteJavaScript: true, 43 | } 44 | }); 45 | // To remove menu from Popup 46 | addWindow.removeMenu(); 47 | 48 | // TODO To serve static html files 49 | addWindow.loadURL(url.format({ 50 | pathname: path.join(__dirname, '../html/peachWindow2.html'), 51 | protocol: 'file:', 52 | slashes:true 53 | })); 54 | 55 | // ! Handle garbage collection 56 | addWindow.on('close', function(){ 57 | addWindow = null; 58 | }); 59 | }; 60 | 61 | module.exports = createAddWindow; -------------------------------------------------------------------------------- /html/addWindow.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Start a New Peach 6 | 7 | 8 |
9 |
10 |
11 |
12 | 13 | 14 |
15 | 16 |
17 |

(For now, close this popup by on the X, top rigth corner)

18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /html/peachWindow.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | Peach QE - Desktop Visualizer for React-Relay Applications 15 | 16 | 17 | 25 | 26 |
27 |
28 | 29 | 30 |
31 |

32 |

33 | 34 | 35 |
36 |
37 |
38 |

39 | The file content will be the same as the editor. 40 |

41 | 42 |
43 |
44 | 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /html/peachWindow2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Peach QE - Desktop Visualizer for React-Relay Applications 9 | 10 | 11 |
12 |
13 |

Instructions

14 |

How to upload your queries

15 |
    16 |
  1. Write and save your queries on a simple (non-enriched) text file.
  2. 17 |
  3. 18 | Recommendations:
    19 | - Follow these 20 |
    naming conventions. 21 | 22 |    ⚬ Field names should use camelCase.
    23 |    ⚬ Type names should use PascalCase.
    24 |    ⚬ Enum names should use PascalCase.
    25 |    ⚬ Enum values should use ALL_CAPS.
    26 |    ⚬ Consider globally unique names for your variables. 27 |
    28 |
    29 | 30 |
  4. 31 |
  5. Drag & Drop the file on the peach-colored area on the right.
  6. 32 |
  7. Click Save Button
  8. 33 |
  9. Locate the Directory "RELAY"
  10. 34 |
  11. Replace the existing imported.js file
  12. 35 |
  13. Close this window
  14. 36 |
37 |
38 |
39 | 40 |
41 |
42 |
Drag your file here.
43 |
44 |
45 |
46 | 47 |
48 |
49 |
50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | /* 2 | This file holds the main window process for Electron, rendering the desktop window of the application 3 | */ 4 | const path = require('path'); 5 | const url = require('url'); 6 | const { app, BrowserWindow, Menu} = require('electron'); 7 | const mainMenuTemplate = require('./electron/menu'); 8 | const remote = require('electron') 9 | const {dialog} = remote 10 | // const createAddWindow = require('./electron/newWindow') 11 | 12 | //***// 13 | 14 | 15 | let mainWindow 16 | 17 | // check for development environment 18 | let isDev = false 19 | 20 | if ( 21 | process.env.NODE_ENV !== undefined && 22 | process.env.NODE_ENV === 'development' 23 | ) { 24 | isDev = true 25 | } 26 | 27 | function createMainWindow() { 28 | mainWindow = new BrowserWindow({ 29 | width: isDev ? 1400 : 1100, 30 | height: 800, 31 | show: false, 32 | backgroundcolor: 'white', 33 | icon: `${__dirname}/assets/icons/png/icon.png`, 34 | webPreferences: { 35 | nodeIntegration: true, 36 | contextIsolation: false, 37 | enableRemoteModule:true, 38 | worldSafeExecuteJavaScript: true, 39 | }, 40 | }) 41 | 42 | let indexPath 43 | 44 | // check for development mode options 45 | if (isDev && process.argv.indexOf('--noDevServer') === -1) { 46 | indexPath = url.format({ 47 | protocol: 'http:', 48 | host: 'localhost:8080', 49 | pathname: 'index.html', 50 | slashes: true, 51 | }) 52 | } else { 53 | indexPath = url.format({ 54 | protocol: 'file:', 55 | pathname: path.join(__dirname, 'dist', 'index.html'), 56 | slashes: true, 57 | }) 58 | } 59 | 60 | mainWindow.loadURL(indexPath) 61 | 62 | // Don't show until we are ready and loaded 63 | mainWindow.once('ready-to-show', () => { 64 | mainWindow.show() 65 | 66 | // Open devtools if dev 67 | if (isDev) { 68 | const { 69 | default: installExtension, 70 | REACT_DEVELOPER_TOOLS, 71 | } = require('electron-devtools-installer') 72 | // add react dev tools 73 | installExtension(REACT_DEVELOPER_TOOLS).catch((err) => 74 | console.log('Error loading React DevTools: ', err) 75 | ) 76 | // mainWindow.webContents.openDevTools() 77 | } 78 | }) 79 | 80 | // ! Quit app when closed 81 | mainWindow.on('closed', () => app.quit()); 82 | // redundant closinf window (Mac) 83 | mainWindow.on('closed', () => (mainWindow = null)) 84 | 85 | // Build menu from template 86 | const mainMenu = Menu.buildFromTemplate(mainMenuTemplate); 87 | // Insert menu 88 | Menu.setApplicationMenu(mainMenu); 89 | 90 | } 91 | 92 | // must wait to load pages until the ready state has fired 93 | app.on('ready', createMainWindow) 94 | 95 | // it is common for apps to remain open until explicitly quit in Mac environment 96 | app.on('window-all-closed', () => { 97 | if (process.platform !== 'darwin') { 98 | app.quit() 99 | } 100 | }) 101 | 102 | app.on('activate', () => { 103 | if (mainWindow === null) { 104 | createMainWindow() 105 | } 106 | }) 107 | 108 | // Stop error, note may become deprecated 109 | app.allowRendererProcessReuse = true 110 | 111 | // TODO electron application codes 112 | 113 | const { ipcMain } = require('electron') 114 | let fs = require('fs') 115 | 116 | 117 | ipcMain.on('ondragstart', (event, filePath) => { 118 | 119 | readFile(filePath); 120 | 121 | function readFile(filepath) { 122 | fs.readFile(filepath, 'utf-8', (err, data) => { 123 | 124 | if(err){ 125 | alert("An error ocurred reading the file :" + err.message) 126 | return 127 | } 128 | 129 | // handle the file content 130 | event.sender.send('fileData', data) 131 | }) 132 | } 133 | }) 134 | 135 | 136 | 137 | ipcMain.on('clickedbutton', (event, data) => { 138 | 139 | // console.log('This is the data we want to save: ', data) 140 | // Resolves to a Promise 141 | dialog.showSaveDialog({ 142 | title: 'Select the File Path to save', 143 | defaultPath: path.join(__dirname, '../relay/imported/imported.js'), 144 | // defaultPath: path.join(__dirname, '../assets/'), 145 | buttonLabel: 'Save', 146 | filters: [ 147 | { 148 | name: 'PeachQE - GraphQL pre-written queries', 149 | extensions: ['js'] 150 | } 151 | ], 152 | properties: [] 153 | }).then(file => { 154 | // Stating whether dialog operation was cancelled or not. 155 | console.log(file.canceled); 156 | if (!file.canceled) { 157 | console.log(file.filePath.toString()); 158 | 159 | // Creating and Writing to the sample.txt file 160 | fs.writeFile( 161 | file.filePath.toString(), 162 | data, 163 | (err) => { 164 | if(err) { 165 | alert("An error ocurred updating the file"+ err.message); 166 | console.log(err); 167 | return; 168 | } 169 | console.log('Saved!'); 170 | // alert("The file has been succesfully saved"); 171 | }) 172 | } 173 | }).catch(err => { 174 | console.log(err); 175 | }); 176 | }); 177 | 178 | //require in exec to run terminal commands in js: 179 | const execSync = require('child_process').execSync; 180 | 181 | ipcMain.on('close-me', (evt, arg) => { 182 | var addwindow = remote.createAddWindow() 183 | execSync('npm run relay', { encoding: 'utf-8' }); 184 | addwindow.close() 185 | }) 186 | 187 | 188 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "peach-qe", 3 | "version": "1.0.0", 4 | "description": "relay visualizer - desktop app", 5 | "author": "OS Labs - Team Peach - CBV", 6 | "license": "MIT", 7 | "engines": { 8 | "node": ">=9.0.0", 9 | "npm": ">=5.0.0", 10 | "yarn": ">=1.0.0" 11 | }, 12 | "browserslist": [ 13 | "last 4 versions" 14 | ], 15 | "main": "main.js", 16 | "scripts": { 17 | "prod": "cross-env NODE_ENV=production webpack --mode production --config webpack.build.config.js && electron --noDevServer .", 18 | "start": "npm run relay && cross-env NODE_ENV=development webpack-dev-server --hot --host 0.0.0.0 --config=./webpack.dev.config.js --mode development", 19 | "build": "cross-env NODE_ENV=production webpack --config webpack.build.config.js --mode production", 20 | "relay": "relay-compiler --schema ./schema.graphql --src ./src/ --watchman false $@", 21 | "package": "npm run build", 22 | "package-mac": "electron-packager . --overwrite --platform=darwin --arch=x64 --icon=assets/icons/mac/icon.icns --prune=true --out=release-builds", 23 | "package-win": "electron-packager . --overwrite --asar=true --platform=win32 --arch=ia32 --icon=assets/icons/win/icon.ico --prune=true --out=release-builds --version-string.CompanyName=CE --version-string.FileDescription=CE --version-string.ProductName=\"Peach QE - Relay Visualizer\"", 24 | "package-linux": "electron-packager . --overwrite --platform=linux --arch=x64 --icon=assets/icons/png/icon.png --prune=true --out=release-builds", 25 | "postpackage": "electron-packager ./ --out=./release-builds" 26 | }, 27 | "dependencies": { 28 | "2": "^3.0.0", 29 | "@babel/plugin-transform-runtime": "^7.14.3", 30 | "@fortawesome/fontawesome-svg-core": "^1.2.28", 31 | "@fortawesome/free-solid-svg-icons": "^5.13.0", 32 | "@fortawesome/react-fontawesome": "^0.1.9", 33 | "axios": "^0.21.1", 34 | "babel-plugin-macros": "^2.8.0", 35 | "babel-plugin-wildcard": "^7.0.0", 36 | "bootstrap": "^5.0.1", 37 | "codemirror": "^5.61.1", 38 | "get-graphql-schema": "^2.1.2", 39 | "jquery": "^3.6.0", 40 | "moment": "^2.29.1", 41 | "prismjs": "^1.23.0", 42 | "react": "^16.13.1", 43 | "react-bootstrap": "^1.6.0", 44 | "react-codemirror": "^1.0.0", 45 | "react-codemirror2": "^7.2.1", 46 | "react-dom": "^16.13.1", 47 | "react-dropdown": "^1.9.2", 48 | "react-icons": "^4.2.0", 49 | "react-moment": "^1.1.1", 50 | "react-relay": "^11.0.2", 51 | "react-router-dom": "^5.2.0", 52 | "react-scroll": "^1.8.2", 53 | "react-search-autocomplete": "^5.2.2", 54 | "react-spring": "^8.0.27", 55 | "relay-runtime": "^11.0.2", 56 | "uuid": "^8.3.2" 57 | }, 58 | "peerDependencies": { 59 | "react-spring": "^8.0.27" 60 | }, 61 | "devDependencies": { 62 | "@babel/core": "^7.9.6", 63 | "@babel/preset-env": "^7.9.6", 64 | "@babel/preset-react": "^7.9.4", 65 | "babel-loader": "^8.1.0", 66 | "babel-plugin-relay": "^11.0.2", 67 | "babel-plugin-transform-runtime": "^6.23.0", 68 | "babili-webpack-plugin": "^0.1.2", 69 | "cross-env": "^7.0.2", 70 | "css-loader": "^3.5.3", 71 | "electron": "^9.0.0", 72 | "electron-devtools-installer": "^3.2.0", 73 | "electron-packager": "^14.2.1", 74 | "file-loader": "^6.0.0", 75 | "graphql": "^15.5.0", 76 | "html-webpack-plugin": "^4.3.0", 77 | "mini-css-extract-plugin": "^0.9.0", 78 | "relay-compiler": "^11.0.2", 79 | "style-loader": "^1.2.0", 80 | "webpack": "^4.43.0", 81 | "webpack-cli": "^3.3.11", 82 | "webpack-dev-server": "^3.10.3" 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /queryHistory.json: -------------------------------------------------------------------------------- 1 | { 2 | "e1db2dd181b89ce359d0cce00afcaf37{\"id\":15125}": { 3 | "operation": { 4 | "fragment": { 5 | "kind": "SingularReaderSelector", 6 | "dataID": "client:root", 7 | "isWithinUnmatchedTypeRefinement": false, 8 | "node": { 9 | "argumentDefinitions": [ 10 | { 11 | "defaultValue": null, 12 | "kind": "LocalArgument", 13 | "name": "id" 14 | } 15 | ], 16 | "kind": "Fragment", 17 | "metadata": null, 18 | "name": "importedQueryQuery", 19 | "selections": [ 20 | { 21 | "alias": null, 22 | "args": [ 23 | { 24 | "kind": "Variable", 25 | "name": "id", 26 | "variableName": "id" 27 | }, 28 | { 29 | "kind": "Literal", 30 | "name": "type", 31 | "value": "ANIME" 32 | } 33 | ], 34 | "concreteType": "Media", 35 | "kind": "LinkedField", 36 | "name": "Media", 37 | "plural": false, 38 | "selections": [ 39 | { 40 | "alias": "_id", 41 | "args": null, 42 | "kind": "ScalarField", 43 | "name": "id", 44 | "storageKey": null 45 | }, 46 | { 47 | "alias": null, 48 | "args": null, 49 | "concreteType": "MediaTitle", 50 | "kind": "LinkedField", 51 | "name": "title", 52 | "plural": false, 53 | "selections": [ 54 | { 55 | "alias": null, 56 | "args": null, 57 | "kind": "ScalarField", 58 | "name": "romaji", 59 | "storageKey": null 60 | }, 61 | { 62 | "alias": null, 63 | "args": null, 64 | "kind": "ScalarField", 65 | "name": "english", 66 | "storageKey": null 67 | }, 68 | { 69 | "alias": null, 70 | "args": null, 71 | "kind": "ScalarField", 72 | "name": "native", 73 | "storageKey": null 74 | } 75 | ], 76 | "storageKey": null 77 | } 78 | ], 79 | "storageKey": null 80 | } 81 | ], 82 | "type": "Query", 83 | "abstractKey": null 84 | }, 85 | "variables": { 86 | "id": 15125 87 | }, 88 | "owner": { 89 | "identifier": "e1db2dd181b89ce359d0cce00afcaf37{\"id\":15125}", 90 | "node": { 91 | "fragment": { 92 | "argumentDefinitions": [ 93 | { 94 | "defaultValue": null, 95 | "kind": "LocalArgument", 96 | "name": "id" 97 | } 98 | ], 99 | "kind": "Fragment", 100 | "metadata": null, 101 | "name": "importedQueryQuery", 102 | "selections": [ 103 | { 104 | "alias": null, 105 | "args": [ 106 | { 107 | "kind": "Variable", 108 | "name": "id", 109 | "variableName": "id" 110 | }, 111 | { 112 | "kind": "Literal", 113 | "name": "type", 114 | "value": "ANIME" 115 | } 116 | ], 117 | "concreteType": "Media", 118 | "kind": "LinkedField", 119 | "name": "Media", 120 | "plural": false, 121 | "selections": [ 122 | { 123 | "alias": "_id", 124 | "args": null, 125 | "kind": "ScalarField", 126 | "name": "id", 127 | "storageKey": null 128 | }, 129 | { 130 | "alias": null, 131 | "args": null, 132 | "concreteType": "MediaTitle", 133 | "kind": "LinkedField", 134 | "name": "title", 135 | "plural": false, 136 | "selections": [ 137 | { 138 | "alias": null, 139 | "args": null, 140 | "kind": "ScalarField", 141 | "name": "romaji", 142 | "storageKey": null 143 | }, 144 | { 145 | "alias": null, 146 | "args": null, 147 | "kind": "ScalarField", 148 | "name": "english", 149 | "storageKey": null 150 | }, 151 | { 152 | "alias": null, 153 | "args": null, 154 | "kind": "ScalarField", 155 | "name": "native", 156 | "storageKey": null 157 | } 158 | ], 159 | "storageKey": null 160 | } 161 | ], 162 | "storageKey": null 163 | } 164 | ], 165 | "type": "Query", 166 | "abstractKey": null 167 | }, 168 | "kind": "Request", 169 | "operation": { 170 | "argumentDefinitions": [ 171 | { 172 | "defaultValue": null, 173 | "kind": "LocalArgument", 174 | "name": "id" 175 | } 176 | ], 177 | "kind": "Operation", 178 | "name": "importedQueryQuery", 179 | "selections": [ 180 | { 181 | "alias": null, 182 | "args": [ 183 | { 184 | "kind": "Variable", 185 | "name": "id", 186 | "variableName": "id" 187 | }, 188 | { 189 | "kind": "Literal", 190 | "name": "type", 191 | "value": "ANIME" 192 | } 193 | ], 194 | "concreteType": "Media", 195 | "kind": "LinkedField", 196 | "name": "Media", 197 | "plural": false, 198 | "selections": [ 199 | { 200 | "alias": "_id", 201 | "args": null, 202 | "kind": "ScalarField", 203 | "name": "id", 204 | "storageKey": null 205 | }, 206 | { 207 | "alias": null, 208 | "args": null, 209 | "concreteType": "MediaTitle", 210 | "kind": "LinkedField", 211 | "name": "title", 212 | "plural": false, 213 | "selections": [ 214 | { 215 | "alias": null, 216 | "args": null, 217 | "kind": "ScalarField", 218 | "name": "romaji", 219 | "storageKey": null 220 | }, 221 | { 222 | "alias": null, 223 | "args": null, 224 | "kind": "ScalarField", 225 | "name": "english", 226 | "storageKey": null 227 | }, 228 | { 229 | "alias": null, 230 | "args": null, 231 | "kind": "ScalarField", 232 | "name": "native", 233 | "storageKey": null 234 | } 235 | ], 236 | "storageKey": null 237 | } 238 | ], 239 | "storageKey": null 240 | } 241 | ] 242 | }, 243 | "params": { 244 | "cacheID": "e1db2dd181b89ce359d0cce00afcaf37", 245 | "id": null, 246 | "metadata": {}, 247 | "name": "importedQueryQuery", 248 | "operationKind": "query", 249 | "text": "query importedQueryQuery(\n $id: Int\n) {\n Media(id: $id, type: ANIME) {\n _id: id\n title {\n romaji\n english\n native\n }\n }\n}\n" 250 | }, 251 | "hash": "e07846b070fdce2af28b3fcf1f7257a6" 252 | }, 253 | "variables": { 254 | "id": 15125 255 | }, 256 | "cacheConfig": { 257 | "force": true 258 | } 259 | } 260 | }, 261 | "request": { 262 | "identifier": "e1db2dd181b89ce359d0cce00afcaf37{\"id\":15125}", 263 | "node": { 264 | "fragment": { 265 | "argumentDefinitions": [ 266 | { 267 | "defaultValue": null, 268 | "kind": "LocalArgument", 269 | "name": "id" 270 | } 271 | ], 272 | "kind": "Fragment", 273 | "metadata": null, 274 | "name": "importedQueryQuery", 275 | "selections": [ 276 | { 277 | "alias": null, 278 | "args": [ 279 | { 280 | "kind": "Variable", 281 | "name": "id", 282 | "variableName": "id" 283 | }, 284 | { 285 | "kind": "Literal", 286 | "name": "type", 287 | "value": "ANIME" 288 | } 289 | ], 290 | "concreteType": "Media", 291 | "kind": "LinkedField", 292 | "name": "Media", 293 | "plural": false, 294 | "selections": [ 295 | { 296 | "alias": "_id", 297 | "args": null, 298 | "kind": "ScalarField", 299 | "name": "id", 300 | "storageKey": null 301 | }, 302 | { 303 | "alias": null, 304 | "args": null, 305 | "concreteType": "MediaTitle", 306 | "kind": "LinkedField", 307 | "name": "title", 308 | "plural": false, 309 | "selections": [ 310 | { 311 | "alias": null, 312 | "args": null, 313 | "kind": "ScalarField", 314 | "name": "romaji", 315 | "storageKey": null 316 | }, 317 | { 318 | "alias": null, 319 | "args": null, 320 | "kind": "ScalarField", 321 | "name": "english", 322 | "storageKey": null 323 | }, 324 | { 325 | "alias": null, 326 | "args": null, 327 | "kind": "ScalarField", 328 | "name": "native", 329 | "storageKey": null 330 | } 331 | ], 332 | "storageKey": null 333 | } 334 | ], 335 | "storageKey": null 336 | } 337 | ], 338 | "type": "Query", 339 | "abstractKey": null 340 | }, 341 | "kind": "Request", 342 | "operation": { 343 | "argumentDefinitions": [ 344 | { 345 | "defaultValue": null, 346 | "kind": "LocalArgument", 347 | "name": "id" 348 | } 349 | ], 350 | "kind": "Operation", 351 | "name": "importedQueryQuery", 352 | "selections": [ 353 | { 354 | "alias": null, 355 | "args": [ 356 | { 357 | "kind": "Variable", 358 | "name": "id", 359 | "variableName": "id" 360 | }, 361 | { 362 | "kind": "Literal", 363 | "name": "type", 364 | "value": "ANIME" 365 | } 366 | ], 367 | "concreteType": "Media", 368 | "kind": "LinkedField", 369 | "name": "Media", 370 | "plural": false, 371 | "selections": [ 372 | { 373 | "alias": "_id", 374 | "args": null, 375 | "kind": "ScalarField", 376 | "name": "id", 377 | "storageKey": null 378 | }, 379 | { 380 | "alias": null, 381 | "args": null, 382 | "concreteType": "MediaTitle", 383 | "kind": "LinkedField", 384 | "name": "title", 385 | "plural": false, 386 | "selections": [ 387 | { 388 | "alias": null, 389 | "args": null, 390 | "kind": "ScalarField", 391 | "name": "romaji", 392 | "storageKey": null 393 | }, 394 | { 395 | "alias": null, 396 | "args": null, 397 | "kind": "ScalarField", 398 | "name": "english", 399 | "storageKey": null 400 | }, 401 | { 402 | "alias": null, 403 | "args": null, 404 | "kind": "ScalarField", 405 | "name": "native", 406 | "storageKey": null 407 | } 408 | ], 409 | "storageKey": null 410 | } 411 | ], 412 | "storageKey": null 413 | } 414 | ] 415 | }, 416 | "params": { 417 | "cacheID": "e1db2dd181b89ce359d0cce00afcaf37", 418 | "id": null, 419 | "metadata": {}, 420 | "name": "importedQueryQuery", 421 | "operationKind": "query", 422 | "text": "query importedQueryQuery(\n $id: Int\n) {\n Media(id: $id, type: ANIME) {\n _id: id\n title {\n romaji\n english\n native\n }\n }\n}\n" 423 | }, 424 | "hash": "e07846b070fdce2af28b3fcf1f7257a6" 425 | }, 426 | "variables": { 427 | "id": 15125 428 | }, 429 | "cacheConfig": { 430 | "force": true 431 | } 432 | }, 433 | "root": { 434 | "dataID": "client:root", 435 | "node": { 436 | "argumentDefinitions": [ 437 | { 438 | "defaultValue": null, 439 | "kind": "LocalArgument", 440 | "name": "id" 441 | } 442 | ], 443 | "kind": "Operation", 444 | "name": "importedQueryQuery", 445 | "selections": [ 446 | { 447 | "alias": null, 448 | "args": [ 449 | { 450 | "kind": "Variable", 451 | "name": "id", 452 | "variableName": "id" 453 | }, 454 | { 455 | "kind": "Literal", 456 | "name": "type", 457 | "value": "ANIME" 458 | } 459 | ], 460 | "concreteType": "Media", 461 | "kind": "LinkedField", 462 | "name": "Media", 463 | "plural": false, 464 | "selections": [ 465 | { 466 | "alias": "_id", 467 | "args": null, 468 | "kind": "ScalarField", 469 | "name": "id", 470 | "storageKey": null 471 | }, 472 | { 473 | "alias": null, 474 | "args": null, 475 | "concreteType": "MediaTitle", 476 | "kind": "LinkedField", 477 | "name": "title", 478 | "plural": false, 479 | "selections": [ 480 | { 481 | "alias": null, 482 | "args": null, 483 | "kind": "ScalarField", 484 | "name": "romaji", 485 | "storageKey": null 486 | }, 487 | { 488 | "alias": null, 489 | "args": null, 490 | "kind": "ScalarField", 491 | "name": "english", 492 | "storageKey": null 493 | }, 494 | { 495 | "alias": null, 496 | "args": null, 497 | "kind": "ScalarField", 498 | "name": "native", 499 | "storageKey": null 500 | } 501 | ], 502 | "storageKey": null 503 | } 504 | ], 505 | "storageKey": null 506 | } 507 | ] 508 | }, 509 | "variables": { 510 | "id": 15125 511 | } 512 | } 513 | }, 514 | "refCount": 1, 515 | "epoch": 1, 516 | "fetchTime": 1622661888940 517 | } 518 | } -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | /* 2 | top-level component responsible for rendering all other major components, 3 | */ 4 | 5 | import React, { useState, Suspense } from 'react'; 6 | 7 | /* COMPONENTS */ 8 | import ImportedMode from './components/ImportedMode'; 9 | import Footer from './components/Footer'; 10 | import SchemaDisplayContainer from './components/SchemaDisplay/SchemaDisplayContainer'; 11 | import WrittenResponseDisplay from './components/ResponseDisplay/WrittenResponseDisplay'; 12 | import QueryContainer from './components/QueryContainer'; 13 | import VariableInput from './components/VariableInput'; 14 | import ErrorBoundary from './components/ErrorBoundary'; 15 | 16 | /* STYLES */ 17 | import Container from 'react-bootstrap/Container'; 18 | import Card from 'react-bootstrap/Card'; 19 | import Row from 'react-bootstrap/Row'; 20 | import Col from 'react-bootstrap/Col'; 21 | import './styles/App.css'; 22 | import './styles/styles.css'; 23 | 24 | /* UTILITIES */ 25 | import gqlEndpoint from './relay/gqlendpoint'; 26 | import db from './database/db'; 27 | 28 | const App = () => { 29 | const [response, setResponse] = useState(''); 30 | const [query, setQuery] = useState(''); 31 | const [variables, setVariables] = useState(''); 32 | 33 | // Define the config we'll need for our Api request 34 | let url = gqlEndpoint.url; 35 | let options = { 36 | method: 'POST', 37 | headers: { 38 | 'Content-Type': 'application/json', 39 | 'Accept': 'application/json', 40 | }, 41 | body: JSON.stringify({ 42 | query: query, 43 | variables: variables 44 | }) 45 | }; 46 | 47 | // Make the HTTP Api request 48 | const submitTypedQuery = () => { 49 | fetch(url, options) 50 | .then(handleResponse) 51 | .then(handleData) 52 | .catch(handleError); 53 | db.addQuery(query); 54 | } 55 | 56 | //Helper functions for submitTypedQuery: 57 | function handleResponse(response) { 58 | return response.json().then(function (json) { 59 | return response.ok ? json : Promise.reject(json); 60 | }); 61 | } 62 | function handleData(data) { 63 | setResponse(data.data); 64 | } 65 | function handleError(error) { 66 | console.error(error); 67 | } 68 | 69 | return ( 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 109 | 110 | 111 | 112 | 113 | 114 | 115 |
116 | 117 | 118 | 121 | 122 | 123 |
124 |
125 | 126 |
127 | 128 | 129 | 130 |