feat: request json body support

This commit is contained in:
Anoop M D 2022-03-20 14:02:33 +05:30
parent 23910d3bb2
commit 7c2e909488
6 changed files with 185 additions and 4 deletions

View File

@ -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;

View File

@ -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 (
<StyledWrapper
className="h-full"
aria-label="Code Editor"
ref={node => {
this._node = node;
}}
/>
);
}
_onEdit = () => {
if (!this.ignoreChangeEvent && this.editor) {
this.cachedValue = this.editor.getValue();
if (this.props.onEdit) {
this.props.onEdit(this.cachedValue);
}
}
};
}

View File

@ -8,7 +8,7 @@ import StyledWrapper from './StyledWrapper';
const HttpRequestPane = ({item, collection, leftPaneWidth}) => {
return (
<StyledWrapper className="h-full">
<Tabs className='react-tabs mt-1 flex flex-grow flex-col h-full' forceRenderTabPanel>
<Tabs className='react-tabs mt-1 flex flex-grow flex-col h-full'>
<TabList>
<Tab tabIndex="-1">Params</Tab>
<Tab tabIndex="-1">Body</Tab>
@ -19,7 +19,7 @@ const HttpRequestPane = ({item, collection, leftPaneWidth}) => {
<QueryParams />
</TabPanel>
<TabPanel>
<RequestBody />
<RequestBody item={item} collection={collection}/>
</TabPanel>
<TabPanel>
<RequestHeaders item={item} collection={collection}/>

View File

@ -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(
<StyledWrapper className="mt-3">
<RequestBodyMode />
<div className="mt-4">
<CodeEditor value={bodyContent || ''} onEdit={onEdit}/>
</div>
</StyledWrapper>
);
};

View File

@ -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) => {

View File

@ -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)