mirror of
https://github.com/usebruno/bruno.git
synced 2024-12-23 07:09:01 +01:00
Merge branch 'main' into fix/remove-jsonbigint-from-cli
This commit is contained in:
commit
6b6fc9a3dc
7653
package-lock.json
generated
7653
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"presets": ["next/babel"],
|
"presets": ["@babel/preset-env"],
|
||||||
"plugins": [["styled-components", { "ssr": true }]]
|
"plugins": [["styled-components", { "ssr": true }]]
|
||||||
}
|
}
|
2
packages/bruno-app/.gitignore
vendored
2
packages/bruno-app/.gitignore
vendored
@ -31,6 +31,6 @@ yarn-error.log*
|
|||||||
|
|
||||||
# next.js
|
# next.js
|
||||||
.next/
|
.next/
|
||||||
out/
|
dist/
|
||||||
|
|
||||||
.env
|
.env
|
@ -1,22 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
output: 'export',
|
|
||||||
reactStrictMode: false,
|
|
||||||
publicRuntimeConfig: {
|
|
||||||
CI: process.env.CI,
|
|
||||||
PLAYWRIGHT: process.env.PLAYWRIGHT,
|
|
||||||
ENV: process.env.ENV
|
|
||||||
},
|
|
||||||
webpack: (config, { isServer }) => {
|
|
||||||
// Fixes npm packages that depend on `fs` module
|
|
||||||
if (!isServer) {
|
|
||||||
config.resolve.fallback.fs = false;
|
|
||||||
}
|
|
||||||
Object.defineProperty(config, 'devtool', {
|
|
||||||
get() {
|
|
||||||
return 'source-map';
|
|
||||||
},
|
|
||||||
set() {},
|
|
||||||
});
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
};
|
|
@ -3,15 +3,15 @@
|
|||||||
"version": "0.3.0",
|
"version": "0.3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cross-env ENV=dev next dev -p 3000",
|
"dev": "rsbuild dev",
|
||||||
"build": "next build",
|
"build": "rsbuild build -m production",
|
||||||
"start": "next start",
|
"preview": "rsbuild preview",
|
||||||
"lint": "next lint",
|
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:prettier": "prettier --check \"./src/**/*.{js,jsx,json,ts,tsx}\"",
|
"test:prettier": "prettier --check \"./src/**/*.{js,jsx,json,ts,tsx}\"",
|
||||||
"prettier": "prettier --write \"./src/**/*.{js,jsx,json,ts,tsx}\""
|
"prettier": "prettier --write \"./src/**/*.{js,jsx,json,ts,tsx}\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@babel/preset-env": "^7.26.0",
|
||||||
"@fontsource/inter": "^5.0.15",
|
"@fontsource/inter": "^5.0.15",
|
||||||
"@prantlf/jsonlint": "^16.0.0",
|
"@prantlf/jsonlint": "^16.0.0",
|
||||||
"@reduxjs/toolkit": "^1.8.0",
|
"@reduxjs/toolkit": "^1.8.0",
|
||||||
@ -49,7 +49,6 @@
|
|||||||
"markdown-it-replace-link": "^1.2.0",
|
"markdown-it-replace-link": "^1.2.0",
|
||||||
"mousetrap": "^1.6.5",
|
"mousetrap": "^1.6.5",
|
||||||
"nanoid": "3.3.4",
|
"nanoid": "3.3.4",
|
||||||
"next": "14.2.16",
|
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"pdfjs-dist": "4.4.168",
|
"pdfjs-dist": "4.4.168",
|
||||||
"platform": "^1.3.6",
|
"platform": "^1.3.6",
|
||||||
@ -57,17 +56,17 @@
|
|||||||
"prettier": "^2.7.1",
|
"prettier": "^2.7.1",
|
||||||
"qs": "^6.11.0",
|
"qs": "^6.11.0",
|
||||||
"query-string": "^7.0.1",
|
"query-string": "^7.0.1",
|
||||||
"react": "18.2.0",
|
"react": "19.0.0",
|
||||||
"react-copy-to-clipboard": "^5.1.0",
|
"react-copy-to-clipboard": "^5.1.0",
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
"react-dnd-html5-backend": "^16.0.1",
|
"react-dnd-html5-backend": "^16.0.1",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "19.0.0",
|
||||||
"react-hot-toast": "^2.4.0",
|
"react-hot-toast": "^2.4.0",
|
||||||
"react-i18next": "^15.0.1",
|
"react-i18next": "^15.0.1",
|
||||||
"react-inspector": "^6.0.2",
|
"react-inspector": "^6.0.2",
|
||||||
"react-pdf": "9.1.1",
|
"react-pdf": "9.1.1",
|
||||||
"react-player": "^2.16.0",
|
"react-player": "^2.16.0",
|
||||||
"react-redux": "^7.2.6",
|
"react-redux": "^7.2.9",
|
||||||
"react-tooltip": "^5.5.2",
|
"react-tooltip": "^5.5.2",
|
||||||
"sass": "^1.46.0",
|
"sass": "^1.46.0",
|
||||||
"strip-json-comments": "^5.0.1",
|
"strip-json-comments": "^5.0.1",
|
||||||
@ -79,13 +78,14 @@
|
|||||||
"yup": "^0.32.11"
|
"yup": "^0.32.11"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.16.0",
|
"@rsbuild/core": "^1.1.2",
|
||||||
"@babel/plugin-transform-spread": "^7.16.7",
|
"@rsbuild/plugin-babel": "^1.0.3",
|
||||||
"@babel/preset-env": "^7.16.4",
|
"@rsbuild/plugin-node-polyfill": "^1.2.0",
|
||||||
"@babel/preset-react": "^7.16.0",
|
"@rsbuild/plugin-react": "^1.0.7",
|
||||||
"@babel/runtime": "^7.16.3",
|
"@rsbuild/plugin-sass": "^1.1.0",
|
||||||
|
"@rsbuild/plugin-styled-components": "1.1.0",
|
||||||
"autoprefixer": "10.4.20",
|
"autoprefixer": "10.4.20",
|
||||||
"babel-loader": "^8.2.3",
|
"babel-plugin-react-compiler": "19.0.0-beta-a7bf2bd-20241110",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"css-loader": "7.1.2",
|
"css-loader": "7.1.2",
|
||||||
"file-loader": "^6.2.0",
|
"file-loader": "^6.2.0",
|
||||||
|
27
packages/bruno-app/rsbuild.config.mjs
Normal file
27
packages/bruno-app/rsbuild.config.mjs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { defineConfig } from '@rsbuild/core';
|
||||||
|
import { pluginReact } from '@rsbuild/plugin-react';
|
||||||
|
import { pluginBabel } from '@rsbuild/plugin-babel';
|
||||||
|
import { pluginStyledComponents } from '@rsbuild/plugin-styled-components';
|
||||||
|
import { pluginSass } from '@rsbuild/plugin-sass';
|
||||||
|
import { pluginNodePolyfill } from '@rsbuild/plugin-node-polyfill'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [
|
||||||
|
pluginNodePolyfill(),
|
||||||
|
pluginReact(),
|
||||||
|
pluginStyledComponents(),
|
||||||
|
pluginSass(),
|
||||||
|
pluginBabel({
|
||||||
|
include: /\.(?:js|jsx|tsx)$/,
|
||||||
|
babelLoaderOptions(opts) {
|
||||||
|
opts.plugins?.unshift('babel-plugin-react-compiler');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
source: {
|
||||||
|
tsconfigPath: './jsconfig.json', // Specifies the path to the JavaScript/TypeScript configuration file
|
||||||
|
},
|
||||||
|
html: {
|
||||||
|
title: 'Bruno'
|
||||||
|
},
|
||||||
|
});
|
@ -11,7 +11,6 @@ const CreateEnvironment = ({ collection, onClose }) => {
|
|||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const inputRef = useRef();
|
const inputRef = useRef();
|
||||||
|
|
||||||
// todo: Add this to global env too.
|
|
||||||
const validateEnvironmentName = (name) => {
|
const validateEnvironmentName = (name) => {
|
||||||
return !collection?.environments?.some((env) => env?.name?.toLowerCase().trim() === name?.toLowerCase().trim());
|
return !collection?.environments?.some((env) => env?.name?.toLowerCase().trim() === name?.toLowerCase().trim());
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const StyledWrapper = styled.div`
|
||||||
|
.editing-mode {
|
||||||
|
cursor: pointer;
|
||||||
|
color: ${(props) => props.theme.colors.text.yellow};
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default StyledWrapper;
|
@ -0,0 +1,63 @@
|
|||||||
|
import 'github-markdown-css/github-markdown.css';
|
||||||
|
import get from 'lodash/get';
|
||||||
|
import { updateFolderDocs } from 'providers/ReduxStore/slices/collections';
|
||||||
|
import { useTheme } from 'providers/Theme';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { saveFolderRoot } from 'providers/ReduxStore/slices/collections/actions';
|
||||||
|
import Markdown from 'components/MarkDown';
|
||||||
|
import CodeEditor from 'components/CodeEditor';
|
||||||
|
import StyledWrapper from './StyledWrapper';
|
||||||
|
|
||||||
|
const Documentation = ({ collection, folder }) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const { displayedTheme } = useTheme();
|
||||||
|
const preferences = useSelector((state) => state.app.preferences);
|
||||||
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
const docs = get(folder, 'root.docs', '');
|
||||||
|
|
||||||
|
const toggleViewMode = () => {
|
||||||
|
setIsEditing((prev) => !prev);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onEdit = (value) => {
|
||||||
|
dispatch(
|
||||||
|
updateFolderDocs({
|
||||||
|
folderUid: folder.uid,
|
||||||
|
collectionUid: collection.uid,
|
||||||
|
docs: value
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSave = () => dispatch(saveFolderRoot(collection.uid, folder.uid));
|
||||||
|
|
||||||
|
if (!folder) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledWrapper className="flex flex-col gap-y-1 h-full w-full relative">
|
||||||
|
<div className="editing-mode" role="tab" onClick={toggleViewMode}>
|
||||||
|
{isEditing ? 'Preview' : 'Edit'}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isEditing ? (
|
||||||
|
<CodeEditor
|
||||||
|
collection={collection}
|
||||||
|
theme={displayedTheme}
|
||||||
|
font={get(preferences, 'font.codeFont', 'default')}
|
||||||
|
fontSize={get(preferences, 'font.codeFontSize')}
|
||||||
|
value={docs || ''}
|
||||||
|
onEdit={onEdit}
|
||||||
|
onSave={onSave}
|
||||||
|
mode="application/text"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Markdown collectionPath={collection.pathname} onDoubleClick={toggleViewMode} content={docs} />
|
||||||
|
)}
|
||||||
|
</StyledWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Documentation;
|
@ -7,6 +7,7 @@ import Script from './Script';
|
|||||||
import Tests from './Tests';
|
import Tests from './Tests';
|
||||||
import StyledWrapper from './StyledWrapper';
|
import StyledWrapper from './StyledWrapper';
|
||||||
import Vars from './Vars';
|
import Vars from './Vars';
|
||||||
|
import Documentation from './Documentation';
|
||||||
import DotIcon from 'components/Icons/Dot';
|
import DotIcon from 'components/Icons/Dot';
|
||||||
|
|
||||||
const ContentIndicator = () => {
|
const ContentIndicator = () => {
|
||||||
@ -60,6 +61,9 @@ const FolderSettings = ({ collection, folder }) => {
|
|||||||
case 'vars': {
|
case 'vars': {
|
||||||
return <Vars collection={collection} folder={folder} />;
|
return <Vars collection={collection} folder={folder} />;
|
||||||
}
|
}
|
||||||
|
case 'docs': {
|
||||||
|
return <Documentation collection={collection} folder={folder} />;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -89,6 +93,9 @@ const FolderSettings = ({ collection, folder }) => {
|
|||||||
Vars
|
Vars
|
||||||
{activeVarsCount > 0 && <sup className="ml-1 font-medium">{activeVarsCount}</sup>}
|
{activeVarsCount > 0 && <sup className="ml-1 font-medium">{activeVarsCount}</sup>}
|
||||||
</div>
|
</div>
|
||||||
|
<div className={getTabClassname('docs')} role="tab" onClick={() => setTab('docs')}>
|
||||||
|
Docs
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<section className={`flex mt-4 h-full`}>{getTabPanel(tab)}</section>
|
<section className={`flex mt-4 h-full`}>{getTabPanel(tab)}</section>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,12 +2,19 @@ import React, { useEffect, useRef } from 'react';
|
|||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import Portal from 'components/Portal';
|
import Portal from 'components/Portal';
|
||||||
import Modal from 'components/Modal';
|
import Modal from 'components/Modal';
|
||||||
import { addGlobalEnvironment } from 'providers/ReduxStore/slices/global-environments';
|
import { addGlobalEnvironment } from 'providers/ReduxStore/slices/global-environments';
|
||||||
|
|
||||||
const CreateEnvironment = ({ onClose }) => {
|
const CreateEnvironment = ({ onClose }) => {
|
||||||
|
const globalEnvs = useSelector((state) => state?.globalEnvironments?.globalEnvironments);
|
||||||
|
|
||||||
|
const validateEnvironmentName = (name) => {
|
||||||
|
const trimmedName = name?.toLowerCase().trim();
|
||||||
|
return globalEnvs.every((env) => env?.name?.toLowerCase().trim() !== trimmedName);
|
||||||
|
};
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const inputRef = useRef();
|
const inputRef = useRef();
|
||||||
const formik = useFormik({
|
const formik = useFormik({
|
||||||
@ -17,9 +24,10 @@ const CreateEnvironment = ({ onClose }) => {
|
|||||||
},
|
},
|
||||||
validationSchema: Yup.object({
|
validationSchema: Yup.object({
|
||||||
name: Yup.string()
|
name: Yup.string()
|
||||||
.min(1, 'must be at least 1 character')
|
.min(1, 'Must be at least 1 character')
|
||||||
.max(50, 'must be 50 characters or less')
|
.max(50, 'Must be 50 characters or less')
|
||||||
.required('name is required')
|
.required('Name is required')
|
||||||
|
.test('duplicate-name', 'Global Environment already exists', validateEnvironmentName)
|
||||||
}),
|
}),
|
||||||
onSubmit: (values) => {
|
onSubmit: (values) => {
|
||||||
dispatch(addGlobalEnvironment({ name: values.name }))
|
dispatch(addGlobalEnvironment({ name: values.name }))
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
|
|
||||||
function Portal({ children, wrapperId }) {
|
function Portal({ children }) {
|
||||||
wrapperId = wrapperId || 'bruno-app-body';
|
return createPortal(children, document.body);
|
||||||
|
|
||||||
return createPortal(children, document.getElementById(wrapperId));
|
|
||||||
}
|
}
|
||||||
export default Portal;
|
export default Portal;
|
||||||
|
@ -20,6 +20,7 @@ import React from 'react';
|
|||||||
* endsWith : ends with
|
* endsWith : ends with
|
||||||
* between : between
|
* between : between
|
||||||
* isEmpty : is empty
|
* isEmpty : is empty
|
||||||
|
* isNotEmpty : is not empty
|
||||||
* isNull : is null
|
* isNull : is null
|
||||||
* isUndefined : is undefined
|
* isUndefined : is undefined
|
||||||
* isDefined : is defined
|
* isDefined : is defined
|
||||||
@ -51,6 +52,7 @@ const AssertionOperator = ({ operator, onChange }) => {
|
|||||||
'endsWith',
|
'endsWith',
|
||||||
'between',
|
'between',
|
||||||
'isEmpty',
|
'isEmpty',
|
||||||
|
'isNotEmpty',
|
||||||
'isNull',
|
'isNull',
|
||||||
'isUndefined',
|
'isUndefined',
|
||||||
'isDefined',
|
'isDefined',
|
||||||
|
@ -24,6 +24,7 @@ import { useTheme } from 'providers/Theme';
|
|||||||
* endsWith : ends with
|
* endsWith : ends with
|
||||||
* between : between
|
* between : between
|
||||||
* isEmpty : is empty
|
* isEmpty : is empty
|
||||||
|
* isNotEmpty : is not empty
|
||||||
* isNull : is null
|
* isNull : is null
|
||||||
* isUndefined : is undefined
|
* isUndefined : is undefined
|
||||||
* isDefined : is defined
|
* isDefined : is defined
|
||||||
@ -61,6 +62,7 @@ const parseAssertionOperator = (str = '') => {
|
|||||||
'endsWith',
|
'endsWith',
|
||||||
'between',
|
'between',
|
||||||
'isEmpty',
|
'isEmpty',
|
||||||
|
'isNotEmpty',
|
||||||
'isNull',
|
'isNull',
|
||||||
'isUndefined',
|
'isUndefined',
|
||||||
'isDefined',
|
'isDefined',
|
||||||
@ -75,6 +77,7 @@ const parseAssertionOperator = (str = '') => {
|
|||||||
|
|
||||||
const unaryOperators = [
|
const unaryOperators = [
|
||||||
'isEmpty',
|
'isEmpty',
|
||||||
|
'isNotEmpty',
|
||||||
'isNull',
|
'isNull',
|
||||||
'isUndefined',
|
'isUndefined',
|
||||||
'isDefined',
|
'isDefined',
|
||||||
@ -113,6 +116,7 @@ const parseAssertionOperator = (str = '') => {
|
|||||||
const isUnaryOperator = (operator) => {
|
const isUnaryOperator = (operator) => {
|
||||||
const unaryOperators = [
|
const unaryOperators = [
|
||||||
'isEmpty',
|
'isEmpty',
|
||||||
|
'isNotEmpty',
|
||||||
'isNull',
|
'isNull',
|
||||||
'isUndefined',
|
'isUndefined',
|
||||||
'isDefined',
|
'isDefined',
|
||||||
|
@ -70,7 +70,7 @@ const QueryUrl = ({ item, collection, handleRun }) => {
|
|||||||
|
|
||||||
const handleGenerateCode = (e) => {
|
const handleGenerateCode = (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (item.request.url !== '' || (item.draft?.request.url !== undefined && item.draft?.request.url !== '')) {
|
if (item?.request?.url !== '' || (item.draft?.request?.url !== undefined && item.draft?.request?.url !== '')) {
|
||||||
setGenerateCodeItemModalOpen(true);
|
setGenerateCodeItemModalOpen(true);
|
||||||
} else {
|
} else {
|
||||||
toast.error('URL is required');
|
toast.error('URL is required');
|
||||||
|
@ -20,7 +20,7 @@ import { DocExplorer } from '@usebruno/graphql-docs';
|
|||||||
import StyledWrapper from './StyledWrapper';
|
import StyledWrapper from './StyledWrapper';
|
||||||
import SecuritySettings from 'components/SecuritySettings';
|
import SecuritySettings from 'components/SecuritySettings';
|
||||||
import FolderSettings from 'components/FolderSettings';
|
import FolderSettings from 'components/FolderSettings';
|
||||||
import { getGlobalEnvironmentVariables } from 'utils/collections/index';
|
import { getGlobalEnvironmentVariables, getGlobalEnvironmentVariablesMasked } from 'utils/collections/index';
|
||||||
import { produce } from 'immer';
|
import { produce } from 'immer';
|
||||||
|
|
||||||
const MIN_LEFT_PANE_WIDTH = 300;
|
const MIN_LEFT_PANE_WIDTH = 300;
|
||||||
@ -45,7 +45,9 @@ const RequestTabPanel = () => {
|
|||||||
if (collection) {
|
if (collection) {
|
||||||
// add selected global env variables to the collection object
|
// add selected global env variables to the collection object
|
||||||
const globalEnvironmentVariables = getGlobalEnvironmentVariables({ globalEnvironments, activeGlobalEnvironmentUid });
|
const globalEnvironmentVariables = getGlobalEnvironmentVariables({ globalEnvironments, activeGlobalEnvironmentUid });
|
||||||
|
const globalEnvSecrets = getGlobalEnvironmentVariablesMasked({ globalEnvironments, activeGlobalEnvironmentUid });
|
||||||
collection.globalEnvironmentVariables = globalEnvironmentVariables;
|
collection.globalEnvironmentVariables = globalEnvironmentVariables;
|
||||||
|
collection.globalEnvSecrets = globalEnvSecrets;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -12,10 +12,15 @@ const DeleteCollectionItem = ({ onClose, item, collection }) => {
|
|||||||
const isFolder = isItemAFolder(item);
|
const isFolder = isItemAFolder(item);
|
||||||
const onConfirm = () => {
|
const onConfirm = () => {
|
||||||
dispatch(deleteItem(item.uid, collection.uid)).then(() => {
|
dispatch(deleteItem(item.uid, collection.uid)).then(() => {
|
||||||
|
|
||||||
if (isFolder) {
|
if (isFolder) {
|
||||||
|
// close all tabs that belong to the folder
|
||||||
|
// including the folder itself and its children
|
||||||
|
const tabUids = [...recursivelyGetAllItemUids(item.items), item.uid]
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
closeTabs({
|
closeTabs({
|
||||||
tabUids: recursivelyGetAllItemUids(item.items)
|
tabUids: tabUids
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -6,6 +6,7 @@ import { useDispatch } from 'react-redux';
|
|||||||
import { isItemAFolder } from 'utils/tabs';
|
import { isItemAFolder } from 'utils/tabs';
|
||||||
import { renameItem, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
import { renameItem, saveRequest } from 'providers/ReduxStore/slices/collections/actions';
|
||||||
import toast from 'react-hot-toast';
|
import toast from 'react-hot-toast';
|
||||||
|
import { closeTabs } from 'providers/ReduxStore/slices/tabs';
|
||||||
|
|
||||||
const RenameCollectionItem = ({ collection, item, onClose }) => {
|
const RenameCollectionItem = ({ collection, item, onClose }) => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
@ -33,7 +34,8 @@ const RenameCollectionItem = ({ collection, item, onClose }) => {
|
|||||||
}
|
}
|
||||||
dispatch(renameItem(values.name, item.uid, collection.uid))
|
dispatch(renameItem(values.name, item.uid, collection.uid))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
toast.success('Request renamed');
|
dispatch(closeTabs({ tabUids: [item.uid] }));
|
||||||
|
toast.success(isFolder ? 'Folder renamed' : 'Request renamed');
|
||||||
onClose();
|
onClose();
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
@ -183,7 +183,7 @@ const CollectionItem = ({ item, collection, searchText }) => {
|
|||||||
const handleGenerateCode = (e) => {
|
const handleGenerateCode = (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
dropdownTippyRef.current.hide();
|
dropdownTippyRef.current.hide();
|
||||||
if (item.request.url !== '' || (item.draft?.request.url !== undefined && item.draft?.request.url !== '')) {
|
if (item?.request?.url !== '' || (item?.draft?.request?.url !== undefined && item?.draft?.request?.url !== '')) {
|
||||||
setGenerateCodeItemModalOpen(true);
|
setGenerateCodeItemModalOpen(true);
|
||||||
} else {
|
} else {
|
||||||
toast.error('URL is required');
|
toast.error('URL is required');
|
||||||
|
@ -63,16 +63,16 @@ const Table = ({ minColumnWidth = 1, headers = [], children }) => {
|
|||||||
[activeColumnIndex, columns, minColumnWidth]
|
[activeColumnIndex, columns, minColumnWidth]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleMouseUp = useCallback(() => {
|
|
||||||
setActiveColumnIndex(null);
|
|
||||||
removeListeners();
|
|
||||||
}, [removeListeners]);
|
|
||||||
|
|
||||||
const removeListeners = useCallback(() => {
|
const removeListeners = useCallback(() => {
|
||||||
window.removeEventListener('mousemove', handleMouseMove);
|
window.removeEventListener('mousemove', handleMouseMove);
|
||||||
window.removeEventListener('mouseup', removeListeners);
|
window.removeEventListener('mouseup', removeListeners);
|
||||||
}, [handleMouseMove]);
|
}, [handleMouseMove]);
|
||||||
|
|
||||||
|
const handleMouseUp = useCallback(() => {
|
||||||
|
setActiveColumnIndex(null);
|
||||||
|
removeListeners?.();
|
||||||
|
}, [removeListeners]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (activeColumnIndex !== null) {
|
if (activeColumnIndex !== null) {
|
||||||
window.addEventListener('mousemove', handleMouseMove);
|
window.addEventListener('mousemove', handleMouseMove);
|
||||||
|
14
packages/bruno-app/src/index.js
Normal file
14
packages/bruno-app/src/index.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom/client';
|
||||||
|
import App from './pages/index';
|
||||||
|
|
||||||
|
const rootElement = document.getElementById('root');
|
||||||
|
|
||||||
|
if (rootElement) {
|
||||||
|
const root = ReactDOM.createRoot(rootElement);
|
||||||
|
root.render(
|
||||||
|
<React.StrictMode>
|
||||||
|
<App />
|
||||||
|
</React.StrictMode>
|
||||||
|
);
|
||||||
|
}
|
@ -25,31 +25,7 @@ import '@fontsource/inter/900.css';
|
|||||||
import { setupPolyfills } from 'utils/common/setupPolyfills';
|
import { setupPolyfills } from 'utils/common/setupPolyfills';
|
||||||
setupPolyfills();
|
setupPolyfills();
|
||||||
|
|
||||||
function SafeHydrate({ children }) {
|
function Main({ children }) {
|
||||||
return <div suppressHydrationWarning>{typeof window === 'undefined' ? null : children}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function NoSsr({ children }) {
|
|
||||||
const SERVER_RENDERED = typeof window === 'undefined';
|
|
||||||
|
|
||||||
if (SERVER_RENDERED) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>{children}</>;
|
|
||||||
}
|
|
||||||
|
|
||||||
function MyApp({ Component, pageProps }) {
|
|
||||||
const [domLoaded, setDomLoaded] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setDomLoaded(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (!domLoaded) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!window.ipcRenderer) {
|
if (!window.ipcRenderer) {
|
||||||
return (
|
return (
|
||||||
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 mx-10 my-10 rounded relative" role="alert">
|
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 mx-10 my-10 rounded relative" role="alert">
|
||||||
@ -65,23 +41,21 @@ function MyApp({ Component, pageProps }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<SafeHydrate>
|
|
||||||
<NoSsr>
|
|
||||||
<Provider store={ReduxStore}>
|
<Provider store={ReduxStore}>
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<ToastProvider>
|
<ToastProvider>
|
||||||
<AppProvider>
|
<AppProvider>
|
||||||
<HotkeysProvider>
|
<HotkeysProvider>
|
||||||
<Component {...pageProps} />
|
{children}
|
||||||
</HotkeysProvider>
|
</HotkeysProvider>
|
||||||
</AppProvider>
|
</AppProvider>
|
||||||
</ToastProvider>
|
</ToastProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</Provider>
|
</Provider>
|
||||||
</NoSsr>
|
|
||||||
</SafeHydrate>
|
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MyApp;
|
export default Main;
|
||||||
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
|||||||
import Document, { Html, Head, Main, NextScript } from 'next/document';
|
|
||||||
import { ServerStyleSheet } from 'styled-components';
|
|
||||||
|
|
||||||
export default class MyDocument extends Document {
|
|
||||||
static async getInitialProps(ctx) {
|
|
||||||
const sheet = new ServerStyleSheet();
|
|
||||||
const originalRenderPage = ctx.renderPage;
|
|
||||||
|
|
||||||
try {
|
|
||||||
ctx.renderPage = () =>
|
|
||||||
originalRenderPage({
|
|
||||||
enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />)
|
|
||||||
});
|
|
||||||
|
|
||||||
const initialProps = await Document.getInitialProps(ctx);
|
|
||||||
return {
|
|
||||||
...initialProps,
|
|
||||||
styles: (
|
|
||||||
<>
|
|
||||||
{initialProps.styles}
|
|
||||||
{sheet.getStyleElement()}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
};
|
|
||||||
} finally {
|
|
||||||
sheet.seal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Html>
|
|
||||||
<Head />
|
|
||||||
<body id="bruno-app-body">
|
|
||||||
<Main />
|
|
||||||
<NextScript />
|
|
||||||
</body>
|
|
||||||
</Html>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +1,16 @@
|
|||||||
import Head from 'next/head';
|
|
||||||
import Bruno from './Bruno';
|
import Bruno from './Bruno';
|
||||||
import GlobalStyle from '../globalStyles';
|
import GlobalStyle from '../globalStyles';
|
||||||
import '../i18n';
|
import '../i18n';
|
||||||
|
import Main from './Main';
|
||||||
|
|
||||||
export default function Home() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Head>
|
|
||||||
<title>Bruno</title>
|
|
||||||
<link rel="icon" href="/favicon.ico" />
|
|
||||||
</Head>
|
|
||||||
|
|
||||||
<GlobalStyle />
|
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
|
<Main>
|
||||||
|
<GlobalStyle />
|
||||||
<Bruno />
|
<Bruno />
|
||||||
|
</Main>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -7,21 +7,19 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import getConfig from 'next/config';
|
|
||||||
import { PostHog } from 'posthog-node';
|
import { PostHog } from 'posthog-node';
|
||||||
import platformLib from 'platform';
|
import platformLib from 'platform';
|
||||||
import { uuid } from 'utils/common';
|
import { uuid } from 'utils/common';
|
||||||
|
|
||||||
const { publicRuntimeConfig } = getConfig();
|
|
||||||
const posthogApiKey = process.env.NEXT_PUBLIC_POSTHOG_API_KEY;
|
const posthogApiKey = process.env.NEXT_PUBLIC_POSTHOG_API_KEY;
|
||||||
let posthogClient = null;
|
let posthogClient = null;
|
||||||
|
|
||||||
const isPlaywrightTestRunning = () => {
|
const isPlaywrightTestRunning = () => {
|
||||||
return publicRuntimeConfig.PLAYWRIGHT ? true : false;
|
return process.env.PLAYWRIGHT ? true : false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const isDevEnv = () => {
|
const isDevEnv = () => {
|
||||||
return publicRuntimeConfig.ENV === 'dev';
|
return import.meta.env.MODE === 'development';
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPosthogClient = () => {
|
const getPosthogClient = () => {
|
||||||
|
@ -6,7 +6,12 @@ import { useSelector, useDispatch } from 'react-redux';
|
|||||||
import EnvironmentSettings from 'components/Environments/EnvironmentSettings';
|
import EnvironmentSettings from 'components/Environments/EnvironmentSettings';
|
||||||
import NetworkError from 'components/ResponsePane/NetworkError';
|
import NetworkError from 'components/ResponsePane/NetworkError';
|
||||||
import NewRequest from 'components/Sidebar/NewRequest';
|
import NewRequest from 'components/Sidebar/NewRequest';
|
||||||
import { sendRequest, saveRequest, saveCollectionRoot } from 'providers/ReduxStore/slices/collections/actions';
|
import {
|
||||||
|
sendRequest,
|
||||||
|
saveRequest,
|
||||||
|
saveCollectionRoot,
|
||||||
|
saveFolderRoot
|
||||||
|
} from 'providers/ReduxStore/slices/collections/actions';
|
||||||
import { findCollectionByUid, findItemInCollection } from 'utils/collections';
|
import { findCollectionByUid, findItemInCollection } from 'utils/collections';
|
||||||
import { closeTabs, switchTab } from 'providers/ReduxStore/slices/tabs';
|
import { closeTabs, switchTab } from 'providers/ReduxStore/slices/tabs';
|
||||||
import { getKeyBindingsForActionAllOS } from './keyMappings';
|
import { getKeyBindingsForActionAllOS } from './keyMappings';
|
||||||
@ -43,7 +48,11 @@ export const HotkeysProvider = (props) => {
|
|||||||
if (collection) {
|
if (collection) {
|
||||||
const item = findItemInCollection(collection, activeTab.uid);
|
const item = findItemInCollection(collection, activeTab.uid);
|
||||||
if (item && item.uid) {
|
if (item && item.uid) {
|
||||||
|
if (activeTab.type === 'folder-settings') {
|
||||||
|
dispatch(saveFolderRoot(collection.uid, item.uid));
|
||||||
|
} else {
|
||||||
dispatch(saveRequest(activeTab.uid, activeTab.collectionUid));
|
dispatch(saveRequest(activeTab.uid, activeTab.collectionUid));
|
||||||
|
}
|
||||||
} else if (activeTab.type === 'collection-settings') {
|
} else if (activeTab.type === 'collection-settings') {
|
||||||
dispatch(saveCollectionRoot(collection.uid));
|
dispatch(saveCollectionRoot(collection.uid));
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import getConfig from 'next/config';
|
|
||||||
import { configureStore } from '@reduxjs/toolkit';
|
import { configureStore } from '@reduxjs/toolkit';
|
||||||
import tasksMiddleware from './middlewares/tasks/middleware';
|
import tasksMiddleware from './middlewares/tasks/middleware';
|
||||||
import debugMiddleware from './middlewares/debug/middleware';
|
import debugMiddleware from './middlewares/debug/middleware';
|
||||||
@ -8,9 +7,8 @@ import tabsReducer from './slices/tabs';
|
|||||||
import notificationsReducer from './slices/notifications';
|
import notificationsReducer from './slices/notifications';
|
||||||
import globalEnvironmentsReducer from './slices/global-environments';
|
import globalEnvironmentsReducer from './slices/global-environments';
|
||||||
|
|
||||||
const { publicRuntimeConfig } = getConfig();
|
|
||||||
const isDevEnv = () => {
|
const isDevEnv = () => {
|
||||||
return publicRuntimeConfig.ENV === 'dev';
|
return import.meta.env.MODE === 'development';
|
||||||
};
|
};
|
||||||
|
|
||||||
let middleware = [tasksMiddleware.middleware];
|
let middleware = [tasksMiddleware.middleware];
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { uuid } from 'utils/common';
|
import { uuid } from 'utils/common';
|
||||||
import path from 'path';
|
import { find, map, forOwn, concat, filter, each, cloneDeep, get, set } from 'lodash';
|
||||||
import { find, map, forOwn, concat, filter, each, cloneDeep, get, set, debounce } from 'lodash';
|
|
||||||
import { createSlice } from '@reduxjs/toolkit';
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
import {
|
import {
|
||||||
addDepth,
|
addDepth,
|
||||||
@ -13,6 +12,7 @@ import {
|
|||||||
findEnvironmentInCollection,
|
findEnvironmentInCollection,
|
||||||
findItemInCollection,
|
findItemInCollection,
|
||||||
findItemInCollectionByPathname,
|
findItemInCollectionByPathname,
|
||||||
|
isItemAFolder,
|
||||||
isItemARequest
|
isItemARequest
|
||||||
} from 'utils/collections';
|
} from 'utils/collections';
|
||||||
import { parsePathParams, parseQueryParams, splitOnFirst, stringifyQueryParams } from 'utils/url';
|
import { parsePathParams, parseQueryParams, splitOnFirst, stringifyQueryParams } from 'utils/url';
|
||||||
@ -1733,6 +1733,15 @@ export const collectionsSlice = createSlice({
|
|||||||
item.draft.request.docs = action.payload.docs;
|
item.draft.request.docs = action.payload.docs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
updateFolderDocs: (state, action) => {
|
||||||
|
const collection = findCollectionByUid(state.collections, action.payload.collectionUid);
|
||||||
|
const folder = collection ? findItemInCollection(collection, action.payload.folderUid) : null;
|
||||||
|
if (folder) {
|
||||||
|
if (isItemAFolder(folder)) {
|
||||||
|
set(folder, 'root.docs', action.payload.docs);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1827,7 +1836,8 @@ export const {
|
|||||||
runRequestEvent,
|
runRequestEvent,
|
||||||
runFolderEvent,
|
runFolderEvent,
|
||||||
resetCollectionRunner,
|
resetCollectionRunner,
|
||||||
updateRequestDocs
|
updateRequestDocs,
|
||||||
|
updateFolderDocs
|
||||||
} = collectionsSlice.actions;
|
} = collectionsSlice.actions;
|
||||||
|
|
||||||
export default collectionsSlice.reducer;
|
export default collectionsSlice.reducer;
|
||||||
|
@ -35,7 +35,6 @@ export const notificationSlice = createSlice({
|
|||||||
state.loading = action.payload.fetching;
|
state.loading = action.payload.fetching;
|
||||||
},
|
},
|
||||||
setNotifications: (state, action) => {
|
setNotifications: (state, action) => {
|
||||||
console.log('notifications', notifications);
|
|
||||||
let notifications = action.payload.notifications || [];
|
let notifications = action.payload.notifications || [];
|
||||||
let readNotificationIds = state.readNotificationIds;
|
let readNotificationIds = state.readNotificationIds;
|
||||||
|
|
||||||
|
@ -93,9 +93,6 @@ if (!SERVER_RENDERED) {
|
|||||||
|
|
||||||
const box = target.getBoundingClientRect();
|
const box = target.getBoundingClientRect();
|
||||||
|
|
||||||
const hoverTime = getHoverTime(cm);
|
|
||||||
state.hoverTimeout = setTimeout(onHover, hoverTime);
|
|
||||||
|
|
||||||
const onMouseMove = function () {
|
const onMouseMove = function () {
|
||||||
clearTimeout(state.hoverTimeout);
|
clearTimeout(state.hoverTimeout);
|
||||||
state.hoverTimeout = setTimeout(onHover, hoverTime);
|
state.hoverTimeout = setTimeout(onHover, hoverTime);
|
||||||
@ -115,6 +112,9 @@ if (!SERVER_RENDERED) {
|
|||||||
onMouseHover(cm, box);
|
onMouseHover(cm, box);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hoverTime = getHoverTime(cm);
|
||||||
|
state.hoverTimeout = setTimeout(onHover, hoverTime);
|
||||||
|
|
||||||
CodeMirror.on(document, 'mousemove', onMouseMove);
|
CodeMirror.on(document, 'mousemove', onMouseMove);
|
||||||
CodeMirror.on(cm.getWrapperElement(), 'mouseout', onMouseOut);
|
CodeMirror.on(cm.getWrapperElement(), 'mouseout', onMouseOut);
|
||||||
}
|
}
|
||||||
|
@ -404,7 +404,7 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {}
|
|||||||
request: {}
|
request: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
let { request, meta } = si?.root || {};
|
let { request, meta, docs } = si?.root || {};
|
||||||
let { headers, script = {}, vars = {}, tests } = request || {};
|
let { headers, script = {}, vars = {}, tests } = request || {};
|
||||||
|
|
||||||
// folder level headers
|
// folder level headers
|
||||||
@ -436,6 +436,11 @@ export const transformCollectionToSaveToExportAsFile = (collection, options = {}
|
|||||||
di.root.request.tests = tests;
|
di.root.request.tests = tests;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// folder level docs
|
||||||
|
if (docs?.length) {
|
||||||
|
di.root.docs = docs;
|
||||||
|
}
|
||||||
|
|
||||||
if (meta?.name) {
|
if (meta?.name) {
|
||||||
di.root.meta = {};
|
di.root.meta = {};
|
||||||
di.root.meta.name = meta?.name;
|
di.root.meta.name = meta?.name;
|
||||||
@ -800,6 +805,19 @@ export const getGlobalEnvironmentVariables = ({ globalEnvironments, activeGlobal
|
|||||||
return variables;
|
return variables;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getGlobalEnvironmentVariablesMasked = ({ globalEnvironments, activeGlobalEnvironmentUid }) => {
|
||||||
|
const environment = globalEnvironments?.find(env => env?.uid === activeGlobalEnvironmentUid);
|
||||||
|
|
||||||
|
if (environment && Array.isArray(environment.variables)) {
|
||||||
|
return environment.variables
|
||||||
|
.filter((variable) => variable.name && variable.value && variable.enabled && variable.secret)
|
||||||
|
.map((variable) => variable.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export const getEnvironmentVariables = (collection) => {
|
export const getEnvironmentVariables = (collection) => {
|
||||||
let variables = {};
|
let variables = {};
|
||||||
if (collection) {
|
if (collection) {
|
||||||
@ -873,8 +891,22 @@ export const getAllVariables = (collection, item) => {
|
|||||||
...requestVariables,
|
...requestVariables,
|
||||||
...runtimeVariables
|
...runtimeVariables
|
||||||
};
|
};
|
||||||
const maskedEnvVariables = getEnvironmentVariablesMasked(collection);
|
|
||||||
|
const mergedVariablesGlobal = {
|
||||||
|
...collectionVariables,
|
||||||
|
...envVariables,
|
||||||
|
...folderVariables,
|
||||||
|
...requestVariables,
|
||||||
|
...runtimeVariables,
|
||||||
|
}
|
||||||
|
|
||||||
|
const maskedEnvVariables = getEnvironmentVariablesMasked(collection) || [];
|
||||||
|
const maskedGlobalEnvVariables = collection?.globalEnvSecrets || [];
|
||||||
|
|
||||||
const filteredMaskedEnvVariables = maskedEnvVariables.filter((key) => !(key in mergedVariables));
|
const filteredMaskedEnvVariables = maskedEnvVariables.filter((key) => !(key in mergedVariables));
|
||||||
|
const filteredMaskedGlobalEnvVariables = maskedGlobalEnvVariables.filter((key) => !(key in mergedVariablesGlobal));
|
||||||
|
|
||||||
|
const uniqueMaskedVariables = [...new Set([...filteredMaskedEnvVariables, ...filteredMaskedGlobalEnvVariables])];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...globalEnvironmentVariables,
|
...globalEnvironmentVariables,
|
||||||
@ -886,7 +918,7 @@ export const getAllVariables = (collection, item) => {
|
|||||||
pathParams: {
|
pathParams: {
|
||||||
...pathParams
|
...pathParams
|
||||||
},
|
},
|
||||||
maskedEnvVariables: filteredMaskedEnvVariables,
|
maskedEnvVariables: uniqueMaskedVariables,
|
||||||
process: {
|
process: {
|
||||||
env: {
|
env: {
|
||||||
...processEnvVariables
|
...processEnvVariables
|
||||||
|
@ -166,6 +166,17 @@ const importScriptsFromEvents = (events, requestObject, options, pushTranslation
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const importCollectionLevelVariables = (variables, requestObject) => {
|
||||||
|
const vars = variables.map((v) => ({
|
||||||
|
uid: uuid(),
|
||||||
|
name: v.key,
|
||||||
|
value: v.value,
|
||||||
|
enabled: true
|
||||||
|
}));
|
||||||
|
|
||||||
|
requestObject.vars.req = vars;
|
||||||
|
};
|
||||||
|
|
||||||
const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, options) => {
|
const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, options) => {
|
||||||
brunoParent.items = brunoParent.items || [];
|
brunoParent.items = brunoParent.items || [];
|
||||||
const folderMap = {};
|
const folderMap = {};
|
||||||
@ -494,6 +505,10 @@ const importPostmanV2Collection = (collection, options) => {
|
|||||||
importScriptsFromEvents(collection.event, brunoCollection.root.request, options, pushTranslationLog);
|
importScriptsFromEvents(collection.event, brunoCollection.root.request, options, pushTranslationLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (collection?.variable){
|
||||||
|
importCollectionLevelVariables(collection.variable, brunoCollection.root.request);
|
||||||
|
}
|
||||||
|
|
||||||
importPostmanV2CollectionItem(brunoCollection, collection.item, collection.auth, options);
|
importPostmanV2CollectionItem(brunoCollection, collection.item, collection.auth, options);
|
||||||
|
|
||||||
return brunoCollection;
|
return brunoCollection;
|
||||||
|
@ -1,207 +1,8 @@
|
|||||||
const { get, each, filter, find, compact } = require('lodash');
|
const { get, each, filter, find, compact } = require('lodash');
|
||||||
const fs = require('fs');
|
const { get, each, filter } = require('lodash');
|
||||||
const os = require('os');
|
|
||||||
const decomment = require('decomment');
|
const decomment = require('decomment');
|
||||||
const crypto = require('node:crypto');
|
const crypto = require('node:crypto');
|
||||||
|
const { mergeHeaders, mergeScripts, mergeVars, getTreePathFromCollectionToItem } = require('../utils/collection');
|
||||||
const mergeHeaders = (collection, request, requestTreePath) => {
|
|
||||||
let headers = new Map();
|
|
||||||
|
|
||||||
let collectionHeaders = get(collection, 'root.request.headers', []);
|
|
||||||
collectionHeaders.forEach((header) => {
|
|
||||||
if (header.enabled) {
|
|
||||||
headers.set(header.name, header.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
for (let i of requestTreePath) {
|
|
||||||
if (i.type === 'folder') {
|
|
||||||
let _headers = get(i, 'root.request.headers', []);
|
|
||||||
_headers.forEach((header) => {
|
|
||||||
if (header.enabled) {
|
|
||||||
headers.set(header.name, header.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const _headers = i?.draft ? get(i, 'draft.request.headers', []) : get(i, 'request.headers', []);
|
|
||||||
_headers.forEach((header) => {
|
|
||||||
if (header.enabled) {
|
|
||||||
headers.set(header.name, header.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
request.headers = Array.from(headers, ([name, value]) => ({ name, value, enabled: true }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const mergeVars = (collection, request, requestTreePath) => {
|
|
||||||
let reqVars = new Map();
|
|
||||||
let collectionRequestVars = get(collection, 'root.request.vars.req', []);
|
|
||||||
let collectionVariables = {};
|
|
||||||
collectionRequestVars.forEach((_var) => {
|
|
||||||
if (_var.enabled) {
|
|
||||||
reqVars.set(_var.name, _var.value);
|
|
||||||
collectionVariables[_var.name] = _var.value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let folderVariables = {};
|
|
||||||
let requestVariables = {};
|
|
||||||
for (let i of requestTreePath) {
|
|
||||||
if (i.type === 'folder') {
|
|
||||||
let vars = get(i, 'root.request.vars.req', []);
|
|
||||||
vars.forEach((_var) => {
|
|
||||||
if (_var.enabled) {
|
|
||||||
reqVars.set(_var.name, _var.value);
|
|
||||||
folderVariables[_var.name] = _var.value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const vars = i?.draft ? get(i, 'draft.request.vars.req', []) : get(i, 'request.vars.req', []);
|
|
||||||
vars.forEach((_var) => {
|
|
||||||
if (_var.enabled) {
|
|
||||||
reqVars.set(_var.name, _var.value);
|
|
||||||
requestVariables[_var.name] = _var.value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
request.collectionVariables = collectionVariables;
|
|
||||||
request.folderVariables = folderVariables;
|
|
||||||
request.requestVariables = requestVariables;
|
|
||||||
|
|
||||||
if(request?.vars) {
|
|
||||||
request.vars.req = Array.from(reqVars, ([name, value]) => ({
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
enabled: true,
|
|
||||||
type: 'request'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
let resVars = new Map();
|
|
||||||
let collectionResponseVars = get(collection, 'root.request.vars.res', []);
|
|
||||||
collectionResponseVars.forEach((_var) => {
|
|
||||||
if (_var.enabled) {
|
|
||||||
resVars.set(_var.name, _var.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
for (let i of requestTreePath) {
|
|
||||||
if (i.type === 'folder') {
|
|
||||||
let vars = get(i, 'root.request.vars.res', []);
|
|
||||||
vars.forEach((_var) => {
|
|
||||||
if (_var.enabled) {
|
|
||||||
resVars.set(_var.name, _var.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const vars = i?.draft ? get(i, 'draft.request.vars.res', []) : get(i, 'request.vars.res', []);
|
|
||||||
vars.forEach((_var) => {
|
|
||||||
if (_var.enabled) {
|
|
||||||
resVars.set(_var.name, _var.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(request?.vars) {
|
|
||||||
request.vars.res = Array.from(resVars, ([name, value]) => ({
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
enabled: true,
|
|
||||||
type: 'response'
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const mergeScripts = (collection, request, requestTreePath, scriptFlow) => {
|
|
||||||
let collectionPreReqScript = get(collection, 'root.request.script.req', '');
|
|
||||||
let collectionPostResScript = get(collection, 'root.request.script.res', '');
|
|
||||||
let collectionTests = get(collection, 'root.request.tests', '');
|
|
||||||
|
|
||||||
let combinedPreReqScript = [];
|
|
||||||
let combinedPostResScript = [];
|
|
||||||
let combinedTests = [];
|
|
||||||
for (let i of requestTreePath) {
|
|
||||||
if (i.type === 'folder') {
|
|
||||||
let preReqScript = get(i, 'root.request.script.req', '');
|
|
||||||
if (preReqScript && preReqScript.trim() !== '') {
|
|
||||||
combinedPreReqScript.push(preReqScript);
|
|
||||||
}
|
|
||||||
|
|
||||||
let postResScript = get(i, 'root.request.script.res', '');
|
|
||||||
if (postResScript && postResScript.trim() !== '') {
|
|
||||||
combinedPostResScript.push(postResScript);
|
|
||||||
}
|
|
||||||
|
|
||||||
let tests = get(i, 'root.request.tests', '');
|
|
||||||
if (tests && tests?.trim?.() !== '') {
|
|
||||||
combinedTests.push(tests);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
request.script.req = compact([collectionPreReqScript, ...combinedPreReqScript, request?.script?.req || '']).join(os.EOL);
|
|
||||||
|
|
||||||
if (scriptFlow === 'sequential') {
|
|
||||||
request.script.res = compact([collectionPostResScript, ...combinedPostResScript, request?.script?.res || '']).join(os.EOL);
|
|
||||||
} else {
|
|
||||||
request.script.res = compact([request?.script?.res || '', ...combinedPostResScript.reverse(), collectionPostResScript]).join(os.EOL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scriptFlow === 'sequential') {
|
|
||||||
request.tests = compact([collectionTests, ...combinedTests, request?.tests || '']).join(os.EOL);
|
|
||||||
} else {
|
|
||||||
request.tests = compact([request?.tests || '', ...combinedTests.reverse(), collectionTests]).join(os.EOL);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const findItem = (items = [], pathname) => {
|
|
||||||
return find(items, (i) => i.pathname === pathname);
|
|
||||||
};
|
|
||||||
|
|
||||||
const findItemInCollection = (collection, pathname) => {
|
|
||||||
let flattenedItems = flattenItems(collection.items);
|
|
||||||
|
|
||||||
return findItem(flattenedItems, pathname);
|
|
||||||
};
|
|
||||||
|
|
||||||
const findParentItemInCollection = (collection, pathname) => {
|
|
||||||
let flattenedItems = flattenItems(collection.items);
|
|
||||||
|
|
||||||
return find(flattenedItems, (item) => {
|
|
||||||
return item.items && find(item.items, (i) => i.pathname === pathname);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const flattenItems = (items = []) => {
|
|
||||||
const flattenedItems = [];
|
|
||||||
|
|
||||||
const flatten = (itms, flattened) => {
|
|
||||||
each(itms, (i) => {
|
|
||||||
flattened.push(i);
|
|
||||||
|
|
||||||
if (i.items && i.items.length) {
|
|
||||||
flatten(i.items, flattened);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
flatten(items, flattenedItems);
|
|
||||||
|
|
||||||
return flattenedItems;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getTreePathFromCollectionToItem = (collection, _item) => {
|
|
||||||
let path = [];
|
|
||||||
let item = findItemInCollection(collection, _item.pathname);
|
|
||||||
while (item) {
|
|
||||||
path.unshift(item);
|
|
||||||
item = findParentItemInCollection(collection, item.pathname);
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
};
|
|
||||||
|
|
||||||
const prepareRequest = (item = {}, collection = {}) => {
|
const prepareRequest = (item = {}, collection = {}) => {
|
||||||
const request = item?.request;
|
const request = item?.request;
|
||||||
@ -312,7 +113,7 @@ const prepareRequest = (item = {}, collection = {}) => {
|
|||||||
|
|
||||||
if (request.body.mode === 'xml') {
|
if (request.body.mode === 'xml') {
|
||||||
if (!contentTypeDefined) {
|
if (!contentTypeDefined) {
|
||||||
axiosRequest.headers['content-type'] = 'text/xml';
|
axiosRequest.headers['content-type'] = 'application/xml';
|
||||||
}
|
}
|
||||||
axiosRequest.data = request.body.xml;
|
axiosRequest.data = request.body.xml;
|
||||||
}
|
}
|
||||||
|
208
packages/bruno-cli/src/utils/collection.js
Normal file
208
packages/bruno-cli/src/utils/collection.js
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
const { get, each, find, compact } = require('lodash');
|
||||||
|
const os = require('os');
|
||||||
|
|
||||||
|
const mergeHeaders = (collection, request, requestTreePath) => {
|
||||||
|
let headers = new Map();
|
||||||
|
|
||||||
|
let collectionHeaders = get(collection, 'root.request.headers', []);
|
||||||
|
collectionHeaders.forEach((header) => {
|
||||||
|
if (header.enabled) {
|
||||||
|
headers.set(header.name, header.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i of requestTreePath) {
|
||||||
|
if (i.type === 'folder') {
|
||||||
|
let _headers = get(i, 'root.request.headers', []);
|
||||||
|
_headers.forEach((header) => {
|
||||||
|
if (header.enabled) {
|
||||||
|
headers.set(header.name, header.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const _headers = i?.draft ? get(i, 'draft.request.headers', []) : get(i, 'request.headers', []);
|
||||||
|
_headers.forEach((header) => {
|
||||||
|
if (header.enabled) {
|
||||||
|
headers.set(header.name, header.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request.headers = Array.from(headers, ([name, value]) => ({ name, value, enabled: true }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergeVars = (collection, request, requestTreePath) => {
|
||||||
|
let reqVars = new Map();
|
||||||
|
let collectionRequestVars = get(collection, 'root.request.vars.req', []);
|
||||||
|
let collectionVariables = {};
|
||||||
|
collectionRequestVars.forEach((_var) => {
|
||||||
|
if (_var.enabled) {
|
||||||
|
reqVars.set(_var.name, _var.value);
|
||||||
|
collectionVariables[_var.name] = _var.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let folderVariables = {};
|
||||||
|
let requestVariables = {};
|
||||||
|
for (let i of requestTreePath) {
|
||||||
|
if (i.type === 'folder') {
|
||||||
|
let vars = get(i, 'root.request.vars.req', []);
|
||||||
|
vars.forEach((_var) => {
|
||||||
|
if (_var.enabled) {
|
||||||
|
reqVars.set(_var.name, _var.value);
|
||||||
|
folderVariables[_var.name] = _var.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const vars = i?.draft ? get(i, 'draft.request.vars.req', []) : get(i, 'request.vars.req', []);
|
||||||
|
vars.forEach((_var) => {
|
||||||
|
if (_var.enabled) {
|
||||||
|
reqVars.set(_var.name, _var.value);
|
||||||
|
requestVariables[_var.name] = _var.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request.collectionVariables = collectionVariables;
|
||||||
|
request.folderVariables = folderVariables;
|
||||||
|
request.requestVariables = requestVariables;
|
||||||
|
|
||||||
|
if(request?.vars) {
|
||||||
|
request.vars.req = Array.from(reqVars, ([name, value]) => ({
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
enabled: true,
|
||||||
|
type: 'request'
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
let resVars = new Map();
|
||||||
|
let collectionResponseVars = get(collection, 'root.request.vars.res', []);
|
||||||
|
collectionResponseVars.forEach((_var) => {
|
||||||
|
if (_var.enabled) {
|
||||||
|
resVars.set(_var.name, _var.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (let i of requestTreePath) {
|
||||||
|
if (i.type === 'folder') {
|
||||||
|
let vars = get(i, 'root.request.vars.res', []);
|
||||||
|
vars.forEach((_var) => {
|
||||||
|
if (_var.enabled) {
|
||||||
|
resVars.set(_var.name, _var.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const vars = i?.draft ? get(i, 'draft.request.vars.res', []) : get(i, 'request.vars.res', []);
|
||||||
|
vars.forEach((_var) => {
|
||||||
|
if (_var.enabled) {
|
||||||
|
resVars.set(_var.name, _var.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(request?.vars) {
|
||||||
|
request.vars.res = Array.from(resVars, ([name, value]) => ({
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
enabled: true,
|
||||||
|
type: 'response'
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergeScripts = (collection, request, requestTreePath, scriptFlow) => {
|
||||||
|
let collectionPreReqScript = get(collection, 'root.request.script.req', '');
|
||||||
|
let collectionPostResScript = get(collection, 'root.request.script.res', '');
|
||||||
|
let collectionTests = get(collection, 'root.request.tests', '');
|
||||||
|
|
||||||
|
let combinedPreReqScript = [];
|
||||||
|
let combinedPostResScript = [];
|
||||||
|
let combinedTests = [];
|
||||||
|
for (let i of requestTreePath) {
|
||||||
|
if (i.type === 'folder') {
|
||||||
|
let preReqScript = get(i, 'root.request.script.req', '');
|
||||||
|
if (preReqScript && preReqScript.trim() !== '') {
|
||||||
|
combinedPreReqScript.push(preReqScript);
|
||||||
|
}
|
||||||
|
|
||||||
|
let postResScript = get(i, 'root.request.script.res', '');
|
||||||
|
if (postResScript && postResScript.trim() !== '') {
|
||||||
|
combinedPostResScript.push(postResScript);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tests = get(i, 'root.request.tests', '');
|
||||||
|
if (tests && tests?.trim?.() !== '') {
|
||||||
|
combinedTests.push(tests);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request.script.req = compact([collectionPreReqScript, ...combinedPreReqScript, request?.script?.req || '']).join(os.EOL);
|
||||||
|
|
||||||
|
if (scriptFlow === 'sequential') {
|
||||||
|
request.script.res = compact([collectionPostResScript, ...combinedPostResScript, request?.script?.res || '']).join(os.EOL);
|
||||||
|
} else {
|
||||||
|
request.script.res = compact([request?.script?.res || '', ...combinedPostResScript.reverse(), collectionPostResScript]).join(os.EOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scriptFlow === 'sequential') {
|
||||||
|
request.tests = compact([collectionTests, ...combinedTests, request?.tests || '']).join(os.EOL);
|
||||||
|
} else {
|
||||||
|
request.tests = compact([request?.tests || '', ...combinedTests.reverse(), collectionTests]).join(os.EOL);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const findItem = (items = [], pathname) => {
|
||||||
|
return find(items, (i) => i.pathname === pathname);
|
||||||
|
};
|
||||||
|
|
||||||
|
const findItemInCollection = (collection, pathname) => {
|
||||||
|
let flattenedItems = flattenItems(collection.items);
|
||||||
|
|
||||||
|
return findItem(flattenedItems, pathname);
|
||||||
|
};
|
||||||
|
|
||||||
|
const findParentItemInCollection = (collection, pathname) => {
|
||||||
|
let flattenedItems = flattenItems(collection.items);
|
||||||
|
|
||||||
|
return find(flattenedItems, (item) => {
|
||||||
|
return item.items && find(item.items, (i) => i.pathname === pathname);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const flattenItems = (items = []) => {
|
||||||
|
const flattenedItems = [];
|
||||||
|
|
||||||
|
const flatten = (itms, flattened) => {
|
||||||
|
each(itms, (i) => {
|
||||||
|
flattened.push(i);
|
||||||
|
|
||||||
|
if (i.items && i.items.length) {
|
||||||
|
flatten(i.items, flattened);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
flatten(items, flattenedItems);
|
||||||
|
|
||||||
|
return flattenedItems;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTreePathFromCollectionToItem = (collection, _item) => {
|
||||||
|
let path = [];
|
||||||
|
let item = findItemInCollection(collection, _item.pathname);
|
||||||
|
while (item) {
|
||||||
|
path.unshift(item);
|
||||||
|
item = findParentItemInCollection(collection, item.pathname);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mergeHeaders,
|
||||||
|
mergeVars,
|
||||||
|
mergeScripts,
|
||||||
|
getTreePathFromCollectionToItem
|
||||||
|
}
|
@ -389,6 +389,8 @@ const change = async (win, pathname, collectionUid, collectionPath) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const unlink = (win, pathname, collectionUid, collectionPath) => {
|
const unlink = (win, pathname, collectionUid, collectionPath) => {
|
||||||
|
console.log(`watcher unlink: ${pathname}`);
|
||||||
|
|
||||||
if (isBruEnvironmentConfig(pathname, collectionPath)) {
|
if (isBruEnvironmentConfig(pathname, collectionPath)) {
|
||||||
return unlinkEnvironmentFile(win, pathname, collectionUid);
|
return unlinkEnvironmentFile(win, pathname, collectionUid);
|
||||||
}
|
}
|
||||||
@ -506,6 +508,33 @@ class Watcher {
|
|||||||
this.watchers[watchPath] = null;
|
this.watchers[watchPath] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getWatcherByItemPath(itemPath) {
|
||||||
|
const paths = Object.keys(this.watchers);
|
||||||
|
|
||||||
|
const watcherPath = paths?.find(collectionPath => {
|
||||||
|
const absCollectionPath = path.resolve(collectionPath);
|
||||||
|
const absItemPath = path.resolve(itemPath);
|
||||||
|
|
||||||
|
return absItemPath.startsWith(absCollectionPath);
|
||||||
|
});
|
||||||
|
|
||||||
|
return watcherPath ? this.watchers[watcherPath] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlinkItemPathInWatcher(itemPath) {
|
||||||
|
const watcher = this.getWatcherByItemPath(itemPath);
|
||||||
|
if (watcher) {
|
||||||
|
watcher.unwatch(itemPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addItemPathInWatcher(itemPath) {
|
||||||
|
const watcher = this.getWatcherByItemPath(itemPath);
|
||||||
|
if (watcher && !watcher?.has?.(itemPath)) {
|
||||||
|
watcher?.add?.(itemPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Watcher;
|
module.exports = Watcher;
|
||||||
|
@ -20,8 +20,10 @@ const {
|
|||||||
normalizeWslPath,
|
normalizeWslPath,
|
||||||
normalizeAndResolvePath,
|
normalizeAndResolvePath,
|
||||||
safeToRename,
|
safeToRename,
|
||||||
|
sanitizeCollectionName,
|
||||||
isWindowsOS,
|
isWindowsOS,
|
||||||
isValidFilename
|
isValidFilename,
|
||||||
|
hasSubDirectories,
|
||||||
} = require('../utils/filesystem');
|
} = require('../utils/filesystem');
|
||||||
const { openCollectionDialog } = require('../app/collections');
|
const { openCollectionDialog } = require('../app/collections');
|
||||||
const { generateUidBasedOnHash, stringifyJson, safeParseJSON, safeStringifyJSON } = require('../utils/common');
|
const { generateUidBasedOnHash, stringifyJson, safeParseJSON, safeStringifyJSON } = require('../utils/common');
|
||||||
@ -67,6 +69,8 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
|||||||
'renderer:create-collection',
|
'renderer:create-collection',
|
||||||
async (event, collectionName, collectionFolderName, collectionLocation) => {
|
async (event, collectionName, collectionFolderName, collectionLocation) => {
|
||||||
try {
|
try {
|
||||||
|
collectionFolderName = sanitizeDirectoryName(collectionFolderName);
|
||||||
|
collectionName = sanitizeCollectionName(collectionName);
|
||||||
const dirPath = path.join(collectionLocation, collectionFolderName);
|
const dirPath = path.join(collectionLocation, collectionFolderName);
|
||||||
if (fs.existsSync(dirPath)) {
|
if (fs.existsSync(dirPath)) {
|
||||||
const files = fs.readdirSync(dirPath);
|
const files = fs.readdirSync(dirPath);
|
||||||
@ -75,9 +79,8 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
|||||||
throw new Error(`collection: ${dirPath} already exists and is not empty`);
|
throw new Error(`collection: ${dirPath} already exists and is not empty`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!isValidPathname(path.basename(dirPath))) {
|
||||||
if (!isValidPathname(dirPath)) {
|
throw new Error(`collection: invalid pathname - ${dirPath}`);
|
||||||
throw new Error(`collection: invalid pathname - ${dir}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fs.existsSync(dirPath)) {
|
if (!fs.existsSync(dirPath)) {
|
||||||
@ -105,13 +108,14 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
|||||||
ipcMain.handle(
|
ipcMain.handle(
|
||||||
'renderer:clone-collection',
|
'renderer:clone-collection',
|
||||||
async (event, collectionName, collectionFolderName, collectionLocation, previousPath) => {
|
async (event, collectionName, collectionFolderName, collectionLocation, previousPath) => {
|
||||||
|
collectionFolderName = sanitizeCollectionName(collectionFolderName);
|
||||||
const dirPath = path.join(collectionLocation, collectionFolderName);
|
const dirPath = path.join(collectionLocation, collectionFolderName);
|
||||||
if (fs.existsSync(dirPath)) {
|
if (fs.existsSync(dirPath)) {
|
||||||
throw new Error(`collection: ${dirPath} already exists`);
|
throw new Error(`collection: ${dirPath} already exists`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isValidPathname(dirPath)) {
|
if (!isValidPathname(path.basename(dirPath))) {
|
||||||
throw new Error(`collection: invalid pathname - ${dir}`);
|
throw new Error(`collection: invalid pathname - ${dirPath}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create dir
|
// create dir
|
||||||
@ -150,6 +154,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
|||||||
// rename collection
|
// rename collection
|
||||||
ipcMain.handle('renderer:rename-collection', async (event, newName, collectionPathname) => {
|
ipcMain.handle('renderer:rename-collection', async (event, newName, collectionPathname) => {
|
||||||
try {
|
try {
|
||||||
|
newName = sanitizeCollectionName(newName);
|
||||||
const brunoJsonFilePath = path.join(collectionPathname, 'bruno.json');
|
const brunoJsonFilePath = path.join(collectionPathname, 'bruno.json');
|
||||||
const content = fs.readFileSync(brunoJsonFilePath, 'utf8');
|
const content = fs.readFileSync(brunoJsonFilePath, 'utf8');
|
||||||
const json = JSON.parse(content);
|
const json = JSON.parse(content);
|
||||||
@ -343,6 +348,12 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
|||||||
|
|
||||||
// rename item
|
// rename item
|
||||||
ipcMain.handle('renderer:rename-item', async (event, oldPath, newPath, newName) => {
|
ipcMain.handle('renderer:rename-item', async (event, oldPath, newPath, newName) => {
|
||||||
|
const tempDir = path.join(os.tmpdir(), `temp-folder-${Date.now()}`);
|
||||||
|
const parentDir = path.dirname(oldPath);
|
||||||
|
const isWindowsOSAndNotWSLAndItemHasSubDirectories = isWindowsOS() && !isWSLPath(oldPath) && hasSubDirectories(oldPath);
|
||||||
|
let parentDirUnwatched = false;
|
||||||
|
let parentDirRewatched = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Normalize paths if they are WSL paths
|
// Normalize paths if they are WSL paths
|
||||||
oldPath = isWSLPath(oldPath) ? normalizeWslPath(oldPath) : normalizeAndResolvePath(oldPath);
|
oldPath = isWSLPath(oldPath) ? normalizeWslPath(oldPath) : normalizeAndResolvePath(oldPath);
|
||||||
@ -365,15 +376,29 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
|||||||
moveRequestUid(bruFile, newBruFilePath);
|
moveRequestUid(bruFile, newBruFilePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isWindowsOS() && !isWSLPath(oldPath)) {
|
watcher.unlinkItemPathInWatcher(parentDir);
|
||||||
const tempDir = path.join(os.tmpdir(), `temp-folder-${Date.now()}`);
|
parentDirUnwatched = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If it is windows OS
|
||||||
|
* And it is not WSL path (meaning its not linux running on windows using WSL)
|
||||||
|
* And it has sub directories
|
||||||
|
* Only then we need to use the temp dir approach to rename the folder
|
||||||
|
*
|
||||||
|
* Windows OS would sometimes throw error when renaming a folder with sub directories
|
||||||
|
* This is a alternative approach to avoid that error
|
||||||
|
*/
|
||||||
|
if (isWindowsOSAndNotWSLAndItemHasSubDirectories) {
|
||||||
await fsExtra.copy(oldPath, tempDir);
|
await fsExtra.copy(oldPath, tempDir);
|
||||||
await fsExtra.move(tempDir, newPath, { overwrite: true });
|
|
||||||
await fsExtra.remove(oldPath);
|
await fsExtra.remove(oldPath);
|
||||||
|
await fsExtra.move(tempDir, newPath, { overwrite: true });
|
||||||
|
await fsExtra.remove(tempDir);
|
||||||
} else {
|
} else {
|
||||||
await fs.renameSync(oldPath, newPath);
|
await fs.renameSync(oldPath, newPath);
|
||||||
}
|
}
|
||||||
|
watcher.addItemPathInWatcher(parentDir);
|
||||||
|
parentDirRewatched = true;
|
||||||
|
|
||||||
return newPath;
|
return newPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -397,12 +422,33 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
|||||||
|
|
||||||
return newPath;
|
return newPath;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// in case an error occurs during the rename file operations after unlinking the parent dir
|
||||||
|
// and the rewatch fails, we need to add it back to watcher
|
||||||
|
if (parentDirUnwatched && !parentDirRewatched) {
|
||||||
|
watcher.addItemPathInWatcher(parentDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// in case the rename file operations fails, and we see that the temp dir exists
|
||||||
|
// and the old path does not exist, we need to restore the data from the temp dir to the old path
|
||||||
|
if (isWindowsOSAndNotWSLAndItemHasSubDirectories) {
|
||||||
|
if (fsExtra.pathExistsSync(tempDir) && !fsExtra.pathExistsSync(oldPath)) {
|
||||||
|
try {
|
||||||
|
await fsExtra.copy(tempDir, oldPath);
|
||||||
|
await fsExtra.remove(tempDir);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Failed to restore data to the old path:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// new folder
|
// new folder
|
||||||
ipcMain.handle('renderer:new-folder', async (event, pathname) => {
|
ipcMain.handle('renderer:new-folder', async (event, pathname) => {
|
||||||
|
const resolvedFolderName = sanitizeDirectoryName(path.basename(pathname));
|
||||||
|
pathname = path.join(path.dirname(pathname), resolvedFolderName);
|
||||||
try {
|
try {
|
||||||
if (!fs.existsSync(pathname)) {
|
if (!fs.existsSync(pathname)) {
|
||||||
fs.mkdirSync(pathname);
|
fs.mkdirSync(pathname);
|
||||||
@ -461,7 +507,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
|||||||
|
|
||||||
ipcMain.handle('renderer:import-collection', async (event, collection, collectionLocation) => {
|
ipcMain.handle('renderer:import-collection', async (event, collection, collectionLocation) => {
|
||||||
try {
|
try {
|
||||||
let collectionName = sanitizeDirectoryName(collection.name);
|
let collectionName = sanitizeCollectionName(collection.name);
|
||||||
let collectionPath = path.join(collectionLocation, collectionName);
|
let collectionPath = path.join(collectionLocation, collectionName);
|
||||||
|
|
||||||
if (fs.existsSync(collectionPath)) {
|
if (fs.existsSync(collectionPath)) {
|
||||||
@ -477,6 +523,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
|||||||
fs.writeFileSync(filePath, content);
|
fs.writeFileSync(filePath, content);
|
||||||
}
|
}
|
||||||
if (item.type === 'folder') {
|
if (item.type === 'folder') {
|
||||||
|
item.name = sanitizeDirectoryName(item.name);
|
||||||
const folderPath = path.join(currentPath, item.name);
|
const folderPath = path.join(currentPath, item.name);
|
||||||
fs.mkdirSync(folderPath);
|
fs.mkdirSync(folderPath);
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ const {
|
|||||||
const Oauth2Store = require('../../store/oauth2');
|
const Oauth2Store = require('../../store/oauth2');
|
||||||
const iconv = require('iconv-lite');
|
const iconv = require('iconv-lite');
|
||||||
const FormData = require('form-data');
|
const FormData = require('form-data');
|
||||||
const { createFormData } = prepareRequest;
|
const { createFormData } = require('../../utils/form-data');
|
||||||
|
|
||||||
const safeStringifyJSON = (data) => {
|
const safeStringifyJSON = (data) => {
|
||||||
try {
|
try {
|
||||||
@ -397,7 +397,7 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
) => {
|
) => {
|
||||||
// run pre-request script
|
// run pre-request script
|
||||||
let scriptResult;
|
let scriptResult;
|
||||||
const requestScript = compact([get(collectionRoot, 'request.script.req'), get(request, 'script.req')]).join(os.EOL);
|
const requestScript = get(request, 'script.req');
|
||||||
if (requestScript?.length) {
|
if (requestScript?.length) {
|
||||||
const scriptRuntime = new ScriptRuntime({ runtime: scriptingConfig?.runtime });
|
const scriptRuntime = new ScriptRuntime({ runtime: scriptingConfig?.runtime });
|
||||||
scriptResult = await scriptRuntime.runRequestScript(
|
scriptResult = await scriptRuntime.runRequestScript(
|
||||||
@ -493,12 +493,7 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// run post-response script
|
// run post-response script
|
||||||
const responseScript = compact(scriptingConfig.flow === 'sequential' ? [
|
const responseScript = get(request, 'script.res');
|
||||||
get(collectionRoot, 'request.script.res'), get(request, 'script.res')
|
|
||||||
] : [
|
|
||||||
get(request, 'script.res'), get(collectionRoot, 'request.script.res')
|
|
||||||
]).join(os.EOL);
|
|
||||||
|
|
||||||
let scriptResult;
|
let scriptResult;
|
||||||
if (responseScript?.length) {
|
if (responseScript?.length) {
|
||||||
const scriptRuntime = new ScriptRuntime({ runtime: scriptingConfig?.runtime });
|
const scriptRuntime = new ScriptRuntime({ runtime: scriptingConfig?.runtime });
|
||||||
@ -675,14 +670,7 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// run tests
|
const testFile = get(request, 'tests');
|
||||||
const testScript = item.draft ? get(item.draft, 'request.tests') : get(item, 'request.tests');
|
|
||||||
const testFile = compact(scriptingConfig.flow === 'sequential' ? [
|
|
||||||
get(collectionRoot, 'request.tests'), testScript,
|
|
||||||
] : [
|
|
||||||
testScript, get(collectionRoot, 'request.tests')
|
|
||||||
]).join(os.EOL);
|
|
||||||
|
|
||||||
if (typeof testFile === 'string') {
|
if (typeof testFile === 'string') {
|
||||||
const testRuntime = new TestRuntime({ runtime: scriptingConfig?.runtime });
|
const testRuntime = new TestRuntime({ runtime: scriptingConfig?.runtime });
|
||||||
const testResults = await testRuntime.runTests(
|
const testResults = await testRuntime.runTests(
|
||||||
@ -1147,14 +1135,7 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// run tests
|
const testFile = get(request, 'tests');
|
||||||
const testScript = item.draft ? get(item.draft, 'request.tests') : get(item, 'request.tests');
|
|
||||||
const testFile = compact(scriptingConfig.flow === 'sequential' ? [
|
|
||||||
get(collectionRoot, 'request.tests'), testScript
|
|
||||||
] : [
|
|
||||||
testScript, get(collectionRoot, 'request.tests')
|
|
||||||
]).join(os.EOL);
|
|
||||||
|
|
||||||
if (typeof testFile === 'string') {
|
if (typeof testFile === 'string') {
|
||||||
const testRuntime = new TestRuntime({ runtime: scriptingConfig?.runtime });
|
const testRuntime = new TestRuntime({ runtime: scriptingConfig?.runtime });
|
||||||
const testResults = await testRuntime.runTests(
|
const testResults = await testRuntime.runTests(
|
||||||
|
@ -1,194 +1,8 @@
|
|||||||
const os = require('os');
|
const { get, each, filter } = require('lodash');
|
||||||
const { get, each, filter, compact, forOwn } = require('lodash');
|
|
||||||
const decomment = require('decomment');
|
const decomment = require('decomment');
|
||||||
const FormData = require('form-data');
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const crypto = require('node:crypto');
|
const crypto = require('node:crypto');
|
||||||
const { getTreePathFromCollectionToItem } = require('../../utils/collection');
|
const { getTreePathFromCollectionToItem, mergeHeaders, mergeScripts, mergeVars } = require('../../utils/collection');
|
||||||
const { buildFormUrlEncodedPayload } = require('../../utils/common');
|
const { buildFormUrlEncodedPayload, createFormData } = require('../../utils/form-data');
|
||||||
|
|
||||||
const mergeFolderLevelHeaders = (request, requestTreePath) => {
|
|
||||||
let folderHeaders = new Map();
|
|
||||||
|
|
||||||
for (let i of requestTreePath) {
|
|
||||||
if (i.type === 'folder') {
|
|
||||||
let headers = get(i, 'root.request.headers', []);
|
|
||||||
headers.forEach((header) => {
|
|
||||||
if (header.enabled) {
|
|
||||||
folderHeaders.set(header.name, header.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (i.uid === request.uid) {
|
|
||||||
const headers = i?.draft ? get(i, 'draft.request.headers', []) : get(i, 'request.headers', []);
|
|
||||||
headers.forEach((header) => {
|
|
||||||
if (header.enabled) {
|
|
||||||
folderHeaders.set(header.name, header.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mergedFolderHeaders = Array.from(folderHeaders, ([name, value]) => ({ name, value, enabled: true }));
|
|
||||||
let requestHeaders = request.headers || [];
|
|
||||||
let requestHeadersMap = new Map();
|
|
||||||
|
|
||||||
for (let header of requestHeaders) {
|
|
||||||
if (header.enabled) {
|
|
||||||
requestHeadersMap.set(header.name, header.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mergedFolderHeaders.forEach((header) => {
|
|
||||||
requestHeadersMap.set(header.name, header.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
request.headers = Array.from(requestHeadersMap, ([name, value]) => ({ name, value, enabled: true }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const mergeVars = (collection, request, requestTreePath) => {
|
|
||||||
let reqVars = new Map();
|
|
||||||
let collectionRequestVars = get(collection, 'root.request.vars.req', []);
|
|
||||||
let collectionVariables = {};
|
|
||||||
collectionRequestVars.forEach((_var) => {
|
|
||||||
if (_var.enabled) {
|
|
||||||
reqVars.set(_var.name, _var.value);
|
|
||||||
collectionVariables[_var.name] = _var.value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let folderVariables = {};
|
|
||||||
let requestVariables = {};
|
|
||||||
for (let i of requestTreePath) {
|
|
||||||
if (i.type === 'folder') {
|
|
||||||
let vars = get(i, 'root.request.vars.req', []);
|
|
||||||
vars.forEach((_var) => {
|
|
||||||
if (_var.enabled) {
|
|
||||||
reqVars.set(_var.name, _var.value);
|
|
||||||
folderVariables[_var.name] = _var.value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const vars = i?.draft ? get(i, 'draft.request.vars.req', []) : get(i, 'request.vars.req', []);
|
|
||||||
vars.forEach((_var) => {
|
|
||||||
if (_var.enabled) {
|
|
||||||
reqVars.set(_var.name, _var.value);
|
|
||||||
requestVariables[_var.name] = _var.value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
request.collectionVariables = collectionVariables;
|
|
||||||
request.folderVariables = folderVariables;
|
|
||||||
request.requestVariables = requestVariables;
|
|
||||||
|
|
||||||
request.vars.req = Array.from(reqVars, ([name, value]) => ({
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
enabled: true,
|
|
||||||
type: 'request'
|
|
||||||
}));
|
|
||||||
|
|
||||||
let resVars = new Map();
|
|
||||||
let collectionResponseVars = get(collection, 'root.request.vars.res', []);
|
|
||||||
collectionResponseVars.forEach((_var) => {
|
|
||||||
if (_var.enabled) {
|
|
||||||
resVars.set(_var.name, _var.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
for (let i of requestTreePath) {
|
|
||||||
if (i.type === 'folder') {
|
|
||||||
let vars = get(i, 'root.request.vars.res', []);
|
|
||||||
vars.forEach((_var) => {
|
|
||||||
if (_var.enabled) {
|
|
||||||
resVars.set(_var.name, _var.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const vars = i?.draft ? get(i, 'draft.request.vars.res', []) : get(i, 'request.vars.res', []);
|
|
||||||
vars.forEach((_var) => {
|
|
||||||
if (_var.enabled) {
|
|
||||||
resVars.set(_var.name, _var.value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
request.vars.res = Array.from(resVars, ([name, value]) => ({
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
enabled: true,
|
|
||||||
type: 'response'
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
const mergeFolderLevelScripts = (request, requestTreePath, scriptFlow) => {
|
|
||||||
let folderCombinedPreReqScript = [];
|
|
||||||
let folderCombinedPostResScript = [];
|
|
||||||
let folderCombinedTests = [];
|
|
||||||
for (let i of requestTreePath) {
|
|
||||||
if (i.type === 'folder') {
|
|
||||||
let preReqScript = get(i, 'root.request.script.req', '');
|
|
||||||
if (preReqScript && preReqScript.trim() !== '') {
|
|
||||||
folderCombinedPreReqScript.push(preReqScript);
|
|
||||||
}
|
|
||||||
|
|
||||||
let postResScript = get(i, 'root.request.script.res', '');
|
|
||||||
if (postResScript && postResScript.trim() !== '') {
|
|
||||||
folderCombinedPostResScript.push(postResScript);
|
|
||||||
}
|
|
||||||
|
|
||||||
let tests = get(i, 'root.request.tests', '');
|
|
||||||
if (tests && tests?.trim?.() !== '') {
|
|
||||||
folderCombinedTests.push(tests);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (folderCombinedPreReqScript.length) {
|
|
||||||
request.script.req = compact([...folderCombinedPreReqScript, request?.script?.req || '']).join(os.EOL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (folderCombinedPostResScript.length) {
|
|
||||||
if (scriptFlow === 'sequential') {
|
|
||||||
request.script.res = compact([...folderCombinedPostResScript, request?.script?.res || '']).join(os.EOL);
|
|
||||||
} else {
|
|
||||||
request.script.res = compact([request?.script?.res || '', ...folderCombinedPostResScript.reverse()]).join(os.EOL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (folderCombinedTests.length) {
|
|
||||||
if (scriptFlow === 'sequential') {
|
|
||||||
request.tests = compact([...folderCombinedTests, request?.tests || '']).join(os.EOL);
|
|
||||||
} else {
|
|
||||||
request.tests = compact([request?.tests || '', ...folderCombinedTests.reverse()]).join(os.EOL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const createFormData = (datas, collectionPath) => {
|
|
||||||
// make axios work in node using form data
|
|
||||||
// reference: https://github.com/axios/axios/issues/1006#issuecomment-320165427
|
|
||||||
const form = new FormData();
|
|
||||||
forOwn(datas, (value, key) => {
|
|
||||||
if (typeof value == 'string') {
|
|
||||||
form.append(key, value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const filePaths = value || [];
|
|
||||||
filePaths?.forEach?.((filePath) => {
|
|
||||||
let trimmedFilePath = filePath.trim();
|
|
||||||
|
|
||||||
if (!path.isAbsolute(trimmedFilePath)) {
|
|
||||||
trimmedFilePath = path.join(collectionPath, trimmedFilePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
form.append(key, fs.createReadStream(trimmedFilePath), path.basename(trimmedFilePath));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return form;
|
|
||||||
};
|
|
||||||
|
|
||||||
const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
|
const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
|
||||||
const collectionAuth = get(collectionRoot, 'request.auth');
|
const collectionAuth = get(collectionRoot, 'request.auth');
|
||||||
@ -353,28 +167,24 @@ const prepareRequest = (item, collection) => {
|
|||||||
let contentTypeDefined = false;
|
let contentTypeDefined = false;
|
||||||
let url = request.url;
|
let url = request.url;
|
||||||
|
|
||||||
// Collection level headers
|
|
||||||
each(get(collectionRoot, 'request.headers', []), (h) => {
|
each(get(collectionRoot, 'request.headers', []), (h) => {
|
||||||
if (h.enabled && h.name.length > 0) {
|
if (h.enabled && h.name?.toLowerCase() === 'content-type') {
|
||||||
headers[h.name] = h.value;
|
|
||||||
if (h.name.toLowerCase() === 'content-type') {
|
|
||||||
contentTypeDefined = true;
|
contentTypeDefined = true;
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// scriptFlow is either "sandwich" or "sequential"
|
|
||||||
const scriptFlow = collection.brunoConfig?.scripts?.flow ?? 'sandwich';
|
const scriptFlow = collection.brunoConfig?.scripts?.flow ?? 'sandwich';
|
||||||
const requestTreePath = getTreePathFromCollectionToItem(collection, item);
|
const requestTreePath = getTreePathFromCollectionToItem(collection, item);
|
||||||
if (requestTreePath && requestTreePath.length > 0) {
|
if (requestTreePath && requestTreePath.length > 0) {
|
||||||
mergeFolderLevelHeaders(request, requestTreePath);
|
mergeHeaders(collection, request, requestTreePath);
|
||||||
mergeFolderLevelScripts(request, requestTreePath, scriptFlow);
|
mergeScripts(collection, request, requestTreePath, scriptFlow);
|
||||||
mergeVars(collection, request, requestTreePath);
|
mergeVars(collection, request, requestTreePath);
|
||||||
request.globalEnvironmentVariables = collection?.globalEnvironmentVariables;
|
request.globalEnvironmentVariables = collection?.globalEnvironmentVariables;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request level headers
|
|
||||||
each(request.headers, (h) => {
|
each(get(request, 'headers', []), (h) => {
|
||||||
if (h.enabled && h.name.length > 0) {
|
if (h.enabled && h.name.length > 0) {
|
||||||
headers[h.name] = h.value;
|
headers[h.name] = h.value;
|
||||||
if (h.name.toLowerCase() === 'content-type') {
|
if (h.name.toLowerCase() === 'content-type') {
|
||||||
@ -414,7 +224,7 @@ const prepareRequest = (item, collection) => {
|
|||||||
|
|
||||||
if (request.body.mode === 'xml') {
|
if (request.body.mode === 'xml') {
|
||||||
if (!contentTypeDefined) {
|
if (!contentTypeDefined) {
|
||||||
axiosRequest.headers['content-type'] = 'text/xml';
|
axiosRequest.headers['content-type'] = 'application/xml';
|
||||||
}
|
}
|
||||||
axiosRequest.data = request.body.xml;
|
axiosRequest.data = request.body.xml;
|
||||||
}
|
}
|
||||||
@ -435,11 +245,11 @@ const prepareRequest = (item, collection) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (request.body.mode === 'multipartForm') {
|
if (request.body.mode === 'multipartForm') {
|
||||||
|
if (!contentTypeDefined) {
|
||||||
axiosRequest.headers['content-type'] = 'multipart/form-data';
|
axiosRequest.headers['content-type'] = 'multipart/form-data';
|
||||||
const params = {};
|
}
|
||||||
const enabledParams = filter(request.body.multipartForm, (p) => p.enabled);
|
const enabledParams = filter(request.body.multipartForm, (p) => p.enabled);
|
||||||
each(enabledParams, (p) => (params[p.name] = p.value));
|
axiosRequest.data = createFormData(enabledParams);
|
||||||
axiosRequest.data = params;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.body.mode === 'graphql') {
|
if (request.body.mode === 'graphql') {
|
||||||
@ -470,4 +280,3 @@ const prepareRequest = (item, collection) => {
|
|||||||
|
|
||||||
module.exports = prepareRequest;
|
module.exports = prepareRequest;
|
||||||
module.exports.setAuthHeaders = setAuthHeaders;
|
module.exports.setAuthHeaders = setAuthHeaders;
|
||||||
module.exports.createFormData = createFormData;
|
|
||||||
|
@ -1,5 +1,161 @@
|
|||||||
const each = require('lodash/each');
|
const { get, each, find, compact } = require('lodash');
|
||||||
const find = require('lodash/find');
|
const os = require('os');
|
||||||
|
|
||||||
|
const mergeHeaders = (collection, request, requestTreePath) => {
|
||||||
|
let headers = new Map();
|
||||||
|
|
||||||
|
let collectionHeaders = get(collection, 'root.request.headers', []);
|
||||||
|
collectionHeaders.forEach((header) => {
|
||||||
|
if (header.enabled) {
|
||||||
|
headers.set(header.name, header.value);
|
||||||
|
if (header?.name?.toLowerCase() === 'content-type') {
|
||||||
|
contentTypeDefined = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i of requestTreePath) {
|
||||||
|
if (i.type === 'folder') {
|
||||||
|
let _headers = get(i, 'root.request.headers', []);
|
||||||
|
_headers.forEach((header) => {
|
||||||
|
if (header.enabled) {
|
||||||
|
headers.set(header.name, header.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const _headers = i?.draft ? get(i, 'draft.request.headers', []) : get(i, 'request.headers', []);
|
||||||
|
_headers.forEach((header) => {
|
||||||
|
if (header.enabled) {
|
||||||
|
headers.set(header.name, header.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request.headers = Array.from(headers, ([name, value]) => ({ name, value, enabled: true }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergeVars = (collection, request, requestTreePath) => {
|
||||||
|
let reqVars = new Map();
|
||||||
|
let collectionRequestVars = get(collection, 'root.request.vars.req', []);
|
||||||
|
let collectionVariables = {};
|
||||||
|
collectionRequestVars.forEach((_var) => {
|
||||||
|
if (_var.enabled) {
|
||||||
|
reqVars.set(_var.name, _var.value);
|
||||||
|
collectionVariables[_var.name] = _var.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let folderVariables = {};
|
||||||
|
let requestVariables = {};
|
||||||
|
for (let i of requestTreePath) {
|
||||||
|
if (i.type === 'folder') {
|
||||||
|
let vars = get(i, 'root.request.vars.req', []);
|
||||||
|
vars.forEach((_var) => {
|
||||||
|
if (_var.enabled) {
|
||||||
|
reqVars.set(_var.name, _var.value);
|
||||||
|
folderVariables[_var.name] = _var.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const vars = i?.draft ? get(i, 'draft.request.vars.req', []) : get(i, 'request.vars.req', []);
|
||||||
|
vars.forEach((_var) => {
|
||||||
|
if (_var.enabled) {
|
||||||
|
reqVars.set(_var.name, _var.value);
|
||||||
|
requestVariables[_var.name] = _var.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request.collectionVariables = collectionVariables;
|
||||||
|
request.folderVariables = folderVariables;
|
||||||
|
request.requestVariables = requestVariables;
|
||||||
|
|
||||||
|
if(request?.vars) {
|
||||||
|
request.vars.req = Array.from(reqVars, ([name, value]) => ({
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
enabled: true,
|
||||||
|
type: 'request'
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
let resVars = new Map();
|
||||||
|
let collectionResponseVars = get(collection, 'root.request.vars.res', []);
|
||||||
|
collectionResponseVars.forEach((_var) => {
|
||||||
|
if (_var.enabled) {
|
||||||
|
resVars.set(_var.name, _var.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (let i of requestTreePath) {
|
||||||
|
if (i.type === 'folder') {
|
||||||
|
let vars = get(i, 'root.request.vars.res', []);
|
||||||
|
vars.forEach((_var) => {
|
||||||
|
if (_var.enabled) {
|
||||||
|
resVars.set(_var.name, _var.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const vars = i?.draft ? get(i, 'draft.request.vars.res', []) : get(i, 'request.vars.res', []);
|
||||||
|
vars.forEach((_var) => {
|
||||||
|
if (_var.enabled) {
|
||||||
|
resVars.set(_var.name, _var.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(request?.vars) {
|
||||||
|
request.vars.res = Array.from(resVars, ([name, value]) => ({
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
enabled: true,
|
||||||
|
type: 'response'
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergeScripts = (collection, request, requestTreePath, scriptFlow) => {
|
||||||
|
let collectionPreReqScript = get(collection, 'root.request.script.req', '');
|
||||||
|
let collectionPostResScript = get(collection, 'root.request.script.res', '');
|
||||||
|
let collectionTests = get(collection, 'root.request.tests', '');
|
||||||
|
|
||||||
|
let combinedPreReqScript = [];
|
||||||
|
let combinedPostResScript = [];
|
||||||
|
let combinedTests = [];
|
||||||
|
for (let i of requestTreePath) {
|
||||||
|
if (i.type === 'folder') {
|
||||||
|
let preReqScript = get(i, 'root.request.script.req', '');
|
||||||
|
if (preReqScript && preReqScript.trim() !== '') {
|
||||||
|
combinedPreReqScript.push(preReqScript);
|
||||||
|
}
|
||||||
|
|
||||||
|
let postResScript = get(i, 'root.request.script.res', '');
|
||||||
|
if (postResScript && postResScript.trim() !== '') {
|
||||||
|
combinedPostResScript.push(postResScript);
|
||||||
|
}
|
||||||
|
|
||||||
|
let tests = get(i, 'root.request.tests', '');
|
||||||
|
if (tests && tests?.trim?.() !== '') {
|
||||||
|
combinedTests.push(tests);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request.script.req = compact([collectionPreReqScript, ...combinedPreReqScript, request?.script?.req || '']).join(os.EOL);
|
||||||
|
|
||||||
|
if (scriptFlow === 'sequential') {
|
||||||
|
request.script.res = compact([collectionPostResScript, ...combinedPostResScript, request?.script?.res || '']).join(os.EOL);
|
||||||
|
} else {
|
||||||
|
request.script.res = compact([request?.script?.res || '', ...combinedPostResScript.reverse(), collectionPostResScript]).join(os.EOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scriptFlow === 'sequential') {
|
||||||
|
request.tests = compact([collectionTests, ...combinedTests, request?.tests || '']).join(os.EOL);
|
||||||
|
} else {
|
||||||
|
request.tests = compact([request?.tests || '', ...combinedTests.reverse(), collectionTests]).join(os.EOL);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const flattenItems = (items = []) => {
|
const flattenItems = (items = []) => {
|
||||||
const flattenedItems = [];
|
const flattenedItems = [];
|
||||||
@ -44,14 +200,12 @@ const getTreePathFromCollectionToItem = (collection, _item) => {
|
|||||||
path.unshift(item);
|
path.unshift(item);
|
||||||
item = findParentItemInCollection(collection, item.uid);
|
item = findParentItemInCollection(collection, item.uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
return path;
|
return path;
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
flattenItems,
|
mergeHeaders,
|
||||||
findItem,
|
mergeVars,
|
||||||
findItemInCollection,
|
mergeScripts,
|
||||||
findParentItemInCollection,
|
|
||||||
getTreePathFromCollectionToItem
|
getTreePathFromCollectionToItem
|
||||||
};
|
}
|
@ -85,24 +85,6 @@ const flattenDataForDotNotation = (data) => {
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Array.<object>} params The request body Array
|
|
||||||
* @returns {object} Returns an obj with repeating key as a array of values
|
|
||||||
* {item: 2, item: 3, item1: 4} becomes {item: [2,3], item1: 4}
|
|
||||||
*/
|
|
||||||
const buildFormUrlEncodedPayload = (params) => {
|
|
||||||
return params.reduce((acc, p) => {
|
|
||||||
if (!acc[p.name]) {
|
|
||||||
acc[p.name] = p.value;
|
|
||||||
} else if (Array.isArray(acc[p.name])) {
|
|
||||||
acc[p.name].push(p.value);
|
|
||||||
} else {
|
|
||||||
acc[p.name] = [acc[p.name], p.value];
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
uuid,
|
uuid,
|
||||||
stringifyJson,
|
stringifyJson,
|
||||||
@ -111,6 +93,5 @@ module.exports = {
|
|||||||
safeParseJSON,
|
safeParseJSON,
|
||||||
simpleHash,
|
simpleHash,
|
||||||
generateUidBasedOnHash,
|
generateUidBasedOnHash,
|
||||||
flattenDataForDotNotation,
|
flattenDataForDotNotation
|
||||||
buildFormUrlEncodedPayload
|
|
||||||
};
|
};
|
||||||
|
@ -38,6 +38,11 @@ const isDirectory = (dirPath) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const hasSubDirectories = (dir) => {
|
||||||
|
const files = fs.readdirSync(dir);
|
||||||
|
return files.some(file => fs.statSync(path.join(dir, file)).isDirectory());
|
||||||
|
};
|
||||||
|
|
||||||
const normalizeAndResolvePath = (pathname) => {
|
const normalizeAndResolvePath = (pathname) => {
|
||||||
if (isSymbolicLink(pathname)) {
|
if (isSymbolicLink(pathname)) {
|
||||||
const absPath = path.dirname(pathname);
|
const absPath = path.dirname(pathname);
|
||||||
@ -156,8 +161,12 @@ const searchForBruFiles = (dir) => {
|
|||||||
return searchForFiles(dir, '.bru');
|
return searchForFiles(dir, '.bru');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sanitizeCollectionName = (name) => {
|
||||||
|
return name.trim();
|
||||||
|
}
|
||||||
|
|
||||||
const sanitizeDirectoryName = (name) => {
|
const sanitizeDirectoryName = (name) => {
|
||||||
return name.replace(/[<>:"/\\|?*\x00-\x1F]+/g, '-');
|
return name.replace(/[<>:"/\\|?*\x00-\x1F]+/g, '-').trim();
|
||||||
};
|
};
|
||||||
|
|
||||||
const isWindowsOS = () => {
|
const isWindowsOS = () => {
|
||||||
@ -222,7 +231,9 @@ module.exports = {
|
|||||||
searchForFiles,
|
searchForFiles,
|
||||||
searchForBruFiles,
|
searchForBruFiles,
|
||||||
sanitizeDirectoryName,
|
sanitizeDirectoryName,
|
||||||
|
sanitizeCollectionName,
|
||||||
isWindowsOS,
|
isWindowsOS,
|
||||||
safeToRename,
|
safeToRename,
|
||||||
isValidFilename
|
isValidFilename,
|
||||||
|
hasSubDirectories
|
||||||
};
|
};
|
||||||
|
60
packages/bruno-electron/src/utils/form-data.js
Normal file
60
packages/bruno-electron/src/utils/form-data.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
const { forOwn } = require('lodash');
|
||||||
|
const FormData = require('form-data');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array.<object>} params The request body Array
|
||||||
|
* @returns {object} Returns an obj with repeating key as a array of values
|
||||||
|
* {item: 2, item: 3, item1: 4} becomes {item: [2,3], item1: 4}
|
||||||
|
*/
|
||||||
|
const buildFormUrlEncodedPayload = (params) => {
|
||||||
|
return params.reduce((acc, p) => {
|
||||||
|
if (!acc[p.name]) {
|
||||||
|
acc[p.name] = p.value;
|
||||||
|
} else if (Array.isArray(acc[p.name])) {
|
||||||
|
acc[p.name].push(p.value);
|
||||||
|
} else {
|
||||||
|
acc[p.name] = [acc[p.name], p.value];
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const createFormData = (data, collectionPath) => {
|
||||||
|
// make axios work in node using form data
|
||||||
|
// reference: https://github.com/axios/axios/issues/1006#issuecomment-320165427
|
||||||
|
const form = new FormData();
|
||||||
|
forEach(data, (datum) => {
|
||||||
|
const { name, type, value } = datum;
|
||||||
|
|
||||||
|
if (type === 'text') {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
value.forEach((val) => form.append(name, val));
|
||||||
|
} else {
|
||||||
|
form.append(name, value);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'file') {
|
||||||
|
const filePaths = value || [];
|
||||||
|
filePaths.forEach((filePath) => {
|
||||||
|
let trimmedFilePath = filePath.trim();
|
||||||
|
|
||||||
|
if (!path.isAbsolute(trimmedFilePath)) {
|
||||||
|
trimmedFilePath = path.join(collectionPath, trimmedFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
form.append(name, fs.createReadStream(trimmedFilePath), path.basename(trimmedFilePath));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return form;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
buildFormUrlEncodedPayload,
|
||||||
|
createFormData
|
||||||
|
};
|
@ -1,7 +1,7 @@
|
|||||||
const { describe, it, expect } = require('@jest/globals');
|
const { describe, it, expect } = require('@jest/globals');
|
||||||
|
|
||||||
const prepareRequest = require('../../src/ipc/network/prepare-request');
|
const prepareRequest = require('../../src/ipc/network/prepare-request');
|
||||||
const { buildFormUrlEncodedPayload } = require('../../src/utils/common');
|
const { buildFormUrlEncodedPayload } = require('../../src/utils/form-data');
|
||||||
|
|
||||||
describe('prepare-request: prepareRequest', () => {
|
describe('prepare-request: prepareRequest', () => {
|
||||||
describe('Decomments request body', () => {
|
describe('Decomments request body', () => {
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
"graphql": "^16.6.0",
|
"graphql": "^16.6.0",
|
||||||
"markdown-it": "^13.0.1",
|
"markdown-it": "^13.0.1",
|
||||||
"postcss": "8.4.47",
|
"postcss": "8.4.47",
|
||||||
"react": "18.2.0",
|
"react": "19.0.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"rollup":"3.29.5",
|
"rollup":"3.29.5",
|
||||||
"rollup-plugin-dts": "^5.0.0",
|
"rollup-plugin-dts": "^5.0.0",
|
||||||
|
@ -58,6 +58,7 @@ chai.use(function (chai, utils) {
|
|||||||
* endsWith : ends with
|
* endsWith : ends with
|
||||||
* between : between
|
* between : between
|
||||||
* isEmpty : is empty
|
* isEmpty : is empty
|
||||||
|
* isNotEmpty : is not empty
|
||||||
* isNull : is null
|
* isNull : is null
|
||||||
* isUndefined : is undefined
|
* isUndefined : is undefined
|
||||||
* isDefined : is defined
|
* isDefined : is defined
|
||||||
@ -95,6 +96,7 @@ const parseAssertionOperator = (str = '') => {
|
|||||||
'endsWith',
|
'endsWith',
|
||||||
'between',
|
'between',
|
||||||
'isEmpty',
|
'isEmpty',
|
||||||
|
'isNotEmpty',
|
||||||
'isNull',
|
'isNull',
|
||||||
'isUndefined',
|
'isUndefined',
|
||||||
'isDefined',
|
'isDefined',
|
||||||
@ -109,6 +111,7 @@ const parseAssertionOperator = (str = '') => {
|
|||||||
|
|
||||||
const unaryOperators = [
|
const unaryOperators = [
|
||||||
'isEmpty',
|
'isEmpty',
|
||||||
|
'isNotEmpty',
|
||||||
'isNull',
|
'isNull',
|
||||||
'isUndefined',
|
'isUndefined',
|
||||||
'isDefined',
|
'isDefined',
|
||||||
@ -147,6 +150,7 @@ const parseAssertionOperator = (str = '') => {
|
|||||||
const isUnaryOperator = (operator) => {
|
const isUnaryOperator = (operator) => {
|
||||||
const unaryOperators = [
|
const unaryOperators = [
|
||||||
'isEmpty',
|
'isEmpty',
|
||||||
|
'isNotEmpty',
|
||||||
'isNull',
|
'isNull',
|
||||||
'isUndefined',
|
'isUndefined',
|
||||||
'isDefined',
|
'isDefined',
|
||||||
@ -345,6 +349,9 @@ class AssertRuntime {
|
|||||||
case 'isEmpty':
|
case 'isEmpty':
|
||||||
expect(lhs).to.be.empty;
|
expect(lhs).to.be.empty;
|
||||||
break;
|
break;
|
||||||
|
case 'isNotEmpty':
|
||||||
|
expect(lhs).to.not.be.empty;
|
||||||
|
break;
|
||||||
case 'isNull':
|
case 'isNull':
|
||||||
expect(lhs).to.be.null;
|
expect(lhs).to.be.null;
|
||||||
break;
|
break;
|
||||||
|
@ -10,11 +10,11 @@ rm -rf packages/bruno-electron/web
|
|||||||
mkdir packages/bruno-electron/web
|
mkdir packages/bruno-electron/web
|
||||||
|
|
||||||
# Copy build
|
# Copy build
|
||||||
cp -r packages/bruno-app/out/* packages/bruno-electron/web
|
cp -r packages/bruno-app/dist/* packages/bruno-electron/web
|
||||||
|
|
||||||
|
|
||||||
# Change paths in next
|
# Change paths in next
|
||||||
sed -i'' -e 's@/_next/@_next/@g' packages/bruno-electron/web/**.html
|
sed -i'' -e 's@/static/@static/@g' packages/bruno-electron/web/**.html
|
||||||
|
|
||||||
# Remove sourcemaps
|
# Remove sourcemaps
|
||||||
find packages/bruno-electron/web -name '*.map' -type f -delete
|
find packages/bruno-electron/web -name '*.map' -type f -delete
|
||||||
|
Loading…
Reference in New Issue
Block a user