mirror of
https://github.com/usebruno/bruno.git
synced 2024-12-22 14:41:04 +01:00
feat(#122): supporting process.env vars in UI and electron layer
This commit is contained in:
parent
c91fef2264
commit
e3ce420216
@ -5,10 +5,20 @@ const Wrapper = styled.div`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
table-layout: fixed;
|
||||||
|
|
||||||
thead,
|
thead,
|
||||||
td {
|
td {
|
||||||
border: 1px solid ${(props) => props.theme.collection.environment.settings.gridBorder};
|
border: 1px solid ${(props) => props.theme.collection.environment.settings.gridBorder};
|
||||||
|
padding: 4px 10px;
|
||||||
|
|
||||||
|
&:nth-child(1) {
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(3) {
|
||||||
|
width: 70px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
thead {
|
thead {
|
||||||
@ -16,7 +26,7 @@ const Wrapper = styled.div`
|
|||||||
font-size: 0.8125rem;
|
font-size: 0.8125rem;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
td {
|
thead td {
|
||||||
padding: 6px 10px;
|
padding: 6px 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,16 @@ import React, { useReducer } from 'react';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
import { IconTrash } from '@tabler/icons';
|
import { IconTrash } from '@tabler/icons';
|
||||||
|
import { useTheme } from 'providers/Theme';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { saveEnvironment } from 'providers/ReduxStore/slices/collections/actions';
|
import { saveEnvironment } from 'providers/ReduxStore/slices/collections/actions';
|
||||||
import reducer from './reducer';
|
import reducer from './reducer';
|
||||||
|
import SingleLineEditor from 'components/SingleLineEditor';
|
||||||
import StyledWrapper from './StyledWrapper';
|
import StyledWrapper from './StyledWrapper';
|
||||||
|
|
||||||
const EnvironmentVariables = ({ environment, collection }) => {
|
const EnvironmentVariables = ({ environment, collection }) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const { storedTheme } = useTheme();
|
||||||
const [state, reducerDispatch] = useReducer(reducer, { hasChanges: false, variables: environment.variables || [] });
|
const [state, reducerDispatch] = useReducer(reducer, { hasChanges: false, variables: environment.variables || [] });
|
||||||
const { variables, hasChanges } = state;
|
const { variables, hasChanges } = state;
|
||||||
|
|
||||||
@ -86,15 +89,11 @@ const EnvironmentVariables = ({ environment, collection }) => {
|
|||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input
|
<SingleLineEditor
|
||||||
type="text"
|
|
||||||
autoComplete="off"
|
|
||||||
autoCorrect="off"
|
|
||||||
autoCapitalize="off"
|
|
||||||
spellCheck="false"
|
|
||||||
value={variable.value}
|
value={variable.value}
|
||||||
className="mousetrap"
|
theme={storedTheme}
|
||||||
onChange={(e) => handleVarChange(e, variable, 'value')}
|
onChange={(newValue) => handleVarChange({ target: { value: newValue } }, variable, 'value')}
|
||||||
|
collection={collection}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
@ -19,7 +19,7 @@ const Wrapper = styled.div`
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
z-index: 1003;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bruno-modal-card {
|
.bruno-modal-card {
|
||||||
@ -28,7 +28,7 @@ const Wrapper = styled.div`
|
|||||||
background: var(--color-background-top);
|
background: var(--color-background-top);
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1003;
|
z-index: 10;
|
||||||
max-width: calc(100% - var(--spacing-base-unit));
|
max-width: calc(100% - var(--spacing-base-unit));
|
||||||
box-shadow: var(--box-shadow-base);
|
box-shadow: var(--box-shadow-base);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -19,6 +19,7 @@ const StyledWrapper = styled.div`
|
|||||||
|
|
||||||
.CodeMirror-scroll {
|
.CodeMirror-scroll {
|
||||||
overflow: hidden !important;
|
overflow: hidden !important;
|
||||||
|
padding-bottom: 50px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.CodeMirror-hscrollbar {
|
.CodeMirror-hscrollbar {
|
||||||
|
@ -31,6 +31,7 @@ class SingleLineEditor extends Component {
|
|||||||
brunoVarInfo: {
|
brunoVarInfo: {
|
||||||
variables: getAllVariables(this.props.collection)
|
variables: getAllVariables(this.props.collection)
|
||||||
},
|
},
|
||||||
|
scrollbarStyle: null,
|
||||||
extraKeys: {
|
extraKeys: {
|
||||||
Enter: () => {
|
Enter: () => {
|
||||||
if (this.props.onRun) {
|
if (this.props.onRun) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import forOwn from 'lodash/forOwn';
|
import forOwn from 'lodash/forOwn';
|
||||||
|
import isObject from 'lodash/isObject';
|
||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
import { uuid } from 'utils/common';
|
import { uuid } from 'utils/common';
|
||||||
import StyledWrapper from './StyledWrapper';
|
import StyledWrapper from './StyledWrapper';
|
||||||
@ -15,6 +16,14 @@ const VariablesTable = ({ variables, collectionVariables }) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const getValueToDisplay = (value) => {
|
||||||
|
if (value === undefined) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return isObject(value) ? JSON.stringify(value) : value;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledWrapper>
|
<StyledWrapper>
|
||||||
<div className="flex flex-col w-full">
|
<div className="flex flex-col w-full">
|
||||||
@ -24,7 +33,9 @@ const VariablesTable = ({ variables, collectionVariables }) => {
|
|||||||
return (
|
return (
|
||||||
<div key={variable.uid} className="flex">
|
<div key={variable.uid} className="flex">
|
||||||
<div className="variable-name text-yellow-600 text-right pr-2">{variable.name}</div>
|
<div className="variable-name text-yellow-600 text-right pr-2">{variable.name}</div>
|
||||||
<div className="variable-value pl-2 whitespace-normal text-left flex-grow">{variable.value}</div>
|
<div className="variable-value pl-2 whitespace-normal text-left flex-grow">
|
||||||
|
{getValueToDisplay(variable.value)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@ -38,7 +49,9 @@ const VariablesTable = ({ variables, collectionVariables }) => {
|
|||||||
return (
|
return (
|
||||||
<div key={variable.uid} className="flex">
|
<div key={variable.uid} className="flex">
|
||||||
<div className="variable-name text-yellow-600 text-right pr-2">{variable.name}</div>
|
<div className="variable-name text-yellow-600 text-right pr-2">{variable.name}</div>
|
||||||
<div className="variable-value pl-2 whitespace-normal text-left flex-grow">{variable.value}</div>
|
<div className="variable-value pl-2 whitespace-normal text-left flex-grow">
|
||||||
|
{getValueToDisplay(variable.value)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
collectionUnlinkDirectoryEvent,
|
collectionUnlinkDirectoryEvent,
|
||||||
collectionUnlinkEnvFileEvent,
|
collectionUnlinkEnvFileEvent,
|
||||||
scriptEnvironmentUpdateEvent,
|
scriptEnvironmentUpdateEvent,
|
||||||
|
processEnvUpdateEvent,
|
||||||
collectionRenamedEvent,
|
collectionRenamedEvent,
|
||||||
runRequestEvent,
|
runRequestEvent,
|
||||||
runFolderEvent
|
runFolderEvent
|
||||||
@ -97,6 +98,10 @@ const useCollectionTreeSync = () => {
|
|||||||
dispatch(scriptEnvironmentUpdateEvent(val));
|
dispatch(scriptEnvironmentUpdateEvent(val));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const _processEnvUpdate = (val) => {
|
||||||
|
dispatch(processEnvUpdateEvent(val));
|
||||||
|
};
|
||||||
|
|
||||||
const _collectionRenamed = (val) => {
|
const _collectionRenamed = (val) => {
|
||||||
dispatch(collectionRenamedEvent(val));
|
dispatch(collectionRenamedEvent(val));
|
||||||
};
|
};
|
||||||
@ -119,7 +124,8 @@ const useCollectionTreeSync = () => {
|
|||||||
const removeListener6 = ipcRenderer.on('main:collection-renamed', _collectionRenamed);
|
const removeListener6 = ipcRenderer.on('main:collection-renamed', _collectionRenamed);
|
||||||
const removeListener7 = ipcRenderer.on('main:run-folder-event', _runFolderEvent);
|
const removeListener7 = ipcRenderer.on('main:run-folder-event', _runFolderEvent);
|
||||||
const removeListener8 = ipcRenderer.on('main:run-request-event', _runRequestEvent);
|
const removeListener8 = ipcRenderer.on('main:run-request-event', _runRequestEvent);
|
||||||
const removeListener9 = ipcRenderer.on('main:console-log', (val) => {
|
const removeListener9 = ipcRenderer.on('main:process-env-update', _processEnvUpdate);
|
||||||
|
const removeListener10 = ipcRenderer.on('main:console-log', (val) => {
|
||||||
console[val.type](...val.args);
|
console[val.type](...val.args);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -133,6 +139,7 @@ const useCollectionTreeSync = () => {
|
|||||||
removeListener7();
|
removeListener7();
|
||||||
removeListener8();
|
removeListener8();
|
||||||
removeListener9();
|
removeListener9();
|
||||||
|
removeListener10();
|
||||||
};
|
};
|
||||||
}, [isElectron]);
|
}, [isElectron]);
|
||||||
};
|
};
|
||||||
|
@ -177,6 +177,14 @@ export const collectionsSlice = createSlice({
|
|||||||
collection.collectionVariables = collectionVariables;
|
collection.collectionVariables = collectionVariables;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
processEnvUpdateEvent: (state, action) => {
|
||||||
|
const { collectionUid, processEnvVariables } = action.payload;
|
||||||
|
const collection = findCollectionByUid(state.collections, collectionUid);
|
||||||
|
|
||||||
|
if (collection) {
|
||||||
|
collection.processEnvVariables = processEnvVariables;
|
||||||
|
}
|
||||||
|
},
|
||||||
requestCancelled: (state, action) => {
|
requestCancelled: (state, action) => {
|
||||||
const { itemUid, collectionUid } = action.payload;
|
const { itemUid, collectionUid } = action.payload;
|
||||||
const collection = findCollectionByUid(state.collections, collectionUid);
|
const collection = findCollectionByUid(state.collections, collectionUid);
|
||||||
@ -1158,6 +1166,7 @@ export const {
|
|||||||
renameItem,
|
renameItem,
|
||||||
cloneItem,
|
cloneItem,
|
||||||
scriptEnvironmentUpdateEvent,
|
scriptEnvironmentUpdateEvent,
|
||||||
|
processEnvUpdateEvent,
|
||||||
requestCancelled,
|
requestCancelled,
|
||||||
responseReceived,
|
responseReceived,
|
||||||
saveRequest,
|
saveRequest,
|
||||||
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -1 +1 @@
|
|||||||
@import "buttons";
|
@import 'buttons';
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
:root {
|
:root {
|
||||||
--color-brand: #546de5;
|
--color-brand: #546de5;
|
||||||
--color-text: rgb(52 52 52);
|
--color-text: rgb(52 52 52);
|
||||||
@ -21,7 +20,8 @@
|
|||||||
--color-method-head: rgb(52 52 52);
|
--color-method-head: rgb(52 52 52);
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
html,
|
||||||
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
@ -38,15 +38,18 @@ body {
|
|||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
body::-webkit-scrollbar, .CodeMirror-vscrollbar::-webkit-scrollbar {
|
body::-webkit-scrollbar,
|
||||||
|
.CodeMirror-vscrollbar::-webkit-scrollbar {
|
||||||
width: 0.6rem;
|
width: 0.6rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
body::-webkit-scrollbar-track, .CodeMirror-vscrollbar::-webkit-scrollbar-track {
|
body::-webkit-scrollbar-track,
|
||||||
|
.CodeMirror-vscrollbar::-webkit-scrollbar-track {
|
||||||
background-color: #f1f1f1;
|
background-color: #f1f1f1;
|
||||||
}
|
}
|
||||||
|
|
||||||
body::-webkit-scrollbar-thumb, .CodeMirror-vscrollbar::-webkit-scrollbar-thumb {
|
body::-webkit-scrollbar-thumb,
|
||||||
|
.CodeMirror-vscrollbar::-webkit-scrollbar-thumb {
|
||||||
background-color: #cdcdcd;
|
background-color: #cdcdcd;
|
||||||
border-radius: 5rem;
|
border-radius: 5rem;
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
let CodeMirror;
|
let CodeMirror;
|
||||||
const SERVER_RENDERED = typeof navigator === 'undefined' || global['PREVENT_CODEMIRROR_RENDER'] === true;
|
const SERVER_RENDERED = typeof navigator === 'undefined' || global['PREVENT_CODEMIRROR_RENDER'] === true;
|
||||||
|
const { get } = require('lodash');
|
||||||
|
|
||||||
if (!SERVER_RENDERED) {
|
if (!SERVER_RENDERED) {
|
||||||
CodeMirror = require('codemirror');
|
CodeMirror = require('codemirror');
|
||||||
@ -20,7 +21,7 @@ if (!SERVER_RENDERED) {
|
|||||||
// str is of format {{variableName}}, extract variableName
|
// str is of format {{variableName}}, extract variableName
|
||||||
// we are seeing that from the gql query editor, the token string is of format variableName
|
// we are seeing that from the gql query editor, the token string is of format variableName
|
||||||
const variableName = str.replace('{{', '').replace('}}', '').trim();
|
const variableName = str.replace('{{', '').replace('}}', '').trim();
|
||||||
const variableValue = options.variables[variableName];
|
const variableValue = get(options.variables, variableName);
|
||||||
|
|
||||||
const into = document.createElement('div');
|
const into = document.createElement('div');
|
||||||
const descriptionDiv = document.createElement('div');
|
const descriptionDiv = document.createElement('div');
|
||||||
|
@ -542,6 +542,11 @@ export const getAllVariables = (collection) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...environmentVariables,
|
...environmentVariables,
|
||||||
...collection.collectionVariables
|
...collection.collectionVariables,
|
||||||
|
process: {
|
||||||
|
env: {
|
||||||
|
...collection.processEnvVariables
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import get from 'lodash/get';
|
||||||
|
import isString from 'lodash/isString';
|
||||||
|
|
||||||
let CodeMirror;
|
let CodeMirror;
|
||||||
const SERVER_RENDERED = typeof navigator === 'undefined' || global['PREVENT_CODEMIRROR_RENDER'] === true;
|
const SERVER_RENDERED = typeof navigator === 'undefined' || global['PREVENT_CODEMIRROR_RENDER'] === true;
|
||||||
|
|
||||||
@ -5,6 +8,11 @@ if (!SERVER_RENDERED) {
|
|||||||
CodeMirror = require('codemirror');
|
CodeMirror = require('codemirror');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pathFoundInVariables = (path, obj) => {
|
||||||
|
const value = get(obj, path);
|
||||||
|
return isString(value);
|
||||||
|
};
|
||||||
|
|
||||||
export const defineCodeMirrorBrunoVariablesMode = (variables, mode) => {
|
export const defineCodeMirrorBrunoVariablesMode = (variables, mode) => {
|
||||||
CodeMirror.defineMode('brunovariables', function (config, parserConfig) {
|
CodeMirror.defineMode('brunovariables', function (config, parserConfig) {
|
||||||
let variablesOverlay = {
|
let variablesOverlay = {
|
||||||
@ -15,7 +23,8 @@ export const defineCodeMirrorBrunoVariablesMode = (variables, mode) => {
|
|||||||
while ((ch = stream.next()) != null) {
|
while ((ch = stream.next()) != null) {
|
||||||
if (ch == '}' && stream.next() == '}') {
|
if (ch == '}' && stream.next() == '}') {
|
||||||
stream.eat('}');
|
stream.eat('}');
|
||||||
if (word in variables) {
|
let found = pathFoundInVariables(word, variables);
|
||||||
|
if (found) {
|
||||||
return 'variable-valid';
|
return 'variable-valid';
|
||||||
} else {
|
} else {
|
||||||
return 'variable-invalid';
|
return 'variable-invalid';
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^10.1.0",
|
||||||
"graphql": "^16.6.0",
|
"graphql": "^16.6.0",
|
||||||
|
"handlebars": "^4.7.8",
|
||||||
"is-valid-path": "^0.1.1",
|
"is-valid-path": "^0.1.1",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mustache": "^4.2.0",
|
"mustache": "^4.2.0",
|
||||||
|
@ -4,11 +4,13 @@ const path = require('path');
|
|||||||
const chokidar = require('chokidar');
|
const chokidar = require('chokidar');
|
||||||
const { hasJsonExtension, hasBruExtension, writeFile } = require('../utils/filesystem');
|
const { hasJsonExtension, hasBruExtension, writeFile } = require('../utils/filesystem');
|
||||||
const { bruToEnvJson, envJsonToBru, bruToJson, jsonToBru } = require('../bru');
|
const { bruToEnvJson, envJsonToBru, bruToJson, jsonToBru } = require('../bru');
|
||||||
|
const { dotenvToJson } = require('@usebruno/lang');
|
||||||
|
|
||||||
const { isLegacyEnvFile, migrateLegacyEnvFile, isLegacyBruFile, migrateLegacyBruFile } = require('../bru/migrate');
|
const { isLegacyEnvFile, migrateLegacyEnvFile, isLegacyBruFile, migrateLegacyBruFile } = require('../bru/migrate');
|
||||||
const { itemSchema } = require('@usebruno/schema');
|
const { itemSchema } = require('@usebruno/schema');
|
||||||
const { uuid } = require('../utils/common');
|
const { uuid } = require('../utils/common');
|
||||||
const { getRequestUid } = require('../cache/requestUids');
|
const { getRequestUid } = require('../cache/requestUids');
|
||||||
|
const { setDotEnvVars } = require('../store/process-env');
|
||||||
|
|
||||||
const isJsonEnvironmentConfig = (pathname, collectionPath) => {
|
const isJsonEnvironmentConfig = (pathname, collectionPath) => {
|
||||||
const dirname = path.dirname(pathname);
|
const dirname = path.dirname(pathname);
|
||||||
@ -17,6 +19,13 @@ const isJsonEnvironmentConfig = (pathname, collectionPath) => {
|
|||||||
return dirname === collectionPath && basename === 'environments.json';
|
return dirname === collectionPath && basename === 'environments.json';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isDotEnvFile = (pathname, collectionPath) => {
|
||||||
|
const dirname = path.dirname(pathname);
|
||||||
|
const basename = path.basename(pathname);
|
||||||
|
|
||||||
|
return dirname === collectionPath && basename === '.env';
|
||||||
|
};
|
||||||
|
|
||||||
const isBruEnvironmentConfig = (pathname, collectionPath) => {
|
const isBruEnvironmentConfig = (pathname, collectionPath) => {
|
||||||
const dirname = path.dirname(pathname);
|
const dirname = path.dirname(pathname);
|
||||||
const envDirectory = path.join(collectionPath, 'environments');
|
const envDirectory = path.join(collectionPath, 'environments');
|
||||||
@ -125,6 +134,25 @@ const unlinkEnvironmentFile = async (win, pathname, collectionUid) => {
|
|||||||
const add = async (win, pathname, collectionUid, collectionPath) => {
|
const add = async (win, pathname, collectionUid, collectionPath) => {
|
||||||
console.log(`watcher add: ${pathname}`);
|
console.log(`watcher add: ${pathname}`);
|
||||||
|
|
||||||
|
if (isDotEnvFile(pathname, collectionPath)) {
|
||||||
|
try {
|
||||||
|
const content = fs.readFileSync(pathname, 'utf8');
|
||||||
|
const jsonData = dotenvToJson(content);
|
||||||
|
|
||||||
|
setDotEnvVars(collectionUid, jsonData);
|
||||||
|
const payload = {
|
||||||
|
collectionUid,
|
||||||
|
processEnvVariables: {
|
||||||
|
...process.env,
|
||||||
|
...jsonData
|
||||||
|
}
|
||||||
|
};
|
||||||
|
win.webContents.send('main:process-env-update', payload);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isJsonEnvironmentConfig(pathname, collectionPath)) {
|
if (isJsonEnvironmentConfig(pathname, collectionPath)) {
|
||||||
try {
|
try {
|
||||||
const dirname = path.dirname(pathname);
|
const dirname = path.dirname(pathname);
|
||||||
@ -220,6 +248,25 @@ const addDirectory = (win, pathname, collectionUid, collectionPath) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const change = async (win, pathname, collectionUid, collectionPath) => {
|
const change = async (win, pathname, collectionUid, collectionPath) => {
|
||||||
|
if (isDotEnvFile(pathname, collectionPath)) {
|
||||||
|
try {
|
||||||
|
const content = fs.readFileSync(pathname, 'utf8');
|
||||||
|
const jsonData = dotenvToJson(content);
|
||||||
|
|
||||||
|
setDotEnvVars(collectionUid, jsonData);
|
||||||
|
const payload = {
|
||||||
|
collectionUid,
|
||||||
|
processEnvVariables: {
|
||||||
|
...process.env,
|
||||||
|
...jsonData
|
||||||
|
}
|
||||||
|
};
|
||||||
|
win.webContents.send('main:process-env-update', payload);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isBruEnvironmentConfig(pathname, collectionPath)) {
|
if (isBruEnvironmentConfig(pathname, collectionPath)) {
|
||||||
return changeEnvironmentFile(win, pathname, collectionUid);
|
return changeEnvironmentFile(win, pathname, collectionUid);
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ const { uuid } = require('../../utils/common');
|
|||||||
const interpolateVars = require('./interpolate-vars');
|
const interpolateVars = require('./interpolate-vars');
|
||||||
const { sortFolder, getAllRequestsInFolderRecursively } = require('./helper');
|
const { sortFolder, getAllRequestsInFolderRecursively } = require('./helper');
|
||||||
const { getPreferences } = require('../../store/preferences');
|
const { getPreferences } = require('../../store/preferences');
|
||||||
|
const { getProcessEnvVars } = require('../../store/process-env');
|
||||||
|
|
||||||
// override the default escape function to prevent escaping
|
// override the default escape function to prevent escaping
|
||||||
Mustache.escape = function (value) {
|
Mustache.escape = function (value) {
|
||||||
@ -129,12 +130,14 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
collectionPath
|
collectionPath
|
||||||
);
|
);
|
||||||
|
|
||||||
mainWindow.webContents.send('main:script-environment-update', {
|
if (result) {
|
||||||
envVariables: result.envVariables,
|
mainWindow.webContents.send('main:script-environment-update', {
|
||||||
collectionVariables: result.collectionVariables,
|
envVariables: result.envVariables,
|
||||||
requestUid,
|
collectionVariables: result.collectionVariables,
|
||||||
collectionUid
|
requestUid,
|
||||||
});
|
collectionUid
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// run pre-request script
|
// run pre-request script
|
||||||
@ -158,7 +161,9 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
interpolateVars(request, envVars, collectionVariables);
|
const processEnvVars = getProcessEnvVars(collectionUid);
|
||||||
|
|
||||||
|
interpolateVars(request, envVars, collectionVariables, processEnvVars);
|
||||||
|
|
||||||
// stringify the request url encoded params
|
// stringify the request url encoded params
|
||||||
if (request.headers['content-type'] === 'application/x-www-form-urlencoded') {
|
if (request.headers['content-type'] === 'application/x-www-form-urlencoded') {
|
||||||
@ -222,12 +227,14 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
collectionPath
|
collectionPath
|
||||||
);
|
);
|
||||||
|
|
||||||
mainWindow.webContents.send('main:script-environment-update', {
|
if (result) {
|
||||||
envVariables: result.envVariables,
|
mainWindow.webContents.send('main:script-environment-update', {
|
||||||
collectionVariables: result.collectionVariables,
|
envVariables: result.envVariables,
|
||||||
requestUid,
|
collectionVariables: result.collectionVariables,
|
||||||
collectionUid
|
requestUid,
|
||||||
});
|
collectionUid
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// run post-response script
|
// run post-response script
|
||||||
@ -520,7 +527,21 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
const preRequestVars = get(request, 'vars.req', []);
|
const preRequestVars = get(request, 'vars.req', []);
|
||||||
if (preRequestVars && preRequestVars.length) {
|
if (preRequestVars && preRequestVars.length) {
|
||||||
const varsRuntime = new VarsRuntime();
|
const varsRuntime = new VarsRuntime();
|
||||||
varsRuntime.runPreRequestVars(preRequestVars, request, envVars, collectionVariables, collectionPath);
|
const result = varsRuntime.runPreRequestVars(
|
||||||
|
preRequestVars,
|
||||||
|
request,
|
||||||
|
envVars,
|
||||||
|
collectionVariables,
|
||||||
|
collectionPath
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
mainWindow.webContents.send('main:script-environment-update', {
|
||||||
|
envVariables: result.envVariables,
|
||||||
|
collectionVariables: result.collectionVariables,
|
||||||
|
collectionUid
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// run pre-request script
|
// run pre-request script
|
||||||
@ -543,8 +564,10 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const processEnvVars = getProcessEnvVars(collectionUid);
|
||||||
|
|
||||||
// interpolate variables inside request
|
// interpolate variables inside request
|
||||||
interpolateVars(request, envVars, collectionVariables);
|
interpolateVars(request, envVars, collectionVariables, processEnvVars);
|
||||||
|
|
||||||
// todo:
|
// todo:
|
||||||
// i have no clue why electron can't send the request object
|
// i have no clue why electron can't send the request object
|
||||||
@ -587,11 +610,13 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
collectionPath
|
collectionPath
|
||||||
);
|
);
|
||||||
|
|
||||||
mainWindow.webContents.send('main:script-environment-update', {
|
if (result) {
|
||||||
envVariables: result.envVariables,
|
mainWindow.webContents.send('main:script-environment-update', {
|
||||||
collectionVariables: result.collectionVariables,
|
envVariables: result.envVariables,
|
||||||
collectionUid
|
collectionVariables: result.collectionVariables,
|
||||||
});
|
collectionUid
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// run response script
|
// run response script
|
||||||
|
@ -1,24 +1,51 @@
|
|||||||
const Mustache = require('mustache');
|
const Handlebars = require('handlebars');
|
||||||
const { each, get, forOwn } = require('lodash');
|
const { each, forOwn, cloneDeep } = require('lodash');
|
||||||
|
|
||||||
// override the default escape function to prevent escaping
|
const interpolateEnvVars = (str, processEnvVars) => {
|
||||||
Mustache.escape = function (value) {
|
if (!str || !str.length || typeof str !== 'string') {
|
||||||
return value;
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
const template = Handlebars.compile(str, { noEscape: true });
|
||||||
|
|
||||||
|
return template({
|
||||||
|
process: {
|
||||||
|
env: {
|
||||||
|
...processEnvVars
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const interpolateVars = (request, envVars = {}, collectionVariables = {}) => {
|
const interpolateVars = (request, envVars = {}, collectionVariables = {}, processEnvVars = {}) => {
|
||||||
|
// we clone envVars because we don't want to modify the original object
|
||||||
|
envVars = cloneDeep(envVars);
|
||||||
|
|
||||||
|
// envVars can inturn have values as {{process.env.VAR_NAME}}
|
||||||
|
// so we need to interpolate envVars first with processEnvVars
|
||||||
|
forOwn(envVars, (value, key) => {
|
||||||
|
envVars[key] = interpolateEnvVars(value, processEnvVars);
|
||||||
|
});
|
||||||
|
|
||||||
const interpolate = (str) => {
|
const interpolate = (str) => {
|
||||||
if (!str || !str.length || typeof str !== 'string') {
|
if (!str || !str.length || typeof str !== 'string') {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const template = Handlebars.compile(str, { noEscape: true });
|
||||||
|
|
||||||
// collectionVariables take precedence over envVars
|
// collectionVariables take precedence over envVars
|
||||||
const combinedVars = {
|
const combinedVars = {
|
||||||
...envVars,
|
...envVars,
|
||||||
...collectionVariables
|
...collectionVariables,
|
||||||
|
process: {
|
||||||
|
env: {
|
||||||
|
...processEnvVars
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return Mustache.render(str, combinedVars);
|
return template(combinedVars);
|
||||||
};
|
};
|
||||||
|
|
||||||
request.url = interpolate(request.url);
|
request.url = interpolate(request.url);
|
||||||
|
37
packages/bruno-electron/src/store/process-env.js
Normal file
37
packages/bruno-electron/src/store/process-env.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* This file stores all the process.env variables under collection scope
|
||||||
|
*
|
||||||
|
* process.env variables are sourced from 2 places:
|
||||||
|
* 1. .env file in the root of the project
|
||||||
|
* 2. process.env variables set in the OS
|
||||||
|
*
|
||||||
|
* Multiple collections can be opened in the same electron app.
|
||||||
|
* Each collection's .env file can have different values for the same process.env variable.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const dotEnvVars = {};
|
||||||
|
|
||||||
|
// collectionUid is a hash based on the collection path)
|
||||||
|
const getProcessEnvVars = (collectionUid) => {
|
||||||
|
// if there are no .env vars for this collection, return the process.env
|
||||||
|
if (!dotEnvVars[collectionUid]) {
|
||||||
|
return {
|
||||||
|
...process.env
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there are .env vars for this collection, return the process.env merged with the .env vars
|
||||||
|
return {
|
||||||
|
...process.env,
|
||||||
|
...dotEnvVars[collectionUid]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const setDotEnvVars = (collectionUid, envVars) => {
|
||||||
|
dotEnvVars[collectionUid] = envVars;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getProcessEnvVars,
|
||||||
|
setDotEnvVars
|
||||||
|
};
|
@ -41,10 +41,39 @@ const generateUidBasedOnHash = (str) => {
|
|||||||
return `${hash}`.padEnd(21, '0');
|
return `${hash}`.padEnd(21, '0');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const flattenDataForDotNotation = (data) => {
|
||||||
|
var result = {};
|
||||||
|
function recurse(current, prop) {
|
||||||
|
if (Object(current) !== current) {
|
||||||
|
result[prop] = current;
|
||||||
|
} else if (Array.isArray(current)) {
|
||||||
|
for (var i = 0, l = current.length; i < l; i++) {
|
||||||
|
recurse(current[i], prop + '[' + i + ']');
|
||||||
|
}
|
||||||
|
if (l == 0) {
|
||||||
|
result[prop] = [];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var isEmpty = true;
|
||||||
|
for (var p in current) {
|
||||||
|
isEmpty = false;
|
||||||
|
recurse(current[p], prop ? prop + '.' + p : p);
|
||||||
|
}
|
||||||
|
if (isEmpty && prop) {
|
||||||
|
result[prop] = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recurse(data, '');
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
uuid,
|
uuid,
|
||||||
stringifyJson,
|
stringifyJson,
|
||||||
parseJson,
|
parseJson,
|
||||||
simpleHash,
|
simpleHash,
|
||||||
generateUidBasedOnHash
|
generateUidBasedOnHash,
|
||||||
|
flattenDataForDotNotation
|
||||||
};
|
};
|
||||||
|
85
packages/bruno-electron/tests/utils/common.spec.js
Normal file
85
packages/bruno-electron/tests/utils/common.spec.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
const { flattenDataForDotNotation } = require('../../src/utils/common');
|
||||||
|
|
||||||
|
describe('utils: flattenDataForDotNotation', () => {
|
||||||
|
test('Flatten a simple object with dot notation', () => {
|
||||||
|
const input = {
|
||||||
|
person: {
|
||||||
|
name: 'John',
|
||||||
|
age: 30,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const expectedOutput = {
|
||||||
|
'person.name': 'John',
|
||||||
|
'person.age': 30,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(flattenDataForDotNotation(input)).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Flatten an object with nested arrays', () => {
|
||||||
|
const input = {
|
||||||
|
users: [
|
||||||
|
{ name: 'Alice', age: 25 },
|
||||||
|
{ name: 'Bob', age: 28 },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const expectedOutput = {
|
||||||
|
'users[0].name': 'Alice',
|
||||||
|
'users[0].age': 25,
|
||||||
|
'users[1].name': 'Bob',
|
||||||
|
'users[1].age': 28,
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(flattenDataForDotNotation(input)).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Flatten an empty object', () => {
|
||||||
|
const input = {};
|
||||||
|
|
||||||
|
const expectedOutput = {};
|
||||||
|
|
||||||
|
expect(flattenDataForDotNotation(input)).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Flatten an object with nested objects', () => {
|
||||||
|
const input = {
|
||||||
|
person: {
|
||||||
|
name: 'Alice',
|
||||||
|
address: {
|
||||||
|
city: 'New York',
|
||||||
|
zipcode: '10001',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const expectedOutput = {
|
||||||
|
'person.name': 'Alice',
|
||||||
|
'person.address.city': 'New York',
|
||||||
|
'person.address.zipcode': '10001',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(flattenDataForDotNotation(input)).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Flatten an object with arrays of objects', () => {
|
||||||
|
const input = {
|
||||||
|
teams: [
|
||||||
|
{ name: 'Team A', members: ['Alice', 'Bob'] },
|
||||||
|
{ name: 'Team B', members: ['Charlie', 'David'] },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const expectedOutput = {
|
||||||
|
'teams[0].name': 'Team A',
|
||||||
|
'teams[0].members[0]': 'Alice',
|
||||||
|
'teams[0].members[1]': 'Bob',
|
||||||
|
'teams[1].name': 'Team B',
|
||||||
|
'teams[1].members[0]': 'Charlie',
|
||||||
|
'teams[1].members[1]': 'David',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(flattenDataForDotNotation(input)).toEqual(expectedOutput);
|
||||||
|
});
|
||||||
|
});
|
@ -4,6 +4,7 @@ const bruToJsonV2 = require('../v2/src/bruToJson');
|
|||||||
const jsonToBruV2 = require('../v2/src/jsonToBru');
|
const jsonToBruV2 = require('../v2/src/jsonToBru');
|
||||||
const bruToEnvJsonV2 = require('../v2/src/envToJson');
|
const bruToEnvJsonV2 = require('../v2/src/envToJson');
|
||||||
const envJsonToBruV2 = require('../v2/src/jsonToEnv');
|
const envJsonToBruV2 = require('../v2/src/jsonToEnv');
|
||||||
|
const dotenvToJson = require('../v2/src/dotenvToJson');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
bruToJson,
|
bruToJson,
|
||||||
@ -14,5 +15,7 @@ module.exports = {
|
|||||||
bruToJsonV2,
|
bruToJsonV2,
|
||||||
jsonToBruV2,
|
jsonToBruV2,
|
||||||
bruToEnvJsonV2,
|
bruToEnvJsonV2,
|
||||||
envJsonToBruV2
|
envJsonToBruV2,
|
||||||
|
|
||||||
|
dotenvToJson
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user