From bd153bf8492c380e11ff2f4a61d9ab26e934b044 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Mon, 3 Oct 2022 00:41:25 +0530 Subject: [PATCH] feat: support for sending url encoded params (resolves #11) --- package-lock.json | 8 ++ package.json | 1 + .../FormUrlEncodedParams/StyledWrapper.js | 45 +++++++ .../RequestPane/FormUrlEncodedParams/index.js | 119 ++++++++++++++++++ .../RequestPane/RequestBody/index.js | 5 + .../ReduxStore/slices/collections.js | 58 +++++++++ renderer/utils/collections/index.js | 16 ++- renderer/utils/network/index.js | 12 +- 8 files changed, 261 insertions(+), 3 deletions(-) create mode 100644 renderer/components/RequestPane/FormUrlEncodedParams/StyledWrapper.js create mode 100644 renderer/components/RequestPane/FormUrlEncodedParams/index.js diff --git a/package-lock.json b/package-lock.json index a86af8b40..cafa2bfba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7190,6 +7190,14 @@ "postcss-selector-parser": "^6.0.6" } }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } + }, "querystring-es3": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", diff --git a/package.json b/package.json index 2af1bc74d..ecd8beb15 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "mousetrap": "^1.6.5", "nanoid": "^3.1.30", "next": "12.0.4", + "qs": "^6.11.0", "react": "17.0.2", "react-dom": "17.0.2", "react-redux": "^7.2.6", diff --git a/renderer/components/RequestPane/FormUrlEncodedParams/StyledWrapper.js b/renderer/components/RequestPane/FormUrlEncodedParams/StyledWrapper.js new file mode 100644 index 000000000..279e3b9c4 --- /dev/null +++ b/renderer/components/RequestPane/FormUrlEncodedParams/StyledWrapper.js @@ -0,0 +1,45 @@ +import styled from 'styled-components'; + +const Wrapper = styled.div` + table { + width: 100%; + border-collapse: collapse; + font-weight: 600; + + thead, td { + border: 1px solid #efefef; + } + + thead { + color: #616161; + font-size: 0.8125rem; + user-select: none; + } + td { + padding: 6px 10px; + } + } + + .btn-add-param { + font-size: 0.8125rem; + } + + input[type="text"] { + width: 100%; + border: solid 1px transparent; + outline: none !important; + + &:focus{ + outline: none !important; + border: solid 1px transparent; + } + } + + input[type="checkbox"] { + cursor: pointer; + position: relative; + top: 1px; + } +`; + +export default Wrapper; diff --git a/renderer/components/RequestPane/FormUrlEncodedParams/index.js b/renderer/components/RequestPane/FormUrlEncodedParams/index.js new file mode 100644 index 000000000..67692e305 --- /dev/null +++ b/renderer/components/RequestPane/FormUrlEncodedParams/index.js @@ -0,0 +1,119 @@ +import React from 'react'; +import get from 'lodash/get'; +import cloneDeep from 'lodash/cloneDeep'; +import { IconTrash } from '@tabler/icons'; +import { useDispatch } from 'react-redux'; +import { addFormUrlEncodedParam, updateFormUrlEncodedParam, deleteFormUrlEncodedParam } from 'providers/ReduxStore/slices/collections'; +import StyledWrapper from './StyledWrapper'; + +const FormUrlEncodedParams = ({item, collection}) => { + const dispatch = useDispatch(); + const params = item.draft ? get(item, 'draft.request.body.formUrlEncoded') : get(item, 'request.body.formUrlEncoded'); + + const addParam = () => { + dispatch(addFormUrlEncodedParam({ + itemUid: item.uid, + collectionUid: collection.uid, + })); + }; + + const handleParamChange = (e, _param, type) => { + const param = cloneDeep(_param); + switch(type) { + case 'name' : { + param.name = e.target.value; + break; + } + case 'value' : { + param.value = e.target.value; + break; + } + case 'description' : { + param.description = e.target.value; + break; + } + case 'enabled' : { + param.enabled = e.target.checked; + break; + } + } + dispatch(updateFormUrlEncodedParam({ + param: param, + itemUid: item.uid, + collectionUid: collection.uid + })); + }; + + const handleRemoveParams = (param) => { + dispatch(deleteFormUrlEncodedParam({ + paramUid: param.uid, + itemUid: item.uid, + collectionUid: collection.uid + })); + }; + + return ( + + + + + + + + + + + + {params && params.length ? params.map((param, index) => { + return ( + + + + + + + ); + }) : null} + +
KeyValueDescription
+ handleParamChange(e, param, 'name')} + /> + + handleParamChange(e, param, 'value')} + /> + + handleParamChange(e, param, 'description')} + /> + +
+ handleParamChange(e, param, 'enabled')} + /> + +
+
+ +
+ ) +}; +export default FormUrlEncodedParams; \ No newline at end of file diff --git a/renderer/components/RequestPane/RequestBody/index.js b/renderer/components/RequestPane/RequestBody/index.js index 16073db04..c2300b318 100644 --- a/renderer/components/RequestPane/RequestBody/index.js +++ b/renderer/components/RequestPane/RequestBody/index.js @@ -1,6 +1,7 @@ import React from 'react'; import get from 'lodash/get'; import CodeEditor from 'components/CodeEditor'; +import FormUrlEncodedParams from 'components/RequestPane/FormUrlEncodedParams'; import { useDispatch } from 'react-redux'; import { updateRequestBody, sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections'; import StyledWrapper from './StyledWrapper'; @@ -47,6 +48,10 @@ const RequestBody = ({item, collection}) => { ); } + if(bodyMode === 'formUrlEncoded') { + return ; + } + return( No Body diff --git a/renderer/providers/ReduxStore/slices/collections.js b/renderer/providers/ReduxStore/slices/collections.js index afe75bc3d..054dcd4a6 100644 --- a/renderer/providers/ReduxStore/slices/collections.js +++ b/renderer/providers/ReduxStore/slices/collections.js @@ -343,6 +343,61 @@ export const collectionsSlice = createSlice({ } } }, + addFormUrlEncodedParam: (state, action) => { + const collection = findCollectionByUid(state.collections, action.payload.collectionUid); + + if(collection) { + const item = findItemInCollection(collection, action.payload.itemUid); + + if(item && isItemARequest(item)) { + if(!item.draft) { + item.draft = cloneDeep(item); + } + item.draft.request.body.formUrlEncoded = item.draft.request.body.formUrlEncoded || []; + item.draft.request.body.formUrlEncoded.push({ + uid: uuid(), + name: '', + value: '', + description: '', + enabled: true + }); + } + } + }, + updateFormUrlEncodedParam: (state, action) => { + const collection = findCollectionByUid(state.collections, action.payload.collectionUid); + + if(collection) { + const item = findItemInCollection(collection, action.payload.itemUid); + + if(item && isItemARequest(item)) { + if(!item.draft) { + item.draft = cloneDeep(item); + } + const param = find(item.draft.request.body.formUrlEncoded, (p) => p.uid === action.payload.param.uid); + if(param) { + param.name = action.payload.param.name; + param.value = action.payload.param.value; + param.description = action.payload.param.description; + param.enabled = action.payload.param.enabled; + } + } + } + }, + deleteFormUrlEncodedParam: (state, action) => { + const collection = findCollectionByUid(state.collections, action.payload.collectionUid); + + if(collection) { + const item = findItemInCollection(collection, action.payload.itemUid); + + if(item && isItemARequest(item)) { + if(!item.draft) { + item.draft = cloneDeep(item); + } + item.draft.request.body.formUrlEncoded = filter(item.draft.request.body.formUrlEncoded, (p) => p.uid !== action.payload.paramUid); + } + } + }, updateRequestBodyMode: (state, action) => { const collection = findCollectionByUid(state.collections, action.payload.collectionUid); @@ -429,6 +484,9 @@ export const { addRequestHeader, updateRequestHeader, deleteRequestHeader, + addFormUrlEncodedParam, + updateFormUrlEncodedParam, + deleteFormUrlEncodedParam, updateRequestBodyMode, updateRequestBody, updateRequestMethod diff --git a/renderer/utils/collections/index.js b/renderer/utils/collections/index.js index 550a9eee8..e40603cb8 100644 --- a/renderer/utils/collections/index.js +++ b/renderer/utils/collections/index.js @@ -128,6 +128,18 @@ export const transformCollectionToSaveToIdb = (collection, options = {}) => { }); }; + const copyFormUrlEncodedParams = (params = []) => { + return map(params, (param) => { + return { + uid: param.uid, + name: param.name, + value: param.value, + description: param.description, + enabled: param.enabled + } + }); + }; + const copyItems = (sourceItems, destItems) => { each(sourceItems, (si) => { const di = { @@ -151,7 +163,7 @@ export const transformCollectionToSaveToIdb = (collection, options = {}) => { text: si.draft.request.body.text, xml: si.draft.request.body.xml, multipartForm: si.draft.request.body.multipartForm, - xmformUrlEncodedl: si.draft.request.body.formUrlEncoded + formUrlEncoded: copyFormUrlEncodedParams(si.draft.request.body.formUrlEncoded) } }; } @@ -168,7 +180,7 @@ export const transformCollectionToSaveToIdb = (collection, options = {}) => { text: si.request.body.text, xml: si.request.body.xml, multipartForm: si.request.body.multipartForm, - xmformUrlEncodedl: si.request.body.formUrlEncoded + formUrlEncoded: copyFormUrlEncodedParams(si.request.body.formUrlEncoded) } } }; diff --git a/renderer/utils/network/index.js b/renderer/utils/network/index.js index 7f2d1610f..4dc69748c 100644 --- a/renderer/utils/network/index.js +++ b/renderer/utils/network/index.js @@ -1,4 +1,6 @@ import each from 'lodash/each'; +import filter from 'lodash/filter'; +import qs from 'qs'; import { rawRequest, gql } from 'graphql-request'; const sendNetworkRequest = async (item) => { @@ -58,8 +60,16 @@ const sendHttpRequest = async (request) => { options.data = request.body.xml; } + if(request.body.mode === 'formUrlEncoded') { + options.headers['Content-Type'] = 'application/x-www-form-urlencoded'; + const params = {}; + const enabledParams = filter(request.body.formUrlEncoded, p => p.enabled); + each(enabledParams, (p) => params[p.name] = p.value); + options.data = qs.stringify(params); + } + console.log('>>> Sending Request'); - console.log(request); + console.log(options); ipcRenderer .invoke('send-http-request', options)