diff --git a/README.md b/README.md index 6950e32de..95e429273 100755 --- a/README.md +++ b/README.md @@ -27,6 +27,10 @@ Swell is a one-stop shop for sending and monitoring your API requests: - Import and export workspaces locally - Compose test suites in JavaScript with Chai-style TDD/BDD assertion syntax +## Considering iterating Swell in the future? + +We highly encourage you to check out the `DEV-README.md` in the `docs` folder. We've included a comprehensive guide on the latest updates, which areas would benefit from future iterations, as well as details of core components. + ## Core features - _HTTP2_: Swell supports full HTTP2 multiplexing of requests and responses. HTTP requests to the same host will be sent over the same connection. Swell will attempt to initiate an HTTP2 connection for all HTTPS requests by default, with the ability to revert to HTTP1.1 for legacy servers. Multiple concurrent streams are allowed for each connection. @@ -83,6 +87,10 @@ Swell is a one-stop shop for sending and monitoring your API requests: - _Collection Runner_: You can also stage requests in the workspace and automate the process of sending off each one. No need to manually press send on each one; instead each request will fire off in the order of staging. +- _File Upload/Dark Mode_: If you click on the body drop down menu, you can select binary, which allows you to upload a file that can be sent along with any request to test backend file upload routes. Additionally, you can toggle dark mode via the button in the top right-hand corner. + + ## Experimental Features - _Mock Server_: Swell allows you to create your own HTTP/2 mock server to facilitate front-end development without depending on a fully built backend server. @@ -115,11 +123,12 @@ Swell is a one-stop shop for sending and monitoring your API requests: - Mocha - Playwright -## Considering iterating Swell in the future? - -Feel free to check out the `DEV-README.md` in the `docs` folder. - ## Authors +- **Karol Krzywon** - [kkrzywon](https://github.com/kkrzywon) +- **Howard Sun** - [howardCodeGit](https://github.com/howardCodeGit) +- **Carter Sarkela** - [CarterSarkela](https://github.com/CarterSarkela) +- **Adeeb Bayat** - [adeebbayat](https://github.com/adeebbayat) +- **Michael Underbrink** - [MUnderbrink90](https://github.com/MUnderbrink90) - **Aaron Cruz** - [AArCruz](https://github.com/AArCruz) - **Katya Villano** - [k-villano](https://github.com/k-villano) - **Brooke Sauro** - [bhsauro](https://github.com/bhsauro) diff --git a/ReadMeGifs/Gifs/FileUploadAndDarkMode.gif b/ReadMeGifs/Gifs/FileUploadAndDarkMode.gif new file mode 100644 index 000000000..7b8517d1e Binary files /dev/null and b/ReadMeGifs/Gifs/FileUploadAndDarkMode.gif differ diff --git a/docs/DEV-README.md b/docs/DEV-README.md index 3da72e46d..e869a175a 100644 --- a/docs/DEV-README.md +++ b/docs/DEV-README.md @@ -7,6 +7,12 @@ NOTE: Here is a super useful excalidraw that describes the processes of the appl here is on redux and interprocess communication in electron https://excalidraw.com/#json=814-HNYqTvOb6ukLgWjye,QhmJCXv8x3-BC_oSH2gjCQ +## Maintaining this Document + +This should serve as an entry point for any developers who wish to iterate on Swell and therefore, should be kept as up-to-date as possible. **At the end of your iteration, you are strongly encouraged to update this document for future developers.** + +Thank you for your consideration and let's work together on making Swell one of the best open-source products to contribute! + --- ## As a developer, what experience can you get out of contributing to Swell? @@ -20,9 +26,9 @@ https://excalidraw.com/#json=814-HNYqTvOb6ukLgWjye,QhmJCXv8x3-BC_oSH2gjCQ - Webpack - Client-side storage (IndexedDB) - GitHub Actions (CI/CD) -- Testing +- Testing: - Unit testing with Jest - - End-to-end (E2E) testing with Playwright and Mocha + - End-to-end (E2E) testing with Playwright and Mocha/Chai - Advanced and/or specialized knowledge of: - Electron - APIs: HTTP/2, GraphQL @@ -48,19 +54,48 @@ There is E2E testing available via `npm run test`. Note that not all tests in th ## What is the way to render an electron app during development for WSL users? -WSL and Electron do not work well together - the application won't load when using `npm run dev`. We have a few solutions you can try, but it is not by any means the only way or even guaranteed method. +WSL and Electron do not work well together without some additional steps - the application won't load when using `npm run dev`. We have a few solutions you can try, but it is not by any means the only way or even guaranteed method. - One solution suggestion is to download the repo directly on your Windows machine and not use WSL. - - You can right-click on the bottom-left of your VSCode and uncheck the remote host so that you still get the command-line functionalities. - Another solution is to use `Xserver` (graphical interface for Linux) to render things from Linux onto your Windows. - - [This article](https://www.beekeeperstudio.io/blog/building-electron-windows-ubuntu-wsl2) was really helpful in getting things to work - - There is another article [here](https://skeptric.com/wsl2-xserver/) that you may want to check out. The two differences that diverge from the article instructions on the WSL Config step with `.bashrc` file and the `VcsXsrv` config step 3 - - inside of your `.bashrc` File: - - You should add the following script instead of what they put: `export DISPLAY=$(/sbin/ip route | awk '/default/ { print $3 }'):0` - - `VcsXsrv`: check Disable access control as well. - - After these steps, you will have to enable WSL to access `Xserver` on Windows Firewall(refer to the [skeptric](https://skeptric.com/wsl2-xserver/) article) - - If `x11 calc` is able to pop up, it means everything is working well. -- There is a long load time when running the server, it may take a few minutes. + - The following below is a streamlined and hopefully beginner friendly step-by-step that may work for most people with additional references below in case you encounter issues. + - **We highly recommend reviewing all the steps first BEFORE proceeding (and ideally the references as well) to have a good understanding before proceeding** + - **Also verify you are using WSL2 and not WSL1, run `wsl --version` within powershell if you are not sure** + - If you need to upgrade from WSL1 to WSL2, you can run `wsl --set-default-version 2` within powershell. + +1. You will need to install the electron dependencies inside of your WSL to make sure electron has all the necessary components (If you want to be cautious, now would be a good time to make a backup of your linux subsystem but it is not essential). Once you are ready, run the following command in you Linux terminal: + - `sudo apt install libgconf-2-4 libatk1.0-0 libatk-bridge2.0-0 libgdk-pixbuf2.0-0 libgtk-3-0 libgbm-dev libnss3-dev libxss-dev` +2. In your WSL system's `.bashrc` file, typically located in you user folder `\\wsl.localhost\\home\\`, add the following line to the end of the file: + - `` export DISPLAY="`sed -n 's/nameserver //p' /etc/resolv.conf`:0" `` + - **PLEASE NOTE: Any time you make changes to the main WSL files like `.bashrc` or `.profile` (especially if troubleshooting later), you will need to restart you entire WSL (Not just shutdown VSCode) in order for the changes to take effect. Save your work/changes, close VSCode, open a windows terminal and type in `wsl --shutdown`. You should then be able to boot up as you normally do with the new settings applied.** +3. Install XServer, specifically VcsXsrv: + - If you have chocolatey installed on Windows, you can run the following command from the Windows command line or from PowerShell `choco install vcxsrv` + - See [here](https://community.chocolatey.org/packages/vcxsrv) for the link on more information about this package. Other repo management tools for Windows are perfectly fine as well (i.e `scoop.sh` or such). + - See [this link](https://chocolatey.org/install) if you don't have a package manager for Windows to get started using Chocolatey. + - Once installed open VcsXsrv (should be listed and searchable as `XLaunch` in Windows) and it’ll guide you through three config screens. Here’s what to pick on each one: + - Choose Multiple Windows + ![alt text](vcxsrv_display_settings.png) + - Choose ‘Start no client’ + ![alt text](vcxsrv_client_startup.png) + - Choose Clipboard, OpenGL integration, and Disable access control + ![alt text](vcxsrv_disable_access_control.png) + - Alternatively: you can also choose Clipboard and OpenGL integration, plus provide -ac as additional parameters + ![alt text](vcxsrv_alt_disable_access_control.png) + - Save your config in any folder that's for easy to reach for launching (as this will be the one you use to actually start-up Xserver), then start the server by double clicking on it. You’ll now see the little X logo in your system tray. We’re ready to go. + - If you make any mistakes in creating your config, you can always open `XLaunch` again and create a new config file to replace the old one or create a different one for testing. +4. You should now be able to run `npm run dev` from the terminal as you would normally to boot up the software. Links will open in your browser as it loads, be patient. It may take up to a few minutes to complete, but eventually you should see a new window pop-up with Swell running. + - Please note there may be several errors that pop up in your terminal that may reference outdated packages or be in reference to other components as well. This is normal depending on the state of the software and any changes you've made, but it's worth reviewing if you encounter issues. +5. **Potentially Necessary** After these steps, if it is still not working, you may have to enable WSL to access `Xserver` on Windows Firewall + - If so, refer to this [skeptric](https://skeptric.com/wsl2-xserver/) article + +Here are the articles we used as reference, we suggest reading through these for any troubleshooting and for a better understanding overall. + +- **NOTE: There are some discrepancies between the two articles and user experiences on where to place the additional lines of code, whether inside of your `.bashrc` file or your `.profile` file. We suggest trying to use ONLY the single line shown in step 2 above in your `.bashrc` file, unless it doesn't work for you** +- **Additionally, there are further variances on what specific lines of code to add the ones outlined above should suffice, but if the do not then please try the steps outlined in these articles. Just remember to restart WSL if changing any code in the `.bashrc` or `.profile` files.** + - [This article](https://www.beekeeperstudio.io/blog/building-electron-windows-ubuntu-wsl2) was really for getting things to work and understanding the larger picture. + - There is another article [here](https://skeptric.com/wsl2-xserver/) that you may want to check out as well and some of us found more helpful and explains how to set up Windows Firewall as referenced in Step 5. + - If following the second article, others have suggested yet another script instead of what they put: `export DISPLAY=$(/sbin/ip route | awk '/default/ { print $3 }'):0` + - Some additional alternatives are `export DISPLAY=$(grep nameserver /etc/resolv.conf | awk '{print $2}'):0` --- @@ -69,6 +104,7 @@ WSL and Electron do not work well together - the application won't load when usi From a functionality standpoint: - Consistent UI/UX styling and color palette +- Toggleable Dark Mode - Make requests via HTTP/2 - Query, Mutation, Subscribe/unsubscribe to GraphQL endpoints - Query, Mutation, Subscribe/unsubscribe to tRPC endpoints @@ -78,65 +114,90 @@ From a functionality standpoint: - Ability to store historical requests and create/delete workspaces - Frontend conversion to TypeScript - From a codebase standpoint: - -- Increase the quality of TypeScript and continue conversion -- Conversion to Redux toolkit _almost_ complete (need to implement hooks like useSelector and useAppDispatch) -- Most working E2E testing (more details in `./test/testSuite.js`) + - Increased the quality of TypeScript with continuing conversion + - Conversion to Redux toolkit is complete (fully implemented useSelector and useAppDispatch) + - 60% coverage of working E2E testing (more details in `./test/testSuite.js`) --- -## What are some of the features that require future iterations? +## What are some of the features that require future iteration? ### _Continue reducing the size and complexity of the codebase_ This codebase has an interesting combination of over-modularization and code de-centralization/duplication occurring at the same time. For example - each type of API endpoint composer window (top right section of the app) is its own module/file, but a lot of the code inside is duplicated (see `Http2Composer.tsx` and `GraphQLComposer.tsx`). The reason many iteration groups have stayed away is because one would need to craft a function/component that is flexible enough to handle the population of the reqRes object and dispatch the state to the various slices. This is not an easy task and may take the entire iteration time allotted. It would be a worthy -The impacts to the product are: +endeavour. The impacts to the product are: -- The codebase can be incredibly difficult to navigate if you are not familiar with the structure - - That being said, the file structure has been extensively modified to make the navigation much easier. - The most challenging aspects are the understanding how state flows through the application, from the front-end to main_process, controllers, etc. - This is the most important thing to understand when iterating on Swell +- The codebase can be challenging to navigate if you are not familiar with the structure + - That being said, the file structure has been extensively modified to make the navigation much easier. The most demanding aspects involve understanding how state flows through the application, from the front-end to main_process, controllers, etc. + - This is the most important thing to understand when iterating on Swell - The app is slow to load in all environments (production, development, test) - Adding multiple entry points to the build process would greatly improve this, but be careful because you can end up making performance much worse in the process -**Some of us have found [ReacTree](https://reactree.dev/) VS Code extension incredibly helpful in visualizing the UI components. Utilizing the extension could be your entry into understanding the structure of the codebase.** +**Some of us have found the [ReacTree](https://reactree.dev/) VS Code extension or the [Sapling](https://marketplace.visualstudio.com/publishers/team-sapling) VS Code extension incredibly helpful in visualizing the UI components. Utilizing the extension could be your entry into understanding the structure of the codebase.** **Some of us have found Redux Dev Tools incredibly helpful when trying to understand the flow of state and actions. Redux Dev Tools is installed when running in development mode and can be accessed as the right-most tab in your developer console panel in Electron.** As you iterate the product, keep in mind the footprint your new feature(s) could add to the codebase. Could you re-use some of the existing modules? Can you even refactor and/or remove the obsolete code to help maintain the health of the codebase? -There are many parts of the codebase that break DRY principles, and with such a large application, really keep in mind that when you add features it is completely necessary. Past iterators added an experimental feature(s) without it fully working and the next team(s) would add their own experimental feature. Fixing features the past teams couldn't get to not only is a great way to learn these technologies but is also a great thing to talk about in interviews. " I fixed the webRTC feature that has been stagnant for 5 years", "I addressed the technical debt and reorganized the state...", or "Increased the quality of typeScript". These all show maturity as a developer and will allow us to focus the entire time of OSP on the 20% problems. +There are many parts of the codebase that break DRY principles, and with such a large application, really keep in mind that when you add features to ask if it is completely necessary. Past iterators added an experimental feature(s) without it fully working and the following team(s) would add their own experimental feature. Fixing features the past teams couldn't get to is not only a great way to learn these technologies but also a great thing to talk about in interviews. "I fixed the webRTC feature that has been stagnant for 5 years", "I addressed the technical debt and reorganized the state...", or "Increased the quality of typeScript". These all show maturity as a developer and will allow us to focus the entire time of OSP on the final 20% problems. + +Legacy Components - As a part of a clean up effort, all files that are no longer being used have been moved to the legacy component folder. Examples of these files come from the migration to shared components. The original location of the components is mentioned in the comments of the relocated files. ### _Ensure consistent redux state management_ The redux state initiation and management for various API endpoints in this codebase are inconsistent. If you cross reference the state initialization, transition/update, and clean up in various modules with `types.ts`, you will notice many TypeScript typing errors due to inconsistent state management. This will need to be cleaned up bit by bit to ensure a state that works across all types of APIs in this application. -### _Basic functionality for more advanced APIs not working as expected_ +### _Continue improving UX/UI consistency in the app_ -For the following technologies - if you reference the gifs in `readme` and try to replicate the steps in the application you may not get the same result: +The UX/UI styling and functionality are not consistent across different API endpoints. For example, there is a `Send Request` button for HTTP/2, but not for GraphQL. - +Moreover, the application lacks instructions on how to utilize some of the more advanced features like WebSocket and tRPC. Having some written explanation of how the feature works on the app would be tremendously helpful. - +Lastly, when making the app smaller on a Windows desktop or using a computer with a smaller screen size, some of the buttons are partially cut out. It would be great to establish a minimum size for each section and/or input field so the application can auto-resize elegantly. -- OpenAPI +You will notice that there are a few places where MUI is used. Material UI is a huge component library that is popular, though figuring out how to lessen Swell's dependence on it would go a long way to reducing its bloat. However previous groups intended fully convert the application to utilize Material UI. In which case following groups should either decide to fully implement it or fully remove the dependency. -If future groups have a desire to iterate on the above features, please ensure the basic functionality works as expected, and update E2E testing in `./test/testSuite.js` before adding new features. +### _Streamline Dark Mode alongside UX/UI changes_ -### _Continue improving UX/UI consistency in the app_ +Dark Mode has been re-implemented as of v1.18 and a toggle button was added to the top right corner. It works across the entire application, however the following could use improvement: -The UX/UI styling and functionality are not consistent across different API endpoints. For example, there is a `Send Request` button for HTTP/2, but not for GraphQL. +- Certain drop-down buttons appear too dark, matching the background in the current state but otherwise are readable and function normally, but the contrast of the text could be improved. -Moreover, the application lacks instructions on how to utilize some of the more advanced features like WebSocket and tRPC. Having some written explanation of how the feature works on the app would be tremendously helpful. +However it's setup is not ideal should serve as a temporary fix. -Lastly, when making the app smaller on a Windows desktop or using a computer with a smaller screen size, some of the buttons are partially cut out. It would be great to establish a minimum size for each section and/or input field so the application can auto-resize elegantly. +Currently the isDark state is held inside of `uiSlice.ts` and the toggle button is rendered inside of `DarkModeSelect.tsx`. The functionality relies heavily on the following components: + +- `darkMode.scss` contains the color palette of the classes +- Many of the individual rendered components (such as headers, buttons, or url entry forms) relied on in-line css ternary operators to check the state of isDark and apply appropriate classes when isDark is `true`. + - For example `${isDark ? 'is-dark-400' : ''}` +- Some items directly rely on the `darkMode.scss`, but for the remainder of Material UI components, there is a theme set in `index.js` to make the background match the color palette. Because Material UI could not use the darkMode.scss colors directly, the colors had to be hard-coded to match the current color configurations. As such there is a lightTheme and a darkTheme that gets directly rendered in `index.js` based on the state of isDark in `uiSlice.ts`. + +A more ideal solution would more closely reassemble the classes themselves getting modified if isDark is set to `true` so that there is less reliance on inline HTML classes utilizing the existing ternary operator. + +Additionally, depending on how future groups iterate on the Material UI components, the themes should be integrated so that there is a single source of truth rather than in both `index.js` and `darkMode.scss`. + +Please also note that the testing created in `darkModeToggleTest.js` checks for the correct icons being shown between light and dark mode and that the correct state gets sent to `uiSlice.ts` when pressed. If the icons are modified, this may fail the test. + +### _Continue TypeScript code improvements and conversion_ + +TypeScript provides strong typing to improve code quality, and maintainability and reduce runtime errors. Currently, TS usage across the app is inconsistent at best. Numerous files are 'converted' to TS, but are using a band-aid type called $TSFixMe. If you look in types.ts, you can see that this is just the any type, which defeats the purpose of using TS in the first place. As of v1.18, all of MainContainer and all of the GraphQL-composer folder are free of type errors. The process for fixing $TSFixMe's involves importing the proper types from types.ts wherever possible and creating custom types in file where necessary. + +It is also important to note that props are being drilled down from MainContainer into child components, which have their own custom prop types in types.ts that extend MainContainer. In the future, contributors may want to consider typing child components separately and only prop drill from MainContainer what is truly necessary in each component. Or, alternatively, it might be better to eliminate prop drilling altogether and pull functions in directly from the redux store -You will notice that there are a few places where MUI is used. Material UI is a huge component library that is popular, though figuring out how to lessen Swell's dependence on it would go a long way to reducing its bloat. +### _CSP Changes and Improvements_ + +#### nonce/sha-256 CSP script/style changes +- Added CSP nonce to the scripts being ran in the app to prevent potential XSS attacks +- Implemented a cache to catch Material UI dynamic styling – still need to a way to parse nonce value from webpack into the cache [index.js] +- Moved rendering from index.js and moved entry point to index-csp.html and used webpack as template +- Added nonce generation and potential sha-256 generation for from webpack + +#### Remaining Items to Implement +- Nonce is currently generated once per build – find a way to change nonce on every launch +- Find a way to integrate nonce into material ui cache on index.js -### _Continue conversion to TypeScript and Redux toolkit_ -TypeScript provides strong typing to improve code quality, and maintainability and reduce runtime errors. Redux toolkit reduces the amount of boilerplate required to use Redux within the application and provides a centralized environment for state initialization and management. ### _Enhance HTTP/2 Mock server functionality_ @@ -147,9 +208,9 @@ Currently, the HTTP/2 mock server has the ability to create a server that is acc - the ability to mock HTML responses (or remove the HTML option from the BodyEntryForm component) - Connect the headers and cookies to the mock endpoint creation -### _WebRTC features are there but buggy when interacting with other parts of the app_ +### _WebRTC features are there but may buggy when interacting with other parts of the app according to previous groups_ -In the latest iteration, the WebRTC feature was changed from STUN Server testing to Client RTC Connection testing, allowing Swell to test if another client is able to create an RTC connection to transmit text and video data. Because the RTCPeerConnection has to be initiated before we generate the SDP, this connection is set up differently from the other networks. Other networks purely need the primitive strings as input, and the response is created on click of `Send` (in the workspace panel). For WebRTC, the connection object is created as an input, and when the user clicks `Send` then the data transmitted data is allowed to be displayed (although data is being transmitted through the connection even before `Send` is clicked). This means the WebRTC ReqRes can't really be saved in history or re-connected beyond the first connection. +In a recent iteration, the WebRTC feature was changed from STUN Server testing to Client RTC Connection testing, allowing Swell to test if another client is able to create an RTC connection to transmit text and video data. Because the RTCPeerConnection has to be initiated before we generate the SDP, this connection is set up differently from the other networks. Other networks purely need the primitive strings as input, and the response is created on click of `Send` (in the workspace panel). For WebRTC, the connection object is created as an input, and when the user clicks `Send` then the data transmitted data is allowed to be displayed (although data is being transmitted through the connection even before `Send` is clicked). This means the WebRTC ReqRes can't really be saved in history or re-connected beyond the first connection. Areas for improvement: @@ -158,6 +219,14 @@ Areas for improvement: - End-to-End:'test/**tests**/subSuites/webRTCTest.js' - Integration: 'test/**tests**/IntegrationTests/webRTCIntegrationTests' +--- + +## Testing Overview and Future Iteration Work + +It is important to note that Swell uses both Mocha and Jest for testing. Jest is primarily used for unit testing, whereas Mocha with Playwright is primarily used for integration/end to end testing. `npm run test` will run both jest and mocha consecutively. Mocha tests are given a generous timeout because we found that without it, tests were very inconsistent if they relied on fetching public endpoint, for example. If you have slow internet, some tests can still fail if the response takes longer than the specified timeout window in the script and/or the file itself with .timeout(). + +Test coverage reports will differ greatly from what it will tell you in the terminal vs what the coverage report says if you open one of the html files in test/coverage. As of v1.18, in the terminal, Mocha coverage is at ~60%, and Jest coverage is at ~42%. There is definitely some overlap (obviously not 102% total coverage), but it's unclear what the actual total test coverage is. The current implementation of the 'total coverage' file is not entirely correct. Future groups may want to look into how to properly generate a total coverage report with the istanbul/nyc module, or with some other method. + ### _Incomplete E2E testing coverage_ Some of the following features either have broken, incomplete or no E2E testing coverage in the repository: @@ -171,11 +240,35 @@ Future iterations should consider fixing or adding E2E testing coverage for thes ### _Integration Testing_ -WebRTC integration testing has minor bugs to be ironed out, causing timeout issues. Integration testing is currently Swell's only way of making sure front-end user interaction appropriately interacts with Redux state management. +WebRTC integration testing has minor bugs to be ironed out, causing timeout issues. Integration testing is currently Swell's only way of making sure front-end user interaction appropriately interacts with Redux state management. + +Additionally, with GraphQL, the specific test that verifies Stress Test functionality is a bit inconsistent. When Stress Test is initiated, Redux toolkit will allow you to track the amount of tests being sent out, missed, and received via Redux state information. For some reason, there is a difference in outcomes between when the Stress Test is initiated through integration tests vs. when initiated through the interface as a user. When initiated (at least in GraphQL) through integration tests, you can see X number of stress tests sent out, but all of them will be missed and none received. + +When initiating the Stress Test using Swell's interface, you will see X number of stress tests sent out, and the same X number of stress tests received. We're unsure of why this is happening. The current 'Stress Test' section of GraphQL integration testing only tracks the number of stress tests sent out and passes if that number is updated on the Redux state. Ideally, to confirm successful Stress Test functionality, we would want to track the number of stress tests sent out and confirm it with the number of stress tests received within the Redux state. It seems to be consistent in HTTP, but not in GraphQL. Also, there is currently no 'Stress Test' section in integration testing for Websockets. -Additionally, with GraphQL, the specific test that verifies Stress Test functionality is a bit inconsistent. When Stress Test is initiated, Redux toolkit will allow you to track the amount of tests being sent out, missed, and received via Redux state information. For some reason, there is a difference in outcomes between when the Stress Test is initiated through integration tests vs. when initiated through the interface as a user. When initiated (at least in GraphQL) through integration tests, you can see X number of stress tests sent out, but all of them will be missed and none received. When initiating the Stress Test using Swell's interface, you will see X number of stress tests sent out, and the same X number of stress tests received. We're unsure of why this is happening. The current 'Stress Test' section of GraphQL integration testing only tracks the number of stress tests sent out and passes if that number is updated on the Redux state. Ideally, to confirm successful Stress Test functionality, we would want to track the number of stress tests sent out and confirm it with the number of stress tests received within the Redux state. It seems to be consistent in HTTP, but not in GraphQL. Also, there is currently no 'Stress Test' section in integration testing for Websockets. +Finally, if future iterators would like to completely cover the list of API-testing protocols with integration testing - there is still HTTP/2 and tRPC integration testing to be made. + +--- -Finally, if future iterators would like to completely cover the list of API-testing protocols with integration testing - there is still HTTP/2 and tRPC integration testing to be made. +## Backlog from Iteration Group v1.18 + +- Fix/Update GitHub Actions for Unit Testing +- Create a feature/function/endpoint to delete a mock route +- Dark Mode: Include settings for all the Drop Down Menus (as mentioned above) +- Randomly generate nonce on app launch (within index.js?) +- Find a way to parse nonce and add it to the MUI cache so 'unsafe-inline' for styles can also be removed +- Reduce endpoint buttons clickable area to only the button that changes color +- WorkspaceCollectionsContainer.tsx uses the connect function, should be converted to hooks like MainContiner +- store.ts - Remove any non-serializable values from the store (namely, replace Date objects with Date strings). +- Update http Jif +- Backend Testing http/2 file upload +- Convert NewRequestButton to MUI +- Convert WebRTCServerEntryForm to MUI +- Convert WebRTCSessionEntryForm to MUI +- Convert WebRTC components to all use material UI as per line 7 of WebRTCComposer.tsx +- Combine newRequestSlice.ts and newRequestFieldSlice.ts +- Update Excalidraw if necessary with new features/redux changes +- ErrorBoundary.tsx may not be functional or necessary (Leave for now) --- @@ -188,13 +281,14 @@ Continuous Integration and Continuous Development have been implemented using Gi - CI tests will automatically run on any pull request - CD packages will deploy on a successful merge request to the master branch. This will automatically deploy the packages in the GitHub Releases tab. It will create the release based on the version number so make sure the version number is correct in the codebase. - CD packages will deploy on EACH merge request to the master branch. Changes made to the master branch should be the final changes. However if multiple merge requests are accept to the master branch, it will create multiple releases. Please keep this in mind and delete the extra releases that are created. - + In order to successfully enable the Continuous Development pipeline, do the following: -1. Create a personal GitHub Token (User Settings -> Developer Settings -> Tokens) + +1. Create a personal GitHub Token (User Settings -> Developer Settings -> Tokens) 2. Ensure the following are selected for the token: repo (All), workflow, write:packages, user:email 3. Copy the personal token generated 4. Add the token to your repos secrets (Settings -> Secrets and Variables -> Actions -> Repository Secret) -After following these steps, Github Actions should have the proper permissions to write to Github Releases. This should create a draft of the release with the necessary files. + After following these steps, Github Actions should have the proper permissions to write to Github Releases. This should create a draft of the release with the necessary files. You should also ensure the repo information is correct in package.json. Look at the section "publish" and ensure that the information is correct. @@ -226,7 +320,7 @@ While electron builder supports [multi-platform build](https://www.electron.buil For Mac users, running `npm run package-mac` and `npm run package-win` (as defined in `package.json`) would allow you to package the Swell app for Mac and Windows environments. If you try to package for the Linux environment (i.e. `npm run package-all`, `npm run package-linux`, `npm run gh-publish`), you will run into issues requiring `snapcraft` and `multipass` to create a Linux virtual machine in order to package the application. You can try to install `snapcraft` and `multipass` via `brew` per instructions, but there has not been much success locally. -The only remaining option to build a Linux package for MacOS users is via the CI/CD pipeline or through asking a developer with WSL or Linux environment to package the application. +The only remaining option to build a Linux package for non-WSL users is via the CI/CD pipeline or through asking a developer with WSL or Linux environment to package the application. - Ask the developer to clone the project into their local WSL/Linux environment. **The user does not need the ability to open the electron application.** - run `npm install && npm run package-linux` @@ -244,33 +338,29 @@ All releases should be done in GitHub. There are many resources on how to create See [Swell's release page](https://github.com/open-source-labs/Swell/releases) for examples. --- + ## How can I update [Swell's website](https://getswell.io/) after the iteration? The website is hosted on AWS, which means you will need credentials to access the files (in S3 buckets) for the latest version of the website. You will need to contact OS Labs for the credentials, or if you are iterating the product as part of a Codesmith program they should have access to the information needed. You also will need to invalidate the previous distribution to ensure your modifications are properly deployed. See gifs below on how to accomplish this. - 1. Uploading a new index.html file - [Modifying the index.zip](https://github.com/open-source-labs/Swell/files/12423736/Modifying.the.index.zip) + [Modifying the index.zip](https://github.com/open-source-labs/Swell/files/12423736/Modifying.the.index.zip) - Ensure you repeat this process for both getswell.io and www.getswell.io as both are public access points to the website. - It might be easier to create a new index.html file on your own code editor, download the current index.html file and transfer the entire document to your newly created index.html file and continue to modify as needed. - You do not need to delete the old versions of the index.html file it will automatically update to your newly modified file. - 2. Uploading Contributor Images on S3 Bucket -[Updating images.zip](https://github.com/open-source-labs/Swell/files/12423737/Updating.images.zip) + [Updating images.zip](https://github.com/open-source-labs/Swell/files/12423737/Updating.images.zip) - Ensure you repeat this process for both getswell.io and www.getswell.io buckets as both are viable public access points - Ensure matching file types and names between the /img/SwellTeamPics/ and the index.html src attribute. 3. Invalidate previous distribution -[Invalidation Workflow.zip](https://github.com/open-source-labs/Swell/files/12423739/Invalidation.Workflow.zip) + [Invalidation Workflow.zip](https://github.com/open-source-labs/Swell/files/12423739/Invalidation.Workflow.zip) - This process should be done after you have finalized all your modifications - - - Things to consider updating: - Ensure the download links are pointing to the latest version @@ -280,9 +370,3 @@ Things to consider updating: --- -## Maintaining this document - -This should serve as an entry point for any developers who wish to iterate on Swell and therefore, should be kept as up-to-date as possible. **At the end of your iteration, you are strongly encouraged to update this document for future developers.** - -Thank you for your consideration and let's work on making Swell one of the best open-source products to contribute![UpdateContImgs.gif.zip] - diff --git a/docs/vcxsrv_alt_disable_access_control.png b/docs/vcxsrv_alt_disable_access_control.png new file mode 100644 index 000000000..5448d24e3 Binary files /dev/null and b/docs/vcxsrv_alt_disable_access_control.png differ diff --git a/docs/vcxsrv_client_startup.png b/docs/vcxsrv_client_startup.png new file mode 100644 index 000000000..98361a349 Binary files /dev/null and b/docs/vcxsrv_client_startup.png differ diff --git a/docs/vcxsrv_disable_access_control.png b/docs/vcxsrv_disable_access_control.png new file mode 100644 index 000000000..f41cb0a21 Binary files /dev/null and b/docs/vcxsrv_disable_access_control.png differ diff --git a/docs/vcxsrv_display_settings.png b/docs/vcxsrv_display_settings.png new file mode 100644 index 000000000..01ed9aeb4 Binary files /dev/null and b/docs/vcxsrv_display_settings.png differ diff --git a/index-csp.html b/index-csp.html index 55870bd7c..de5b94290 100644 --- a/index-csp.html +++ b/index-csp.html @@ -1,10 +1,17 @@ - - - - Swell - - - - + + + + + + <%= htmlWebpackPlugin.options.title %> + + + + +
+ + + + \ No newline at end of file diff --git a/jest.config.js b/jest.config.js index 701a0e917..c84d21cab 100644 --- a/jest.config.js +++ b/jest.config.js @@ -6,7 +6,7 @@ module.exports = { // "collectCoverage": true, electron: '/__mocks__/electronMock.js', '\\.(css|less|sass|scss)$': '/__mocks__/styleMocks.js', - '\\.(gif|ttf|eot|svg|png)$': '/__mocks__/fileMock.js', + '\\.(gif|ttf|eot|svg|png)$': '/test/__mocks__/fileMock.js', '^dexie$': '/node_modules/dexie' }, testPathIgnorePatterns: [ diff --git a/main.js b/main.js index 14a7e9735..607705d87 100644 --- a/main.js +++ b/main.js @@ -31,7 +31,14 @@ // app - Control your application's event lifecycle // ipcMain - Communicate asynchronously from the main process to renderer processes // ** Entry point for Electron ** -const { app, BrowserWindow, ipcMain, dialog, shell, session } = require('electron'); +const { + app, + BrowserWindow, + ipcMain, + dialog, + shell, + session, +} = require('electron'); const os = require('node:os'); const { autoUpdater } = require('electron-updater'); @@ -70,10 +77,8 @@ require('./main_process/main_trpcController.js')(); // require mac touchbar const { touchBar } = require('./main_process/main_touchbar.js'); - - -const contextMenu = require('electron-context-menu') -contextMenu() +const contextMenu = require('electron-context-menu'); +contextMenu(); // configure logging // autoUpdater.logger = log; @@ -155,12 +160,12 @@ function createWindow() { // dev mode title mainWindow.setTitle('Swell (devMode)'); - - // if we are in developer mode Add React & Redux DevTools to Electron App + // if we are in developer mode Add React & Redux DevTools to Electron App // this manually installs the depricated dev tools that are compatible with electron - session.defaultSession.loadExtension(reactDevToolsPath) + session.defaultSession + .loadExtension(reactDevToolsPath) .then((name) => console.log(`Added Extension: ${name}`)) .catch((err) => console.log('An error occurred: ', err)); // ****** if current react dev tools version did work with electron we would use the below commented code **** @@ -229,7 +234,7 @@ app.on('ready', () => { * should not be removed. The servers must be required upon app startup (especially in * packaged versions) or else the packaged app would not recognize the servers at all. */ - const express = require('./src/server/server'); + const express = require('./src/server/server.js'); const mockServer = require('./src/server/mockServer.js'); autoUpdater.checkForUpdates(); } @@ -519,7 +524,7 @@ const { fork } = require('child_process'); // starts the mock server by forking a Node child process ipcMain.on('start-mock-server', () => { - mockServerProcess = fork('node', ['./src/server/mockServer.js']); + mockServerProcess = fork('./src/server/mockServer.js'); mockServerProcess.on('error', (err) => { console.log('Error starting mock server', err); }); diff --git a/main_process/SSEController.js b/main_process/SSEController.js index 456e96ea8..ea990e868 100644 --- a/main_process/SSEController.js +++ b/main_process/SSEController.js @@ -53,10 +53,6 @@ SSEController.readStream = (reqResObj, event, timeDiff) => { const sse = new EventSource(reqResObj.url); SSEController.sseOpenConnections[reqResObj.id] = sse; - sse.onopen = () => { - // console.log(`SSE at ${reqResObj.url} opened!`); - }; - sse.onmessage = (message) => { // message is not a javascript object, so we spread its contents into one const newMessage = { ...message }; diff --git a/main_process/main_testingController.js b/main_process/main_testingController.js index 70e54bce5..79e8b45ca 100644 --- a/main_process/main_testingController.js +++ b/main_process/main_testingController.js @@ -78,7 +78,7 @@ testHttpController.runTest = ( addOneResult({ message: errObj.message, - status: 'FAILhi', + status: 'FAIL', expected: errObj.expected, actual: err, }); diff --git a/package.json b/package.json index d7d1169d0..f8133ee47 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "swell", - "version": "1.17.0", + "version": "1.18.0", "description": "Swell", "main": "main.js", "repository": "https://github.com/open-source-labs/Swell", "scripts": { "report": "istanbul report --dir ./test/coverage/total-coverage html", - "test": "webpack --mode=production --config ./webpack.production.js && cross-env process.env.NODE_ENV=test nyc --report-dir ./test/coverage/mocha-coverage --reporter json --reporter text --reporter html mocha --timeout 3000 --exit", + "test": "jest && webpack --mode=production --config ./webpack.production.js && cross-env process.env.NODE_ENV=test nyc --report-dir ./test/coverage/mocha-coverage --reporter json --reporter text --reporter html mocha --timeout 15000 --exit", "server-sse": "node ./test/SSE_HTTP1_server.js", "server-gql": "node ./test/graphqlServer.mjs", "server-grpc": "node ./test/grpcServer.js", @@ -16,7 +16,7 @@ "server-websocket": "node ./test/websocketServer.js", "server-webrtc": "node ./test/webrtcWSServer.js", "test-jest": "jest", - "test-mocha": "webpack --mode=production --config ./webpack.production.js && cross-env process.env.NODE_ENV=test mocha --timeout 3000 --exit", + "test-mocha": "webpack --mode=production --config ./webpack.production.js && cross-env process.env.NODE_ENV=test nyc --report-dir ./test/coverage/mocha-coverage --reporter json --reporter text --reporter html mocha --timeout 15000 --exit", "test-mocha-zero": "webpack --mode=production --config ./webpack.production.js && cross-env process.env.NODE_ENV=test mocha --timeout 0 --exit", "format": "prettier --write \"**/*.+(js|jsx| tsx| json|css|md)\"", "lint": "eslint .", @@ -97,8 +97,9 @@ "dependencies": { "@apollo/client": "^3.5.0", "@apollo/server": "^4.6.0", - "@emotion/react": "^11.11.1", - "@emotion/styled": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5", "@graphql-tools/schema": "^8.3.10", "@grpc/grpc-js": "^1.6.7", "@grpc/proto-loader": "^0.6.9", @@ -115,6 +116,7 @@ "bulma": "^0.9.3", "bulma-checkradio": "^2.1.3", "bulma-switch": "^2.0.4", + "chai": "^4.3.10", "classnames": "^2.3.1", "cookie-parser": "^1.4.6", "cors": "^2.8.5", @@ -127,10 +129,11 @@ "electron-devtools-installer": "^3.2.0", "electron-log": "^4.4.6", "electron-updater": "^5.3.0", + "esm": "^3.2.25", "eventsource": "^2.0.1", "express": "^4.18.0", "express-sse": "^0.5.3", - "fs": "0.0.1-security", + "fs": "^0.0.1-security", "fs-extra": "^10.1.0", "graphql": "^16.4.0", "graphql-tag": "^2.12.6", @@ -184,6 +187,7 @@ "@testing-library/jest-dom": "^6.1.4", "@testing-library/react": "^14.0.0", "@types/cookie": "^0.5.1", + "@types/cookie-parser": "^1.4.7", "@types/node": "^17.0.28", "@types/react": "^18.0.8", "@types/react-dom": "^18.0.0", @@ -196,7 +200,6 @@ "@typescript-eslint/eslint-plugin": "^5.21.0", "@typescript-eslint/parser": "^5.21.0", "babel-loader": "^8.2.5", - "chai": "^4.3.10", "chai-http": "^4.4.0", "concurrently": "^8.0.1", "cross-env": "^7.0.3", @@ -227,12 +230,13 @@ "postcss-pxtorem": "^6.0.0", "prettier": "^2.6.2", "prettier-eslint": "^14.0.1", + "redux-mock-store": "^1.5.4", "sass-loader": "^12.6.0", "source-map-loader": "^3.0.1", "style-loader": "^3.3.1", "ts-migrate": "^0.1.28", - "ts-node": "^10.9.1", - "typescript": "^4.6.3", + "ts-node": "^10.9.2", + "typescript": "^4.9.5", "url-loader": "^4.1.1", "webpack": "^5.72.0", "webpack-bundle-analyzer": "^4.5.0", @@ -247,6 +251,31 @@ "url": "http://www.getswell.io" }, "contributors": [ + { + "name": "Carter Sarkela", + "email": "cartersarkela@gmail.com", + "url": "https://github.com/CarterSarkela" + }, + { + "name": "Karol Krzywon", + "email": "kkrzywon@gmail.com", + "url": "https://github.com/kkrzywon" + }, + { + "name": "Howard Sun", + "email": "howardsun@protonmail.com", + "url": "https://github.com/howardCodeGit" + }, + { + "name": "Michael Underbrink", + "email": "MichaelUnderbrink@gmail.com", + "url": "https://github.com/MUnderbrink90" + }, + { + "name": "Adeeb Bayat", + "email": "adeebnbayat@gmail.com", + "url": "https://github.com/adeebbayat" + }, { "name": "Chris Suzukida", "email": "chris.suzukida@gmail.com", @@ -543,3 +572,4 @@ } ] } + diff --git a/preload.js b/preload.js index f553dc756..688580eeb 100644 --- a/preload.js +++ b/preload.js @@ -74,6 +74,7 @@ const apiObj = { console.log('Channel not allowed: ', channel); } }, + versions: process.versions, }; // this is because we need to have context isolation to be false for spectron diff --git a/src/assets/style/darkMode.scss b/src/assets/style/darkMode.scss index ead0179c1..80e36a1c1 100644 --- a/src/assets/style/darkMode.scss +++ b/src/assets/style/darkMode.scss @@ -1,10 +1,12 @@ $neutral-100: #aeaeae; -$neutral-200: #9f9f9f; +$neutral-200: #9c9cb1; $neutral-300: #575757; $neutral-400: #1f282e; $neutral-500: #434343; $neutral-600: #000000; -$text: #00000099; +$text: #1e3163; +$text-dark: #000000; +$text-dark-placeholder: #558a78; .is-dark-mode { background-color: $neutral-500 !important; @@ -41,13 +43,16 @@ $text: #00000099; color: white; } -.dark-protocol-text { - color: $text !important; +.dark-text { + color: $text-dark !important; } .dark-address-input { background-color: $neutral-300 !important; color: white !important; } +.dark-address-input::placeholder { + color: $text-dark-placeholder !important; +} .dark-divider { // this is the divider underneath tabs -Prince border-left: 1px solid $neutral-300; diff --git a/src/assets/style/workspace.scss b/src/assets/style/workspace.scss index 5f89685e9..00177dc72 100644 --- a/src/assets/style/workspace.scss +++ b/src/assets/style/workspace.scss @@ -266,4 +266,4 @@ display: flex; align-content: center; flex-direction: column; -} \ No newline at end of file +} diff --git a/src/client/components/App.tsx b/src/client/components/App.tsx index f613fb55a..b9beec912 100644 --- a/src/client/components/App.tsx +++ b/src/client/components/App.tsx @@ -39,6 +39,7 @@ const App = () => { * All of the main components are rendered from here. Excluding the update pop * up, there are only 3 main containers for this application. */ + return (
@@ -60,7 +61,7 @@ const App = () => { /> - {/* Main container. Contains the composer and response panes. */} + {/* Main container, Contains the composer and response panes. */} diff --git a/src/client/components/Versions.tsx b/src/client/components/Versions.tsx new file mode 100644 index 000000000..b018d5e85 --- /dev/null +++ b/src/client/components/Versions.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { useState } from 'react'; + +function Versions(): JSX.Element { + const [versions] = useState(window.api.versions); + + return ( +
    +
  • Electron v{versions.electron}
  • +
  • Chromium v{versions.chrome}
  • +
  • Node v{versions.node}
  • +
+ ); +} + +export default Versions; + diff --git a/src/client/components/legacy-components/HistoryConatainerBackup.tsx b/src/client/components/legacy-components/HistoryConatainerBackup.tsx new file mode 100644 index 000000000..311cdf17e --- /dev/null +++ b/src/client/components/legacy-components/HistoryConatainerBackup.tsx @@ -0,0 +1,130 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { useAppDispatch, useAppSelector } from '../../toolkit-refactor/hooks'; + +import * as HistorySlice from '../../toolkit-refactor/slices/historySlice'; +import { fieldsReplaced } from '../../toolkit-refactor/slices/newRequestFieldsSlice'; + +import { + newRequestCookiesSet, + newRequestStreamsSet, + newRequestBodySet, + newRequestHeadersSet, +} from '../../toolkit-refactor/slices/newRequestSlice'; + +import HistoryDate from './HistoryDate'; +import ClearHistoryBtn from './buttons/ClearHistoryBtn'; +import { Dispatch } from 'redux'; + +import { $TSFixMe, + ReqRes, + NewRequestBody, + CookieOrHeader, + NewRequestStreams, + NewRequestFields } from '../../../types' + +import { RootState } from '../../toolkit-refactor/store'; + +interface NewRequestCookiesSet { + cookiesArr: CookieOrHeader[]; + count: number; +} + +interface NewRequestHeadersSet { + headersArr: CookieOrHeader[]; + count: number; +};; + +interface Props { + history: [], + historyCleared: () => void, + historyDeleted: string, + fieldsReplaced: (obj: {}) => void, + newRequestHeadersSet: (obj: NewRequestHeadersSet) => void, + newRequestCookiesSet: (obj: NewRequestCookiesSet) => void, + newRequestBodySet: (obj: NewRequestBody) => void, + newRequestStreamsSet: NewRequestStreams, + className: string +}; + +/**@todo switch to hooks? */ +const mapStateToProps = (store: RootState) => ({ + history: store.history, + newRequestFields: store.newRequestFields, + newRequestStreams: store.newRequest.newRequestStreams, +}); + +/**@todo switch to hooks? */ +const mapDispatchToProps = (dispatch: Dispatch) => ({ + historyCleared: () => { + dispatch(HistorySlice.historyCleared()); + }, + historyDeleted: (reqRes: ReqRes) => { + dispatch(HistorySlice.historyDeleted(reqRes)); + }, + newRequestHeadersSet: (requestHeadersObj: $TSFixMe) => { + dispatch(newRequestHeadersSet(requestHeadersObj)); + }, + fieldsReplaced: (requestFields: $TSFixMe) => { + dispatch(fieldsReplaced(requestFields)); + }, + newRequestBodySet: (requestBodyObj: $TSFixMe) => { + dispatch(newRequestBodySet(requestBodyObj)); + }, + newRequestCookiesSet: (requestCookiesObj: $TSFixMe) => { + dispatch(newRequestCookiesSet(requestCookiesObj)); + }, + newRequestStreamsSet: (requestStreamsObj: $TSFixMe) => { + dispatch(newRequestStreamsSet(requestStreamsObj)); + }, +}); + +const HistoryContainer = (props: Props) => { + const { + history, + historyCleared, + historyDeleted, + fieldsReplaced, + newRequestHeadersSet, + newRequestCookiesSet, + newRequestBodySet, + newRequestStreamsSet, + } = props; + + const isDark = useAppSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); + + // history is already sorted by created_at from getHistory + const historyDates = history.map((date: Date, index: number): JSX.Element => ( + + )); + + return ( + + + + +
{historyDates}
+
+ ); +}; + +export default connect(mapStateToProps, mapDispatchToProps)(HistoryContainer); \ No newline at end of file diff --git a/src/client/components/legacy-components/Http2Body.tsx b/src/client/components/legacy-components/Http2Body.tsx new file mode 100644 index 000000000..392bca7bf --- /dev/null +++ b/src/client/components/legacy-components/Http2Body.tsx @@ -0,0 +1,103 @@ +/* +This component was commented out and moved to the legacy component files due to it's functionality being replaced by the shared component BinaryTypeSelect file. +Following the references up the app, the Http2MetaData is the last and only reference for the chain of files Http2Body, http2MetaData, and HttpBodyTypeSelect. +This component was originally located in the Http2Composer folder. +*/ + +// import React from 'react'; +// // Http2Body needs access to the Redux store. +// import { connect } from 'react-redux'; + +// import { AppDispatch, RootState } from '../../toolkit-refactor/store'; +// import { $TSFixMeObject } from '../../../types'; + +// import { +// newRequestBodySet, +// newRequestHeadersSet, +// } from '../../toolkit-refactor/slices/newRequestSlice'; + +// // Import local components +// import BodyTypeSelect from './HttpBodyTypeSelect'; +// // Import MUI components +// import { Box } from '@mui/material'; +// import WWWForm from '../main/sharedComponents/requestForms/WWWForm'; +// import JSONTextArea from '../main/sharedComponents/JSONTextArea'; +// import TextCodeArea from '../main/sharedComponents/TextCodeArea'; + +// /**@todo switch to hooks? */ +// const mapStateToProps = (store: RootState) => { +// return { +// newRequestHeaders: store.newRequest.newRequestHeaders, +// newRequestBody: store.newRequest.newRequestBody, +// warningMessage: store.warningMessage, +// }; +// }; + +// /**@todo switch to hooks? */ +// const mapDispatchToProps = (dispatch: AppDispatch) => ({ +// newRequestHeadersSet: (requestHeadersObj: $TSFixMeObject) => { +// dispatch(newRequestHeadersSet(requestHeadersObj)); +// }, +// newRequestBodySet: (requestBodyObj: $TSFixMeObject) => { +// dispatch(newRequestBodySet(requestBodyObj)); +// }, +// }); + +// function Http2Body({ +// newRequestHeaders, +// newRequestBody, +// warningMessage, +// newRequestHeadersSet, +// newRequestBodySet, +// }) { +// const bodyEntryArea = () => { +// //BodyType of none : display nothing +// if (newRequestBody.bodyType === 'none') { +// return; +// } +// //BodyType of XWWW... : display WWWForm entry +// if (newRequestBody.bodyType === 'x-www-form-urlencoded') { +// return ( +// +// ); +// } +// //RawType of application/json : Text area box with error checking +// if (newRequestBody.rawType === 'application/json') { +// return ( +// +// ); +// } + +// return ( +// { +// newRequestBodySet({ +// ...newRequestBody, +// bodyContent: value, +// }); +// }} +// /> +// ); +// }; +// return ( +// +// +// {bodyEntryArea()} +// +// ); +// } + +// export default connect(mapStateToProps, mapDispatchToProps)(Http2Body); diff --git a/src/client/components/legacy-components/Http2MetaData.tsx b/src/client/components/legacy-components/Http2MetaData.tsx new file mode 100644 index 000000000..8bf3303fe --- /dev/null +++ b/src/client/components/legacy-components/Http2MetaData.tsx @@ -0,0 +1,92 @@ + +/* +This component was commented out and moved to the legacy component files due to it's functionality being replaced by the shared component BinaryTypeSelect file. +Following the references up the app, the Http2MetaData is the last and only reference for the chain of files Http2Body, http2MetaData, and HttpBodyTypeSelect. +*/ + +// import React, { useState } from 'react'; +// // Import local components +// import KeyValueTable from '../main/http2-composer/KeyValueTable'; +// import Http2Body from './Http2Body'; +// // Import MUI components +// import { Box, Tabs, Tab } from '@mui/material'; + +// interface TabPanelProps { +// children?: React.ReactNode; +// index: number; +// value: number; +// } + +// function TabPanel(props: TabPanelProps) { +// const { children, value, index, ...other } = props; +// return ( +// +// ); +// } + +// function a11yProps(index: number) { +// return { +// id: `http2-tab-${index}`, +// 'aria-controls': `http2-tabpanel-${index}`, +// }; +// } + +// export default function Http2MetaData({ +// parameters, +// setParameters, +// headers, +// setHeaders, +// cookies, +// setCookies, +// http2Method, +// }) { +// const [tab, setTab] = useState(0); +// const handleTabChange = (event: React.SyntheticEvent, tab: number) => { +// setTab(tab); +// }; +// return ( +// +// +// +// +// +// +// +// +// +// +// {/** @todo add parameter support for HTTP2 requests */} +// +// +// +// +// +// +// +// +// +// +// {http2Method === 'GET' ? ( +// 'No body required for this method' +// ) : ( +// +// )} +// +// +// Render Test Snippets Here +// +// +// ); +// } diff --git a/src/client/components/legacy-components/HttpBodyTypeSelect.tsx b/src/client/components/legacy-components/HttpBodyTypeSelect.tsx new file mode 100644 index 000000000..e20812322 --- /dev/null +++ b/src/client/components/legacy-components/HttpBodyTypeSelect.tsx @@ -0,0 +1,141 @@ +/* +This component was commented out and moved to the legacy component files due to it's functionality being replaced by the shared component BinaryTypeSelect file. +Following the references up the app, the Http2MetaData is the last and only reference for the chain of files Http2Body, http2MetaData, and HttpBodyTypeSelect. +*/ + +// import React, { useState, useRef, useEffect } from 'react'; +// import { connect } from 'react-redux'; + +// import { RootState } from '../../toolkit-refactor/store'; + +// import { +// newRequestBodySet, +// newRequestHeadersSet, +// } from '../../toolkit-refactor/slices/newRequestSlice'; + +// import { +// Box, +// Button, +// Select, +// MenuItem, +// FormControl, +// SelectChangeEvent, +// } from '@mui/material'; + +// /**@todo switch to use hooks? */ +// const mapStateToProps = (store: RootState) => { +// return { +// newRequestHeaders: store.newRequest.newRequestHeaders, +// newRequestBody: store.newRequest.newRequestBody, +// }; +// }; + +// /**@todo switch to use hooks? */ +// const mapDispatchToProps = (dispatch) => ({ +// newRequestHeadersSet: (requestHeadersObj) => { +// dispatch(newRequestHeadersSet(requestHeadersObj)); +// }, +// newRequestBodySet: (requestBodyObj) => { +// dispatch(newRequestBodySet(requestBodyObj)); +// }, +// }); + +// function HttpBodyTypeSelect({ +// newRequestHeaders, +// newRequestBody, +// newRequestHeadersSet, +// newRequestBodySet, +// }) { +// const handleBodyTypeSelect = (event: SelectChangeEvent) => { +// newRequestBodySet({ +// ...newRequestBody, +// bodyType: event.target.value, +// }); +// }; + +// const handleRawBodyTypeSelect = (event: SelectChangeEvent) => { +// newRequestBodySet({ +// ...newRequestBody, +// rawType: event.target.value, +// }); +// }; + +// const prettifyJSON = () => { +// const prettyString = JSON.stringify( +// JSON.parse(newRequestBody.bodyContent), +// null, +// 4 +// ); +// newRequestBodySet({ +// ...newRequestBody, +// bodyContent: prettyString, +// }); +// }; + +// return ( +// +// +// +// +// {newRequestBody.bodyType === 'raw' && ( +// +// +// +// )} +// {newRequestBody.bodyType === 'raw' && +// newRequestBody.rawType === 'application/json' && ( +// +// )} +// +// ); +// } + +// export default connect(mapStateToProps, mapDispatchToProps)(HttpBodyTypeSelect); diff --git a/src/client/components/legacy-components/MainContainer.backup b/src/client/components/legacy-components/MainContainer.backup new file mode 100644 index 000000000..2c393c574 --- /dev/null +++ b/src/client/components/legacy-components/MainContainer.backup @@ -0,0 +1,143 @@ +import React from 'react'; +import { Routes, Route } from 'react-router-dom'; +import { connect } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; +import { ReqRes, $TSFixMe, $TSFixMeObject, RequestWebRTC } from '../../../types'; + +import * as ReqResSlice from '../../toolkit-refactor/slices/reqResSlice'; + +import { + composerFieldsReset, + newRequestSSESet, + newRequestCookiesSet, + newRequestStreamsSet, + newRequestBodySet, + newRequestHeadersSet, +} from '../../toolkit-refactor/slices/newRequestSlice'; + +import { openApiRequestsReplaced } from '../../toolkit-refactor/slices/newRequestOpenApiSlice'; + +import { + setWorkspaceActiveTab, + /*, setComposerDisplay */ +} from '../../toolkit-refactor/slices/uiSlice'; + +import { + fieldsReplaced, + newTestContentSet, +} from '../../toolkit-refactor/slices/newRequestFieldsSlice'; +import { setWarningMessage } from '../../toolkit-refactor/slices/warningMessageSlice'; + +// Import local components. +import Http2Composer from './http2-composer/Http2Composer'; +import GraphQLComposer from './GraphQL-composer/GraphQLComposer'; +import GRPCComposer from './GRPC-composer/GRPCComposer'; +import WebSocketComposer from './WebSocket-composer/WebSocketComposer'; +import WebRTCComposer from './WebRTC-composer/WebRTCComposer'; +import OpenAPIComposer from './OpenAPI-composer/OpenAPIComposer'; +import WebhookComposer from './WebHook-composer/WebhookComposer'; +import TRPCComposer from './TRPC-composer/TRPCComposer'; +import MockServerComposer from './MockServer-composer/MockServerComposer'; +import ResponsePaneContainer from './response-composer/ResponsePaneContainer'; + +// Import MUI components +import { Box } from '@mui/material'; +import { AppDispatch, RootState } from '../../toolkit-refactor/store'; +import Split from 'react-split'; + + + + + + + +/**@todo switch to hooks? */ +const mapStateToProps = (store: RootState) => { + return { + reqResArray: store.reqRes.reqResArray, + newRequestFields: store.newRequestFields, + newRequestHeaders: store.newRequest.newRequestHeaders, + newRequestStreams: store.newRequest.newRequestStreams, + newRequestBody: store.newRequest.newRequestBody, + // newRequestOpenAPI: store.newRequestOpenApi, + newRequestCookies: store.newRequest.newRequestCookies, + newRequestSSE: store.newRequest.newRequestSSE, + warningMessage: store.warningMessage, + introspectionData: store.introspectionData, + }; +}; + +/**@todo switch to hooks? */ +const mapDispatchToProps = (dispatch: AppDispatch) => ({ + reqResItemAdded: (reqRes: ReqRes) => { + dispatch(ReqResSlice.reqResItemAdded(reqRes)); + }, + setWarningMessage: (message: $TSFixMe) => { + dispatch(setWarningMessage(message)); + }, + // setComposerDisplay: (composerDisplay) => { + // dispatch(setComposerDisplay(composerDisplay)); + // }, + newRequestHeadersSet: (requestHeadersObj: $TSFixMeObject) => { + dispatch(newRequestHeadersSet(requestHeadersObj)); + }, + newRequestStreamsSet: (requestStreamsObj: $TSFixMeObject) => { + dispatch(newRequestStreamsSet(requestStreamsObj)); + }, + fieldsReplaced: (requestFields: $TSFixMe) => { + dispatch(fieldsReplaced(requestFields)); + }, + newRequestBodySet: (requestBodyObj: $TSFixMeObject) => { + dispatch(newRequestBodySet(requestBodyObj)); + }, + newTestContentSet: (testContent: $TSFixMe) => { + dispatch(newTestContentSet(testContent)); + }, + newRequestCookiesSet: (requestCookiesObj: $TSFixMeObject) => { + dispatch(newRequestCookiesSet(requestCookiesObj)); + }, + newRequestSSESet: (requestSSEBool: boolean) => { + dispatch(newRequestSSESet(requestSSEBool)); + }, + openApiRequestsReplaced: (parsedDocument: $TSFixMe) => { + dispatch(openApiRequestsReplaced(parsedDocument)); + }, + composerFieldsReset: () => { + dispatch(composerFieldsReset()); + }, + setWorkspaceActiveTab: (tabName: $TSFixMe) => { + dispatch(setWorkspaceActiveTab(tabName)); + }, + +}); + + +function MainContainer(props: $TSFixMeObject) { + return ( + + + + + } /> + } /> + } /> + } /> + {/* WebRTC has been completely refactored to hooks - no props needed */} + } /> + } /> + } /> + } /> + } + /> + + + + + + ); +} + +export default connect(mapStateToProps, mapDispatchToProps)(MainContainer); + diff --git a/src/client/components/main/GRPC-composer/GRPCAutoInputForm.tsx b/src/client/components/main/GRPC-composer/GRPCAutoInputForm.tsx index d21fbb245..618db218f 100644 --- a/src/client/components/main/GRPC-composer/GRPCAutoInputForm.tsx +++ b/src/client/components/main/GRPC-composer/GRPCAutoInputForm.tsx @@ -6,18 +6,7 @@ import GRPCServiceOrRequestSelect from './GRPCServiceOrRequestSelect'; import { $TSFixMe, NewRequestStreams } from '../../../../types'; interface Props { - newRequestStreams: { - selectedService: string | null; - selectedRequest: string | null; - services: any[]; - streamsArr: any[]; - streamContent: string[]; - selectedPackage: string | null; - selectedStreamingType: string | null; - selectedServiceObj: any; - protoContent: string | null; - initialQuery: string; - }; + newRequestStreams: NewRequestStreams newRequestStreamsSet: (arg: NewRequestStreams) => void; } @@ -41,12 +30,12 @@ const GRPCAutoInputForm: React.FC = (props) => { } = props.newRequestStreams; // event handler for changes made to the Select Services dropdown list - const setService = (e) => { - setServiceOption(e.target.textContent); + const setService = (e: React.ChangeEvent) => { + setServiceOption(e.target.textContent!); const serviceName = e.target.textContent !== 'Select Service' ? e.target.textContent : null; - const serviceObj = services.find( - (ser) => ser.name === e.target.textContent + const serviceObj = services!.find( + (ser: Record) => ser.name === e.target.textContent ); // clears all stream query bodies except the first one let streamsArr = [props.newRequestStreams.streamsArr[0]]; @@ -67,9 +56,9 @@ const GRPCAutoInputForm: React.FC = (props) => { }; // event handler for changes made to the Select Requests dropdown list - const setRequest = (e) => { + const setRequest = (e: React.ChangeEvent) => { //update component state - setRequestOption(e.target.textContent); + setRequestOption(e.target.textContent!); //clear streams array and content except first index const newStreamsArr = [streamsArr[0]]; const newStreamContent = [streamContent[0]]; diff --git a/src/client/components/main/GRPC-composer/GRPCComposer.tsx b/src/client/components/main/GRPC-composer/GRPCComposer.tsx index 9805b6c25..3a3973a39 100644 --- a/src/client/components/main/GRPC-composer/GRPCComposer.tsx +++ b/src/client/components/main/GRPC-composer/GRPCComposer.tsx @@ -10,12 +10,12 @@ import GRPCProtoEntryForm from './GRPCProtoEntryForm'; import NewRequestButton from '../sharedComponents/requestButtons/NewRequestButton'; import TestEntryForm from '../sharedComponents/requestForms/TestEntryForm'; -import { $TSFixMe, ReqRes } from '../../../../types.js'; +import { CookieOrHeader, GRPCComposerProps, ReqRes } from '../../../../types.js'; // Import MUI components import { Box } from '@mui/material'; -export default function GRPCComposer(props: $TSFixMe) { +export default function GRPCComposer(props: GRPCComposerProps) { // destructure the props from mainContainer const { @@ -50,7 +50,6 @@ export default function GRPCComposer(props: $TSFixMe) { selectedService, selectedRequest, selectedPackage, - streamingType, initialQuery, streamsArr, streamContent, @@ -73,7 +72,6 @@ export default function GRPCComposer(props: $TSFixMe) { // but needs to be -> [{id:0, "query": ""}] const [streamsArrLength, setStreamsArrayLength] = useState(newRequestStreams.streamsArr.length); - const requestValidationCheck = () => { interface ValidationMessage { uri?: string; @@ -87,7 +85,7 @@ export default function GRPCComposer(props: $TSFixMe) { const addNewRequest = () => { const warnings = requestValidationCheck(); - if (Object.keys(warnings).length > 0) { + if (warnings !== true) { setWarningMessage(warnings); return; } @@ -110,13 +108,13 @@ export default function GRPCComposer(props: $TSFixMe) { queryArr.push(JSON.parse(query)); } // grabbing streaming type to set method in reqRes.request.method - const grpcStream = document.getElementById('stream').innerText; + const grpcStream = document.getElementById('stream')!.innerText; // create reqres obj to be passed to controller for further actions/tasks const reqRes: ReqRes = { id: uuid(), createdAt: new Date(), - protocol: '', + protocol, url, graphQL, gRPC, @@ -128,7 +126,7 @@ export default function GRPCComposer(props: $TSFixMe) { checkSelected: false, request: { method: grpcStream, - headers: headersArr.filter((header) => header.active && !!header.key), + headers: headersArr.filter((header: CookieOrHeader) => header.active && !!header.key), body: streamQueries, bodyType, rawType, @@ -151,7 +149,6 @@ export default function GRPCComposer(props: $TSFixMe) { service: selectedService, rpc: selectedRequest, packageName: selectedPackage, - streamingType, queryArr, initialQuery, streamsArr, @@ -162,7 +159,6 @@ export default function GRPCComposer(props: $TSFixMe) { }; // add request to history - /** @todo Fix TS error */ historyController.addHistoryToIndexedDb(reqRes); reqResItemAdded(reqRes); @@ -205,6 +201,7 @@ export default function GRPCComposer(props: $TSFixMe) { // update state in the store newRequestStreamsSet({ + ...newRequestStreams, streamsArr: newStreamsArr, count: newStreamsArr.length, streamContent: newRequestStreams.streamContent, @@ -212,10 +209,10 @@ export default function GRPCComposer(props: $TSFixMe) { } }, [streamsArrLength]); - const HeaderEntryFormStyle = { - //trying to change style to conditional created strange duplication effect when continuously changing protocol - display: !/wss?:\/\//.test(protocol) ? 'block' : 'none', - }; + // const HeaderEntryFormStyle = { + // //trying to change style to conditional created strange duplication effect when continuously changing protocol + // display: !/wss?:\/\//.test(protocol) ? 'block' : 'none', + // }; return ( = (props) => { @@ -17,7 +16,7 @@ const GRPCProtoEntryForm: React.FC = (props) => { // import proto file via electron file import dialog and have it displayed in proto textarea box const importProtos = () => { - grpcController.importProto(props.newRequestStreams) + grpcController.importProto(props.newRequestStreams); }; // saves protoContent in the store whenever client make changes to proto file or pastes a copy @@ -29,25 +28,24 @@ const GRPCProtoEntryForm: React.FC = (props) => { }); saveChanges(false); }; - + // update protoContent state in the store after making changes to the proto file const submitUpdatedProto = () => { //only update if changes aren't saved if (!changesSaved) { - try{ + try { grpcController.sendParserData(props.newRequestStreams.protoContent); grpcController.protoParserReturn(props.newRequestStreams); saveChanges(true); - } catch (err) { console.log(err); saveChanges(false); } } - return + return; }; - const isDark = useSelector((state: RootState) => state.ui.isDark); + const isDark = useAppSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); const saveChangesBtnText = changesSaved ? 'Changes Saved' : 'Save Changes'; @@ -65,17 +63,13 @@ const GRPCProtoEntryForm: React.FC = (props) => {
Proto
void; + newRequestBody: NewRequestBody + newRequestBodySet: NewRequestBodySet; warningMessage: { body: string } | null; introspectionData: Record | null; } @@ -31,7 +26,7 @@ const GraphQLBodyEntryForm: React.FC = (props) => { if (!bodyIsNew) setValue(bodyContent); }, [bodyContent, bodyIsNew]); - const isDark = useSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); + const isDark = useAppSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); return (
diff --git a/src/client/components/main/GraphQL-composer/GraphQLComposer.tsx b/src/client/components/main/GraphQL-composer/GraphQLComposer.tsx index 51a6cc062..640d3a57d 100644 --- a/src/client/components/main/GraphQL-composer/GraphQLComposer.tsx +++ b/src/client/components/main/GraphQL-composer/GraphQLComposer.tsx @@ -17,10 +17,10 @@ import TestContainer from '../sharedComponents/stressTest/TestContainer'; // Import MUI components import { Box } from '@mui/material'; -import { $TSFixMe, ReqRes } from '../../../../types'; +import { $TSFixMe, ReqRes, GraphQlComposerProps, Protocol, ReqResRequest, CookieOrHeader } from '../../../../types'; // Translated from GraphQLContainer.jsx -export default function GraphQLComposer(props: $TSFixMe) { +export default function GraphQLComposer(props: GraphQlComposerProps) { const { composerFieldsReset, fieldsReplaced, @@ -113,8 +113,11 @@ export default function GraphQLComposer(props: $TSFixMe) { setWarningMessage(warnings); return; } - - const protocol: string = url.match(/(https?:\/\/)|(wss?:\/\/)/)[0]; + //the ! asserts here that protocol will not be null for the TS compiler + //also asserting that the result of match will be of type Protocol + //this is safe because requestValidationCheck is run above which handles errors if the protocol is invalid + const protocol: Protocol = url.match(/(https?:\/\/)|(wss?:\/\/)/)![0] as Protocol; + if(protocol === null) throw new Error('Invalid Protocol'); const URIWithoutProtocol: string = `${url.split(protocol)[1]}/`; const host: string = protocol + URIWithoutProtocol.split('/')[0]; let path: string = `/${URIWithoutProtocol.split('/') @@ -133,13 +136,12 @@ export default function GraphQLComposer(props: $TSFixMe) { createdAt: new Date(), protocol: /wss?:\/\//.test(protocol) ? 'ws://' - : url.match(/https?:\/\//)[0], + : url.match(/https?:\/\//)![0] as Protocol, //see above comment where we can see this type assertion is safe host, path, url, graphQL, gRPC, - webrtc, timeSent: null, timeReceived: null, connection: 'uninitialized', @@ -149,10 +151,10 @@ export default function GraphQLComposer(props: $TSFixMe) { request: { method, headers: headersArr.filter( - (header: $TSFixMe) => header.active && !!header.key + (header: CookieOrHeader) => header.active && !!header.key ), cookies: cookiesArr.filter( - (cookie: $TSFixMe) => cookie.active && !!cookie.key + (cookie: CookieOrHeader) => cookie.active && !!cookie.key ), body: bodyContent || '', bodyType, @@ -248,6 +250,7 @@ export default function GraphQLComposer(props: $TSFixMe) { /> diff --git a/src/client/components/main/GraphQL-composer/GraphQLIntrospectionLog.tsx b/src/client/components/main/GraphQL-composer/GraphQLIntrospectionLog.tsx index 90347c439..00b027da1 100644 --- a/src/client/components/main/GraphQL-composer/GraphQLIntrospectionLog.tsx +++ b/src/client/components/main/GraphQL-composer/GraphQLIntrospectionLog.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { useSelector } from 'react-redux'; +import { useAppDispatch, useAppSelector } from '../../../toolkit-refactor/hooks'; import { GraphQLSchema } from 'graphql'; import graphQLController from '../../../controllers/graphQLController'; @@ -12,31 +12,32 @@ interface IntrospectionData { } const GraphQLIntrospectionLog: React.FC = () => { - const headers = useSelector( + const headers = useAppSelector( (store: RootState) => store.newRequest.newRequestHeaders.headersArr ); - const cookies = useSelector( + const cookies = useAppSelector( (store: RootState) => store.newRequest.newRequestCookies.cookiesArr ); - const introspectionData: (IntrospectionData | string) = useSelector( + const introspectionData: (IntrospectionData | string) = useAppSelector( (store: RootState) => store.introspectionData ); - const url: string = useSelector( + const url: string = useAppSelector( (store: RootState) => store.newRequestFields.url ); - const isDark: boolean = useSelector((store: RootState) => store.ui.isDark); + const isDark: boolean = useAppSelector((store: RootState) => store.ui.isDark); return (
- {introspectionData === 'Error: Please enter a valid GraphQL API URI' && ( -
{introspectionData}
+ {/* added the .schemaSDL and toString() below to fix type errors. Not sure the intent of the original engineer */} + {introspectionData.schemaSDL === 'Error: Please enter a valid GraphQL API URI' && ( +
{introspectionData.toString()}
)} {!!introspectionData.schemaSDL && ( { }; export default GraphQLIntrospectionLog; - - - - - - - - - - -// import React from 'react'; -// import { useSelector } from 'react-redux'; -// import graphQLController from '../../../controllers/graphQLController'; -// import TextCodeArea from '../new-request/TextCodeArea.tsx'; - -// const GraphQLIntrospectionLog = () => { -// const headers = useSelector( -// (store) => store.newRequest.newRequestHeaders.headersArr -// ); -// const cookies = useSelector( -// (store) => store.newRequest.newRequestCookies.cookiesArr -// ); -// const introspectionData = useSelector((store) => store.introspectionData); -// const url = useSelector((store) => store.newRequestFields.url); -// const isDark = useSelector((store) => store.ui.isDark); - -// return ( -//
-// -//
-// {introspectionData === -// 'Error: Please enter a valid GraphQL API URI' && ( -//
{introspectionData}
-// )} -// {!!introspectionData.schemaSDL && ( -// {}} -// readOnly={true} -// /> -// )} -//
-//
-// ); -// }; -// export default GraphQLIntrospectionLog; diff --git a/src/client/components/main/GraphQL-composer/GraphQLMethodAndEndpointEntryForm.tsx b/src/client/components/main/GraphQL-composer/GraphQLMethodAndEndpointEntryForm.tsx index a2b573691..d5b686082 100644 --- a/src/client/components/main/GraphQL-composer/GraphQLMethodAndEndpointEntryForm.tsx +++ b/src/client/components/main/GraphQL-composer/GraphQLMethodAndEndpointEntryForm.tsx @@ -1,21 +1,16 @@ import React, { useState, useRef, useEffect } from 'react'; -import { useSelector } from 'react-redux'; +import { useAppDispatch, useAppSelector } from '../../../toolkit-refactor/hooks'; import { GraphQLSchema } from 'graphql'; import dropDownArrow from '../../../../assets/icons/arrow_drop_down_white_192x192.png'; -import { NewRequestFields, NewRequestBody } from '../../../../types'; - -interface IntrospectionData { - schemaSDL: string | null; - clientSchema: GraphQLSchema | null; -} +import { NewRequestFields, NewRequestBody, NewRequestBodySet } from '../../../../types'; interface Props { warningMessage: { uri?: string }; setWarningMessage: (message: { uri?: string }) => void; fieldsReplaced: (fields: NewRequestFields) => void; newRequestFields: NewRequestFields; - newRequestBodySet: (body: { [key: string]: any }) => void; + newRequestBodySet: NewRequestBodySet; newRequestBody: NewRequestBody; } @@ -61,23 +56,11 @@ const GraphQLMethodAndEndpointEntryForm: React.FC = ({ const methodChangeHandler = (value: string) => { clearWarningIfApplicable(); - let methodReplaceRegexType: string | undefined - const methodReplaceRegex = new RegExp(`${newRequestFields.method}`, 'mi'); // GraphQL features if (value === 'QUERY' || 'MUTATION' || 'SUBSCRIPTION') { - if (value === 'QUERY') { - methodReplaceRegexType = 'query' - } - else if (value === 'MUTATION') { - methodReplaceRegexType = 'mutation' - } - else if (value === 'SUBSCRIPTION') { - methodReplaceRegexType = 'subscription' - } - const newBody = methodReplaceRegex.test(newRequestBody.bodyContent) - ? newRequestBody.bodyContent.replace(methodReplaceRegex, methodReplaceRegexType) + ? newRequestBody.bodyContent.replace(methodReplaceRegex, value.toLowerCase()) : `${newRequestBody.bodyContent}`; newRequestBodySet({ @@ -95,7 +78,7 @@ const GraphQLMethodAndEndpointEntryForm: React.FC = ({ }; - const isDark = useSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); + const isDark = useAppSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); return (
@@ -166,8 +149,8 @@ const GraphQLMethodAndEndpointEntryForm: React.FC = ({ void; + newRequestBody: NewRequestBody; + newRequestBodySet: NewRequestBodySet; } const GraphQLVariableEntryForm: React.FC = ({ @@ -22,8 +20,7 @@ const GraphQLVariableEntryForm: React.FC = ({ if (!bodyIsNew) setValue(bodyVariables); }, [bodyVariables, bodyIsNew]); - const isDark = useSelector((store: any) => store.ui.isDark); - + const isDark = useAppSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); return (
Variables
diff --git a/src/client/components/main/MainContainer.tsx b/src/client/components/main/MainContainer.tsx index 37a2d1efc..a66982710 100644 --- a/src/client/components/main/MainContainer.tsx +++ b/src/client/components/main/MainContainer.tsx @@ -1,136 +1,164 @@ -import React from 'react'; -import { Routes, Route } from 'react-router-dom'; -import { connect } from 'react-redux'; -import { ReqRes, $TSFixMe, $TSFixMeObject, RequestWebRTC } from '../../../types'; - -import * as ReqResSlice from '../../toolkit-refactor/slices/reqResSlice'; - -import { - composerFieldsReset, - newRequestSSESet, - newRequestCookiesSet, - newRequestStreamsSet, - newRequestBodySet, - newRequestHeadersSet, -} from '../../toolkit-refactor/slices/newRequestSlice'; - -import { openApiRequestsReplaced } from '../../toolkit-refactor/slices/newRequestOpenApiSlice'; - -import { - setWorkspaceActiveTab, - /*, setComposerDisplay */ -} from '../../toolkit-refactor/slices/uiSlice'; - -import { - fieldsReplaced, - newTestContentSet, -} from '../../toolkit-refactor/slices/newRequestFieldsSlice'; -import { setWarningMessage } from '../../toolkit-refactor/slices/warningMessageSlice'; - -// Import local components. -import Http2Composer from './http2-composer/Http2Composer'; -import GraphQLComposer from './GraphQL-composer/GraphQLComposer'; -import GRPCComposer from './GRPC-composer/GRPCComposer'; -import WebSocketComposer from './WebSocket-composer/WebSocketComposer'; -import WebRTCComposer from './WebRTC-composer/WebRTCComposer'; -import OpenAPIComposer from './OpenAPI-composer/OpenAPIComposer'; -import WebhookComposer from './WebHook-composer/WebhookComposer'; -import TRPCComposer from './TRPC-composer/TRPCComposer'; -import MockServerComposer from './MockServer-composer/MockServerComposer'; -import ResponsePaneContainer from './response-composer/ResponsePaneContainer'; - -// Import MUI components -import { Box } from '@mui/material'; -import { AppDispatch, RootState } from '../../toolkit-refactor/store'; -import Split from 'react-split'; - -/**@todo switch to hooks? */ -const mapStateToProps = (store: RootState) => { - return { - reqResArray: store.reqRes.reqResArray, - newRequestFields: store.newRequestFields, - newRequestHeaders: store.newRequest.newRequestHeaders, - newRequestStreams: store.newRequest.newRequestStreams, - newRequestBody: store.newRequest.newRequestBody, - // newRequestOpenAPI: store.newRequestOpenApi, - newRequestCookies: store.newRequest.newRequestCookies, - newRequestSSE: store.newRequest.newRequestSSE, - warningMessage: store.warningMessage, - introspectionData: store.introspectionData, - }; -}; - -/**@todo switch to hooks? */ -const mapDispatchToProps = (dispatch: AppDispatch) => ({ - reqResItemAdded: (reqRes: ReqRes) => { - dispatch(ReqResSlice.reqResItemAdded(reqRes)); - }, - setWarningMessage: (message: $TSFixMe) => { - dispatch(setWarningMessage(message)); - }, - // setComposerDisplay: (composerDisplay) => { - // dispatch(setComposerDisplay(composerDisplay)); - // }, - newRequestHeadersSet: (requestHeadersObj: $TSFixMeObject) => { - dispatch(newRequestHeadersSet(requestHeadersObj)); - }, - newRequestStreamsSet: (requestStreamsObj: $TSFixMeObject) => { - dispatch(newRequestStreamsSet(requestStreamsObj)); - }, - fieldsReplaced: (requestFields: $TSFixMe) => { - dispatch(fieldsReplaced(requestFields)); - }, - newRequestBodySet: (requestBodyObj: $TSFixMeObject) => { - dispatch(newRequestBodySet(requestBodyObj)); - }, - newTestContentSet: (testContent: $TSFixMe) => { - dispatch(newTestContentSet(testContent)); - }, - newRequestCookiesSet: (requestCookiesObj: $TSFixMeObject) => { - dispatch(newRequestCookiesSet(requestCookiesObj)); - }, - newRequestSSESet: (requestSSEBool: boolean) => { - dispatch(newRequestSSESet(requestSSEBool)); - }, - openApiRequestsReplaced: (parsedDocument: $TSFixMe) => { - dispatch(openApiRequestsReplaced(parsedDocument)); - }, - composerFieldsReset: () => { - dispatch(composerFieldsReset()); - }, - setWorkspaceActiveTab: (tabName: $TSFixMe) => { - dispatch(setWorkspaceActiveTab(tabName)); - }, - -}); - - -function MainContainer(props: $TSFixMeObject) { - return ( - - - - - } /> - } /> - } /> - } /> - {/* WebRTC has been completely refactored to hooks - no props needed */} - } /> - } /> - } /> - } /> - } - /> - - - - - - ); -} - -export default connect(mapStateToProps, mapDispatchToProps)(MainContainer); - +import React from 'react'; +import { Routes, Route } from 'react-router-dom'; +import { useAppDispatch, useAppSelector } from '../../toolkit-refactor/hooks'; +import { + ReqRes, + RequestWebRTC, + ValidationMessage, + MainContainerProps, + NewRequestHeaders, + NewRequestStreams, + NewRequestFields, + NewRequestBody, + NewRequestCookies, + NewRequestOpenApi, +} from '../../../types'; + +import * as ReqResSlice from '../../toolkit-refactor/slices/reqResSlice'; + +import { + composerFieldsReset, + newRequestSSESet, + newRequestCookiesSet, + newRequestStreamsSet, + newRequestBodySet, + newRequestHeadersSet, +} from '../../toolkit-refactor/slices/newRequestSlice'; + +import { openApiRequestsReplaced } from '../../toolkit-refactor/slices/newRequestOpenApiSlice'; + +import { + setWorkspaceActiveTab, + /*, setComposerDisplay */ +} from '../../toolkit-refactor/slices/uiSlice'; + +import { + fieldsReplaced, + newTestContentSet, +} from '../../toolkit-refactor/slices/newRequestFieldsSlice'; +import { setWarningMessage } from '../../toolkit-refactor/slices/warningMessageSlice'; + +// Import local components. +import Http2Composer from './http2-composer/Http2Composer'; +import GraphQLComposer from './GraphQL-composer/GraphQLComposer'; +import GRPCComposer from './GRPC-composer/GRPCComposer'; +import WebSocketComposer from './WebSocket-composer/WebSocketComposer'; +import WebRTCComposer from './WebRTC-composer/WebRTCComposer'; +import OpenAPIComposer from './OpenAPI-composer/OpenAPIComposer'; +import WebhookComposer from './WebHook-composer/WebhookComposer'; +import TRPCComposer from './TRPC-composer/TRPCComposer'; +import MockServerComposer from './MockServer-composer/MockServerComposer'; +import ResponsePaneContainer from './response-composer/ResponsePaneContainer'; + +// Import MUI components +import { Box } from '@mui/material'; +import Split from 'react-split'; +import { PayloadAction } from '@reduxjs/toolkit'; + +function MainContainer() { + const dispatch = useAppDispatch(); + + // Hooks conversion while keeping Prop drilling for core functionality + // Accessing state from redux store, types inferred + const reqResArray = useAppSelector((state) => state.reqRes.reqResArray); + const newRequestFields = useAppSelector((state) => state.newRequestFields); + const newRequestHeaders = useAppSelector( + (state) => state.newRequest.newRequestHeaders + ); + const newRequestStreams = useAppSelector( + (state) => state.newRequest.newRequestStreams + ); + const newRequestBody = useAppSelector( + (state) => state.newRequest.newRequestBody + ); + // const newRequestOpenAPI = useAppSelector((state) => state.newRequestOpenApi); // Commented out in original MainContainer before hooks conversion (?) + const newRequestCookies = useAppSelector( + (state) => state.newRequest.newRequestCookies + ); + const newRequestSSE = useAppSelector( + (state) => state.newRequest.newRequestSSE + ); + const warningMessage = useAppSelector((state) => state.warningMessage); + const introspectionData = useAppSelector((state) => state.introspectionData); + + // Dispatching actions + const reqResItemAdded = (reqRes: ReqRes) => + dispatch(ReqResSlice.reqResItemAdded(reqRes)); + const setWarningMessageAction = (message: ValidationMessage) => + dispatch(setWarningMessage(message)); + // const setComposerDisplay = (composerDisplay) => + // dispatch(setComposerDisplay(composerDisplay)); // Kept this in from old MainContainer for posterity + const newRequestHeadersSetAction = (requestHeadersObj: NewRequestHeaders) => + dispatch(newRequestHeadersSet(requestHeadersObj)); + const newRequestStreamsSetAction = (requestStreamsObj: NewRequestStreams) => + dispatch(newRequestStreamsSet(requestStreamsObj)); + const fieldsReplacedAction = (requestFields: NewRequestFields) => + dispatch(fieldsReplaced(requestFields)); + const newRequestBodySetAction = (requestBodyObj: NewRequestBody) => + dispatch(newRequestBodySet(requestBodyObj)); + const newTestContentSetAction = (testContent: string) => + dispatch(newTestContentSet(testContent)); + const newRequestCookiesSetAction = (requestCookiesObj: NewRequestCookies) => + dispatch(newRequestCookiesSet(requestCookiesObj)); + const newRequestSSESetAction = (requestSSEBool: boolean) => + dispatch(newRequestSSESet(requestSSEBool)); + const openApiRequestsReplacedAction = (request: NewRequestOpenApi) => + dispatch(openApiRequestsReplaced(request)); + const composerFieldsResetAction = () => dispatch(composerFieldsReset()); + const setWorkspaceActiveTabAction = (tabName: string) => + dispatch(setWorkspaceActiveTab(tabName)); + + const props: MainContainerProps = { + reqResArray, + newRequestFields, + newRequestHeaders, + newRequestStreams, + newRequestBody, + newRequestCookies, + newRequestSSE, + warningMessage, + introspectionData, + reqResItemAdded, + setWarningMessage: setWarningMessageAction, + newRequestHeadersSet: newRequestHeadersSetAction, + newRequestStreamsSet: newRequestStreamsSetAction, + fieldsReplaced: fieldsReplacedAction, + newRequestBodySet: newRequestBodySetAction, + newTestContentSet: newTestContentSetAction, + newRequestCookiesSet: newRequestCookiesSetAction, + newRequestSSESet: newRequestSSESetAction, + openApiRequestsReplaced: openApiRequestsReplacedAction, + composerFieldsReset: composerFieldsResetAction, + setWorkspaceActiveTab: setWorkspaceActiveTabAction, + }; + return ( + + + + + } /> + } /> + } /> + } + /> + {/* WebRTC has been completely refactored to hooks - no props needed */} + } /> + } /> + } /> + } /> + } + /> + + + + + + ); +} + +export default MainContainer; + diff --git a/src/client/components/main/MockServer-composer/MockServerComposer.tsx b/src/client/components/main/MockServer-composer/MockServerComposer.tsx index b97134ff3..34b328da3 100644 --- a/src/client/components/main/MockServer-composer/MockServerComposer.tsx +++ b/src/client/components/main/MockServer-composer/MockServerComposer.tsx @@ -1,6 +1,9 @@ // react-redux import React, { useState, useEffect } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; +import { + useAppDispatch, + useAppSelector, +} from '../../../toolkit-refactor/hooks'; import { startServer, stopServer, @@ -16,13 +19,17 @@ import BodyEntryForm from '../sharedComponents/requestForms/BodyEntryForm'; // mui import { Box, Button, Modal, Typography } from '@mui/material'; +// Import types for props type and define props on RestMethodAndEndpointEntryFormProps +import { MockServerComposerProps } from '../../../../types'; +import { METHODS } from 'http'; +import { placeholder } from '@uiw/react-codemirror'; + /** * grab context from Electron window * note: api is the ipcRenderer object (see preload.js) */ const { api } = window as any; -// TODO: add typing to the props object // TODO: add an option to see the list of existing routes that shows up in the response window // TODO: add endpoint validation // TODO: add the ability to mock HTML responses (or remove the HTML option from the BodyEntryForm component) @@ -41,13 +48,13 @@ const style = { p: 4, }; -const MockServerComposer = (props) => { +const MockServerComposer = (props: MockServerComposerProps) => { const [showModal, setShowModal] = useState(false); const [userDefinedEndpoint, setUserDefinedEndpoint] = useState(''); - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); // grab the isServerStarted state from the Redux store - let isServerStarted = useSelector( + let isServerStarted = useAppSelector( (state: any) => state.mockServer.isServerStarted ); @@ -64,7 +71,7 @@ const MockServerComposer = (props) => { const stopMockServer = () => { api.send('stop-mock-server'); dispatch(stopServer()); - alert('Mock server stopped'); + console.log('server stopped') }; // toggles the mock server on and off diff --git a/src/client/components/main/OpenAPI-composer/OpenAPIComposer.tsx b/src/client/components/main/OpenAPI-composer/OpenAPIComposer.tsx index cfd7298b8..216fbd763 100644 --- a/src/client/components/main/OpenAPI-composer/OpenAPIComposer.tsx +++ b/src/client/components/main/OpenAPI-composer/OpenAPIComposer.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import { useSelector } from 'react-redux'; +import { useAppDispatch, useAppSelector } from '../../../toolkit-refactor/hooks'; import { v4 as uuid } from 'uuid'; import { RootState } from '../../../toolkit-refactor/store'; // Import controllers @@ -19,7 +19,7 @@ import { $TSFixMe, ReqRes } from '../../../../types'; export default function OpenAPIComposer(props: $TSFixMe) { // This is a better way to import what the components needs, not the mess of prop drilling - const newRequestsOpenAPI: $TSFixMe = useSelector((state: RootState) => state.newRequestOpenApi); + const newRequestsOpenAPI: $TSFixMe = useAppSelector((state: RootState) => state.newRequestOpenApi); const { composerFieldsReset, diff --git a/src/client/components/main/OpenAPI-composer/OpenAPIDocumentEntryForm.tsx b/src/client/components/main/OpenAPI-composer/OpenAPIDocumentEntryForm.tsx index 7d0aecaf8..268c16502 100644 --- a/src/client/components/main/OpenAPI-composer/OpenAPIDocumentEntryForm.tsx +++ b/src/client/components/main/OpenAPI-composer/OpenAPIDocumentEntryForm.tsx @@ -1,12 +1,11 @@ import React from 'react'; -import { useSelector } from 'react-redux'; -import { RootState } from '../../../toolkit-refactor/store'; +import { useAppDispatch, useAppSelector } from '../../../toolkit-refactor/hooks'; import openApiController from '../../../controllers/openApiController'; -// this component is working as intened +// this component is working as intended const OpenAPIDocumentEntryForm: React.FC = () => { - const isDark = useSelector((state: RootState) => state.ui.isDark); + const isDark = useAppSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); const importDoc = (): void => { openApiController.sendDocument(); @@ -17,9 +16,7 @@ const OpenAPIDocumentEntryForm: React.FC = () => {
= ({ } ); - const isDark = useSelector((state: RootState) => state.ui.isDark); + const isDark = useAppSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); return (
@@ -135,9 +134,7 @@ const OpenAPIServerForm: React.FC = ({
Servers
diff --git a/TRPCBodyEntryForm.tsx b/src/client/components/main/TRPC-composer/TRPCBodyEntryForm.tsx similarity index 71% rename from TRPCBodyEntryForm.tsx rename to src/client/components/main/TRPC-composer/TRPCBodyEntryForm.tsx index 95bfd5c4b..e5c865090 100644 --- a/TRPCBodyEntryForm.tsx +++ b/src/client/components/main/TRPC-composer/TRPCBodyEntryForm.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; -import { RootState } from './src/client/toolkit-refactor/store'; -import TextCodeArea from './src/client/components/main/sharedComponents/TextCodeArea'; +import { useAppDispatch, useAppSelector } from '../../../toolkit-refactor/hooks'; +import { RootState } from '../../../toolkit-refactor/store'; +import TextCodeArea from '../sharedComponents/TextCodeArea'; /** * renders entry form for TRPC request @@ -9,13 +9,13 @@ import TextCodeArea from './src/client/components/main/sharedComponents/TextCode const TRPCBodyEntryForm = (props: any) => { const { newRequestBodySet } = props; - const dispatch = useDispatch(); - const newRequestBody = useSelector( + const dispatch = useAppDispatch(); + const newRequestBody = useAppSelector( (store: RootState) => store.newRequest.newRequestBody ); const { bodyContent } = newRequestBody; - const isDark = useSelector((store: RootState) => store.ui.isDark); + const isDark = useAppSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); const [cmValue, setValue] = useState(bodyContent); return ( diff --git a/src/client/components/main/TRPC-composer/TRPCComposer.tsx b/src/client/components/main/TRPC-composer/TRPCComposer.tsx index 99816c56d..f242d0b13 100644 --- a/src/client/components/main/TRPC-composer/TRPCComposer.tsx +++ b/src/client/components/main/TRPC-composer/TRPCComposer.tsx @@ -5,7 +5,7 @@ import SendRequestButton from '../sharedComponents/requestButtons/SendRequestBut // Import local components import TRPCMethodAndEndpointEntryForm from './TRPCMethodAndEndpointEntryForm'; // Import Redux hooks -import { useSelector, useDispatch } from 'react-redux'; +import { useAppDispatch, useAppSelector } from '../../../toolkit-refactor/hooks'; // Import Actions from RTK slice import historyController from '../../../controllers/historyController'; @@ -39,7 +39,7 @@ const PROCEDURE_DEFAULT = { // after its done manipulating the state it will return the updated array that will be use as the new procedures state //********************************** */ - +//TODO: Implicit any used throughout this file function reducer(procedures, action) { if (action.type === 'METHOD') { // @@ -82,7 +82,7 @@ function reducer(procedures, action) { } export default function TRPCComposer(props) { - const dispatch = useDispatch(); // dispatch for main redux store + const dispatch = useAppDispatch(); // dispatch for main redux store const [showSubscription, setShowSubscription] = useState(false); // manage subscription component const subscriptionHandler = (bool) => { @@ -143,12 +143,12 @@ export default function TRPCComposer(props) { } = props; /** newRequestFields slice from redux store, contains general request info*/ - const requestFields = useSelector( + const requestFields = useAppSelector( (state: RootState) => state.newRequestFields ); /** reqRes slice from redux store, contains request and response data */ - const newRequest = useSelector((state: RootState) => state.newRequest); + const newRequest = useAppSelector((state: RootState) => state.newRequest); const addProcedures = () => { // reducer dispatch for adding a new procedure to the procedures array diff --git a/src/client/components/main/TRPC-composer/TRPCMethodAndEndpointEntryForm.tsx b/src/client/components/main/TRPC-composer/TRPCMethodAndEndpointEntryForm.tsx index 908f8dbeb..4956306fc 100644 --- a/src/client/components/main/TRPC-composer/TRPCMethodAndEndpointEntryForm.tsx +++ b/src/client/components/main/TRPC-composer/TRPCMethodAndEndpointEntryForm.tsx @@ -2,16 +2,19 @@ /* eslint-disable jsx-a11y/no-static-element-interactions */ /* eslint-disable jsx-a11y/click-events-have-key-events */ import React, { useState, useRef, useEffect } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; +import { useAppDispatch, useAppSelector } from '../../../toolkit-refactor/hooks'; import { RootState } from '../../../toolkit-refactor/store'; import { fieldsReplaced } from '../../../toolkit-refactor/slices/newRequestFieldsSlice'; const TRPCMethodAndEndpointEntryForm = (props: any) => { - const requestFields = useSelector( + const requestFields = useAppSelector( (state: RootState) => state.newRequestFields ); - const dispatch = useDispatch(); + + const isDark = useAppSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); + + const dispatch = useAppDispatch(); const clearWarningIfApplicable = () => { if (props.warningMessage.uri) props.setWarningMessage({}); }; @@ -37,7 +40,9 @@ const TRPCMethodAndEndpointEntryForm = (props: any) => { tRPC
{ const [dropdownIsActive, setDropdownIsActive] = useState(false); const dropdownEl = useRef(); @@ -41,7 +40,7 @@ const TRPCPrceduresEndPoint = (props) => { payload: { index: props.index }, }); }; - const isDark = useSelector((store: RootState) => store.ui.isDark); + const isDark = useAppSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); return (
{ store.ui.isDark); const [endPoint, setEndpoint] = useState(''); const [link, setLink] = useState(''); const [responseBody, setResponseBody] = useState(''); @@ -72,7 +74,7 @@ export default function TRPCSubscriptionContainer(props) { return (
-

Your subscription

+

Your Subscription

Subscription
+
+
{subscriptionStarted && (
Log will appear down here diff --git a/src/client/components/main/TRPC-composer/TRPCVariableForm.tsx b/src/client/components/main/TRPC-composer/TRPCVariableForm.tsx index 7a5988c96..4dfe3cd3e 100644 --- a/src/client/components/main/TRPC-composer/TRPCVariableForm.tsx +++ b/src/client/components/main/TRPC-composer/TRPCVariableForm.tsx @@ -1,12 +1,12 @@ import React from 'react'; import TextCodeArea from '../sharedComponents/TextCodeArea'; -import { useSelector } from 'react-redux'; +import { useAppDispatch, useAppSelector } from '../../../toolkit-refactor/hooks'; import { vscodeDark } from '@uiw/codemirror-theme-vscode'; - +//TODO: implicit and literal any used in this file export default function TRPCVariableForm(props) { // input for for user to attach argument with their procedures - const isDark = useSelector((store: any) => store.ui.isDark); - const onChangeHandler = (string) => { + const isDark = useAppSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); + const onChangeHandler = (string: string) => { // this function dispatch action to the main reducer function inside of trpc composer props.proceduresDipatch({ type: 'VARIABLE', diff --git a/src/client/components/main/WebHook-composer/WebhookComposer.tsx b/src/client/components/main/WebHook-composer/WebhookComposer.tsx index 7e6ed8f0f..4b421e55a 100644 --- a/src/client/components/main/WebHook-composer/WebhookComposer.tsx +++ b/src/client/components/main/WebHook-composer/WebhookComposer.tsx @@ -1,7 +1,7 @@ -import React, { useState, useEffect, FC } from 'react'; -import { useSelector } from 'react-redux'; +import React, { useState, useEffect } from 'react'; import { v4 as uuid } from 'uuid'; import { io } from 'socket.io-client'; +import { useAppSelector } from '../../../toolkit-refactor/hooks'; // Import MUI components import { Box } from '@mui/material'; @@ -17,7 +17,8 @@ export default function WebhookComposer(props: $TSFixMe) { * This seems to have been put in place before the dedicated UI slice had a * chance to be created. */ - const isDark = false; + const isDark = useAppSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); + const { // composerFieldsReset, @@ -191,6 +192,7 @@ export default function WebhookComposer(props: $TSFixMe) { isDark ? 'dark-address-input' : '' } ml-2 input input-is-medium is-info`} type="text" + placeholder="Server URL" value={whUrl} readOnly //solved react error dev console /> diff --git a/src/client/components/main/WebRTC-composer/WebRTCComposer.tsx b/src/client/components/main/WebRTC-composer/WebRTCComposer.tsx index 6b4a22445..cb45e5b61 100644 --- a/src/client/components/main/WebRTC-composer/WebRTCComposer.tsx +++ b/src/client/components/main/WebRTC-composer/WebRTCComposer.tsx @@ -13,16 +13,15 @@ import NewRequestButton from '../sharedComponents/requestButtons/NewRequestButto // Import MUI components import { Box } from '@mui/material'; import WebRTCVideoBox from './WebRTCVideoBox'; -import { useSelector } from 'react-redux'; import { RootState } from '../../../toolkit-refactor/store'; -import { useDispatch } from 'react-redux'; +import { useAppDispatch, useAppSelector } from '../../../toolkit-refactor/hooks'; import { composerFieldsReset } from '../../../toolkit-refactor/slices/newRequestSlice'; import { setWorkspaceActiveTab } from '../../../toolkit-refactor/slices/uiSlice'; import { reqResItemAdded } from '../../../toolkit-refactor/slices/reqResSlice'; export default function WebRTCComposer() { - const dispatch = useDispatch(); - const newRequestWebRTC: RequestWebRTC = useSelector( + const dispatch = useAppDispatch(); + const newRequestWebRTC: RequestWebRTC = useAppSelector( (store: RootState) => store.newRequest.newRequestWebRTC ); diff --git a/src/client/components/main/WebRTC-composer/WebRTCServerEntryForm.tsx b/src/client/components/main/WebRTC-composer/WebRTCServerEntryForm.tsx index eefafa6a0..0145b8fc9 100644 --- a/src/client/components/main/WebRTC-composer/WebRTCServerEntryForm.tsx +++ b/src/client/components/main/WebRTC-composer/WebRTCServerEntryForm.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import { useSelector } from 'react-redux'; // import dropDownArrow from '../../../../assets/icons/arrow_drop_down_white_192x192.png'; // import CodeMirror from '@uiw/react-codemirror'; // import { EditorView } from '@codemirror/view'; @@ -9,7 +8,7 @@ import { useSelector } from 'react-redux'; // import MenuItem from '@mui/material/MenuItem'; import { RequestWebRTC } from '../../../../types'; import TextCodeArea from '../sharedComponents/TextCodeArea'; -import { useDispatch } from 'react-redux'; +import { useAppDispatch, useAppSelector } from '../../../toolkit-refactor/hooks'; import { newRequestWebRTCSet } from '../../../toolkit-refactor/slices/newRequestSlice'; import webrtcPeerController from '../../../controllers/webrtcPeerController'; import { RootState } from '../../../toolkit-refactor/store'; @@ -24,8 +23,8 @@ interface Props { } const WebRTCServerEntryForm: React.FC = (props: Props) => { - const dispatch = useDispatch(); - const newRequestWebRTC: RequestWebRTC = useSelector( + const dispatch = useAppDispatch(); + const newRequestWebRTC: RequestWebRTC = useAppSelector( (store: RootState) => store.newRequest.newRequestWebRTC ); diff --git a/src/client/components/main/WebRTC-composer/WebRTCSessionEntryForm.tsx b/src/client/components/main/WebRTC-composer/WebRTCSessionEntryForm.tsx index 3da72d19b..d9a8c29b1 100644 --- a/src/client/components/main/WebRTC-composer/WebRTCSessionEntryForm.tsx +++ b/src/client/components/main/WebRTC-composer/WebRTCSessionEntryForm.tsx @@ -3,7 +3,7 @@ import { NewRequestWebRTCSet, RequestWebRTC } from '../../../../types'; import webrtcPeerController from '../../../controllers/webrtcPeerController'; import dropDownArrow from '../../../../assets/icons/arrow_drop_down_white_192x192.png'; -import { useDispatch, useSelector } from 'react-redux'; +import { useAppDispatch, useAppSelector } from '../../../toolkit-refactor/hooks'; import { newRequestWebRTCSet } from '../../../toolkit-refactor/slices/newRequestSlice'; import { RootState } from '../../../toolkit-refactor/store'; @@ -12,11 +12,13 @@ interface Props { } const WebRTCSessionEntryForm: React.FC = (props: Props) => { - const dispatch = useDispatch(); - const newRequestWebRTC: RequestWebRTC = useSelector( + const dispatch = useAppDispatch(); + const newRequestWebRTC: RequestWebRTC = useAppSelector( (store: RootState) => store.newRequest.newRequestWebRTC ); + const isDark = useAppSelector((store: { ui: { isDark: boolean }}) => store.ui.isDark); + const { setShowRTCEntryForms } = props; const [entryTypeDropdownIsActive, setEntryTypeDropdownIsActive] = useState(false); @@ -87,7 +89,9 @@ const WebRTCSessionEntryForm: React.FC = (props: Props) => {
; @@ -33,7 +33,7 @@ const WSEndpointEntryForm: React.FC = ({ }); }; - const isDark = useSelector((store: any) => store.ui.isDark); + const isDark = useAppSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); return (
= ({
{ - return { - newRequestHeaders: store.newRequest.newRequestHeaders, - newRequestBody: store.newRequest.newRequestBody, - }; -}; - -/**@todo switch to use hooks? */ -const mapDispatchToProps = (dispatch) => ({ - newRequestHeadersSet: (requestHeadersObj) => { - dispatch(newRequestHeadersSet(requestHeadersObj)); - }, - newRequestBodySet: (requestBodyObj) => { - dispatch(newRequestBodySet(requestBodyObj)); - }, -}); - -function BodyTypeSelect({ - newRequestHeaders, - newRequestBody, - newRequestHeadersSet, - newRequestBodySet, -}) { - const handleBodyTypeSelect = (event: SelectChangeEvent) => { - newRequestBodySet({ - ...newRequestBody, - bodyType: event.target.value, - }); - }; - - const handleRawBodyTypeSelect = (event: SelectChangeEvent) => { - newRequestBodySet({ - ...newRequestBody, - rawType: event.target.value, - }); - }; - - const prettifyJSON = () => { - const prettyString = JSON.stringify( - JSON.parse(newRequestBody.bodyContent), - null, - 4 - ); - newRequestBodySet({ - ...newRequestBody, - bodyContent: prettyString, - }); - }; - - return ( - - - - - {newRequestBody.bodyType === 'raw' && ( - - - - )} - {newRequestBody.bodyType === 'raw' && - newRequestBody.rawType === 'application/json' && ( - - )} - - ); -} - -export default connect(mapStateToProps, mapDispatchToProps)(BodyTypeSelect); diff --git a/src/client/components/main/http2-composer/Http2Body.tsx b/src/client/components/main/http2-composer/Http2Body.tsx deleted file mode 100644 index 9833e8da5..000000000 --- a/src/client/components/main/http2-composer/Http2Body.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import React from 'react'; -// Http2Body needs access to the Redux store. -import { connect } from 'react-redux'; - -import { AppDispatch, RootState } from '../../../toolkit-refactor/store'; -import { $TSFixMeObject } from '../../../../types'; - -import { - newRequestBodySet, - newRequestHeadersSet, -} from '../../../toolkit-refactor/slices/newRequestSlice'; - -// Import local components -import BodyTypeSelect from './BodyTypeSelect'; -// Import MUI components -import { Box } from '@mui/material'; -import WWWForm from '../sharedComponents/requestForms/WWWForm'; -import JSONTextArea from '../sharedComponents/JSONTextArea'; -import TextCodeArea from '../sharedComponents/TextCodeArea'; - -/**@todo switch to hooks? */ -const mapStateToProps = (store: RootState) => { - return { - newRequestHeaders: store.newRequest.newRequestHeaders, - newRequestBody: store.newRequest.newRequestBody, - warningMessage: store.warningMessage, - }; -}; - -/**@todo switch to hooks? */ -const mapDispatchToProps = (dispatch: AppDispatch) => ({ - newRequestHeadersSet: (requestHeadersObj: $TSFixMeObject) => { - dispatch(newRequestHeadersSet(requestHeadersObj)); - }, - newRequestBodySet: (requestBodyObj: $TSFixMeObject) => { - dispatch(newRequestBodySet(requestBodyObj)); - }, -}); - -function Http2Body({ - newRequestHeaders, - newRequestBody, - warningMessage, - newRequestHeadersSet, - newRequestBodySet, -}) { - const bodyEntryArea = () => { - //BodyType of none : display nothing - if (newRequestBody.bodyType === 'none') { - return; - } - //BodyType of XWWW... : display WWWForm entry - if (newRequestBody.bodyType === 'x-www-form-urlencoded') { - return ( - - ); - } - //RawType of application/json : Text area box with error checking - if (newRequestBody.rawType === 'application/json') { - return ( - - ); - } - - return ( - { - newRequestBodySet({ - ...newRequestBody, - bodyContent: value, - }); - }} - /> - ); - }; - return ( - - - {bodyEntryArea()} - - ); -} - -export default connect(mapStateToProps, mapDispatchToProps)(Http2Body); diff --git a/src/client/components/main/http2-composer/Http2Composer.tsx b/src/client/components/main/http2-composer/Http2Composer.tsx index 2db0a7683..f072961e3 100644 --- a/src/client/components/main/http2-composer/Http2Composer.tsx +++ b/src/client/components/main/http2-composer/Http2Composer.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { v4 as uuid } from 'uuid'; -import { useDispatch } from 'react-redux'; +import { useAppDispatch, useAppSelector } from '../../../toolkit-refactor/hooks'; import { responseDataSaved } from '../../../toolkit-refactor/slices/reqResSlice'; import { @@ -29,7 +29,7 @@ import { type } from 'os'; // Translated from RestContainer.jsx export default function Http2Composer(props: MainContainerProps) { - const dispatch = useDispatch(); + const dispatch = useAppDispatch(); // Destructuring store props. const { // currentTab, diff --git a/src/client/components/main/http2-composer/Http2MetaData.tsx b/src/client/components/main/http2-composer/Http2MetaData.tsx deleted file mode 100644 index 87d0aecf4..000000000 --- a/src/client/components/main/http2-composer/Http2MetaData.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React, { useState } from 'react'; -// Import local components -import KeyValueTable from './KeyValueTable'; -import Http2Body from './Http2Body'; -// Import MUI components -import { Box, Tabs, Tab } from '@mui/material'; - -interface TabPanelProps { - children?: React.ReactNode; - index: number; - value: number; -} - -function TabPanel(props: TabPanelProps) { - const { children, value, index, ...other } = props; - return ( - - ); -} - -function a11yProps(index: number) { - return { - id: `http2-tab-${index}`, - 'aria-controls': `http2-tabpanel-${index}`, - }; -} - -export default function Http2MetaData({ - parameters, - setParameters, - headers, - setHeaders, - cookies, - setCookies, - http2Method, -}) { - const [tab, setTab] = useState(0); - const handleTabChange = (event: React.SyntheticEvent, tab: number) => { - setTab(tab); - }; - return ( - - - - - - - - - - - {/** @todo add parameter support for HTTP2 requests */} - - - - - - - - - - - {http2Method === 'GET' ? ( - 'No body required for this method' - ) : ( - - )} - - - Render Test Snippets Here - - - ); -} diff --git a/src/client/components/main/http2-composer/RestMethodAndEndpointEntryForm.jsx b/src/client/components/main/http2-composer/RestMethodAndEndpointEntryForm.jsx index e7753d908..5f0bbcf07 100644 --- a/src/client/components/main/http2-composer/RestMethodAndEndpointEntryForm.jsx +++ b/src/client/components/main/http2-composer/RestMethodAndEndpointEntryForm.jsx @@ -1,5 +1,8 @@ import React, { useState, useRef, useEffect } from 'react'; -import { useSelector } from 'react-redux'; +import { + useAppDispatch, + useAppSelector, +} from '../../../toolkit-refactor/hooks'; import dropDownArrow from '../../../../assets/icons/arrow_drop_down_white_192x192.png'; const RestMethodAndEndpointEntryForm = ({ @@ -10,10 +13,10 @@ const RestMethodAndEndpointEntryForm = ({ newRequestBodySet, newRequestBody, newTestContentSet, - placeholder='Enter URL or paste text here', + placeholder = 'Enter URL or paste text here', value, }) => { - const isDark = useSelector((state) => state.ui.isDark); + const isDark = useAppSelector((state) => state.ui.isDark); const [dropdownIsActive, setDropdownIsActive] = useState(false); const dropdownEl = useRef(); diff --git a/src/client/components/main/response-composer/CookieContainer.tsx b/src/client/components/main/response-composer/CookieContainer.tsx index 9f0a4c9e9..13f337827 100644 --- a/src/client/components/main/response-composer/CookieContainer.tsx +++ b/src/client/components/main/response-composer/CookieContainer.tsx @@ -2,7 +2,7 @@ /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ /* eslint-disable jsx-a11y/click-events-have-key-events */ import React, { useState } from 'react'; -import { useSelector } from 'react-redux'; +import { useAppDispatch, useAppSelector } from '../../../toolkit-refactor/hooks'; interface CookieProps { className?: string; @@ -13,7 +13,7 @@ interface CookieProps { export default function CookieContainer({ className, cookie }: CookieProps) { const [showCookie, setShowCookie] = useState(false); - const isDark = useSelector((state: any) => state.ui.isDark); + const isDark = useAppSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); const cookies = Object.entries(cookie).map(([key, value], index) => { if (!key || !value) return null; diff --git a/src/client/components/main/response-composer/EmptyState.tsx b/src/client/components/main/response-composer/EmptyState.tsx index df963c86b..f4a55c57a 100644 --- a/src/client/components/main/response-composer/EmptyState.tsx +++ b/src/client/components/main/response-composer/EmptyState.tsx @@ -1,14 +1,22 @@ import React from 'react'; import logofaded from '../../../../assets/img/swell-logo-faded.avif'; +import { useAppSelector } from '../../../toolkit-refactor/hooks'; interface EmptyStateProps { connection?: any; } export default function EmptyState({ connection }: EmptyStateProps) { + const isDark = useAppSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); + return (
- faded-logo + faded-logo
); } diff --git a/src/client/components/main/response-composer/EventPreview.tsx b/src/client/components/main/response-composer/EventPreview.tsx index 8f8f3efda..cb3abaf1d 100644 --- a/src/client/components/main/response-composer/EventPreview.tsx +++ b/src/client/components/main/response-composer/EventPreview.tsx @@ -1,7 +1,7 @@ /* eslint-disable jsx-a11y/no-static-element-interactions */ /* eslint-disable jsx-a11y/click-events-have-key-events */ import React, { useState } from 'react'; -import { useSelector } from 'react-redux'; +import { useAppDispatch, useAppSelector } from '../../../toolkit-refactor/hooks'; import dropDownArrow from '../../../../assets/icons/caret-down-tests.svg'; import dropDownArrowUp from '../../../../assets/icons/caret-up-tests.svg'; @@ -13,12 +13,19 @@ interface Props { const EventPreview: React.FC = ({ contents }) => { const [showPreview, setShowPreview] = useState(false); const handleShowPreview = () => setShowPreview(!showPreview); - const isDark = useSelector((state: any) => state.ui.isDark); + const isDark = useAppSelector((store: { ui: { isDark: boolean } }) => store.ui.isDark); + + // Added modification to make the text white when dark mode is selected + const modifiedContents = isDark + ? `${JSON.parse(contents)}` + : JSON.parse(contents); return (
{ handleShowPreview(); }} @@ -51,9 +58,12 @@ const EventPreview: React.FC = ({ contents }) => { )}
{showPreview === true && ( -
+