From ad9e415eba45dea0f839b03a0e57400d444a501f Mon Sep 17 00:00:00 2001 From: Chris Casola Date: Fri, 20 Oct 2023 13:43:11 -0400 Subject: [PATCH] feat: add bulk edit mode for request headers Closes #185 --- .../RequestPane/HttpRequestPane/index.js | 2 +- .../RequestPane/RequestBody/StyledWrapper.js | 3 +- .../RequestHeaders/StyledWrapper.js | 12 +- .../RequestPane/RequestHeaders/index.js | 230 ++++++++++++------ .../ReduxStore/slices/collections/index.js | 21 ++ 5 files changed, 184 insertions(+), 84 deletions(-) diff --git a/packages/bruno-app/src/components/RequestPane/HttpRequestPane/index.js b/packages/bruno-app/src/components/RequestPane/HttpRequestPane/index.js index 83475184..80a61f0f 100644 --- a/packages/bruno-app/src/components/RequestPane/HttpRequestPane/index.js +++ b/packages/bruno-app/src/components/RequestPane/HttpRequestPane/index.js @@ -136,7 +136,7 @@ const HttpRequestPane = ({ item, collection, leftPaneWidth }) => { ) : null}
diff --git a/packages/bruno-app/src/components/RequestPane/RequestBody/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/RequestBody/StyledWrapper.js index 83ebd814..09df0651 100644 --- a/packages/bruno-app/src/components/RequestPane/RequestBody/StyledWrapper.js +++ b/packages/bruno-app/src/components/RequestPane/RequestBody/StyledWrapper.js @@ -2,8 +2,7 @@ import styled from 'styled-components'; const Wrapper = styled.div` div.CodeMirror { - /* todo: find a better way */ - height: calc(100vh - 220px); + height: 100%; } `; diff --git a/packages/bruno-app/src/components/RequestPane/RequestHeaders/StyledWrapper.js b/packages/bruno-app/src/components/RequestPane/RequestHeaders/StyledWrapper.js index 9f723cb8..04f8f76b 100644 --- a/packages/bruno-app/src/components/RequestPane/RequestHeaders/StyledWrapper.js +++ b/packages/bruno-app/src/components/RequestPane/RequestHeaders/StyledWrapper.js @@ -30,10 +30,20 @@ const Wrapper = styled.div` } } - .btn-add-header { + .top-controls { + display: flex; + justify-content: right; font-size: 0.8125rem; } + .bottom-controls { + font-size: 0.8125rem; + } + + div.CodeMirror { + height: 100%; + } + input[type='text'] { width: 100%; border: solid 1px transparent; diff --git a/packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js b/packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js index db8b20f5..95a1d009 100644 --- a/packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js +++ b/packages/bruno-app/src/components/RequestPane/RequestHeaders/index.js @@ -1,12 +1,18 @@ -import React from 'react'; +import React, { useState } from 'react'; import get from 'lodash/get'; import cloneDeep from 'lodash/cloneDeep'; import { IconTrash } from '@tabler/icons'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { useTheme } from 'providers/Theme'; -import { addRequestHeader, updateRequestHeader, deleteRequestHeader } from 'providers/ReduxStore/slices/collections'; +import { + addRequestHeader, + updateRequestHeader, + deleteRequestHeader, + setRequestHeaders +} from 'providers/ReduxStore/slices/collections'; import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions'; import SingleLineEditor from 'components/SingleLineEditor'; +import CodeEditor from 'components/CodeEditor'; import StyledWrapper from './StyledWrapper'; import { headers as StandardHTTPHeaders } from 'know-your-http-well'; const headerAutoCompleteList = StandardHTTPHeaders.map((e) => e.header); @@ -14,6 +20,7 @@ const headerAutoCompleteList = StandardHTTPHeaders.map((e) => e.header); const RequestHeaders = ({ item, collection }) => { const dispatch = useDispatch(); const { storedTheme } = useTheme(); + const preferences = useSelector((state) => state.app.preferences); const headers = item.draft ? get(item, 'draft.request.headers') : get(item, 'request.headers'); const addHeader = () => { @@ -62,85 +69,148 @@ const RequestHeaders = ({ item, collection }) => { ); }; + const [bulkEdit, setBulkEdit] = useState(false); + const [bulkText, setBulkText] = useState(''); + + const handleBulkEdit = (value) => { + setBulkText(value); + + const keyValPairs = value + .split(/\r?\n/) + .map((pair) => { + const sep = pair.indexOf(':'); + if (sep < 0) { + return []; + } + return [pair.slice(0, sep).trim(), pair.slice(sep + 1).trim()]; + }) + .filter((pair) => pair.length === 2); + + dispatch( + setRequestHeaders({ + collectionUid: collection.uid, + itemUid: item.uid, + headers: keyValPairs.map(([name, value]) => ({ + name, + value + })) + }) + ); + }; + + const toggleBulkEdit = () => { + if (!bulkEdit) { + setBulkText( + headers + .filter((header) => header.enabled) + .map((header) => `${header.name}: ${header.value}`) + .join('\n') + ); + } + setBulkEdit(!bulkEdit); + }; + return ( - - - - - - - - - - - {headers && headers.length - ? headers.map((header) => { - return ( - - - - + + + + ); + }) + : null} + +
NameValue
- - handleHeaderValueChange( - { - target: { - value: newValue - } - }, - header, - 'name' - ) - } - autocomplete={headerAutoCompleteList} - onRun={handleRun} - collection={collection} - /> - - - handleHeaderValueChange( - { - target: { - value: newValue - } - }, - header, - 'value' - ) - } - onRun={handleRun} - collection={collection} - /> - -
- handleHeaderValueChange(e, header, 'enabled')} + +
+ +
+ {bulkEdit && ( +
+ +
+ )} + {!bulkEdit && ( + + + + + + + + + + {headers && headers.length + ? headers.map((header) => { + return ( + + - - ); - }) - : null} - -
NameValue
+ + handleHeaderValueChange( + { + target: { + value: newValue + } + }, + header, + 'name' + ) + } + autocomplete={headerAutoCompleteList} + onRun={handleRun} + collection={collection} /> - - -
- +
+ + handleHeaderValueChange( + { + target: { + value: newValue + } + }, + header, + 'value' + ) + } + onRun={handleRun} + collection={collection} + /> + +
+ handleHeaderValueChange(e, header, 'enabled')} + /> + +
+
+ )} +
+ {!bulkEdit && ( + + )} +
); }; diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js index 4ad30d20..9de4bb9d 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -532,6 +532,26 @@ export const collectionsSlice = createSlice({ } } }, + setRequestHeaders: (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.headers = map(action.payload.headers, (header) => ({ + uid: uuid(), + name: header.name, + value: header.value, + description: '', + enabled: true + })); + } + } + }, addFormUrlEncodedParam: (state, action) => { const collection = findCollectionByUid(state.collections, action.payload.collectionUid); @@ -1390,6 +1410,7 @@ export const { addRequestHeader, updateRequestHeader, deleteRequestHeader, + setRequestHeaders, addFormUrlEncodedParam, updateFormUrlEncodedParam, deleteFormUrlEncodedParam,