From 7c2e90948807681a6b42877544dfde8b0cdd9924 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Sun, 20 Mar 2022 14:02:33 +0530 Subject: [PATCH] feat: request json body support --- .../components/CodeEditor/StyledWrapper.js | 14 ++ renderer/components/CodeEditor/index.js | 121 ++++++++++++++++++ .../RequestPane/HttpRequestPane/index.js | 4 +- .../RequestPane/RequestBody/index.js | 26 +++- .../ReduxStore/slices/collections.js | 20 ++- renderer/utils/network/index.js | 4 + 6 files changed, 185 insertions(+), 4 deletions(-) create mode 100644 renderer/components/CodeEditor/StyledWrapper.js create mode 100644 renderer/components/CodeEditor/index.js diff --git a/renderer/components/CodeEditor/StyledWrapper.js b/renderer/components/CodeEditor/StyledWrapper.js new file mode 100644 index 000000000..eefb1f917 --- /dev/null +++ b/renderer/components/CodeEditor/StyledWrapper.js @@ -0,0 +1,14 @@ +import styled from 'styled-components'; + +const StyledWrapper = styled.div` + div.CodeMirror { + border: solid 1px var(--color-codemirror-border); + } + + textarea.cm-editor { + position: relative; + } +`; + +export default StyledWrapper; + diff --git a/renderer/components/CodeEditor/index.js b/renderer/components/CodeEditor/index.js new file mode 100644 index 000000000..f59e97ced --- /dev/null +++ b/renderer/components/CodeEditor/index.js @@ -0,0 +1,121 @@ +/** + * Copyright (c) 2021 GraphQL Contributors. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import StyledWrapper from './StyledWrapper'; + +let CodeMirror; +const SERVER_RENDERED = typeof navigator === 'undefined' || global['PREVENT_CODEMIRROR_RENDER'] === true; + +if (!SERVER_RENDERED) { + CodeMirror = require('codemirror'); +} + +export default class QueryEditor extends React.Component { + constructor(props) { + super(props); + + // Keep a cached version of the value, this cache will be updated when the + // editor is updated, which can later be used to protect the editor from + // unnecessary updates during the update lifecycle. + this.cachedValue = props.value || ''; + } + + componentDidMount() { + const editor = (this.editor = CodeMirror(this._node, { + value: this.props.value || '', + lineNumbers: true, + lineWrapping: true, + tabSize: 2, + mode: 'application/ld+json', + keyMap: 'sublime', + autoCloseBrackets: true, + matchBrackets: true, + showCursorWhenSelecting: true, + foldGutter: true, + gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"], + readOnly: this.props.readOnly ? 'nocursor' : false, + extraKeys: { + 'Cmd-Enter': () => { + if (this.props.onChange) { + // empty + } + }, + 'Ctrl-Enter': () => { + if (this.props.onChange) { + // empty + } + }, + 'Cmd-S': () => { + if (this.props.onRunQuery) { + // empty + } + }, + + 'Ctrl-S': () => { + if (this.props.onRunQuery) { + // empty + } + } + }, + })); + if (editor) { + editor.on('change', this._onEdit); + } + } + + componentDidUpdate(prevProps) { + // Ensure the changes caused by this update are not interpretted as + // user-input changes which could otherwise result in an infinite + // event loop. + this.ignoreChangeEvent = true; + if (this.props.schema !== prevProps.schema && this.editor) { + this.editor.options.lint.schema = this.props.schema; + this.editor.options.hintOptions.schema = this.props.schema; + this.editor.options.info.schema = this.props.schema; + this.editor.options.jump.schema = this.props.schema; + CodeMirror.signal(this.editor, 'change', this.editor); + } + if ( + this.props.value !== prevProps.value && + this.props.value !== this.cachedValue && + this.editor + ) { + this.cachedValue = this.props.value; + this.editor.setValue(this.props.value); + } + this.ignoreChangeEvent = false; + } + + componentWillUnmount() { + if (this.editor) { + this.editor.off('change', this._onEdit); + this.editor = null; + } + } + + render() { + return ( + { + this._node = node; + }} + /> + ); + } + + _onEdit = () => { + if (!this.ignoreChangeEvent && this.editor) { + this.cachedValue = this.editor.getValue(); + if (this.props.onEdit) { + this.props.onEdit(this.cachedValue); + } + } + }; +} diff --git a/renderer/components/RequestPane/HttpRequestPane/index.js b/renderer/components/RequestPane/HttpRequestPane/index.js index f6a19ca3b..3d755abf2 100644 --- a/renderer/components/RequestPane/HttpRequestPane/index.js +++ b/renderer/components/RequestPane/HttpRequestPane/index.js @@ -8,7 +8,7 @@ import StyledWrapper from './StyledWrapper'; const HttpRequestPane = ({item, collection, leftPaneWidth}) => { return ( - + Params Body @@ -19,7 +19,7 @@ const HttpRequestPane = ({item, collection, leftPaneWidth}) => { - + diff --git a/renderer/components/RequestPane/RequestBody/index.js b/renderer/components/RequestPane/RequestBody/index.js index 50c8ead6c..bb4d2c236 100644 --- a/renderer/components/RequestPane/RequestBody/index.js +++ b/renderer/components/RequestPane/RequestBody/index.js @@ -1,11 +1,35 @@ import React from 'react'; +import get from 'lodash/get'; +import CodeEditor from 'components/CodeEditor'; +import { useDispatch } from 'react-redux'; +import { requestChanged } from 'providers/ReduxStore/slices/tabs'; +import { updateRequestBody } from 'providers/ReduxStore/slices/collections'; import RequestBodyMode from './RequestBodyMode'; import StyledWrapper from './StyledWrapper'; -const RequestBody = () => { +const RequestBody = ({item, collection}) => { + const dispatch = useDispatch(); + const bodyContent = item.draft ? get(item, 'draft.request.body.content') : get(item, 'request.body.content'); + + const onEdit = (value) => { + dispatch(requestChanged({ + itemUid: item.uid, + collectionUid: collection.uid + })); + dispatch(updateRequestBody({ + mode: 'json', + content: value, + itemUid: item.uid, + collectionUid: collection.uid, + })); + }; + return( +
+ +
); }; diff --git a/renderer/providers/ReduxStore/slices/collections.js b/renderer/providers/ReduxStore/slices/collections.js index 632bd989c..ec83f4d9d 100644 --- a/renderer/providers/ReduxStore/slices/collections.js +++ b/renderer/providers/ReduxStore/slices/collections.js @@ -187,6 +187,23 @@ export const collectionsSlice = createSlice({ item.draft.request.headers = filter(item.draft.request.headers, (h) => h.uid !== action.payload.headerUid); } } + }, + updateRequestBody: (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 = cloneItem(item); + } + item.draft.request.body = { + mode: action.payload.mode, + content: action.payload.content + } + } + } } } }); @@ -205,7 +222,8 @@ export const { requestUrlChanged, addRequestHeader, updateRequestHeader, - deleteRequestHeader + deleteRequestHeader, + updateRequestBody } = collectionsSlice.actions; export const loadCollectionsFromIdb = () => (dispatch) => { diff --git a/renderer/utils/network/index.js b/renderer/utils/network/index.js index 21b958dcc..398398b0e 100644 --- a/renderer/utils/network/index.js +++ b/renderer/utils/network/index.js @@ -40,6 +40,10 @@ const sendHttpRequest = async (request) => { headers: headers }; + if(request.body && request.body.mode === 'json' && request.body.content) { + options.data = request.body.content; + } + ipcRenderer .invoke('send-http-request', options) .then(resolve)