mirror of
https://github.com/usebruno/bruno.git
synced 2024-11-21 23:43:15 +01:00
Merge branch 'main' into feature/env-secrets
This commit is contained in:
commit
2dadad3af0
@ -13,10 +13,12 @@
|
|||||||
"packages/bruno-testbench",
|
"packages/bruno-testbench",
|
||||||
"packages/bruno-graphql-docs"
|
"packages/bruno-graphql-docs"
|
||||||
],
|
],
|
||||||
|
"homepage": "https://usebruno.com",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@faker-js/faker": "^7.6.0",
|
"@faker-js/faker": "^7.6.0",
|
||||||
"@jest/globals": "^29.2.0",
|
"@jest/globals": "^29.2.0",
|
||||||
"@playwright/test": "^1.27.1",
|
"@playwright/test": "^1.27.1",
|
||||||
|
"about-window": "^1.15.2",
|
||||||
"husky": "^8.0.3",
|
"husky": "^8.0.3",
|
||||||
"jest": "^29.2.0",
|
"jest": "^29.2.0",
|
||||||
"pretty-quick": "^3.1.3",
|
"pretty-quick": "^3.1.3",
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-github-btn": "^1.4.0",
|
"react-github-btn": "^1.4.0",
|
||||||
"react-hot-toast": "^2.4.0",
|
"react-hot-toast": "^2.4.0",
|
||||||
|
"react-inspector": "^6.0.2",
|
||||||
"react-redux": "^7.2.6",
|
"react-redux": "^7.2.6",
|
||||||
"react-tooltip": "^5.5.2",
|
"react-tooltip": "^5.5.2",
|
||||||
"sass": "^1.46.0",
|
"sass": "^1.46.0",
|
||||||
|
@ -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;
|
||||||
|
|
||||||
@ -100,15 +103,11 @@ const EnvironmentVariables = ({ environment, collection }) => {
|
|||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input
|
<SingleLineEditor
|
||||||
type="text"
|
value={variable.value}
|
||||||
autoComplete="off"
|
theme={storedTheme}
|
||||||
autoCorrect="off"
|
onChange={(newValue) => handleVarChange({ target: { value: newValue } }, variable, 'value')}
|
||||||
autoCapitalize="off"
|
collection={collection}
|
||||||
spellCheck="false"
|
|
||||||
value={variable.value || ''}
|
|
||||||
className="mousetrap"
|
|
||||||
onChange={(e) => handleVarChange(e, variable, 'value')}
|
|
||||||
/>
|
/>
|
||||||
</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;
|
||||||
|
@ -13,6 +13,7 @@ import RequestNotFound from './RequestNotFound';
|
|||||||
import QueryUrl from 'components/RequestPane/QueryUrl';
|
import QueryUrl from 'components/RequestPane/QueryUrl';
|
||||||
import NetworkError from 'components/ResponsePane/NetworkError';
|
import NetworkError from 'components/ResponsePane/NetworkError';
|
||||||
import RunnerResults from 'components/RunnerResults';
|
import RunnerResults from 'components/RunnerResults';
|
||||||
|
import VariablesEditor from 'components/VariablesEditor';
|
||||||
import { DocExplorer } from '@usebruno/graphql-docs';
|
import { DocExplorer } from '@usebruno/graphql-docs';
|
||||||
|
|
||||||
import StyledWrapper from './StyledWrapper';
|
import StyledWrapper from './StyledWrapper';
|
||||||
@ -123,6 +124,10 @@ const RequestTabPanel = () => {
|
|||||||
return <RunnerResults collection={collection} />;
|
return <RunnerResults collection={collection} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (focusedTab.type === 'variables') {
|
||||||
|
return <VariablesEditor collection={collection} />;
|
||||||
|
}
|
||||||
|
|
||||||
const item = findItemInCollection(collection, activeTabUid);
|
const item = findItemInCollection(collection, activeTabUid);
|
||||||
if (!item || !item.uid) {
|
if (!item || !item.uid) {
|
||||||
return <RequestNotFound itemUid={activeTabUid} />;
|
return <RequestNotFound itemUid={activeTabUid} />;
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { IconFiles, IconRun } from '@tabler/icons';
|
import { uuid } from 'utils/common';
|
||||||
|
import { IconFiles, IconRun, IconEye } from '@tabler/icons';
|
||||||
import EnvironmentSelector from 'components/Environments/EnvironmentSelector';
|
import EnvironmentSelector from 'components/Environments/EnvironmentSelector';
|
||||||
import VariablesView from 'components/VariablesView';
|
import { addTab } from 'providers/ReduxStore/slices/tabs';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { toggleRunnerView } from 'providers/ReduxStore/slices/collections';
|
import { toggleRunnerView } from 'providers/ReduxStore/slices/collections';
|
||||||
import StyledWrapper from './StyledWrapper';
|
import StyledWrapper from './StyledWrapper';
|
||||||
@ -17,6 +18,16 @@ const CollectionToolBar = ({ collection }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const viewVariables = () => {
|
||||||
|
dispatch(
|
||||||
|
addTab({
|
||||||
|
uid: uuid(),
|
||||||
|
collectionUid: collection.uid,
|
||||||
|
type: 'variables'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledWrapper>
|
<StyledWrapper>
|
||||||
<div className="flex items-center p-2">
|
<div className="flex items-center p-2">
|
||||||
@ -28,7 +39,9 @@ const CollectionToolBar = ({ collection }) => {
|
|||||||
<span className="mr-2">
|
<span className="mr-2">
|
||||||
<IconRun className="cursor-pointer" size={20} strokeWidth={1.5} onClick={handleRun} />
|
<IconRun className="cursor-pointer" size={20} strokeWidth={1.5} onClick={handleRun} />
|
||||||
</span>
|
</span>
|
||||||
<VariablesView collection={collection} />
|
<span className="mr-3">
|
||||||
|
<IconEye className="cursor-pointer" size={18} strokeWidth={1.5} onClick={viewVariables} />
|
||||||
|
</span>
|
||||||
<EnvironmentSelector collection={collection} />
|
<EnvironmentSelector collection={collection} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { IconVariable } from '@tabler/icons';
|
||||||
|
|
||||||
|
const SpecialTab = ({ handleCloseClick, text }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex items-center tab-label pl-2">
|
||||||
|
<IconVariable size={18} strokeWidth={1.5} className="text-yellow-600" />
|
||||||
|
<span className="ml-1">{text}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex px-2 close-icon-container" onClick={(e) => handleCloseClick(e)}>
|
||||||
|
<svg focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512" className="close-icon">
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M207.6 256l107.72-107.72c6.23-6.23 6.23-16.34 0-22.58l-25.03-25.03c-6.23-6.23-16.34-6.23-22.58 0L160 208.4 52.28 100.68c-6.23-6.23-16.34-6.23-22.58 0L4.68 125.7c-6.23 6.23-6.23 16.34 0 22.58L112.4 256 4.68 363.72c-6.23 6.23-6.23 16.34 0 22.58l25.03 25.03c6.23 6.23 16.34 6.23 22.58 0L160 303.6l107.72 107.72c6.23 6.23 16.34 6.23 22.58 0l25.03-25.03c6.23-6.23 6.23-16.34 0-22.58L207.6 256z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SpecialTab;
|
@ -5,6 +5,7 @@ import { useDispatch } from 'react-redux';
|
|||||||
import { findItemInCollection } from 'utils/collections';
|
import { findItemInCollection } from 'utils/collections';
|
||||||
import StyledWrapper from './StyledWrapper';
|
import StyledWrapper from './StyledWrapper';
|
||||||
import RequestTabNotFound from './RequestTabNotFound';
|
import RequestTabNotFound from './RequestTabNotFound';
|
||||||
|
import SpecialTab from './SpecialTab';
|
||||||
|
|
||||||
const RequestTab = ({ tab, collection }) => {
|
const RequestTab = ({ tab, collection }) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@ -56,6 +57,14 @@ const RequestTab = ({ tab, collection }) => {
|
|||||||
return color;
|
return color;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (tab.type === 'variables') {
|
||||||
|
return (
|
||||||
|
<StyledWrapper className="flex items-center justify-between tab-container px-1">
|
||||||
|
<SpecialTab handleCloseClick={handleCloseClick} text="Variables" />
|
||||||
|
</StyledWrapper>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const item = findItemInCollection(collection, tab.uid);
|
const item = findItemInCollection(collection, tab.uid);
|
||||||
|
|
||||||
if (!item) {
|
if (!item) {
|
||||||
|
@ -114,7 +114,7 @@ const RequestTabs = () => {
|
|||||||
role="tab"
|
role="tab"
|
||||||
onClick={() => handleClick(tab)}
|
onClick={() => handleClick(tab)}
|
||||||
>
|
>
|
||||||
<RequestTab key={tab.uid} tab={tab} collection={activeCollection} activeTab={activeTab} />
|
<RequestTab key={tab.uid} tab={tab} collection={activeCollection} />
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
|
@ -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) {
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const StyledWrapper = styled.div`
|
||||||
|
table {
|
||||||
|
thead,
|
||||||
|
td {
|
||||||
|
border: 1px solid ${(props) => props.theme.table.border};
|
||||||
|
|
||||||
|
li {
|
||||||
|
background-color: ${(props) => props.theme.bg} !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.muted {
|
||||||
|
color: ${(props) => props.theme.colors.text.muted};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default StyledWrapper;
|
92
packages/bruno-app/src/components/VariablesEditor/index.js
Normal file
92
packages/bruno-app/src/components/VariablesEditor/index.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import get from 'lodash/get';
|
||||||
|
import filter from 'lodash/filter';
|
||||||
|
import { Inspector } from 'react-inspector';
|
||||||
|
import { useTheme } from 'providers/Theme';
|
||||||
|
import { findEnvironmentInCollection } from 'utils/collections';
|
||||||
|
import StyledWrapper from './StyledWrapper';
|
||||||
|
|
||||||
|
const KeyValueExplorer = ({ data, theme }) => {
|
||||||
|
data = data || {};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<table className="border-collapse">
|
||||||
|
<tbody>
|
||||||
|
{Object.entries(data).map(([key, value]) => (
|
||||||
|
<tr key={key}>
|
||||||
|
<td className="px-2 py-1">{key}</td>
|
||||||
|
<td className="px-2 py-1">
|
||||||
|
<Inspector data={value} theme={theme} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const EnvVariables = ({ collection, theme }) => {
|
||||||
|
const environment = findEnvironmentInCollection(collection, collection.activeEnvironmentUid);
|
||||||
|
|
||||||
|
if (!environment) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h1 className="font-semibold mt-4 mb-2">Environment Variables</h1>
|
||||||
|
<div className="muted">No environment selected</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const envVars = get(environment, 'variables', []);
|
||||||
|
const enabledEnvVars = filter(envVars, (variable) => variable.enabled);
|
||||||
|
const envVarsObj = enabledEnvVars.reduce((acc, curr) => {
|
||||||
|
acc[curr.name] = curr.value;
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex items-center mt-4 mb-2">
|
||||||
|
<h1 className="font-semibold">Environment Variables</h1>
|
||||||
|
<span className="muted ml-2">({environment.name})</span>
|
||||||
|
</div>
|
||||||
|
{enabledEnvVars.length > 0 ? (
|
||||||
|
<KeyValueExplorer data={envVarsObj} theme={theme} />
|
||||||
|
) : (
|
||||||
|
<div className="muted">No environment variables found</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CollectionVariables = ({ collection, theme }) => {
|
||||||
|
const collectionVariablesFound = Object.keys(collection.collectionVariables).length > 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h1 className="font-semibold mb-2">Collection Variables</h1>
|
||||||
|
{collectionVariablesFound ? (
|
||||||
|
<KeyValueExplorer data={collection.collectionVariables} theme={theme} />
|
||||||
|
) : (
|
||||||
|
<div className="muted">No collection variables found</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const VariablesEditor = ({ collection }) => {
|
||||||
|
const { storedTheme } = useTheme();
|
||||||
|
|
||||||
|
const reactInspectorTheme = storedTheme === 'light' ? 'chromeLight' : 'chromeDark';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledWrapper className="px-4 py-4">
|
||||||
|
<CollectionVariables collection={collection} theme={reactInspectorTheme} />
|
||||||
|
<EnvVariables collection={collection} theme={reactInspectorTheme} />
|
||||||
|
</StyledWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VariablesEditor;
|
@ -1,19 +0,0 @@
|
|||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
|
||||||
position: absolute;
|
|
||||||
min-width: fit-content;
|
|
||||||
font-size: 14px;
|
|
||||||
top: 36px;
|
|
||||||
right: 0;
|
|
||||||
white-space: nowrap;
|
|
||||||
z-index: 1000;
|
|
||||||
background-color: ${(props) => props.theme.variables.bg};
|
|
||||||
|
|
||||||
.popover {
|
|
||||||
border-radius: 2px;
|
|
||||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default Wrapper;
|
|
@ -1,26 +0,0 @@
|
|||||||
import React, { useRef } from 'react';
|
|
||||||
import StyledWrapper from './StyledWrapper';
|
|
||||||
import useOnClickOutside from 'hooks/useOnClickOutside';
|
|
||||||
|
|
||||||
const PopOver = ({ children, iconRef, handleClose }) => {
|
|
||||||
const popOverRef = useRef(null);
|
|
||||||
|
|
||||||
useOnClickOutside(popOverRef, (e) => {
|
|
||||||
if (iconRef && iconRef.current) {
|
|
||||||
if (e.target == iconRef.current || iconRef.current.contains(e.target)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handleClose();
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledWrapper>
|
|
||||||
<div className="popover" ref={popOverRef}>
|
|
||||||
<div className="popover-content">{children}</div>
|
|
||||||
</div>
|
|
||||||
</StyledWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default PopOver;
|
|
@ -1,15 +0,0 @@
|
|||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
const StyledWrapper = styled.div`
|
|
||||||
position: relative;
|
|
||||||
align-self: stretch;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.view-environment {
|
|
||||||
width: 1rem;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default StyledWrapper;
|
|
@ -1,19 +0,0 @@
|
|||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
const StyledWrapper = styled.div`
|
|
||||||
.variable-name {
|
|
||||||
color: ${(props) => props.theme.variables.name.color};
|
|
||||||
}
|
|
||||||
|
|
||||||
.variable-name {
|
|
||||||
min-width: 180px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.variable-value {
|
|
||||||
max-width: 600px;
|
|
||||||
inline-size: 600px;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export default StyledWrapper;
|
|
@ -1,53 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import forOwn from 'lodash/forOwn';
|
|
||||||
import cloneDeep from 'lodash/cloneDeep';
|
|
||||||
import { uuid } from 'utils/common';
|
|
||||||
import StyledWrapper from './StyledWrapper';
|
|
||||||
|
|
||||||
const VariablesTable = ({ variables, collectionVariables }) => {
|
|
||||||
const collectionVars = [];
|
|
||||||
|
|
||||||
forOwn(cloneDeep(collectionVariables), (value, key) => {
|
|
||||||
collectionVars.push({
|
|
||||||
uid: uuid(),
|
|
||||||
name: key,
|
|
||||||
value: value
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledWrapper>
|
|
||||||
<div className="flex flex-col w-full">
|
|
||||||
<div className="mb-2 font-medium">Environment Variables</div>
|
|
||||||
{variables && variables.length ? (
|
|
||||||
variables.map((variable) => {
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<small>No env variables found</small>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="mt-2 font-medium">Collection Variables</div>
|
|
||||||
{collectionVars && collectionVars.length ? (
|
|
||||||
collectionVars.map((variable) => {
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
})
|
|
||||||
) : (
|
|
||||||
<small>No collection variables found</small>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</StyledWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default VariablesTable;
|
|
@ -1,48 +0,0 @@
|
|||||||
import React, { useState, useRef } from 'react';
|
|
||||||
import get from 'lodash/get';
|
|
||||||
import filter from 'lodash/filter';
|
|
||||||
import { findEnvironmentInCollection } from 'utils/collections';
|
|
||||||
import VariablesTable from './VariablesTable';
|
|
||||||
import StyledWrapper from './StyledWrapper';
|
|
||||||
import PopOver from './Popover';
|
|
||||||
import { IconEye } from '@tabler/icons';
|
|
||||||
|
|
||||||
const VariablesView = ({ collection }) => {
|
|
||||||
const iconRef = useRef(null);
|
|
||||||
const [popOverOpen, setPopOverOpen] = useState(false);
|
|
||||||
|
|
||||||
const environment = findEnvironmentInCollection(collection, collection.activeEnvironmentUid);
|
|
||||||
const variables = get(environment, 'variables', []);
|
|
||||||
const enabledVariables = filter(variables, (variable) => variable.enabled);
|
|
||||||
const showVariablesTable =
|
|
||||||
enabledVariables.length > 0 ||
|
|
||||||
(collection.collectionVariables && Object.keys(collection.collectionVariables).length > 0);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledWrapper className="mr-2 server-syncstatus-icon" ref={iconRef}>
|
|
||||||
<div
|
|
||||||
className="flex p-1 items-center"
|
|
||||||
onClick={() => setPopOverOpen(true)}
|
|
||||||
onMouseEnter={() => setPopOverOpen(true)}
|
|
||||||
onMouseLeave={() => setPopOverOpen(false)}
|
|
||||||
>
|
|
||||||
<div className="cursor-pointer view-environment">
|
|
||||||
<IconEye size={18} strokeWidth={1.5} />
|
|
||||||
</div>
|
|
||||||
{popOverOpen && (
|
|
||||||
<PopOver iconRef={iconRef} handleClose={() => setPopOverOpen(false)}>
|
|
||||||
<div className="px-2 py-1">
|
|
||||||
{showVariablesTable ? (
|
|
||||||
<VariablesTable variables={enabledVariables} collectionVariables={collection.collectionVariables} />
|
|
||||||
) : (
|
|
||||||
'No variables found'
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</PopOver>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</StyledWrapper>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default VariablesView;
|
|
@ -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,
|
||||||
|
@ -19,12 +19,22 @@ export const tabsSlice = createSlice({
|
|||||||
if (alreadyExists) {
|
if (alreadyExists) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action.payload.type === 'variables') {
|
||||||
|
const tab = find(state.tabs, (t) => t.collectionUid === action.payload.collectionUid && t.type === 'variables');
|
||||||
|
if (tab) {
|
||||||
|
state.activeTabUid = tab.uid;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
state.tabs.push({
|
state.tabs.push({
|
||||||
uid: action.payload.uid,
|
uid: action.payload.uid,
|
||||||
collectionUid: action.payload.collectionUid,
|
collectionUid: action.payload.collectionUid,
|
||||||
requestPaneWidth: null,
|
requestPaneWidth: null,
|
||||||
requestPaneTab: action.payload.requestPaneTab || 'params',
|
requestPaneTab: action.payload.requestPaneTab || 'params',
|
||||||
responsePaneTab: 'response'
|
responsePaneTab: 'response',
|
||||||
|
type: action.payload.type || 'request'
|
||||||
});
|
});
|
||||||
state.activeTabUid = action.payload.uid;
|
state.activeTabUid = action.payload.uid;
|
||||||
},
|
},
|
||||||
@ -55,16 +65,22 @@ export const tabsSlice = createSlice({
|
|||||||
closeTabs: (state, action) => {
|
closeTabs: (state, action) => {
|
||||||
const activeTab = find(state.tabs, (t) => t.uid === state.activeTabUid);
|
const activeTab = find(state.tabs, (t) => t.uid === state.activeTabUid);
|
||||||
const tabUids = action.payload.tabUids || [];
|
const tabUids = action.payload.tabUids || [];
|
||||||
|
|
||||||
|
// remove the tabs from the state
|
||||||
state.tabs = filter(state.tabs, (t) => !tabUids.includes(t.uid));
|
state.tabs = filter(state.tabs, (t) => !tabUids.includes(t.uid));
|
||||||
|
|
||||||
if (activeTab && state.tabs.length) {
|
if (activeTab && state.tabs.length) {
|
||||||
const { collectionUid } = activeTab;
|
const { collectionUid } = activeTab;
|
||||||
const activeTabStillExists = find(state.tabs, (t) => t.uid === state.activeTabUid);
|
const activeTabStillExists = find(state.tabs, (t) => t.uid === state.activeTabUid);
|
||||||
|
|
||||||
|
// if the active tab no longer exists, set the active tab to the last tab in the list
|
||||||
|
// this implies that the active tab was closed
|
||||||
if (!activeTabStillExists) {
|
if (!activeTabStillExists) {
|
||||||
// attempt to load sibling tabs (based on collections) of the dead tab
|
// load sibling tabs of the current collection
|
||||||
const siblingTabs = filter(state.tabs, (t) => t.collectionUid === collectionUid);
|
const siblingTabs = filter(state.tabs, (t) => t.collectionUid === collectionUid);
|
||||||
|
|
||||||
|
// if there are sibling tabs, set the active tab to the last sibling tab
|
||||||
|
// otherwise, set the active tab to the last tab in the list
|
||||||
if (siblingTabs && siblingTabs.length) {
|
if (siblingTabs && siblingTabs.length) {
|
||||||
state.activeTabUid = last(siblingTabs).uid;
|
state.activeTabUid = last(siblingTabs).uid;
|
||||||
} else {
|
} else {
|
||||||
|
@ -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';
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"version": "v0.14.1",
|
"version": "v0.14.1",
|
||||||
"name": "bruno",
|
"name": "bruno",
|
||||||
"description": "Opensource API Client",
|
"description": "Opensource API Client for Exploring and Testing APIs",
|
||||||
"homepage": "https://www.usebruno.com",
|
"homepage": "https://www.usebruno.com",
|
||||||
"private": true,
|
"private": true,
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
@ -28,6 +28,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",
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
const { ipcMain } = require('electron');
|
const { ipcMain } = require('electron');
|
||||||
|
const openAboutWindow = require('about-window').default;
|
||||||
|
const { join } = require('path');
|
||||||
|
|
||||||
const template = [
|
const template = [
|
||||||
{
|
{
|
||||||
label: 'Collection',
|
label: 'Collection',
|
||||||
submenu: [
|
submenu: [
|
||||||
{
|
{
|
||||||
label: 'Open Local Collection',
|
label: 'Open Collection',
|
||||||
click() {
|
click() {
|
||||||
ipcMain.emit('main:open-collection');
|
ipcMain.emit('main:open-collection');
|
||||||
}
|
}
|
||||||
@ -21,7 +23,8 @@ const template = [
|
|||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{ role: 'cut' },
|
{ role: 'cut' },
|
||||||
{ role: 'copy' },
|
{ role: 'copy' },
|
||||||
{ role: 'paste' }
|
{ role: 'paste' },
|
||||||
|
{ role: 'selectAll' }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -42,7 +45,19 @@ const template = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'help',
|
role: 'help',
|
||||||
submenu: [{ label: 'Learn More' }]
|
submenu: [
|
||||||
|
{
|
||||||
|
label: 'About Bruno',
|
||||||
|
click: () =>
|
||||||
|
openAboutWindow({
|
||||||
|
product_name: 'Bruno',
|
||||||
|
icon_path: join(__dirname, '../../resources/icons/png/256x256.png'),
|
||||||
|
homepage: 'https://www.usebruno.com/',
|
||||||
|
package_json_dir: join(__dirname, '../..')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ label: 'Documentation', click: () => ipcMain.emit('main:open-docs') }
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -4,12 +4,14 @@ 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 { decryptString } = require('../utils/encryption');
|
const { decryptString } = require('../utils/encryption');
|
||||||
|
const { setDotEnvVars } = require('../store/process-env');
|
||||||
const EnvironmentSecretsStore = require('../store/env-secrets');
|
const EnvironmentSecretsStore = require('../store/env-secrets');
|
||||||
|
|
||||||
const environmentSecretsStore = new EnvironmentSecretsStore();
|
const environmentSecretsStore = new EnvironmentSecretsStore();
|
||||||
@ -21,6 +23,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');
|
||||||
@ -158,6 +167,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);
|
||||||
@ -253,6 +281,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, collectionPath);
|
return changeEnvironmentFile(win, pathname, collectionUid, collectionPath);
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ const { BrowserWindow, app, Menu } = require('electron');
|
|||||||
const { setContentSecurityPolicy } = require('electron-util');
|
const { setContentSecurityPolicy } = require('electron-util');
|
||||||
|
|
||||||
const menuTemplate = require('./app/menu-template');
|
const menuTemplate = require('./app/menu-template');
|
||||||
const LastOpenedCollections = require('./app/last-opened-collections');
|
const LastOpenedCollections = require('./store/last-opened-collections');
|
||||||
const registerNetworkIpc = require('./ipc/network');
|
const registerNetworkIpc = require('./ipc/network');
|
||||||
const registerCollectionsIpc = require('./ipc/collection');
|
const registerCollectionsIpc = require('./ipc/collection');
|
||||||
const Watcher = require('./app/watcher');
|
const Watcher = require('./app/watcher');
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const { ipcMain } = require('electron');
|
const { ipcMain, shell } = require('electron');
|
||||||
const { envJsonToBru, bruToJson, jsonToBru } = require('../bru');
|
const { envJsonToBru, bruToJson, jsonToBru } = require('../bru');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -17,7 +17,7 @@ const { stringifyJson } = require('../utils/common');
|
|||||||
const { openCollectionDialog, openCollection } = require('../app/collections');
|
const { openCollectionDialog, openCollection } = require('../app/collections');
|
||||||
const { generateUidBasedOnHash } = require('../utils/common');
|
const { generateUidBasedOnHash } = require('../utils/common');
|
||||||
const { moveRequestUid, deleteRequestUid } = require('../cache/requestUids');
|
const { moveRequestUid, deleteRequestUid } = require('../cache/requestUids');
|
||||||
const { setPreferences } = require('../app/preferences');
|
const { setPreferences } = require('../store/preferences');
|
||||||
const EnvironmentSecretsStore = require('../store/env-secrets');
|
const EnvironmentSecretsStore = require('../store/env-secrets');
|
||||||
|
|
||||||
const environmentSecretsStore = new EnvironmentSecretsStore();
|
const environmentSecretsStore = new EnvironmentSecretsStore();
|
||||||
@ -460,6 +460,11 @@ const registerMainEventHandlers = (mainWindow, watcher, lastOpenedCollections) =
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ipcMain.on('main:open-docs', () => {
|
||||||
|
const docsURL = 'https://docs.usebruno.com';
|
||||||
|
shell.openExternal(docsURL);
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.on('main:collection-opened', (win, pathname, uid) => {
|
ipcMain.on('main:collection-opened', (win, pathname, uid) => {
|
||||||
watcher.addWatcher(win, pathname, uid);
|
watcher.addWatcher(win, pathname, uid);
|
||||||
lastOpenedCollections.add(pathname);
|
lastOpenedCollections.add(pathname);
|
||||||
|
@ -12,7 +12,8 @@ const { cancelTokens, saveCancelToken, deleteCancelToken } = require('../../util
|
|||||||
const { uuid } = require('../../utils/common');
|
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('../../app/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,6 +130,7 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
collectionPath
|
collectionPath
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
mainWindow.webContents.send('main:script-environment-update', {
|
mainWindow.webContents.send('main:script-environment-update', {
|
||||||
envVariables: result.envVariables,
|
envVariables: result.envVariables,
|
||||||
collectionVariables: result.collectionVariables,
|
collectionVariables: result.collectionVariables,
|
||||||
@ -136,6 +138,7 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
collectionUid
|
collectionUid
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// run pre-request script
|
// run pre-request script
|
||||||
const requestScript = get(request, 'script.req');
|
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
|
// 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,6 +227,7 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
collectionPath
|
collectionPath
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
mainWindow.webContents.send('main:script-environment-update', {
|
mainWindow.webContents.send('main:script-environment-update', {
|
||||||
envVariables: result.envVariables,
|
envVariables: result.envVariables,
|
||||||
collectionVariables: result.collectionVariables,
|
collectionVariables: result.collectionVariables,
|
||||||
@ -229,6 +235,7 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
collectionUid
|
collectionUid
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// run post-response script
|
// run post-response script
|
||||||
const responseScript = get(request, 'script.res');
|
const responseScript = get(request, 'script.res');
|
||||||
@ -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,12 +610,14 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
collectionPath
|
collectionPath
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
mainWindow.webContents.send('main:script-environment-update', {
|
mainWindow.webContents.send('main:script-environment-update', {
|
||||||
envVariables: result.envVariables,
|
envVariables: result.envVariables,
|
||||||
collectionVariables: result.collectionVariables,
|
collectionVariables: result.collectionVariables,
|
||||||
collectionUid
|
collectionUid
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// run response script
|
// run response script
|
||||||
const responseScript = get(request, 'script.res');
|
const responseScript = get(request, 'script.res');
|
||||||
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
@ -10,6 +10,7 @@ const punycode = require('punycode');
|
|||||||
const Bru = require('../bru');
|
const Bru = require('../bru');
|
||||||
const BrunoRequest = require('../bruno-request');
|
const BrunoRequest = require('../bruno-request');
|
||||||
const BrunoResponse = require('../bruno-response');
|
const BrunoResponse = require('../bruno-response');
|
||||||
|
const { cleanJson } = require('../utils');
|
||||||
|
|
||||||
// Inbuilt Library Support
|
// Inbuilt Library Support
|
||||||
const atob = require('atob');
|
const atob = require('atob');
|
||||||
@ -37,7 +38,7 @@ class ScriptRuntime {
|
|||||||
if (onConsoleLog && typeof onConsoleLog === 'function') {
|
if (onConsoleLog && typeof onConsoleLog === 'function') {
|
||||||
const customLogger = (type) => {
|
const customLogger = (type) => {
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
onConsoleLog(type, args);
|
onConsoleLog(type, cleanJson(args));
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
context.console = {
|
context.console = {
|
||||||
@ -81,8 +82,8 @@ class ScriptRuntime {
|
|||||||
await asyncVM();
|
await asyncVM();
|
||||||
return {
|
return {
|
||||||
request,
|
request,
|
||||||
envVariables,
|
envVariables: cleanJson(envVariables),
|
||||||
collectionVariables
|
collectionVariables: cleanJson(collectionVariables)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,7 +101,7 @@ class ScriptRuntime {
|
|||||||
if (onConsoleLog && typeof onConsoleLog === 'function') {
|
if (onConsoleLog && typeof onConsoleLog === 'function') {
|
||||||
const customLogger = (type) => {
|
const customLogger = (type) => {
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
onConsoleLog(type, args);
|
onConsoleLog(type, cleanJson(args));
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
context.console = {
|
context.console = {
|
||||||
@ -136,8 +137,8 @@ class ScriptRuntime {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
response,
|
response,
|
||||||
envVariables,
|
envVariables: cleanJson(envVariables),
|
||||||
collectionVariables
|
collectionVariables: cleanJson(collectionVariables)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ const BrunoRequest = require('../bruno-request');
|
|||||||
const BrunoResponse = require('../bruno-response');
|
const BrunoResponse = require('../bruno-response');
|
||||||
const Test = require('../test');
|
const Test = require('../test');
|
||||||
const TestResults = require('../test-results');
|
const TestResults = require('../test-results');
|
||||||
|
const { cleanJson } = require('../utils');
|
||||||
|
|
||||||
// Inbuilt Library Support
|
// Inbuilt Library Support
|
||||||
const atob = require('atob');
|
const atob = require('atob');
|
||||||
@ -49,7 +50,7 @@ class TestRuntime {
|
|||||||
if (onConsoleLog && typeof onConsoleLog === 'function') {
|
if (onConsoleLog && typeof onConsoleLog === 'function') {
|
||||||
const customLogger = (type) => {
|
const customLogger = (type) => {
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
onConsoleLog(type, args);
|
onConsoleLog(type, cleanJson(args));
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
context.console = {
|
context.console = {
|
||||||
@ -82,9 +83,9 @@ class TestRuntime {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
request,
|
request,
|
||||||
envVariables,
|
envVariables: cleanJson(envVariables),
|
||||||
collectionVariables,
|
collectionVariables: cleanJson(collectionVariables),
|
||||||
results: __brunoTestResults.getResults()
|
results: cleanJson(__brunoTestResults.getResults())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,9 +118,28 @@ const createResponseParser = (response = {}) => {
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Objects that are created inside vm2 execution context result in an serilaization error when sent to the renderer process
|
||||||
|
* Error sending from webFrameMain: Error: Failed to serialize arguments
|
||||||
|
* at s.send (node:electron/js2c/browser_init:169:631)
|
||||||
|
* at g.send (node:electron/js2c/browser_init:165:2156)
|
||||||
|
* How to reproduce
|
||||||
|
* Remove the cleanJson fix and execute the below post response script
|
||||||
|
* bru.setVar("a", {b:3});
|
||||||
|
* Todo: Find a better fix
|
||||||
|
*/
|
||||||
|
const cleanJson = (data) => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(JSON.stringify(data));
|
||||||
|
} catch (e) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
evaluateJsExpression,
|
evaluateJsExpression,
|
||||||
evaluateJsTemplateLiteral,
|
evaluateJsTemplateLiteral,
|
||||||
createResponseParser,
|
createResponseParser,
|
||||||
internalExpressionCache
|
internalExpressionCache,
|
||||||
|
cleanJson
|
||||||
};
|
};
|
||||||
|
@ -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