feat: making request and response scripts work

This commit is contained in:
Anoop M D 2023-02-06 23:00:50 +05:30
parent 60c96f7d27
commit 22a14aa67a
15 changed files with 153 additions and 86 deletions

View File

@ -89,7 +89,7 @@ const HttpRequestPane = ({ item, collection, leftPaneWidth }) => {
</div> </div>
) : null} ) : null}
</div> </div>
<section className="flex w-full mt-5">{getTabPanel(focusedTab.requestPaneTab)}</section> <section className={`flex w-full ${focusedTab.requestPaneTab === 'script' ? '' : 'mt-5'}`}>{getTabPanel(focusedTab.requestPaneTab)}</section>
</StyledWrapper> </StyledWrapper>
); );
}; };

View File

@ -2,8 +2,12 @@ import styled from 'styled-components';
const StyledWrapper = styled.div` const StyledWrapper = styled.div`
div.CodeMirror { div.CodeMirror {
/* todo: find a better way */ height: inherit;
height: calc(100vh - 220px); }
div.title {
color: rgb(155 155 155);
font-weight: 500;
} }
`; `;

View File

@ -2,20 +2,21 @@ import React from 'react';
import get from 'lodash/get'; import get from 'lodash/get';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import CodeEditor from 'components/CodeEditor'; import CodeEditor from 'components/CodeEditor';
import { updateRequestScript } from 'providers/ReduxStore/slices/collections'; import { updateRequestScript, updateResponseScript } from 'providers/ReduxStore/slices/collections';
import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions'; import { sendRequest, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
import { useTheme } from 'providers/Theme'; import { useTheme } from 'providers/Theme';
import StyledWrapper from './StyledWrapper'; import StyledWrapper from './StyledWrapper';
const Script = ({ item, collection }) => { const Script = ({ item, collection }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const script = item.draft ? get(item, 'draft.request.script') : get(item, 'request.script'); const requestScript = item.draft ? get(item, 'draft.request.script.req') : get(item, 'request.script.req');
const responseScript = item.draft ? get(item, 'draft.request.script.res') : get(item, 'request.script.res');
const { const {
storedTheme storedTheme
} = useTheme(); } = useTheme();
const onEdit = (value) => { const onRequestScriptEdit = (value) => {
dispatch( dispatch(
updateRequestScript({ updateRequestScript({
script: value, script: value,
@ -25,19 +26,43 @@ const Script = ({ item, collection }) => {
); );
}; };
const onResponseScriptEdit = (value) => {
dispatch(
updateResponseScript({
script: value,
itemUid: item.uid,
collectionUid: collection.uid
})
);
};
const onRun = () => dispatch(sendRequest(item, collection.uid)); const onRun = () => dispatch(sendRequest(item, collection.uid));
const onSave = () => dispatch(saveRequest(item.uid, collection.uid)); const onSave = () => dispatch(saveRequest(item.uid, collection.uid));
return ( return (
<StyledWrapper className="w-full"> <StyledWrapper className="w-full flex flex-col">
<CodeEditor <div className='flex-1'>
collection={collection} value={script || ''} <div className='mb-1 title'>Request</div>
theme={storedTheme} <CodeEditor
onEdit={onEdit} collection={collection} value={requestScript || ''}
mode='javascript' theme={storedTheme}
onRun={onRun} onEdit={onRequestScriptEdit}
onSave={onSave} mode='javascript'
/> onRun={onRun}
onSave={onSave}
/>
</div>
<div className='flex-1 mt-6'>
<div className='mt-1 mb-1 title'>Response</div>
<CodeEditor
collection={collection} value={responseScript || ''}
theme={storedTheme}
onEdit={onResponseScriptEdit}
mode='javascript'
onRun={onRun}
onSave={onSave}
/>
</div>
</StyledWrapper> </StyledWrapper>
); );
}; };

View File

@ -652,7 +652,23 @@ export const collectionsSlice = createSlice({
if (!item.draft) { if (!item.draft) {
item.draft = cloneDeep(item); item.draft = cloneDeep(item);
} }
item.draft.request.script = action.payload.script; item.draft.request.script = item.draft.request.script || {};
item.draft.request.script.req = action.payload.script;
}
}
},
updateResponseScript: (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.script = item.draft.request.script || {};
item.draft.request.script.res = action.payload.script;
} }
} }
}, },
@ -971,6 +987,7 @@ export const {
updateRequestGraphqlQuery, updateRequestGraphqlQuery,
updateRequestGraphqlVariables, updateRequestGraphqlVariables,
updateRequestScript, updateRequestScript,
updateResponseScript,
updateRequestTests, updateRequestTests,
updateRequestMethod, updateRequestMethod,
collectionAddFileEvent, collectionAddFileEvent,

View File

@ -193,9 +193,9 @@ const darkTheme = {
codemirror: { codemirror: {
bg: '#1e1e1e', bg: '#1e1e1e',
border: 'transparent', border: '#373737',
gutter: { gutter: {
bg: '#1e1e1e' bg: '#262626'
}, },
variable: { variable: {
valid: 'rgb(11 178 126)', valid: 'rgb(11 178 126)',

View File

@ -181,7 +181,6 @@ const add = async (win, pathname, collectionUid, collectionPath) => {
try { try {
const bru = fs.readFileSync(pathname, 'utf8'); const bru = fs.readFileSync(pathname, 'utf8');
file.data = bruToJson(bru); file.data = bruToJson(bru);
console.log(JSON.stringify(file.data, null, 2));
hydrateRequestWithUuid(file.data, pathname); hydrateRequestWithUuid(file.data, pathname);
win.webContents.send('main:collection-tree-updated', 'addFile', file); win.webContents.send('main:collection-tree-updated', 'addFile', file);
} catch (err) { } catch (err) {

View File

@ -39,7 +39,7 @@ const bruToJson = (bru) => {
"params": _.get(json, "query", []), "params": _.get(json, "query", []),
"headers": _.get(json, "headers", []), "headers": _.get(json, "headers", []),
"body": _.get(json, "body", {}), "body": _.get(json, "body", {}),
"script": _.get(json, "script", ""), "script": _.get(json, "script", {}),
"tests": _.get(json, "tests", "") "tests": _.get(json, "tests", "")
} }
}; };
@ -84,8 +84,8 @@ const jsonToBru = (json) => {
query: _.get(json, 'request.params', []), query: _.get(json, 'request.params', []),
headers: _.get(json, 'request.headers', []), headers: _.get(json, 'request.headers', []),
body: _.get(json, 'request.body', {}), body: _.get(json, 'request.body', {}),
script: _.get(json, 'script', ''), script: _.get(json, 'request.script', {}),
tests: _.get(json, 'tests', ''), tests: _.get(json, 'request.tests', ''),
}; };
return jsonToBruV2(bruJson); return jsonToBruV2(bruJson);

View File

@ -99,10 +99,10 @@ const registerNetworkIpc = (mainWindow, watcher, lastOpenedCollections) => {
const envVars = getEnvVars(environment); const envVars = getEnvVars(environment);
if(request.script && request.script.length) { const requestScript = get(request, 'script.req');
let script = request.script + '\n if (typeof onRequest === "function") {onRequest(__brunoRequest);}'; if(requestScript && requestScript.length) {
const scriptRuntime = new ScriptRuntime(); const scriptRuntime = new ScriptRuntime();
const result = scriptRuntime.runRequestScript(script, request, envVars, collectionVariables, collectionPath); const result = scriptRuntime.runRequestScript(requestScript, request, envVars, collectionVariables, collectionPath);
mainWindow.webContents.send('main:script-environment-update', { mainWindow.webContents.send('main:script-environment-update', {
environment: result.environment, environment: result.environment,
@ -130,10 +130,10 @@ const registerNetworkIpc = (mainWindow, watcher, lastOpenedCollections) => {
const response = await axios(request); const response = await axios(request);
if(request.script && request.script.length) { const responseScript = get(request, 'script.res');
let script = request.script + '\n if (typeof onResponse === "function") {onResponse(__brunoResponse);}'; if(responseScript && responseScript.length) {
const scriptRuntime = new ScriptRuntime(); const scriptRuntime = new ScriptRuntime();
const result = scriptRuntime.runResponseScript(script, response, envVars, collectionVariables, collectionPath); const result = scriptRuntime.runResponseScript(responseScript, response, envVars, collectionVariables, collectionPath);
mainWindow.webContents.send('main:script-environment-update', { mainWindow.webContents.send('main:script-environment-update', {
environment: result.environment, environment: result.environment,
@ -280,10 +280,10 @@ const registerNetworkIpc = (mainWindow, watcher, lastOpenedCollections) => {
request.data = form; request.data = form;
} }
if(request.script && request.script.length) { const requestScript = get(request, 'script.req');
let script = request.script + '\n if (typeof onRequest === "function") {onRequest(__brunoRequest);}'; if(requestScript && requestScript.length) {
const scriptRuntime = new ScriptRuntime(); const scriptRuntime = new ScriptRuntime();
const result = scriptRuntime.runRequestScript(script, request, envVars, collectionVariables, collectionPath); const result = scriptRuntime.runRequestScript(requestScript, request, envVars, collectionVariables, collectionPath);
mainWindow.webContents.send('main:script-environment-update', { mainWindow.webContents.send('main:script-environment-update', {
environment: result.environment, environment: result.environment,
@ -312,10 +312,10 @@ const registerNetworkIpc = (mainWindow, watcher, lastOpenedCollections) => {
const response = await axios(request); const response = await axios(request);
timeEnd = Date.now(); timeEnd = Date.now();
if(request.script && request.script.length) { const responseScript = get(request, 'script.res');
let script = request.script + '\n if (typeof onResponse === "function") {onResponse(__brunoResponse);}'; if(responseScript && responseScript.length) {
const scriptRuntime = new ScriptRuntime(); const scriptRuntime = new ScriptRuntime();
const result = scriptRuntime.runResponseScript(script, response, envVars, collectionVariables, collectionPath); const result = scriptRuntime.runResponseScript(responseScript, response, envVars, collectionVariables, collectionPath);
mainWindow.webContents.send('main:script-environment-update', { mainWindow.webContents.send('main:script-environment-update', {
environment: result.environment, environment: result.environment,

View File

@ -59,7 +59,7 @@ const prepareRequest = (request) => {
axiosRequest.data = graphqlQuery; axiosRequest.data = graphqlQuery;
} }
if (request.script && request.script.length) { if (request.script) {
axiosRequest.script = request.script; axiosRequest.script = request.script;
} }

View File

@ -1,40 +1,10 @@
// Inbuilt Library Support
const atob = require('atob');
const btoa = require('btoa');
const _ = require('lodash');
const moment = require('moment');
const uuid = require('uuid');
const nanoid = require('nanoid');
const CryptoJS = require('crypto-js');
class Bru { class Bru {
constructor(environment, collectionVariables) { constructor(environment, collectionVariables) {
this._environment = environment; this._environment = environment;
this._collectionVariables = collectionVariables; this._collectionVariables = collectionVariables;
} }
require(module) {
switch(module) {
case 'atob':
return atob;
case 'btoa':
return btoa;
case 'lodash':
return _;
case 'moment':
return moment;
case 'uuid':
return uuid;
case 'nanoid':
return nanoid;
case 'crypto-js':
return CryptoJS;
default:
throw new Error(`Module ${module} is not supported`);
}
}
getEnvVar(key) { getEnvVar(key) {
return this._environment[key]; return this._environment[key];
} }

View File

@ -35,11 +35,11 @@ class BrunoRequest {
this._request.headers[name] = value; this._request.headers[name] = value;
} }
getData() { getBody() {
return this._request.data; return this._request.data;
} }
setData(data) { setBody(data) {
this._request.data = data; this._request.data = data;
} }
} }

View File

@ -1,6 +1,10 @@
class BrunoResponse { class BrunoResponse {
constructor(response) { constructor(response) {
this._response = response; this._response = response;
this.status = response.status;
this.statusText = response.statusText;
this.headers = response.headers;
this.body = response.data;
} }
getStatus() { getStatus() {
@ -15,7 +19,7 @@ class BrunoResponse {
return this._response.headers; return this._response.headers;
} }
getData() { getBody() {
return this._response.data; return this._response.data;
} }
} }

View File

@ -4,24 +4,42 @@ const Bru = require('./bru');
const BrunoRequest = require('./bruno-request'); const BrunoRequest = require('./bruno-request');
const BrunoResponse = require('./bruno-response'); const BrunoResponse = require('./bruno-response');
// Inbuilt Library Support
const atob = require('atob');
const btoa = require('btoa');
const lodash = require('lodash');
const moment = require('moment');
const uuid = require('uuid');
const nanoid = require('nanoid');
const CryptoJS = require('crypto-js');
class ScriptRuntime { class ScriptRuntime {
constructor() { constructor() {
} }
runRequestScript(script, request, environment, collectionVariables, collectionPath) { runRequestScript(script, request, environment, collectionVariables, collectionPath) {
const bru = new Bru(environment, collectionVariables); const $bru = new Bru(environment, collectionVariables);
const __brunoRequest = new BrunoRequest(request); const $req = new BrunoRequest(request);
const context = { const context = {
bru, $bru,
__brunoRequest $req
}; };
const vm = new NodeVM({ const vm = new NodeVM({
sandbox: context, sandbox: context,
require: { require: {
context: 'sandbox', context: 'sandbox',
external: true, external: true,
root: [collectionPath] root: [collectionPath],
mock: {
atob,
btoa,
lodash,
moment,
uuid,
nanoid,
'crypto-js': CryptoJS
}
} }
}); });
@ -35,19 +53,28 @@ class ScriptRuntime {
} }
runResponseScript(script, response, environment, collectionVariables, collectionPath) { runResponseScript(script, response, environment, collectionVariables, collectionPath) {
const bru = new Bru(environment, collectionVariables); const $bru = new Bru(environment, collectionVariables);
const __brunoResponse = new BrunoResponse(response); const $res = new BrunoResponse(response);
const context = { const context = {
bru, $bru,
__brunoResponse $res
}; };
const vm = new NodeVM({ const vm = new NodeVM({
sandbox: context, sandbox: context,
require: { require: {
context: 'sandbox', context: 'sandbox',
external: true, external: true,
root: [collectionPath] root: [collectionPath],
mock: {
atob,
btoa,
lodash,
moment,
uuid,
nanoid,
'crypto-js': CryptoJS
}
} }
}); });

View File

@ -7,23 +7,32 @@ const BrunoResponse = require('./bruno-response');
const Test = require('./test'); const Test = require('./test');
const TestResults = require('./test-results'); const TestResults = require('./test-results');
// Inbuilt Library Support
const atob = require('atob');
const btoa = require('btoa');
const lodash = require('lodash');
const moment = require('moment');
const uuid = require('uuid');
const nanoid = require('nanoid');
const CryptoJS = require('crypto-js');
class TestRuntime { class TestRuntime {
constructor() { constructor() {
} }
runTests(testsFile, request, response, environment, collectionVariables, collectionPath) { runTests(testsFile, request, response, environment, collectionVariables, collectionPath) {
const bru = new Bru(environment, collectionVariables); const $bru = new Bru(environment, collectionVariables);
const req = new BrunoRequest(request); const $req = new BrunoRequest(request);
const res = new BrunoResponse(response); const $res = new BrunoResponse(response);
const __brunoTestResults = new TestResults(); const __brunoTestResults = new TestResults();
const test = Test(__brunoTestResults, chai); const test = Test(__brunoTestResults, chai);
const context = { const context = {
bru,
req,
res,
test, test,
$bru,
$req,
$res,
expect: chai.expect, expect: chai.expect,
assert: chai.assert, assert: chai.assert,
__brunoTestResults: __brunoTestResults __brunoTestResults: __brunoTestResults
@ -34,7 +43,16 @@ class TestRuntime {
require: { require: {
context: 'sandbox', context: 'sandbox',
external: true, external: true,
root: [collectionPath] root: [collectionPath],
mock: {
atob,
btoa,
lodash,
moment,
uuid,
nanoid,
'crypto-js': CryptoJS
}
} }
}); });

View File

@ -53,7 +53,10 @@ const requestSchema = Yup.object({
headers: Yup.array().of(keyValueSchema).required('headers are required'), headers: Yup.array().of(keyValueSchema).required('headers are required'),
params: Yup.array().of(keyValueSchema).required('params are required'), params: Yup.array().of(keyValueSchema).required('params are required'),
body: requestBodySchema, body: requestBodySchema,
script: Yup.string().nullable(), script: Yup.object({
req: Yup.string().nullable(),
res: Yup.string().nullable()
}).noUnknown(true).strict(),
tests: Yup.string().nullable() tests: Yup.string().nullable()
}).noUnknown(true).strict(); }).noUnknown(true).strict();