+
@@ -156,7 +155,7 @@ const ProxySettings = ({ proxyConfig, onUpdate }) => {
{formik.errors.auth.username}
) : null}
-
+
@@ -178,7 +177,7 @@ const ProxySettings = ({ proxyConfig, onUpdate }) => {
-
diff --git a/packages/bruno-app/src/components/CollectionSettings/Script/StyledWrapper.js b/packages/bruno-app/src/components/CollectionSettings/Script/StyledWrapper.js
new file mode 100644
index 000000000..66ba1ed3d
--- /dev/null
+++ b/packages/bruno-app/src/components/CollectionSettings/Script/StyledWrapper.js
@@ -0,0 +1,13 @@
+import styled from 'styled-components';
+
+const StyledWrapper = styled.div`
+ div.CodeMirror {
+ height: inherit;
+ }
+
+ div.title {
+ color: var(--color-tab-inactive);
+ }
+`;
+
+export default StyledWrapper;
diff --git a/packages/bruno-app/src/components/CollectionSettings/Script/index.js b/packages/bruno-app/src/components/CollectionSettings/Script/index.js
new file mode 100644
index 000000000..7cfff272e
--- /dev/null
+++ b/packages/bruno-app/src/components/CollectionSettings/Script/index.js
@@ -0,0 +1,73 @@
+import React from 'react';
+import get from 'lodash/get';
+import { useDispatch } from 'react-redux';
+import CodeEditor from 'components/CodeEditor';
+import { updateCollectionRequestScript, updateCollectionResponseScript } from 'providers/ReduxStore/slices/collections';
+import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
+import { useTheme } from 'providers/Theme';
+import StyledWrapper from './StyledWrapper';
+
+const Script = ({ collection }) => {
+ const dispatch = useDispatch();
+ const requestScript = get(collection, 'root.request.script.req', '');
+ const responseScript = get(collection, 'root.request.script.res', '');
+
+ const { storedTheme } = useTheme();
+
+ const onRequestScriptEdit = (value) => {
+ dispatch(
+ updateCollectionRequestScript({
+ script: value,
+ collectionUid: collection.uid
+ })
+ );
+ };
+
+ const onResponseScriptEdit = (value) => {
+ dispatch(
+ updateCollectionResponseScript({
+ script: value,
+ collectionUid: collection.uid
+ })
+ );
+ };
+
+ const handleSave = () => {
+ dispatch(saveCollectionRoot(collection.uid));
+ };
+
+ return (
+
+
+
+
+
+
+ Save
+
+
+
+ );
+};
+
+export default Script;
diff --git a/packages/bruno-app/src/components/CollectionSettings/StyledWrapper.js b/packages/bruno-app/src/components/CollectionSettings/StyledWrapper.js
index e4d976b0d..b88a31e0d 100644
--- a/packages/bruno-app/src/components/CollectionSettings/StyledWrapper.js
+++ b/packages/bruno-app/src/components/CollectionSettings/StyledWrapper.js
@@ -1,6 +1,32 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
+ max-width: 800px;
+
+ div.tabs {
+ div.tab {
+ padding: 6px 0px;
+ border: none;
+ border-bottom: solid 2px transparent;
+ margin-right: 1.25rem;
+ color: var(--color-tab-inactive);
+ cursor: pointer;
+
+ &:focus,
+ &:active,
+ &:focus-within,
+ &:focus-visible,
+ &:target {
+ outline: none !important;
+ box-shadow: none !important;
+ }
+
+ &.active {
+ color: ${(props) => props.theme.tabs.active.color} !important;
+ border-bottom: solid 2px ${(props) => props.theme.tabs.active.border} !important;
+ }
+ }
+ }
table {
thead,
td {
diff --git a/packages/bruno-app/src/components/CollectionSettings/Tests/StyledWrapper.js b/packages/bruno-app/src/components/CollectionSettings/Tests/StyledWrapper.js
new file mode 100644
index 000000000..ec278887d
--- /dev/null
+++ b/packages/bruno-app/src/components/CollectionSettings/Tests/StyledWrapper.js
@@ -0,0 +1,5 @@
+import styled from 'styled-components';
+
+const StyledWrapper = styled.div``;
+
+export default StyledWrapper;
diff --git a/packages/bruno-app/src/components/CollectionSettings/Tests/index.js b/packages/bruno-app/src/components/CollectionSettings/Tests/index.js
new file mode 100644
index 000000000..469d2b409
--- /dev/null
+++ b/packages/bruno-app/src/components/CollectionSettings/Tests/index.js
@@ -0,0 +1,47 @@
+import React from 'react';
+import get from 'lodash/get';
+import { useDispatch } from 'react-redux';
+import CodeEditor from 'components/CodeEditor';
+import { updateCollectionTests } from 'providers/ReduxStore/slices/collections';
+import { saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
+import { useTheme } from 'providers/Theme';
+import StyledWrapper from './StyledWrapper';
+
+const Tests = ({ collection }) => {
+ const dispatch = useDispatch();
+ const tests = get(collection, 'root.request.tests', '');
+
+ const { storedTheme } = useTheme();
+
+ const onEdit = (value) => {
+ dispatch(
+ updateCollectionTests({
+ tests: value,
+ collectionUid: collection.uid
+ })
+ );
+ };
+
+ const handleSave = () => dispatch(saveCollectionRoot(collection.uid));
+
+ return (
+
+
+
+
+
+ Save
+
+
+
+ );
+};
+
+export default Tests;
diff --git a/packages/bruno-app/src/components/CollectionSettings/index.js b/packages/bruno-app/src/components/CollectionSettings/index.js
index 98d8aed48..eaf44e758 100644
--- a/packages/bruno-app/src/components/CollectionSettings/index.js
+++ b/packages/bruno-app/src/components/CollectionSettings/index.js
@@ -1,14 +1,29 @@
import React from 'react';
+import classnames from 'classnames';
import get from 'lodash/get';
import cloneDeep from 'lodash/cloneDeep';
import toast from 'react-hot-toast';
import { updateBrunoConfig } from 'providers/ReduxStore/slices/collections/actions';
+import { updateSettingsSelectedTab } from 'providers/ReduxStore/slices/collections';
import { useDispatch } from 'react-redux';
import ProxySettings from './ProxySettings';
+import Headers from './Headers';
+import Auth from './Auth';
+import Script from './Script';
+import Test from './Tests';
import StyledWrapper from './StyledWrapper';
const CollectionSettings = ({ collection }) => {
const dispatch = useDispatch();
+ const tab = collection.settingsSelectedTab;
+ const setTab = (tab) => {
+ dispatch(
+ updateSettingsSelectedTab({
+ collectionUid: collection.uid,
+ tab
+ })
+ );
+ };
const proxyConfig = get(collection, 'brunoConfig.proxy', {});
@@ -22,11 +37,52 @@ const CollectionSettings = ({ collection }) => {
.catch((err) => console.log(err) && toast.error('Failed to update collection settings'));
};
- return (
-
- Collection Settings
+ const getTabPanel = (tab) => {
+ switch (tab) {
+ case 'headers': {
+ return ;
+ }
+ case 'auth': {
+ return ;
+ }
+ case 'script': {
+ return ;
+ }
+ case 'tests': {
+ return ;
+ }
+ case 'proxy': {
+ return ;
+ }
+ }
+ };
-
+ const getTabClassname = (tabName) => {
+ return classnames(`tab select-none ${tabName}`, {
+ active: tabName === tab
+ });
+ };
+
+ return (
+
+
+
setTab('headers')}>
+ Headers
+
+
setTab('auth')}>
+ Auth
+
+
setTab('script')}>
+ Script
+
+
setTab('tests')}>
+ Tests
+
+
setTab('proxy')}>
+ Proxy
+
+
+
);
};
diff --git a/packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js b/packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js
index 64a58addf..aebc3db75 100644
--- a/packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js
+++ b/packages/bruno-app/src/components/RequestTabs/RequestTab/SpecialTab.js
@@ -8,7 +8,7 @@ const SpecialTab = ({ handleCloseClick, type }) => {
return (
<>
- Settings
+ Collection
>
);
}
diff --git a/packages/bruno-app/src/providers/Hotkeys/index.js b/packages/bruno-app/src/providers/Hotkeys/index.js
index 522fa0d46..468061305 100644
--- a/packages/bruno-app/src/providers/Hotkeys/index.js
+++ b/packages/bruno-app/src/providers/Hotkeys/index.js
@@ -51,7 +51,8 @@ export const HotkeysProvider = (props) => {
if (item && item.uid) {
dispatch(saveRequest(activeTab.uid, activeTab.collectionUid));
} else {
- setShowSaveRequestModal(true);
+ // todo: when ephermal requests go live
+ // setShowSaveRequestModal(true);
}
}
}
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 42b8318ac..111f26d3e 100644
--- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
+++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/actions.js
@@ -91,6 +91,29 @@ export const saveRequest = (itemUid, collectionUid) => (dispatch, getState) => {
});
};
+export const saveCollectionRoot = (collectionUid) => (dispatch, getState) => {
+ const state = getState();
+ const collection = findCollectionByUid(state.collections.collections, collectionUid);
+ console.log(collection.root);
+
+ return new Promise((resolve, reject) => {
+ if (!collection) {
+ return reject(new Error('Collection not found'));
+ }
+
+ const { ipcRenderer } = window;
+
+ ipcRenderer
+ .invoke('renderer:save-collection-root', collection.pathname, collection.root)
+ .then(() => toast.success('Collection Settings saved successfully'))
+ .then(resolve)
+ .catch((err) => {
+ toast.error('Failed to save collection settings!');
+ reject(err);
+ });
+ });
+};
+
export const sendRequest = (item, collectionUid) => (dispatch, getState) => {
const state = getState();
const collection = findCollectionByUid(state.collections.collections, collectionUid);
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 f4eebec66..e31b53355 100644
--- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
+++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
@@ -7,6 +7,8 @@ import concat from 'lodash/concat';
import filter from 'lodash/filter';
import each from 'lodash/each';
import cloneDeep from 'lodash/cloneDeep';
+import get from 'lodash/get';
+import set from 'lodash/set';
import { createSlice } from '@reduxjs/toolkit';
import { splitOnFirst } from 'utils/url';
import {
@@ -40,6 +42,8 @@ export const collectionsSlice = createSlice({
const collectionUids = map(state.collections, (c) => c.uid);
const collection = action.payload;
+ collection.settingsSelectedTab = 'headers';
+
// TODO: move this to use the nextAction approach
// last action is used to track the last action performed on the collection
// this is optional
@@ -107,6 +111,15 @@ export const collectionsSlice = createSlice({
collection.nextAction = nextAction;
}
},
+ updateSettingsSelectedTab: (state, action) => {
+ const { collectionUid, tab } = action.payload;
+
+ const collection = findCollectionByUid(state.collections, collectionUid);
+
+ if (collection) {
+ collection.settingsSelectedTab = tab;
+ }
+ },
collectionUnlinkEnvFileEvent: (state, action) => {
const { data: environment, meta } = action.payload;
const collection = findCollectionByUid(state.collections, meta.collectionUid);
@@ -930,10 +943,100 @@ export const collectionsSlice = createSlice({
}
}
},
+ updateCollectionAuthMode: (state, action) => {
+ const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
+
+ if (collection) {
+ set(collection, 'root.request.auth.mode', action.payload.mode);
+ }
+ },
+ updateCollectionAuth: (state, action) => {
+ const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
+
+ if (collection) {
+ switch (action.payload.mode) {
+ case 'bearer':
+ set(collection, 'root.request.auth.bearer', action.payload.content);
+ break;
+ case 'basic':
+ set(collection, 'root.request.auth.basic', action.payload.content);
+ break;
+ }
+ }
+ },
+ updateCollectionRequestScript: (state, action) => {
+ const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
+
+ if (collection) {
+ set(collection, 'root.request.script.req', action.payload.script);
+ }
+ },
+ updateCollectionResponseScript: (state, action) => {
+ const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
+
+ if (collection) {
+ set(collection, 'root.request.script.res', action.payload.script);
+ }
+ },
+
+ updateCollectionTests: (state, action) => {
+ const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
+
+ if (collection) {
+ set(collection, 'root.request.tests', action.payload.tests);
+ }
+ },
+ addCollectionHeader: (state, action) => {
+ const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
+
+ if (collection) {
+ const headers = get(collection, 'root.request.headers', []);
+ headers.push({
+ uid: uuid(),
+ name: '',
+ value: '',
+ description: '',
+ enabled: true
+ });
+ set(collection, 'root.request.headers', headers);
+ }
+ },
+ updateCollectionHeader: (state, action) => {
+ const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
+
+ if (collection) {
+ const headers = get(collection, 'root.request.headers', []);
+ const header = find(headers, (h) => h.uid === action.payload.header.uid);
+ if (header) {
+ header.name = action.payload.header.name;
+ header.value = action.payload.header.value;
+ header.description = action.payload.header.description;
+ header.enabled = action.payload.header.enabled;
+ }
+ }
+ },
+ deleteCollectionHeader: (state, action) => {
+ const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
+
+ if (collection) {
+ let headers = get(collection, 'root.request.headers', []);
+ headers = filter(headers, (h) => h.uid !== action.payload.headerUid);
+ set(collection, 'root.request.headers', headers);
+ }
+ },
collectionAddFileEvent: (state, action) => {
const file = action.payload.file;
+ const isCollectionRoot = file.meta.collectionRoot ? true : false;
const collection = findCollectionByUid(state.collections, file.meta.collectionUid);
+ if (isCollectionRoot) {
+ if (collection) {
+ collection.root = file.data;
+ }
+ console.log('collectionAddFileEvent', file);
+ return;
+ }
+
if (collection) {
const dirname = getDirectoryName(file.meta.pathname);
const subDirectories = getSubdirectoriesFromRoot(collection.pathname, dirname);
@@ -1018,6 +1121,12 @@ export const collectionsSlice = createSlice({
const { file } = action.payload;
const collection = findCollectionByUid(state.collections, file.meta.collectionUid);
+ // check and update collection root
+ if (collection && file.meta.collectionRoot) {
+ collection.root = file.data;
+ return;
+ }
+
if (collection) {
const item = findItemInCollection(collection, file.data.uid);
@@ -1222,6 +1331,7 @@ export const {
sortCollections,
updateLastAction,
updateNextAction,
+ updateSettingsSelectedTab,
collectionUnlinkEnvFileEvent,
saveEnvironment,
selectEnvironment,
@@ -1267,6 +1377,14 @@ export const {
addVar,
updateVar,
deleteVar,
+ addCollectionHeader,
+ updateCollectionHeader,
+ deleteCollectionHeader,
+ updateCollectionAuthMode,
+ updateCollectionAuth,
+ updateCollectionRequestScript,
+ updateCollectionResponseScript,
+ updateCollectionTests,
collectionAddFileEvent,
collectionAddDirectoryEvent,
collectionChangeFileEvent,
diff --git a/packages/bruno-app/src/styles/globals.css b/packages/bruno-app/src/styles/globals.css
index ee784ff1f..c820ff134 100644
--- a/packages/bruno-app/src/styles/globals.css
+++ b/packages/bruno-app/src/styles/globals.css
@@ -54,8 +54,11 @@ body::-webkit-scrollbar-thumb,
border-radius: 5rem;
}
-/* making all the checkboxes and radios bigger */
-input[type='checkbox'],
-input[type='radio'] {
- transform: scale(1.25);
-}
+/*
+ * todo: this will be supported in the future to be changed via applying a theme
+ * making all the checkboxes and radios bigger
+ * input[type='checkbox'],
+ * input[type='radio'] {
+ * transform: scale(1.1);
+ * }
+ */
diff --git a/packages/bruno-app/src/utils/network/index.js b/packages/bruno-app/src/utils/network/index.js
index 042546854..a498a722c 100644
--- a/packages/bruno-app/src/utils/network/index.js
+++ b/packages/bruno-app/src/utils/network/index.js
@@ -23,7 +23,7 @@ const sendHttpRequest = async (item, collection, environment, collectionVariable
const { ipcRenderer } = window;
ipcRenderer
- .invoke('send-http-request', item, collection.uid, collection.pathname, environment, collectionVariables)
+ .invoke('send-http-request', item, collection, environment, collectionVariables)
.then(resolve)
.catch(reject);
});
diff --git a/packages/bruno-electron/src/app/watcher.js b/packages/bruno-electron/src/app/watcher.js
index 6b5d57381..51fa79f6f 100644
--- a/packages/bruno-electron/src/app/watcher.js
+++ b/packages/bruno-electron/src/app/watcher.js
@@ -3,7 +3,7 @@ const fs = require('fs');
const path = require('path');
const chokidar = require('chokidar');
const { hasBruExtension } = require('../utils/filesystem');
-const { bruToEnvJson, bruToJson } = require('../bru');
+const { bruToEnvJson, bruToJson, collectionBruToJson } = require('../bru');
const { dotenvToJson } = require('@usebruno/lang');
const { uuid } = require('../utils/common');
@@ -37,6 +37,13 @@ const isBruEnvironmentConfig = (pathname, collectionPath) => {
return dirname === envDirectory && hasBruExtension(basename);
};
+const isCollectionRootBruFile = (pathname, collectionPath) => {
+ const dirname = path.dirname(pathname);
+ const basename = path.basename(pathname);
+
+ return dirname === collectionPath && basename === 'collection.bru';
+};
+
const hydrateRequestWithUuid = (request, pathname) => {
request.uid = getRequestUid(pathname);
@@ -59,6 +66,20 @@ const hydrateRequestWithUuid = (request, pathname) => {
return request;
};
+const hydrateBruCollectionFileWithUuid = (collectionRoot) => {
+ const params = _.get(collectionRoot, 'request.params', []);
+ const headers = _.get(collectionRoot, 'request.headers', []);
+ const requestVars = _.get(collectionRoot, 'request.vars.req', []);
+ const responseVars = _.get(collectionRoot, 'request.vars.res', []);
+
+ params.forEach((param) => (param.uid = uuid()));
+ headers.forEach((header) => (header.uid = uuid()));
+ requestVars.forEach((variable) => (variable.uid = uuid()));
+ responseVars.forEach((variable) => (variable.uid = uuid()));
+
+ return collectionRoot;
+};
+
const envHasSecrets = (environment = {}) => {
const secrets = _.filter(environment.variables, (v) => v.secret);
@@ -195,6 +216,30 @@ const add = async (win, pathname, collectionUid, collectionPath) => {
return addEnvironmentFile(win, pathname, collectionUid, collectionPath);
}
+ if (isCollectionRootBruFile(pathname, collectionPath)) {
+ const file = {
+ meta: {
+ collectionUid,
+ pathname,
+ name: path.basename(pathname),
+ collectionRoot: true
+ }
+ };
+
+ try {
+ let bruContent = fs.readFileSync(pathname, 'utf8');
+
+ file.data = collectionBruToJson(bruContent);
+
+ hydrateBruCollectionFileWithUuid(file.data);
+ win.webContents.send('main:collection-tree-updated', 'addFile', file);
+ return;
+ } catch (err) {
+ console.error(err);
+ return;
+ }
+ }
+
if (hasBruExtension(pathname)) {
const file = {
meta: {
@@ -208,6 +253,7 @@ const add = async (win, pathname, collectionUid, collectionPath) => {
let bruContent = fs.readFileSync(pathname, 'utf8');
file.data = bruToJson(bruContent);
+
hydrateRequestWithUuid(file.data, pathname);
win.webContents.send('main:collection-tree-updated', 'addFile', file);
} catch (err) {
@@ -274,6 +320,30 @@ const change = async (win, pathname, collectionUid, collectionPath) => {
return changeEnvironmentFile(win, pathname, collectionUid, collectionPath);
}
+ if (isCollectionRootBruFile(pathname, collectionPath)) {
+ const file = {
+ meta: {
+ collectionUid,
+ pathname,
+ name: path.basename(pathname),
+ collectionRoot: true
+ }
+ };
+
+ try {
+ let bruContent = fs.readFileSync(pathname, 'utf8');
+
+ file.data = collectionBruToJson(bruContent);
+
+ hydrateBruCollectionFileWithUuid(file.data);
+ win.webContents.send('main:collection-tree-updated', 'change', file);
+ return;
+ } catch (err) {
+ console.error(err);
+ return;
+ }
+ }
+
if (hasBruExtension(pathname)) {
try {
const file = {
diff --git a/packages/bruno-electron/src/bru/index.js b/packages/bruno-electron/src/bru/index.js
index ad4299593..18f739f5c 100644
--- a/packages/bruno-electron/src/bru/index.js
+++ b/packages/bruno-electron/src/bru/index.js
@@ -1,5 +1,56 @@
const _ = require('lodash');
-const { bruToJsonV2, jsonToBruV2, bruToEnvJsonV2, envJsonToBruV2 } = require('@usebruno/lang');
+const {
+ bruToJsonV2,
+ jsonToBruV2,
+ bruToEnvJsonV2,
+ envJsonToBruV2,
+ collectionBruToJson: _collectionBruToJson,
+ jsonToCollectionBru: _jsonToCollectionBru
+} = require('@usebruno/lang');
+
+const collectionBruToJson = (bru) => {
+ try {
+ const json = _collectionBruToJson(bru);
+
+ const transformedJson = {
+ request: {
+ params: _.get(json, 'query', []),
+ headers: _.get(json, 'headers', []),
+ auth: _.get(json, 'auth', {}),
+ script: _.get(json, 'script', {}),
+ vars: _.get(json, 'vars', {}),
+ tests: _.get(json, 'tests', '')
+ }
+ };
+
+ return transformedJson;
+ } catch (error) {
+ return Promise.reject(error);
+ }
+};
+
+const jsonToCollectionBru = (json) => {
+ try {
+ const collectionBruJson = {
+ query: _.get(json, 'request.params', []),
+ headers: _.get(json, 'request.headers', []),
+ auth: _.get(json, 'request.auth', {}),
+ script: {
+ req: _.get(json, 'request.script.req', ''),
+ res: _.get(json, 'request.script.res', '')
+ },
+ vars: {
+ req: _.get(json, 'request.vars.req', []),
+ res: _.get(json, 'request.vars.req', [])
+ },
+ tests: _.get(json, 'request.tests', '')
+ };
+
+ return _jsonToCollectionBru(collectionBruJson);
+ } catch (error) {
+ return Promise.reject(error);
+ }
+};
const bruToEnvJson = (bru) => {
try {
@@ -128,5 +179,7 @@ module.exports = {
bruToJson,
jsonToBru,
bruToEnvJson,
- envJsonToBru
+ envJsonToBru,
+ collectionBruToJson,
+ jsonToCollectionBru
};
diff --git a/packages/bruno-electron/src/ipc/collection.js b/packages/bruno-electron/src/ipc/collection.js
index 03a15305b..944a04f01 100644
--- a/packages/bruno-electron/src/ipc/collection.js
+++ b/packages/bruno-electron/src/ipc/collection.js
@@ -2,7 +2,7 @@ const _ = require('lodash');
const fs = require('fs');
const path = require('path');
const { ipcMain, shell } = require('electron');
-const { envJsonToBru, bruToJson, jsonToBru } = require('../bru');
+const { envJsonToBru, bruToJson, jsonToBru, jsonToCollectionBru } = require('../bru');
const {
isValidPathname,
@@ -101,6 +101,17 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
}
});
+ ipcMain.handle('renderer:save-collection-root', async (event, collectionPathname, collectionRoot) => {
+ try {
+ const collectionBruFilePath = path.join(collectionPathname, 'collection.bru');
+
+ const content = jsonToCollectionBru(collectionRoot);
+ await writeFile(collectionBruFilePath, content);
+ } catch (error) {
+ return Promise.reject(error);
+ }
+ });
+
// new request
ipcMain.handle('renderer:new-request', async (event, pathname, request) => {
try {
diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js
index a0b66099c..1bd3b2e22 100644
--- a/packages/bruno-electron/src/ipc/network/index.js
+++ b/packages/bruno-electron/src/ipc/network/index.js
@@ -1,3 +1,4 @@
+const os = require('os');
const qs = require('qs');
const https = require('https');
const axios = require('axios');
@@ -5,7 +6,7 @@ const decomment = require('decomment');
const Mustache = require('mustache');
const FormData = require('form-data');
const { ipcMain } = require('electron');
-const { forOwn, extend, each, get } = require('lodash');
+const { forOwn, extend, each, get, compact } = require('lodash');
const { VarsRuntime, AssertRuntime, ScriptRuntime, TestRuntime } = require('@usebruno/js');
const prepareRequest = require('./prepare-request');
const prepareGqlIntrospectionRequest = require('./prepare-gql-introspection-request');
@@ -81,90 +82,202 @@ const getSize = (data) => {
const registerNetworkIpc = (mainWindow) => {
// handler for sending http request
- ipcMain.handle(
- 'send-http-request',
- async (event, item, collectionUid, collectionPath, environment, collectionVariables) => {
- const cancelTokenUid = uuid();
- const requestUid = uuid();
+ ipcMain.handle('send-http-request', async (event, item, collection, environment, collectionVariables) => {
+ const collectionUid = collection.uid;
+ const collectionPath = collection.pathname;
+ const cancelTokenUid = uuid();
+ const requestUid = uuid();
- const onConsoleLog = (type, args) => {
- console[type](...args);
+ const onConsoleLog = (type, args) => {
+ console[type](...args);
- mainWindow.webContents.send('main:console-log', {
- type,
- args
+ mainWindow.webContents.send('main:console-log', {
+ type,
+ args
+ });
+ };
+
+ mainWindow.webContents.send('main:run-request-event', {
+ type: 'request-queued',
+ requestUid,
+ collectionUid,
+ itemUid: item.uid,
+ cancelTokenUid
+ });
+
+ const collectionRoot = get(collection, 'root', {});
+ const _request = item.draft ? item.draft.request : item.request;
+ const request = prepareRequest(_request, collectionRoot);
+ const envVars = getEnvVars(environment);
+ const processEnvVars = getProcessEnvVars(collectionUid);
+ const brunoConfig = getBrunoConfig(collectionUid);
+ const scriptingConfig = get(brunoConfig, 'scripts', {});
+
+ try {
+ // make axios work in node using form data
+ // reference: https://github.com/axios/axios/issues/1006#issuecomment-320165427
+ if (request.headers && request.headers['content-type'] === 'multipart/form-data') {
+ const form = new FormData();
+ forOwn(request.data, (value, key) => {
+ form.append(key, value);
});
- };
+ extend(request.headers, form.getHeaders());
+ request.data = form;
+ }
+ const cancelToken = axios.CancelToken.source();
+ request.cancelToken = cancelToken.token;
+ saveCancelToken(cancelTokenUid, cancelToken);
+
+ // run pre-request vars
+ const preRequestVars = get(request, 'vars.req', []);
+ if (preRequestVars && preRequestVars.length) {
+ const varsRuntime = new VarsRuntime();
+ const result = varsRuntime.runPreRequestVars(
+ preRequestVars,
+ request,
+ envVars,
+ collectionVariables,
+ collectionPath,
+ processEnvVars
+ );
+
+ if (result) {
+ mainWindow.webContents.send('main:script-environment-update', {
+ envVariables: result.envVariables,
+ collectionVariables: result.collectionVariables,
+ requestUid,
+ collectionUid
+ });
+ }
+ }
+
+ // run pre-request script
+ const requestScript = compact([get(collectionRoot, 'request.script.req'), get(request, 'script.req')]).join(
+ os.EOL
+ );
+ if (requestScript && requestScript.length) {
+ const scriptRuntime = new ScriptRuntime();
+ const result = await scriptRuntime.runRequestScript(
+ decomment(requestScript),
+ request,
+ envVars,
+ collectionVariables,
+ collectionPath,
+ onConsoleLog,
+ processEnvVars,
+ scriptingConfig
+ );
+
+ mainWindow.webContents.send('main:script-environment-update', {
+ envVariables: result.envVariables,
+ collectionVariables: result.collectionVariables,
+ requestUid,
+ collectionUid
+ });
+ }
+
+ interpolateVars(request, envVars, collectionVariables, processEnvVars);
+
+ // stringify the request url encoded params
+ if (request.headers['content-type'] === 'application/x-www-form-urlencoded') {
+ request.data = qs.stringify(request.data);
+ }
+
+ // todo:
+ // i have no clue why electron can't send the request object
+ // without safeParseJSON(safeStringifyJSON(request.data))
mainWindow.webContents.send('main:run-request-event', {
- type: 'request-queued',
- requestUid,
+ type: 'request-sent',
+ requestSent: {
+ url: request.url,
+ method: request.method,
+ headers: request.headers,
+ data: safeParseJSON(safeStringifyJSON(request.data))
+ },
collectionUid,
itemUid: item.uid,
+ requestUid,
cancelTokenUid
});
- const _request = item.draft ? item.draft.request : item.request;
- const request = prepareRequest(_request);
- const envVars = getEnvVars(environment);
- const processEnvVars = getProcessEnvVars(collectionUid);
- const brunoConfig = getBrunoConfig(collectionUid);
- const scriptingConfig = get(brunoConfig, 'scripts', {});
-
- try {
- // make axios work in node using form data
- // reference: https://github.com/axios/axios/issues/1006#issuecomment-320165427
- if (request.headers && request.headers['content-type'] === 'multipart/form-data') {
- const form = new FormData();
- forOwn(request.data, (value, key) => {
- form.append(key, value);
- });
- extend(request.headers, form.getHeaders());
- request.data = form;
- }
-
- const cancelToken = axios.CancelToken.source();
- request.cancelToken = cancelToken.token;
- saveCancelToken(cancelTokenUid, cancelToken);
-
- // run pre-request vars
- const preRequestVars = get(request, 'vars.req', []);
- if (preRequestVars && preRequestVars.length) {
- const varsRuntime = new VarsRuntime();
- const result = varsRuntime.runPreRequestVars(
- preRequestVars,
- request,
- envVars,
- collectionVariables,
- collectionPath,
- processEnvVars
- );
-
- if (result) {
- mainWindow.webContents.send('main:script-environment-update', {
- envVariables: result.envVariables,
- collectionVariables: result.collectionVariables,
- requestUid,
- collectionUid
- });
+ const preferences = getPreferences();
+ const sslVerification = get(preferences, 'request.sslVerification', true);
+ const httpsAgentRequestFields = {};
+ if (!sslVerification) {
+ httpsAgentRequestFields['rejectUnauthorized'] = false;
+ } else {
+ const cacertArray = [preferences['cacert'], process.env.SSL_CERT_FILE, process.env.NODE_EXTRA_CA_CERTS];
+ cacertFile = cacertArray.find((el) => el);
+ if (cacertFile && cacertFile.length > 1) {
+ try {
+ const fs = require('fs');
+ caCrt = fs.readFileSync(cacertFile);
+ httpsAgentRequestFields['ca'] = caCrt;
+ } catch (err) {
+ console.log('Error reading CA cert file:' + cacertFile, err);
}
}
+ }
- // run pre-request script
- const requestScript = get(request, 'script.req');
- if (requestScript && requestScript.length) {
- const scriptRuntime = new ScriptRuntime();
- const result = await scriptRuntime.runRequestScript(
- decomment(requestScript),
- request,
- envVars,
- collectionVariables,
- collectionPath,
- onConsoleLog,
- processEnvVars,
- scriptingConfig
- );
+ // proxy configuration
+ const brunoConfig = getBrunoConfig(collectionUid);
+ const proxyEnabled = get(brunoConfig, 'proxy.enabled', false);
+ if (proxyEnabled) {
+ let proxy;
+ const interpolationOptions = {
+ envVars,
+ collectionVariables,
+ processEnvVars
+ };
+
+ const proxyProtocol = interpolateString(get(brunoConfig, 'proxy.protocol'), interpolationOptions);
+ const proxyHostname = interpolateString(get(brunoConfig, 'proxy.hostname'), interpolationOptions);
+ const proxyPort = interpolateString(get(brunoConfig, 'proxy.port'), interpolationOptions);
+ const proxyAuthEnabled = get(brunoConfig, 'proxy.auth.enabled', false);
+
+ if (proxyAuthEnabled) {
+ const proxyAuthUsername = interpolateString(get(brunoConfig, 'proxy.auth.username'), interpolationOptions);
+ const proxyAuthPassword = interpolateString(get(brunoConfig, 'proxy.auth.password'), interpolationOptions);
+
+ proxy = `${proxyProtocol}://${proxyAuthUsername}:${proxyAuthPassword}@${proxyHostname}:${proxyPort}`;
+ } else {
+ proxy = `${proxyProtocol}://${proxyHostname}:${proxyPort}`;
+ }
+
+ request.httpsAgent = new HttpsProxyAgent(
+ proxy,
+ Object.keys(httpsAgentRequestFields).length > 0 ? { ...httpsAgentRequestFields } : undefined
+ );
+
+ request.httpAgent = new HttpProxyAgent(proxy);
+ } else if (Object.keys(httpsAgentRequestFields).length > 0) {
+ request.httpsAgent = new https.Agent({
+ ...httpsAgentRequestFields
+ });
+ }
+
+ const axiosInstance = makeAxiosInstance();
+
+ /** @type {import('axios').AxiosResponse} */
+ const response = await axiosInstance(request);
+
+ // run post-response vars
+ const postResponseVars = get(request, 'vars.res', []);
+ if (postResponseVars && postResponseVars.length) {
+ const varsRuntime = new VarsRuntime();
+ const result = varsRuntime.runPostResponseVars(
+ postResponseVars,
+ request,
+ response,
+ envVars,
+ collectionVariables,
+ collectionPath,
+ processEnvVars
+ );
+
+ if (result) {
mainWindow.webContents.send('main:script-environment-update', {
envVariables: result.envVariables,
collectionVariables: result.collectionVariables,
@@ -172,141 +285,116 @@ const registerNetworkIpc = (mainWindow) => {
collectionUid
});
}
+ }
- interpolateVars(request, envVars, collectionVariables, processEnvVars);
+ // run post-response script
+ const responseScript = compact([get(collectionRoot, 'request.script.res'), get(request, 'script.res')]).join(
+ os.EOL
+ );
+ if (responseScript && responseScript.length) {
+ const scriptRuntime = new ScriptRuntime();
+ const result = await scriptRuntime.runResponseScript(
+ decomment(responseScript),
+ request,
+ response,
+ envVars,
+ collectionVariables,
+ collectionPath,
+ onConsoleLog,
+ processEnvVars,
+ scriptingConfig
+ );
- // stringify the request url encoded params
- if (request.headers['content-type'] === 'application/x-www-form-urlencoded') {
- request.data = qs.stringify(request.data);
- }
+ mainWindow.webContents.send('main:script-environment-update', {
+ envVariables: result.envVariables,
+ collectionVariables: result.collectionVariables,
+ requestUid,
+ collectionUid
+ });
+ }
+
+ // run assertions
+ const assertions = get(request, 'assertions');
+ if (assertions) {
+ const assertRuntime = new AssertRuntime();
+ const results = assertRuntime.runAssertions(
+ assertions,
+ request,
+ response,
+ envVars,
+ collectionVariables,
+ collectionPath
+ );
- // todo:
- // i have no clue why electron can't send the request object
- // without safeParseJSON(safeStringifyJSON(request.data))
mainWindow.webContents.send('main:run-request-event', {
- type: 'request-sent',
- requestSent: {
- url: request.url,
- method: request.method,
- headers: request.headers,
- data: safeParseJSON(safeStringifyJSON(request.data))
- },
- collectionUid,
+ type: 'assertion-results',
+ results: results,
itemUid: item.uid,
requestUid,
- cancelTokenUid
+ collectionUid
+ });
+ }
+
+ // run tests
+ const testFile = compact([
+ get(collectionRoot, 'request.tests'),
+ item.draft ? get(item.draft, 'request.tests') : get(item, 'request.tests')
+ ]).join(os.EOL);
+ if (typeof testFile === 'string') {
+ const testRuntime = new TestRuntime();
+ const testResults = await testRuntime.runTests(
+ decomment(testFile),
+ request,
+ response,
+ envVars,
+ collectionVariables,
+ collectionPath,
+ onConsoleLog,
+ processEnvVars,
+ scriptingConfig
+ );
+
+ mainWindow.webContents.send('main:run-request-event', {
+ type: 'test-results',
+ results: testResults.results,
+ itemUid: item.uid,
+ requestUid,
+ collectionUid
});
- const preferences = getPreferences();
- const sslVerification = get(preferences, 'request.sslVerification', true);
- const httpsAgentRequestFields = {};
- if (!sslVerification) {
- httpsAgentRequestFields['rejectUnauthorized'] = false;
- } else {
- const cacertArray = [preferences['cacert'], process.env.SSL_CERT_FILE, process.env.NODE_EXTRA_CA_CERTS];
- cacertFile = cacertArray.find((el) => el);
- if (cacertFile && cacertFile.length > 1) {
- try {
- const fs = require('fs');
- caCrt = fs.readFileSync(cacertFile);
- httpsAgentRequestFields['ca'] = caCrt;
- } catch (err) {
- console.log('Error reading CA cert file:' + cacertFile, err);
- }
- }
- }
+ mainWindow.webContents.send('main:script-environment-update', {
+ envVariables: testResults.envVariables,
+ collectionVariables: testResults.collectionVariables,
+ requestUid,
+ collectionUid
+ });
+ }
- // proxy configuration
- const brunoConfig = getBrunoConfig(collectionUid);
- const proxyEnabled = get(brunoConfig, 'proxy.enabled', false);
- if (proxyEnabled) {
- let proxy;
+ deleteCancelToken(cancelTokenUid);
+ // Prevents the duration on leaking to the actual result
+ const requestDuration = response.headers.get('request-duration');
+ response.headers.delete('request-duration');
- const interpolationOptions = {
- envVars,
- collectionVariables,
- processEnvVars
- };
+ return {
+ status: response.status,
+ statusText: response.statusText,
+ headers: response.headers,
+ data: response.data,
+ duration: requestDuration
+ };
+ } catch (error) {
+ // todo: better error handling
+ // need to convey the error to the UI
+ // and need not be always a network error
+ deleteCancelToken(cancelTokenUid);
- const proxyProtocol = interpolateString(get(brunoConfig, 'proxy.protocol'), interpolationOptions);
- const proxyHostname = interpolateString(get(brunoConfig, 'proxy.hostname'), interpolationOptions);
- const proxyPort = interpolateString(get(brunoConfig, 'proxy.port'), interpolationOptions);
- const proxyAuthEnabled = get(brunoConfig, 'proxy.auth.enabled', false);
-
- if (proxyAuthEnabled) {
- const proxyAuthUsername = interpolateString(get(brunoConfig, 'proxy.auth.username'), interpolationOptions);
- const proxyAuthPassword = interpolateString(get(brunoConfig, 'proxy.auth.password'), interpolationOptions);
-
- proxy = `${proxyProtocol}://${proxyAuthUsername}:${proxyAuthPassword}@${proxyHostname}:${proxyPort}`;
- } else {
- proxy = `${proxyProtocol}://${proxyHostname}:${proxyPort}`;
- }
-
- request.httpsAgent = new HttpsProxyAgent(
- proxy,
- Object.keys(httpsAgentRequestFields).length > 0 ? { ...httpsAgentRequestFields } : undefined
- );
-
- request.httpAgent = new HttpProxyAgent(proxy);
- } else if (Object.keys(httpsAgentRequestFields).length > 0) {
- request.httpsAgent = new https.Agent({
- ...httpsAgentRequestFields
- });
- }
-
- const axiosInstance = makeAxiosInstance();
-
- /** @type {import('axios').AxiosResponse} */
- const response = await axiosInstance(request);
-
- // run post-response vars
- const postResponseVars = get(request, 'vars.res', []);
- if (postResponseVars && postResponseVars.length) {
- const varsRuntime = new VarsRuntime();
- const result = varsRuntime.runPostResponseVars(
- postResponseVars,
- request,
- response,
- envVars,
- collectionVariables,
- collectionPath,
- processEnvVars
- );
-
- if (result) {
- mainWindow.webContents.send('main:script-environment-update', {
- envVariables: result.envVariables,
- collectionVariables: result.collectionVariables,
- requestUid,
- collectionUid
- });
- }
- }
-
- // run post-response script
- const responseScript = get(request, 'script.res');
- if (responseScript && responseScript.length) {
- const scriptRuntime = new ScriptRuntime();
- const result = await scriptRuntime.runResponseScript(
- decomment(responseScript),
- request,
- response,
- envVars,
- collectionVariables,
- collectionPath,
- onConsoleLog,
- processEnvVars,
- scriptingConfig
- );
-
- mainWindow.webContents.send('main:script-environment-update', {
- envVariables: result.envVariables,
- collectionVariables: result.collectionVariables,
- requestUid,
- collectionUid
- });
- }
+ if (axios.isCancel(error)) {
+ let error = new Error('Request cancelled');
+ error.isCancel = true;
+ return Promise.reject(error);
+ }
+ if (error && error.response) {
// run assertions
const assertions = get(request, 'assertions');
if (assertions) {
@@ -314,7 +402,7 @@ const registerNetworkIpc = (mainWindow) => {
const results = assertRuntime.runAssertions(
assertions,
request,
- response,
+ error.response,
envVars,
collectionVariables,
collectionPath
@@ -330,13 +418,16 @@ const registerNetworkIpc = (mainWindow) => {
}
// run tests
- const testFile = item.draft ? get(item.draft, 'request.tests') : get(item, 'request.tests');
+ const testFile = compact([
+ get(collectionRoot, 'request.tests'),
+ item.draft ? get(item.draft, 'request.tests') : get(item, 'request.tests')
+ ]).join(os.EOL);
if (typeof testFile === 'string') {
const testRuntime = new TestRuntime();
const testResults = await testRuntime.runTests(
decomment(testFile),
request,
- response,
+ error.response,
envVars,
collectionVariables,
collectionPath,
@@ -361,101 +452,21 @@ const registerNetworkIpc = (mainWindow) => {
});
}
- deleteCancelToken(cancelTokenUid);
- // Prevents the duration on leaking to the actual result
- const requestDuration = response.headers.get('request-duration');
- response.headers.delete('request-duration');
-
+ // Prevents the duration from leaking to the actual result
+ const requestDuration = error.response.headers.get('request-duration');
+ error.response.headers.delete('request-duration');
return {
- status: response.status,
- statusText: response.statusText,
- headers: response.headers,
- data: response.data,
- duration: requestDuration
+ status: error.response.status,
+ statusText: error.response.statusText,
+ headers: error.response.headers,
+ data: error.response.data,
+ duration: requestDuration ?? 0
};
- } catch (error) {
- // todo: better error handling
- // need to convey the error to the UI
- // and need not be always a network error
- deleteCancelToken(cancelTokenUid);
-
- if (axios.isCancel(error)) {
- let error = new Error('Request cancelled');
- error.isCancel = true;
- return Promise.reject(error);
- }
-
- if (error && error.response) {
- // run assertions
- const assertions = get(request, 'assertions');
- if (assertions) {
- const assertRuntime = new AssertRuntime();
- const results = assertRuntime.runAssertions(
- assertions,
- request,
- error.response,
- envVars,
- collectionVariables,
- collectionPath
- );
-
- mainWindow.webContents.send('main:run-request-event', {
- type: 'assertion-results',
- results: results,
- itemUid: item.uid,
- requestUid,
- collectionUid
- });
- }
-
- // run tests
- const testFile = item.draft ? get(item.draft, 'request.tests') : get(item, 'request.tests');
- if (typeof testFile === 'string') {
- const testRuntime = new TestRuntime();
- const testResults = await testRuntime.runTests(
- decomment(testFile),
- request,
- error.response,
- envVars,
- collectionVariables,
- collectionPath,
- onConsoleLog,
- processEnvVars,
- scriptingConfig
- );
-
- mainWindow.webContents.send('main:run-request-event', {
- type: 'test-results',
- results: testResults.results,
- itemUid: item.uid,
- requestUid,
- collectionUid
- });
-
- mainWindow.webContents.send('main:script-environment-update', {
- envVariables: testResults.envVariables,
- collectionVariables: testResults.collectionVariables,
- requestUid,
- collectionUid
- });
- }
-
- // Prevents the duration from leaking to the actual result
- const requestDuration = error.response.headers.get('request-duration');
- error.response.headers.delete('request-duration');
- return {
- status: error.response.status,
- statusText: error.response.statusText,
- headers: error.response.headers,
- data: error.response.data,
- duration: requestDuration ?? 0
- };
- }
-
- return Promise.reject(error);
}
+
+ return Promise.reject(error);
}
- );
+ });
ipcMain.handle('cancel-http-request', async (event, cancelTokenUid) => {
return new Promise((resolve, reject) => {
@@ -516,6 +527,7 @@ const registerNetworkIpc = (mainWindow) => {
const folderUid = folder ? folder.uid : null;
const brunoConfig = getBrunoConfig(collectionUid);
const scriptingConfig = get(brunoConfig, 'scripts', {});
+ const collectionRoot = get(collection, 'root', {});
const onConsoleLog = (type, args) => {
console[type](...args);
@@ -574,7 +586,7 @@ const registerNetworkIpc = (mainWindow) => {
});
const _request = item.draft ? item.draft.request : item.request;
- const request = prepareRequest(_request);
+ const request = prepareRequest(_request, collectionRoot);
const processEnvVars = getProcessEnvVars(collectionUid);
try {
@@ -611,7 +623,9 @@ const registerNetworkIpc = (mainWindow) => {
}
// run pre-request script
- const requestScript = get(request, 'script.req');
+ const requestScript = compact([get(collectionRoot, 'request.script.req'), get(request, 'script.req')]).join(
+ os.EOL
+ );
if (requestScript && requestScript.length) {
const scriptRuntime = new ScriptRuntime();
const result = await scriptRuntime.runRequestScript(
@@ -724,7 +738,10 @@ const registerNetworkIpc = (mainWindow) => {
}
// run response script
- const responseScript = get(request, 'script.res');
+ const responseScript = compact([
+ get(collectionRoot, 'request.script.res'),
+ get(request, 'script.res')
+ ]).join(os.EOL);
if (responseScript && responseScript.length) {
const scriptRuntime = new ScriptRuntime();
const result = await scriptRuntime.runResponseScript(
@@ -768,7 +785,10 @@ const registerNetworkIpc = (mainWindow) => {
}
// run tests
- const testFile = item.draft ? get(item.draft, 'request.tests') : get(item, 'request.tests');
+ const testFile = compact([
+ get(collectionRoot, 'request.tests'),
+ item.draft ? get(item.draft, 'request.tests') : get(item, 'request.tests')
+ ]).join(os.EOL);
if (typeof testFile === 'string') {
const testRuntime = new TestRuntime();
const testResults = await testRuntime.runTests(
@@ -848,7 +868,10 @@ const registerNetworkIpc = (mainWindow) => {
}
// run tests
- const testFile = item.draft ? get(item.draft, 'request.tests') : get(item, 'request.tests');
+ const testFile = compact([
+ get(collectionRoot, 'request.tests'),
+ item.draft ? get(item.draft, 'request.tests') : get(item, 'request.tests')
+ ]).join(os.EOL);
if (typeof testFile === 'string') {
const testRuntime = new TestRuntime();
const testResults = await testRuntime.runTests(
diff --git a/packages/bruno-electron/src/ipc/network/prepare-request.js b/packages/bruno-electron/src/ipc/network/prepare-request.js
index 922c9929e..42f4b39f8 100644
--- a/packages/bruno-electron/src/ipc/network/prepare-request.js
+++ b/packages/bruno-electron/src/ipc/network/prepare-request.js
@@ -1,9 +1,20 @@
const { get, each, filter } = require('lodash');
const decomment = require('decomment');
-const prepareRequest = (request) => {
+const prepareRequest = (request, collectionRoot) => {
const headers = {};
let contentTypeDefined = false;
+
+ // collection headers
+ each(get(collectionRoot, 'request.headers', []), (h) => {
+ if (h.enabled) {
+ headers[h.name] = h.value;
+ if (h.name.toLowerCase() === 'content-type') {
+ contentTypeDefined = true;
+ }
+ }
+ });
+
each(request.headers, (h) => {
if (h.enabled) {
headers[h.name] = h.value;
@@ -20,6 +31,23 @@ const prepareRequest = (request) => {
};
// Authentication
+ // A request can override the collection auth with another auth
+ // But it cannot override the collection auth with no auth
+ // We will provide support for disabling the auth via scripting in the future
+ const collectionAuth = get(collectionRoot, 'request.auth');
+ if (collectionAuth) {
+ if (collectionAuth.mode === 'basic') {
+ axiosRequest.auth = {
+ username: get(collectionAuth, 'basic.username'),
+ password: get(collectionAuth, 'basic.password')
+ };
+ }
+
+ if (collectionAuth.mode === 'bearer') {
+ axiosRequest.headers['authorization'] = `Bearer ${get(collectionAuth, 'bearer.token')}`;
+ }
+ }
+
if (request.auth) {
if (request.auth.mode === 'basic') {
axiosRequest.auth = {
diff --git a/packages/bruno-lang/src/index.js b/packages/bruno-lang/src/index.js
index f27179c45..55a9569d7 100644
--- a/packages/bruno-lang/src/index.js
+++ b/packages/bruno-lang/src/index.js
@@ -1,21 +1,23 @@
-const { bruToJson, jsonToBru, bruToEnvJson, envJsonToBru } = require('../v1/src');
-
const bruToJsonV2 = require('../v2/src/bruToJson');
const jsonToBruV2 = require('../v2/src/jsonToBru');
const bruToEnvJsonV2 = require('../v2/src/envToJson');
const envJsonToBruV2 = require('../v2/src/jsonToEnv');
const dotenvToJson = require('../v2/src/dotenvToJson');
-module.exports = {
- bruToJson,
- jsonToBru,
- bruToEnvJson,
- envJsonToBru,
+const collectionBruToJson = require('../v2/src/collectionBruToJson');
+const jsonToCollectionBru = require('../v2/src/jsonToCollectionBru');
+// Todo: remove V2 suffixes
+// Changes will have to be made to the CLI and GUI
+
+module.exports = {
bruToJsonV2,
jsonToBruV2,
bruToEnvJsonV2,
envJsonToBruV2,
+ collectionBruToJson,
+ jsonToCollectionBru,
+
dotenvToJson
};
diff --git a/packages/bruno-lang/v2/src/jsonToCollectionBru.js b/packages/bruno-lang/v2/src/jsonToCollectionBru.js
index f20fbc680..57a5ea7bf 100644
--- a/packages/bruno-lang/v2/src/jsonToCollectionBru.js
+++ b/packages/bruno-lang/v2/src/jsonToCollectionBru.js
@@ -12,7 +12,7 @@ const stripLastLine = (text) => {
return text.replace(/(\r?\n)$/, '');
};
-const jsonToBru = (json) => {
+const jsonToCollectionBru = (json) => {
const { meta, query, headers, auth, script, tests, vars, docs } = json;
let bru = '';
@@ -182,4 +182,4 @@ ${indentString(docs)}
return stripLastLine(bru);
};
-module.exports = jsonToBru;
+module.exports = jsonToCollectionBru;
diff --git a/readme.md b/readme.md
index 29cb5ad93..8021a5b7a 100644
--- a/readme.md
+++ b/readme.md
@@ -73,6 +73,7 @@ Even if you are not able to make contributions via code, please don't hesitate t
[Twitter](https://twitter.com/use_bruno)
[Website](https://www.usebruno.com)
[Discord](https://discord.com/invite/KgcZUncpjq)
+[LinkedIn](https://www.linkedin.com/company/usebruno)
### License 📄