diff --git a/packages/bruno-app/src/components/ResponsePane/Timeline/StyledWrapper.js b/packages/bruno-app/src/components/ResponsePane/Timeline/StyledWrapper.js new file mode 100644 index 000000000..88a5931be --- /dev/null +++ b/packages/bruno-app/src/components/ResponsePane/Timeline/StyledWrapper.js @@ -0,0 +1,23 @@ +import styled from 'styled-components'; + +const StyledWrapper = styled.div` + .line { + white-space: pre-line; + word-wrap: break-word; + word-break: break-all; + + .arrow { + opacity: 0.5; + } + + &.request { + color: ${(props) => props.theme.colors.text.green}; + } + + &.response { + color: ${(props) => props.theme.colors.text.purple}; + } + } +`; + +export default StyledWrapper; diff --git a/packages/bruno-app/src/components/ResponsePane/Timeline/index.js b/packages/bruno-app/src/components/ResponsePane/Timeline/index.js new file mode 100644 index 000000000..75477f8f7 --- /dev/null +++ b/packages/bruno-app/src/components/ResponsePane/Timeline/index.js @@ -0,0 +1,54 @@ +import React from 'react'; +import forOwn from 'lodash/forOwn'; +import StyledWrapper from './StyledWrapper'; + +const Timeline = ({ item }) => { + const request = item.requestSent || {}; + const response = item.response || {}; + const requestHeaders = []; + const responseHeaders = response.headers || []; + + forOwn(request.headers, (value, key) => { + requestHeaders.push({ + name: key, + value + }); + }); + + return ( + +
+
+          {'>'} {request.method} {request.url}
+        
+ {requestHeaders.map((h) => { + return ( +
+              {'>'} {h.name}: {h.value}
+            
+ ); + })} + +
+          {'>'} data {request.data ? request.data : 'null'}
+        
+
+ +
+
+          {'<'} {response.status} {response.statusText}
+        
+ + {responseHeaders.map((h) => { + return ( +
+              {'<'} {h[0]}: {h[1]}
+            
+ ); + })} +
+
+ ); +}; + +export default Timeline; diff --git a/packages/bruno-app/src/components/ResponsePane/index.js b/packages/bruno-app/src/components/ResponsePane/index.js index ace848842..b41578dc1 100644 --- a/packages/bruno-app/src/components/ResponsePane/index.js +++ b/packages/bruno-app/src/components/ResponsePane/index.js @@ -10,6 +10,7 @@ import ResponseHeaders from './ResponseHeaders'; import StatusCode from './StatusCode'; import ResponseTime from './ResponseTime'; import ResponseSize from './ResponseSize'; +import Timeline from './Timeline'; import StyledWrapper from './StyledWrapper'; const ResponsePane = ({ rightPaneWidth, item, collection }) => { @@ -37,6 +38,9 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => { case 'headers': { return ; } + case 'timeline': { + return ; + } default: { return
404 | Not found
; @@ -84,6 +88,9 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
selectTab('headers')}> Headers
+
selectTab('timeline')}> + Timeline +
{!isLoading ? (
diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js index 8a0551791..4fb58d0e4 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js @@ -107,13 +107,16 @@ export const sendRequest = (item, collectionUid) => (dispatch, getState) => { return reject(new Error('Collection not found')); } - dispatch( - requestSent({ - itemUid: item.uid, - collectionUid: collectionUid, - cancelTokenUid: cancelTokenUid - }) - ); + const onRequestSent = (req) => { + dispatch( + requestSent({ + requestSent: req, + itemUid: item.uid, + collectionUid: collectionUid, + cancelTokenUid: cancelTokenUid + }) + ); + }; const itemCopy = cloneDeep(item); const collectionCopy = cloneDeep(collection); @@ -125,7 +128,7 @@ export const sendRequest = (item, collectionUid) => (dispatch, getState) => { } } - sendNetworkRequest(itemCopy, { cancelTokenUid: cancelTokenUid }) + sendNetworkRequest(itemCopy, { cancelTokenUid: cancelTokenUid }, onRequestSent) .then((response) => { return dispatch( responseReceived({ 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 ab8f22f8e..7ac998102 100644 --- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js +++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js @@ -179,12 +179,13 @@ export const collectionsSlice = createSlice({ } }, requestSent: (state, action) => { - const { itemUid, collectionUid, cancelTokenUid } = action.payload; + const { itemUid, collectionUid, cancelTokenUid, requestSent } = action.payload; const collection = findCollectionByUid(state.collections, collectionUid); if (collection) { const item = findItemInCollection(collection, itemUid); if (item) { + item.requestSent = requestSent item.response = item.response || {}; item.response.state = 'sending'; item.cancelTokenUid = cancelTokenUid; diff --git a/packages/bruno-app/src/themes/dark.js b/packages/bruno-app/src/themes/dark.js index dff763d7c..2669f4557 100644 --- a/packages/bruno-app/src/themes/dark.js +++ b/packages/bruno-app/src/themes/dark.js @@ -6,8 +6,10 @@ const darkTheme = { colors: { text: { + green: 'rgb(11 178 126)', danger: '#f06f57', - muted: '#9d9d9d' + muted: '#9d9d9d', + purple: '#cd56d6' }, bg: { danger: '#d03544' diff --git a/packages/bruno-app/src/themes/light.js b/packages/bruno-app/src/themes/light.js index da6afd736..12c1eabbf 100644 --- a/packages/bruno-app/src/themes/light.js +++ b/packages/bruno-app/src/themes/light.js @@ -6,8 +6,10 @@ const lightTheme = { colors: { text: { + green: '#047857', danger: 'rgb(185, 28, 28)', - muted: '#4b5563' + muted: '#4b5563', + purple: '#8e44ad', }, bg: { danger: '#dc3545' diff --git a/packages/bruno-app/src/utils/network/browser.js b/packages/bruno-app/src/utils/network/browser.js deleted file mode 100644 index d32a328c2..000000000 --- a/packages/bruno-app/src/utils/network/browser.js +++ /dev/null @@ -1,38 +0,0 @@ -const axios = require('axios'); -import { saveCancelToken, deleteCancelToken } from 'utils/network/cancelTokens'; - -export const sendHttpRequestInBrowser = async (request, options) => { - try { - if (options && options.cancelTokenUid) { - const cancelToken = axios.CancelToken.source(); - request.cancelToken = cancelToken.token; - saveCancelToken(options.cancelTokenUid, cancelToken); - } - - const result = await axios(request); - - if (options && options.cancelTokenUid) { - deleteCancelToken(options.cancelTokenUid); - } - - return { - status: result.status, - headers: result.headers, - data: result.data - }; - } catch (error) { - if (options && options.cancelTokenUid) { - deleteCancelToken(options.cancelTokenUid); - } - - if (error.response) { - return { - status: error.response.status, - headers: error.response.headers, - data: error.response.data - }; - } - - return Promise.reject(error); - } -}; diff --git a/packages/bruno-app/src/utils/network/index.js b/packages/bruno-app/src/utils/network/index.js index 1efde5692..337a15f01 100644 --- a/packages/bruno-app/src/utils/network/index.js +++ b/packages/bruno-app/src/utils/network/index.js @@ -2,15 +2,12 @@ import get from 'lodash/get'; import each from 'lodash/each'; import filter from 'lodash/filter'; import qs from 'qs'; -import { sendHttpRequestInBrowser } from './browser'; -import { isElectron } from 'utils/common/platform'; -import cancelTokens, { deleteCancelToken } from 'utils/network/cancelTokens'; -export const sendNetworkRequest = async (item, options) => { +export const sendNetworkRequest = async (item, options, onRequestSent) => { return new Promise((resolve, reject) => { if (['http-request', 'graphql-request'].includes(item.type)) { const timeStart = Date.now(); - sendHttpRequest(item.draft ? item.draft.request : item.request, options) + sendHttpRequest(item.draft ? item.draft.request : item.request, options, onRequestSent) .then((response) => { const timeEnd = Date.now(); resolve({ @@ -19,6 +16,7 @@ export const sendNetworkRequest = async (item, options) => { headers: Object.entries(response.headers), size: response.headers['content-length'] || 0, status: response.status, + statusText: response.statusText, duration: timeEnd - timeStart }); }) @@ -27,7 +25,7 @@ export const sendNetworkRequest = async (item, options) => { }); }; -const sendHttpRequest = async (request, options) => { +const sendHttpRequest = async (request, options, onRequestSent) => { return new Promise((resolve, reject) => { const { ipcRenderer } = window; @@ -91,28 +89,20 @@ const sendHttpRequest = async (request, options) => { console.log('>>> Sending Request'); console.log(axiosRequest); - if (isElectron()) { - ipcRenderer.invoke('send-http-request', axiosRequest, options).then(resolve).catch(reject); - } else { - sendHttpRequestInBrowser(axiosRequest, options).then(resolve).catch(reject); - } + onRequestSent(axiosRequest); + + ipcRenderer + .invoke('send-http-request', axiosRequest, options) + .then(resolve) + .catch(reject); }); }; export const cancelNetworkRequest = async (cancelTokenUid) => { - if (isElectron()) { - return new Promise((resolve, reject) => { - ipcRenderer.invoke('cancel-http-request', cancelTokenUid).then(resolve).catch(reject); - }); - } - return new Promise((resolve, reject) => { - if (cancelTokenUid && cancelTokens[cancelTokenUid]) { - cancelTokens[cancelTokenUid].cancel(); - deleteCancelToken(cancelTokenUid); - resolve(); - } else { - reject(new Error('cancel token not found')); - } + ipcRenderer + .invoke('cancel-http-request', cancelTokenUid) + .then(resolve) + .catch(reject); }); }; diff --git a/packages/bruno-electron/package.json b/packages/bruno-electron/package.json index 2389bc311..f9d346ed5 100644 --- a/packages/bruno-electron/package.json +++ b/packages/bruno-electron/package.json @@ -31,7 +31,7 @@ "dmg-license": "^1.0.11" }, "devDependencies": { - "electron": "^21.1.1", + "electron": "21.1.1", "electron-builder": "23.0.2", "electron-icon-maker": "^0.0.5" } diff --git a/packages/bruno-electron/src/ipc/network.js b/packages/bruno-electron/src/ipc/network.js index f290c0243..c1f8e6d95 100644 --- a/packages/bruno-electron/src/ipc/network.js +++ b/packages/bruno-electron/src/ipc/network.js @@ -33,6 +33,7 @@ const registerNetworkIpc = () => { return { status: result.status, + statusText: result.statusText, headers: result.headers, data: result.data }; @@ -44,6 +45,7 @@ const registerNetworkIpc = () => { if(error.response) { return { status: error.response.status, + statusText: error.response.statusText, headers: error.response.headers, data: error.response.data }