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%;
|
||||
border-collapse: collapse;
|
||||
font-weight: 600;
|
||||
table-layout: fixed;
|
||||
|
||||
thead,
|
||||
td {
|
||||
border: 1px solid ${(props) => props.theme.collection.environment.settings.gridBorder};
|
||||
padding: 4px 10px;
|
||||
|
||||
&:nth-child(1) {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
&:nth-child(3) {
|
||||
width: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
thead {
|
||||
@ -16,7 +26,7 @@ const Wrapper = styled.div`
|
||||
font-size: 0.8125rem;
|
||||
user-select: none;
|
||||
}
|
||||
td {
|
||||
thead td {
|
||||
padding: 6px 10px;
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,16 @@ import React, { useReducer } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { IconTrash } from '@tabler/icons';
|
||||
import { useTheme } from 'providers/Theme';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { saveEnvironment } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import reducer from './reducer';
|
||||
import SingleLineEditor from 'components/SingleLineEditor';
|
||||
import StyledWrapper from './StyledWrapper';
|
||||
|
||||
const EnvironmentVariables = ({ environment, collection }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { storedTheme } = useTheme();
|
||||
const [state, reducerDispatch] = useReducer(reducer, { hasChanges: false, variables: environment.variables || [] });
|
||||
const { variables, hasChanges } = state;
|
||||
|
||||
@ -86,15 +89,11 @@ const EnvironmentVariables = ({ environment, collection }) => {
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<input
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
<SingleLineEditor
|
||||
value={variable.value}
|
||||
className="mousetrap"
|
||||
onChange={(e) => handleVarChange(e, variable, 'value')}
|
||||
theme={storedTheme}
|
||||
onChange={(newValue) => handleVarChange({ target: { value: newValue } }, variable, 'value')}
|
||||
collection={collection}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
|
@ -19,7 +19,7 @@ const Wrapper = styled.div`
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
overflow-y: auto;
|
||||
z-index: 1003;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.bruno-modal-card {
|
||||
@ -28,7 +28,7 @@ const Wrapper = styled.div`
|
||||
background: var(--color-background-top);
|
||||
border-radius: var(--border-radius);
|
||||
position: relative;
|
||||
z-index: 1003;
|
||||
z-index: 10;
|
||||
max-width: calc(100% - var(--spacing-base-unit));
|
||||
box-shadow: var(--box-shadow-base);
|
||||
display: flex;
|
||||
|
@ -19,6 +19,7 @@ const StyledWrapper = styled.div`
|
||||
|
||||
.CodeMirror-scroll {
|
||||
overflow: hidden !important;
|
||||
padding-bottom: 50px !important;
|
||||
}
|
||||
|
||||
.CodeMirror-hscrollbar {
|
||||
|
@ -31,6 +31,7 @@ class SingleLineEditor extends Component {
|
||||
brunoVarInfo: {
|
||||
variables: getAllVariables(this.props.collection)
|
||||
},
|
||||
scrollbarStyle: null,
|
||||
extraKeys: {
|
||||
Enter: () => {
|
||||
if (this.props.onRun) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import forOwn from 'lodash/forOwn';
|
||||
import isObject from 'lodash/isObject';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import { uuid } from 'utils/common';
|
||||
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 (
|
||||
<StyledWrapper>
|
||||
<div className="flex flex-col w-full">
|
||||
@ -24,7 +33,9 @@ const VariablesTable = ({ variables, collectionVariables }) => {
|
||||
return (
|
||||
<div key={variable.uid} className="flex">
|
||||
<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>
|
||||
);
|
||||
})
|
||||
@ -38,7 +49,9 @@ const VariablesTable = ({ variables, collectionVariables }) => {
|
||||
return (
|
||||
<div key={variable.uid} className="flex">
|
||||
<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>
|
||||
);
|
||||
})
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
collectionUnlinkDirectoryEvent,
|
||||
collectionUnlinkEnvFileEvent,
|
||||
scriptEnvironmentUpdateEvent,
|
||||
processEnvUpdateEvent,
|
||||
collectionRenamedEvent,
|
||||
runRequestEvent,
|
||||
runFolderEvent
|
||||
@ -97,6 +98,10 @@ const useCollectionTreeSync = () => {
|
||||
dispatch(scriptEnvironmentUpdateEvent(val));
|
||||
};
|
||||
|
||||
const _processEnvUpdate = (val) => {
|
||||
dispatch(processEnvUpdateEvent(val));
|
||||
};
|
||||
|
||||
const _collectionRenamed = (val) => {
|
||||
dispatch(collectionRenamedEvent(val));
|
||||
};
|
||||
@ -119,7 +124,8 @@ const useCollectionTreeSync = () => {
|
||||
const removeListener6 = ipcRenderer.on('main:collection-renamed', _collectionRenamed);
|
||||
const removeListener7 = ipcRenderer.on('main:run-folder-event', _runFolderEvent);
|
||||
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);
|
||||
});
|
||||
|
||||
@ -133,6 +139,7 @@ const useCollectionTreeSync = () => {
|
||||
removeListener7();
|
||||
removeListener8();
|
||||
removeListener9();
|
||||
removeListener10();
|
||||
};
|
||||
}, [isElectron]);
|
||||
};
|
||||
|
@ -177,6 +177,14 @@ export const collectionsSlice = createSlice({
|
||||
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) => {
|
||||
const { itemUid, collectionUid } = action.payload;
|
||||
const collection = findCollectionByUid(state.collections, collectionUid);
|
||||
@ -1158,6 +1166,7 @@ export const {
|
||||
renameItem,
|
||||
cloneItem,
|
||||
scriptEnvironmentUpdateEvent,
|
||||
processEnvUpdateEvent,
|
||||
requestCancelled,
|
||||
responseReceived,
|
||||
saveRequest,
|
||||
|
@ -1 +0,0 @@
|
||||
|
@ -1 +1 @@
|
||||
@import "buttons";
|
||||
@import 'buttons';
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
:root {
|
||||
--color-brand: #546de5;
|
||||
--color-text: rgb(52 52 52);
|
||||
@ -21,7 +20,8 @@
|
||||
--color-method-head: rgb(52 52 52);
|
||||
}
|
||||
|
||||
html, body {
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 1rem;
|
||||
@ -38,15 +38,18 @@ body {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar, .CodeMirror-vscrollbar::-webkit-scrollbar {
|
||||
body::-webkit-scrollbar,
|
||||
.CodeMirror-vscrollbar::-webkit-scrollbar {
|
||||
width: 0.6rem;
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar-track, .CodeMirror-vscrollbar::-webkit-scrollbar-track {
|
||||
body::-webkit-scrollbar-track,
|
||||
.CodeMirror-vscrollbar::-webkit-scrollbar-track {
|
||||
background-color: #f1f1f1;
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar-thumb, .CodeMirror-vscrollbar::-webkit-scrollbar-thumb {
|
||||
body::-webkit-scrollbar-thumb,
|
||||
.CodeMirror-vscrollbar::-webkit-scrollbar-thumb {
|
||||
background-color: #cdcdcd;
|
||||
border-radius: 5rem;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
let CodeMirror;
|
||||
const SERVER_RENDERED = typeof navigator === 'undefined' || global['PREVENT_CODEMIRROR_RENDER'] === true;
|
||||
const { get } = require('lodash');
|
||||
|
||||
if (!SERVER_RENDERED) {
|
||||
CodeMirror = require('codemirror');
|
||||
@ -20,7 +21,7 @@ if (!SERVER_RENDERED) {
|
||||
// str is of format {{variableName}}, extract variableName
|
||||
// we are seeing that from the gql query editor, the token string is of format variableName
|
||||
const variableName = str.replace('{{', '').replace('}}', '').trim();
|
||||
const variableValue = options.variables[variableName];
|
||||
const variableValue = get(options.variables, variableName);
|
||||
|
||||
const into = document.createElement('div');
|
||||
const descriptionDiv = document.createElement('div');
|
||||
|
@ -542,6 +542,11 @@ export const getAllVariables = (collection) => {
|
||||
|
||||
return {
|
||||
...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;
|
||||
const SERVER_RENDERED = typeof navigator === 'undefined' || global['PREVENT_CODEMIRROR_RENDER'] === true;
|
||||
|
||||
@ -5,6 +8,11 @@ if (!SERVER_RENDERED) {
|
||||
CodeMirror = require('codemirror');
|
||||
}
|
||||
|
||||
const pathFoundInVariables = (path, obj) => {
|
||||
const value = get(obj, path);
|
||||
return isString(value);
|
||||
};
|
||||
|
||||
export const defineCodeMirrorBrunoVariablesMode = (variables, mode) => {
|
||||
CodeMirror.defineMode('brunovariables', function (config, parserConfig) {
|
||||
let variablesOverlay = {
|
||||
@ -15,7 +23,8 @@ export const defineCodeMirrorBrunoVariablesMode = (variables, mode) => {
|
||||
while ((ch = stream.next()) != null) {
|
||||
if (ch == '}' && stream.next() == '}') {
|
||||
stream.eat('}');
|
||||
if (word in variables) {
|
||||
let found = pathFoundInVariables(word, variables);
|
||||
if (found) {
|
||||
return 'variable-valid';
|
||||
} else {
|
||||
return 'variable-invalid';
|
||||
|
@ -27,6 +27,7 @@
|
||||
"form-data": "^4.0.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"graphql": "^16.6.0",
|
||||
"handlebars": "^4.7.8",
|
||||
"is-valid-path": "^0.1.1",
|
||||
"lodash": "^4.17.21",
|
||||
"mustache": "^4.2.0",
|
||||
|
@ -4,11 +4,13 @@ const path = require('path');
|
||||
const chokidar = require('chokidar');
|
||||
const { hasJsonExtension, hasBruExtension, writeFile } = require('../utils/filesystem');
|
||||
const { bruToEnvJson, envJsonToBru, bruToJson, jsonToBru } = require('../bru');
|
||||
const { dotenvToJson } = require('@usebruno/lang');
|
||||
|
||||
const { isLegacyEnvFile, migrateLegacyEnvFile, isLegacyBruFile, migrateLegacyBruFile } = require('../bru/migrate');
|
||||
const { itemSchema } = require('@usebruno/schema');
|
||||
const { uuid } = require('../utils/common');
|
||||
const { getRequestUid } = require('../cache/requestUids');
|
||||
const { setDotEnvVars } = require('../store/process-env');
|
||||
|
||||
const isJsonEnvironmentConfig = (pathname, collectionPath) => {
|
||||
const dirname = path.dirname(pathname);
|
||||
@ -17,6 +19,13 @@ const isJsonEnvironmentConfig = (pathname, collectionPath) => {
|
||||
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 dirname = path.dirname(pathname);
|
||||
const envDirectory = path.join(collectionPath, 'environments');
|
||||
@ -125,6 +134,25 @@ const unlinkEnvironmentFile = async (win, pathname, collectionUid) => {
|
||||
const add = async (win, pathname, collectionUid, collectionPath) => {
|
||||
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)) {
|
||||
try {
|
||||
const dirname = path.dirname(pathname);
|
||||
@ -220,6 +248,25 @@ const addDirectory = (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)) {
|
||||
return changeEnvironmentFile(win, pathname, collectionUid);
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ const { uuid } = require('../../utils/common');
|
||||
const interpolateVars = require('./interpolate-vars');
|
||||
const { sortFolder, getAllRequestsInFolderRecursively } = require('./helper');
|
||||
const { getPreferences } = require('../../store/preferences');
|
||||
const { getProcessEnvVars } = require('../../store/process-env');
|
||||
|
||||
// override the default escape function to prevent escaping
|
||||
Mustache.escape = function (value) {
|
||||
@ -129,6 +130,7 @@ const registerNetworkIpc = (mainWindow) => {
|
||||
collectionPath
|
||||
);
|
||||
|
||||
if (result) {
|
||||
mainWindow.webContents.send('main:script-environment-update', {
|
||||
envVariables: result.envVariables,
|
||||
collectionVariables: result.collectionVariables,
|
||||
@ -136,6 +138,7 @@ const registerNetworkIpc = (mainWindow) => {
|
||||
collectionUid
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// run pre-request script
|
||||
const requestScript = get(request, 'script.req');
|
||||
@ -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
|
||||
if (request.headers['content-type'] === 'application/x-www-form-urlencoded') {
|
||||
@ -222,6 +227,7 @@ const registerNetworkIpc = (mainWindow) => {
|
||||
collectionPath
|
||||
);
|
||||
|
||||
if (result) {
|
||||
mainWindow.webContents.send('main:script-environment-update', {
|
||||
envVariables: result.envVariables,
|
||||
collectionVariables: result.collectionVariables,
|
||||
@ -229,6 +235,7 @@ const registerNetworkIpc = (mainWindow) => {
|
||||
collectionUid
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// run post-response script
|
||||
const responseScript = get(request, 'script.res');
|
||||
@ -520,7 +527,21 @@ const registerNetworkIpc = (mainWindow) => {
|
||||
const preRequestVars = get(request, 'vars.req', []);
|
||||
if (preRequestVars && preRequestVars.length) {
|
||||
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
|
||||
@ -543,8 +564,10 @@ const registerNetworkIpc = (mainWindow) => {
|
||||
});
|
||||
}
|
||||
|
||||
const processEnvVars = getProcessEnvVars(collectionUid);
|
||||
|
||||
// interpolate variables inside request
|
||||
interpolateVars(request, envVars, collectionVariables);
|
||||
interpolateVars(request, envVars, collectionVariables, processEnvVars);
|
||||
|
||||
// todo:
|
||||
// i have no clue why electron can't send the request object
|
||||
@ -587,12 +610,14 @@ const registerNetworkIpc = (mainWindow) => {
|
||||
collectionPath
|
||||
);
|
||||
|
||||
if (result) {
|
||||
mainWindow.webContents.send('main:script-environment-update', {
|
||||
envVariables: result.envVariables,
|
||||
collectionVariables: result.collectionVariables,
|
||||
collectionUid
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// run response script
|
||||
const responseScript = get(request, 'script.res');
|
||||
|
@ -1,24 +1,51 @@
|
||||
const Mustache = require('mustache');
|
||||
const { each, get, forOwn } = require('lodash');
|
||||
const Handlebars = require('handlebars');
|
||||
const { each, forOwn, cloneDeep } = require('lodash');
|
||||
|
||||
// override the default escape function to prevent escaping
|
||||
Mustache.escape = function (value) {
|
||||
return value;
|
||||
const interpolateEnvVars = (str, processEnvVars) => {
|
||||
if (!str || !str.length || typeof str !== 'string') {
|
||||
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) => {
|
||||
if (!str || !str.length || typeof str !== 'string') {
|
||||
return str;
|
||||
}
|
||||
|
||||
const template = Handlebars.compile(str, { noEscape: true });
|
||||
|
||||
// collectionVariables take precedence over envVars
|
||||
const combinedVars = {
|
||||
...envVars,
|
||||
...collectionVariables
|
||||
...collectionVariables,
|
||||
process: {
|
||||
env: {
|
||||
...processEnvVars
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return Mustache.render(str, combinedVars);
|
||||
return template(combinedVars);
|
||||
};
|
||||
|
||||
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');
|
||||
};
|
||||
|
||||
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 = {
|
||||
uuid,
|
||||
stringifyJson,
|
||||
parseJson,
|
||||
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 bruToEnvJsonV2 = require('../v2/src/envToJson');
|
||||
const envJsonToBruV2 = require('../v2/src/jsonToEnv');
|
||||
const dotenvToJson = require('../v2/src/dotenvToJson');
|
||||
|
||||
module.exports = {
|
||||
bruToJson,
|
||||
@ -14,5 +15,7 @@ module.exports = {
|
||||
bruToJsonV2,
|
||||
jsonToBruV2,
|
||||
bruToEnvJsonV2,
|
||||
envJsonToBruV2
|
||||
envJsonToBruV2,
|
||||
|
||||
dotenvToJson
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user