Merge branch 'usebruno:main' into feature/add-raw-file-request-body-option

This commit is contained in:
zachary-berdell-elliott 2024-11-18 21:25:18 -07:00 committed by GitHub
commit fda9b7bd76
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 1994 additions and 1423 deletions

3272
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,9 @@ const StyledWrapper = styled.div`
.CodeMirror-foldmarker { .CodeMirror-foldmarker {
text-shadow: none; text-shadow: none;
color: ${(props) => props.theme.textLink}; color: ${(props) => props.theme.textLink};
background: none;
padding: 0;
margin: 0;
} }
.CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-horizontal div,
@ -75,6 +78,10 @@ const StyledWrapper = styled.div`
.cm-variable-invalid { .cm-variable-invalid {
color: red; color: red;
} }
.CodeMirror-search-hint {
display: inline;
}
`; `;
export default StyledWrapper; export default StyledWrapper;

View File

@ -339,7 +339,7 @@ export default class CodeEditor extends React.Component {
} }
return ( return (
<StyledWrapper <StyledWrapper
className="h-full w-full flex flex-col relative" className="h-full w-full flex flex-col relative graphiql-container"
aria-label="Code Editor" aria-label="Code Editor"
font={this.props.font} font={this.props.font}
fontSize={this.props.fontSize} fontSize={this.props.fontSize}

View File

@ -17,6 +17,15 @@ import Presets from './Presets';
import Info from './Info'; import Info from './Info';
import StyledWrapper from './StyledWrapper'; import StyledWrapper from './StyledWrapper';
import Vars from './Vars/index'; import Vars from './Vars/index';
import DotIcon from 'components/Icons/Dot';
const ContentIndicator = () => {
return (
<sup className="ml-[.125rem] opacity-80 font-medium">
<DotIcon width="10"></DotIcon>
</sup>
);
};
const CollectionSettings = ({ collection }) => { const CollectionSettings = ({ collection }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -30,10 +39,23 @@ const CollectionSettings = ({ collection }) => {
); );
}; };
const proxyConfig = get(collection, 'brunoConfig.proxy', {}); const root = collection?.root;
const hasScripts = root?.request?.script?.res || root?.request?.script?.req;
const hasTests = root?.request?.tests;
const hasDocs = root?.docs;
const headers = get(collection, 'root.request.headers', []);
const activeHeadersCount = headers.filter((header) => header.enabled).length;
const requestVars = get(collection, 'root.request.vars.req', []);
const responseVars = get(collection, 'root.request.vars.res', []);
const activeVarsCount = requestVars.filter((v) => v.enabled).length + responseVars.filter((v) => v.enabled).length;
const auth = get(collection, 'root.request.auth', {}).mode;
const proxyConfig = get(collection, 'brunoConfig.proxy', {});
const clientCertConfig = get(collection, 'brunoConfig.clientCertificates.certs', []); const clientCertConfig = get(collection, 'brunoConfig.clientCertificates.certs', []);
const onProxySettingsUpdate = (config) => { const onProxySettingsUpdate = (config) => {
const brunoConfig = cloneDeep(collection.brunoConfig); const brunoConfig = cloneDeep(collection.brunoConfig);
brunoConfig.proxy = config; brunoConfig.proxy = config;
@ -126,30 +148,38 @@ const CollectionSettings = ({ collection }) => {
<div className="flex flex-wrap items-center tabs" role="tablist"> <div className="flex flex-wrap items-center tabs" role="tablist">
<div className={getTabClassname('headers')} role="tab" onClick={() => setTab('headers')}> <div className={getTabClassname('headers')} role="tab" onClick={() => setTab('headers')}>
Headers Headers
{activeHeadersCount > 0 && <sup className="ml-1 font-medium">{activeHeadersCount}</sup>}
</div> </div>
<div className={getTabClassname('vars')} role="tab" onClick={() => setTab('vars')}> <div className={getTabClassname('vars')} role="tab" onClick={() => setTab('vars')}>
Vars Vars
{activeVarsCount > 0 && <sup className="ml-1 font-medium">{activeVarsCount}</sup>}
</div> </div>
<div className={getTabClassname('auth')} role="tab" onClick={() => setTab('auth')}> <div className={getTabClassname('auth')} role="tab" onClick={() => setTab('auth')}>
Auth Auth
{auth !== 'none' && <ContentIndicator />}
</div> </div>
<div className={getTabClassname('script')} role="tab" onClick={() => setTab('script')}> <div className={getTabClassname('script')} role="tab" onClick={() => setTab('script')}>
Script Script
{hasScripts && <ContentIndicator />}
</div> </div>
<div className={getTabClassname('tests')} role="tab" onClick={() => setTab('tests')}> <div className={getTabClassname('tests')} role="tab" onClick={() => setTab('tests')}>
Tests Tests
{hasTests && <ContentIndicator />}
</div> </div>
<div className={getTabClassname('presets')} role="tab" onClick={() => setTab('presets')}> <div className={getTabClassname('presets')} role="tab" onClick={() => setTab('presets')}>
Presets Presets
</div> </div>
<div className={getTabClassname('proxy')} role="tab" onClick={() => setTab('proxy')}> <div className={getTabClassname('proxy')} role="tab" onClick={() => setTab('proxy')}>
Proxy Proxy
{Object.keys(proxyConfig).length > 0 && <ContentIndicator />}
</div> </div>
<div className={getTabClassname('clientCert')} role="tab" onClick={() => setTab('clientCert')}> <div className={getTabClassname('clientCert')} role="tab" onClick={() => setTab('clientCert')}>
Client Certificates Client Certificates
{clientCertConfig.length > 0 && <ContentIndicator />}
</div> </div>
<div className={getTabClassname('docs')} role="tab" onClick={() => setTab('docs')}> <div className={getTabClassname('docs')} role="tab" onClick={() => setTab('docs')}>
Docs Docs
{hasDocs && <ContentIndicator />}
</div> </div>
<div className={getTabClassname('info')} role="tab" onClick={() => setTab('info')}> <div className={getTabClassname('info')} role="tab" onClick={() => setTab('info')}>
Info Info

View File

@ -7,6 +7,15 @@ 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 DotIcon from 'components/Icons/Dot';
const ContentIndicator = () => {
return (
<sup className="ml-[.125rem] opacity-80 font-medium">
<DotIcon width="10"></DotIcon>
</sup>
);
};
const FolderSettings = ({ collection, folder }) => { const FolderSettings = ({ collection, folder }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -16,6 +25,17 @@ const FolderSettings = ({ collection, folder }) => {
tab = folderLevelSettingsSelectedTab[folder?.uid]; tab = folderLevelSettingsSelectedTab[folder?.uid];
} }
const folderRoot = collection?.items.find((item) => item.uid === folder?.uid)?.root;
const hasScripts = folderRoot?.request?.script?.res || folderRoot?.request?.script?.req;
const hasTests = folderRoot?.request?.tests;
const headers = folderRoot?.request?.headers || [];
const activeHeadersCount = headers.filter((header) => header.enabled).length;
const requestVars = folderRoot?.request?.vars?.req || [];
const responseVars = folderRoot?.request?.vars?.res || [];
const activeVarsCount = requestVars.filter((v) => v.enabled).length + responseVars.filter((v) => v.enabled).length;
const setTab = (tab) => { const setTab = (tab) => {
dispatch( dispatch(
updatedFolderSettingsSelectedTab({ updatedFolderSettingsSelectedTab({
@ -55,15 +75,19 @@ const FolderSettings = ({ collection, folder }) => {
<div className="flex flex-wrap items-center tabs" role="tablist"> <div className="flex flex-wrap items-center tabs" role="tablist">
<div className={getTabClassname('headers')} role="tab" onClick={() => setTab('headers')}> <div className={getTabClassname('headers')} role="tab" onClick={() => setTab('headers')}>
Headers Headers
{activeHeadersCount > 0 && <sup className="ml-1 font-medium">{activeHeadersCount}</sup>}
</div> </div>
<div className={getTabClassname('script')} role="tab" onClick={() => setTab('script')}> <div className={getTabClassname('script')} role="tab" onClick={() => setTab('script')}>
Script Script
{hasScripts && <ContentIndicator />}
</div> </div>
<div className={getTabClassname('test')} role="tab" onClick={() => setTab('test')}> <div className={getTabClassname('test')} role="tab" onClick={() => setTab('test')}>
Test Test
{hasTests && <ContentIndicator />}
</div> </div>
<div className={getTabClassname('vars')} role="tab" onClick={() => setTab('vars')}> <div className={getTabClassname('vars')} role="tab" onClick={() => setTab('vars')}>
Vars Vars
{activeVarsCount > 0 && <sup className="ml-1 font-medium">{activeVarsCount}</sup>}
</div> </div>
</div> </div>
<section className={`flex mt-4 h-full`}>{getTabPanel(tab)}</section> <section className={`flex mt-4 h-full`}>{getTabPanel(tab)}</section>

View File

@ -17,9 +17,11 @@ import { find, get } from 'lodash';
import Documentation from 'components/Documentation/index'; import Documentation from 'components/Documentation/index';
const ContentIndicator = () => { const ContentIndicator = () => {
return <sup className="ml-[.125rem] opacity-80 font-medium"> return (
<sup className="ml-[.125rem] opacity-80 font-medium">
<DotIcon width="10"></DotIcon> <DotIcon width="10"></DotIcon>
</sup> </sup>
);
}; };
const HttpRequestPane = ({ item, collection, leftPaneWidth }) => { const HttpRequestPane = ({ item, collection, leftPaneWidth }) => {
@ -100,6 +102,7 @@ const HttpRequestPane = ({ item, collection, leftPaneWidth }) => {
const docs = getPropertyFromDraftOrRequest('request.docs'); const docs = getPropertyFromDraftOrRequest('request.docs');
const requestVars = getPropertyFromDraftOrRequest('request.vars.req'); const requestVars = getPropertyFromDraftOrRequest('request.vars.req');
const responseVars = getPropertyFromDraftOrRequest('request.vars.res'); const responseVars = getPropertyFromDraftOrRequest('request.vars.res');
const auth = getPropertyFromDraftOrRequest('request.auth');
const activeParamsLength = params.filter((param) => param.enabled).length; const activeParamsLength = params.filter((param) => param.enabled).length;
const activeHeadersLength = headers.filter((header) => header.enabled).length; const activeHeadersLength = headers.filter((header) => header.enabled).length;
@ -125,6 +128,7 @@ const HttpRequestPane = ({ item, collection, leftPaneWidth }) => {
</div> </div>
<div className={getTabClassname('auth')} role="tab" onClick={() => selectTab('auth')}> <div className={getTabClassname('auth')} role="tab" onClick={() => selectTab('auth')}>
Auth Auth
{auth.mode !== 'none' && <ContentIndicator />}
</div> </div>
<div className={getTabClassname('vars')} role="tab" onClick={() => selectTab('vars')}> <div className={getTabClassname('vars')} role="tab" onClick={() => selectTab('vars')}>
Vars Vars

View File

@ -50,6 +50,10 @@ const StyledWrapper = styled.div`
.cm-variable-invalid { .cm-variable-invalid {
color: red; color: red;
} }
.CodeMirror-search-hint {
display: inline;
}
`; `;
export default StyledWrapper; export default StyledWrapper;

View File

@ -209,7 +209,7 @@ export default class QueryEditor extends React.Component {
return ( return (
<> <>
<StyledWrapper <StyledWrapper
className="h-full w-full flex flex-col relative" className="h-full w-full flex flex-col relative graphiql-container"
aria-label="Query Editor" aria-label="Query Editor"
font={this.props.font} font={this.props.font}
fontSize={this.props.fontSize} fontSize={this.props.fontSize}

View File

@ -184,7 +184,7 @@ const Sidebar = () => {
Star Star
</GitHubButton> */} </GitHubButton> */}
</div> </div>
<div className="flex flex-grow items-center justify-end text-xs mr-2">v1.34.0</div> <div className="flex flex-grow items-center justify-end text-xs mr-2">v1.34.2</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -60,7 +60,7 @@ const trackStart = () => {
event: 'start', event: 'start',
properties: { properties: {
os: platformLib.os.family, os: platformLib.os.family,
version: '1.34.0' version: '1.34.2'
} }
}); });
}; };

View File

@ -201,7 +201,7 @@ export const sendCollectionOauth2Request = (collectionUid, itemUid) => (dispatch
const environment = findEnvironmentInCollection(collectionCopy, collection.activeEnvironmentUid); const environment = findEnvironmentInCollection(collectionCopy, collection.activeEnvironmentUid);
_sendCollectionOauth2Request(collection, environment, collectionCopy.runtimeVariables) _sendCollectionOauth2Request(collectionCopy, environment, collectionCopy.runtimeVariables)
.then((response) => { .then((response) => {
if (response?.data?.error) { if (response?.data?.error) {
toast.error(response?.data?.error); toast.error(response?.data?.error);

View File

@ -23,23 +23,26 @@
--color-method-options: rgb(52 52 52); --color-method-options: rgb(52 52 52);
--color-method-head: rgb(52 52 52); --color-method-head: rgb(52 52 52);
} }
:root,.graphiql-container,.CodeMirror-info,.CodeMirror-lint-tooltip,reach-portal {
*,.graphiql-container,.CodeMirror-info,.CodeMirror-lint-tooltip,reach-portal {
/* Required CSS variables after upgrading GraphiQL from v1.5.9 to v2.4.7 */ /* Required CSS variables after upgrading GraphiQL from v1.5.9 to v2.4.7 */
--color-primary: 0, 0%, 0% !important; /* Colors */
--color-secondary: 0, 0%, 0% !important; --color-primary: 320, 95%, 43% !important;
--color-tertiary: 0, 0%, 0% !important; --color-secondary: 242, 51%, 61% !important;
--color-info: 0, 0%, 0% !important; --color-tertiary: 188, 100%, 36% !important;
--color-success: 0, 0%, 0% !important; --color-info: 208, 100%, 46% !important;
--color-warning: 0, 0%, 0% !important; --color-success: 158, 60%, 42% !important;
--color-error: 0, 0%, 0% !important; --color-warning: 36, 100%, 41% !important;
--color-neutral: 0, 0%, 0% !important; --color-error: 13, 93%, 58% !important;
--color-base: 0, 0%, 100% !important; --color-neutral: 219, 28%, 32% !important;
--alpha-secondary: .76; --color-base: 219, 28%, 100% !important;
--alpha-tertiary: .5;
--alpha-background-heavy: .15; /* Color alpha values */
--alpha-background-medium: .1; --alpha-secondary: 0.76 !important;
--alpha-background-light: .07; --alpha-tertiary: 0.5 !important;
--alpha-background-heavy: 0.15 !important;
--alpha-background-medium: 0.1 !important;
--alpha-background-light: 0.07 !important;
--font-size-hint: .75rem; --font-size-hint: .75rem;
--font-size-inline-code: .8125rem; --font-size-inline-code: .8125rem;
--font-size-body: .9375rem; --font-size-body: .9375rem;

View File

@ -246,7 +246,7 @@ const runSingleRequest = async function (
data: null, data: null,
responseTime: 0 responseTime: 0
}, },
error: err.message, error: err?.message || err?.errors?.map(e => e?.message)?.at(0) || err?.code || 'Request Failed!',
assertionResults: [], assertionResults: [],
testResults: [], testResults: [],
nextRequestName: nextRequestName nextRequestName: nextRequestName

View File

@ -1,5 +1,5 @@
{ {
"version": "v1.34.0", "version": "v1.34.2",
"name": "bruno", "name": "bruno",
"description": "Opensource API Client for Exploring and Testing APIs", "description": "Opensource API Client for Exploring and Testing APIs",
"homepage": "https://www.usebruno.com", "homepage": "https://www.usebruno.com",
@ -63,6 +63,6 @@
}, },
"devDependencies": { "devDependencies": {
"electron": "31.2.1", "electron": "31.2.1",
"electron-builder": "23.0.2" "electron-builder": "25.1.8"
} }
} }

View File

@ -77,14 +77,20 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
try { try {
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`); const files = fs.readdirSync(dirPath);
if (files.length > 0) {
throw new Error(`collection: ${dirPath} already exists and is not empty`);
}
} }
if (!isValidPathname(dirPath)) { if (!isValidPathname(dirPath)) {
throw new Error(`collection: invalid pathname - ${dir}`); throw new Error(`collection: invalid pathname - ${dir}`);
} }
if (!fs.existsSync(dirPath)) {
await createDirectory(dirPath); await createDirectory(dirPath);
}
const uid = generateUidBasedOnHash(dirPath); const uid = generateUidBasedOnHash(dirPath);
const brunoConfig = { const brunoConfig = {

View File

@ -741,7 +741,7 @@ const registerNetworkIpc = (mainWindow) => {
const collectionRoot = get(collection, 'root', {}); const collectionRoot = get(collection, 'root', {});
const _request = collectionRoot?.request; const _request = collectionRoot?.request;
const request = prepareCollectionRequest(_request, collectionRoot, collectionPath); const request = prepareCollectionRequest(_request, collection, collectionPath);
request.__bruno__executionMode = 'standalone'; request.__bruno__executionMode = 'standalone';
const envVars = getEnvVars(environment); const envVars = getEnvVars(environment);
const processEnvVars = getProcessEnvVars(collectionUid); const processEnvVars = getProcessEnvVars(collectionUid);

View File

@ -1,7 +1,8 @@
const { get, each } = require('lodash'); const { get, each } = require('lodash');
const { setAuthHeaders } = require('./prepare-request'); const { setAuthHeaders } = require('./prepare-request');
const prepareCollectionRequest = (request, collectionRoot) => { const prepareCollectionRequest = (request, collection) => {
const collectionRoot = get(collection, 'root', {});
const headers = {}; const headers = {};
let contentTypeDefined = false; let contentTypeDefined = false;
let url = request.url; let url = request.url;
@ -35,6 +36,8 @@ const prepareCollectionRequest = (request, collectionRoot) => {
axiosRequest = setAuthHeaders(axiosRequest, request, collectionRoot); axiosRequest = setAuthHeaders(axiosRequest, request, collectionRoot);
axiosRequest.globalEnvironmentVariables = collection?.globalEnvironmentVariables;
if (request.script) { if (request.script) {
axiosRequest.script = request.script; axiosRequest.script = request.script;
} }

View File

@ -24,7 +24,7 @@ get(data, '..items[?]', { id: 2, amount: 20 })
``` ```
Array mapping [?] with corresponding mapper function Array mapping [?] with corresponding mapper function
```js ```js
get(data, '..items[?].amount', i => i.amount + 10) get(data, '..items..amount[?]', amt => amt + 10)
``` ```
### Publish to Npm Registry ### Publish to Npm Registry

View File

@ -5,7 +5,7 @@ meta {
} }
get { get {
url: https://www.usebruno.com/images/landing-2.png url: https://gloutnikov.com/post/2024/bruno.png
body: none body: none
auth: none auth: none
} }