mirror of
https://github.com/usebruno/bruno.git
synced 2024-12-22 14:41:04 +01:00
Merge branch 'main' into feature/save-prompt-tab-context-menu
This commit is contained in:
commit
34b5b4769f
@ -69,6 +69,7 @@ if (!SERVER_RENDERED) {
|
||||
'bru.getVar(key)',
|
||||
'bru.setVar(key,value)',
|
||||
'bru.deleteVar(key)',
|
||||
'bru.deleteAllVars()',
|
||||
'bru.setNextRequest(requestName)',
|
||||
'req.disableParsingResponseJson()',
|
||||
'bru.getRequestVar(key)',
|
||||
|
@ -34,7 +34,7 @@ const StyledWrapper = styled.div`
|
||||
|
||||
.key-button {
|
||||
display: inline-block;
|
||||
color: ${(props) => props.theme.colors.text.white};
|
||||
color: ${(props) => props.theme.table.input.color};
|
||||
border-radius: 4px;
|
||||
padding: 1px 5px;
|
||||
font-family: monospace;
|
||||
|
@ -185,7 +185,7 @@ const Sidebar = () => {
|
||||
Star
|
||||
</GitHubButton> */}
|
||||
</div>
|
||||
<div className="flex flex-grow items-center justify-end text-xs mr-2">v1.31.0</div>
|
||||
<div className="flex flex-grow items-center justify-end text-xs mr-2">v1.32.1</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -19,7 +19,7 @@ import {
|
||||
runRequestEvent,
|
||||
scriptEnvironmentUpdateEvent
|
||||
} from 'providers/ReduxStore/slices/collections';
|
||||
import { collectionAddEnvFileEvent, openCollectionEvent, hydrateCollectionsWithUiStateSnapshot } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { collectionAddEnvFileEvent, openCollectionEvent, hydrateCollectionWithUiStateSnapshot } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import toast from 'react-hot-toast';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { isElectron } from 'utils/common/platform';
|
||||
@ -150,7 +150,7 @@ const useIpcEvents = () => {
|
||||
});
|
||||
|
||||
const removeSnapshotHydrationListener = ipcRenderer.on('main:hydrate-app-with-ui-state-snapshot', (val) => {
|
||||
dispatch(hydrateCollectionsWithUiStateSnapshot(val));
|
||||
dispatch(hydrateCollectionWithUiStateSnapshot(val));
|
||||
})
|
||||
|
||||
return () => {
|
||||
|
@ -60,7 +60,7 @@ const trackStart = () => {
|
||||
event: 'start',
|
||||
properties: {
|
||||
os: platformLib.os.family,
|
||||
version: '1.31.0'
|
||||
version: '1.32.1'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -971,17 +971,19 @@ export const selectEnvironment = (environmentUid, collectionUid) => (dispatch, g
|
||||
}
|
||||
|
||||
const collectionCopy = cloneDeep(collection);
|
||||
if (environmentUid) {
|
||||
const environment = findEnvironmentInCollection(collectionCopy, environmentUid);
|
||||
if (environment) {
|
||||
ipcRenderer.invoke('renderer:update-ui-state-snapshot', { type: 'COLLECTION_ENVIRONMENT', data: { collectionPath: collection?.pathname, environmentName: environment?.name } })
|
||||
dispatch(_selectEnvironment({ environmentUid, collectionUid }));
|
||||
resolve();
|
||||
}
|
||||
else {
|
||||
return reject(new Error('Environment not found'));
|
||||
}
|
||||
}
|
||||
|
||||
const environmentName = environmentUid
|
||||
? findEnvironmentInCollection(collectionCopy, environmentUid)?.name
|
||||
: null;
|
||||
|
||||
if (environmentUid && !environmentName) {
|
||||
return reject(new Error('Environment not found'));
|
||||
}
|
||||
|
||||
ipcRenderer.invoke('renderer:update-ui-state-snapshot', { type: 'COLLECTION_ENVIRONMENT', data: { collectionPath: collection?.pathname, environmentName }});
|
||||
|
||||
dispatch(_selectEnvironment({ environmentUid, collectionUid }));
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
@ -1146,7 +1148,7 @@ export const saveCollectionSecurityConfig = (collectionUid, securityConfig) => (
|
||||
};
|
||||
|
||||
|
||||
export const hydrateCollectionsWithUiStateSnapshot = (payload) => (dispatch, getState) => {
|
||||
export const hydrateCollectionWithUiStateSnapshot = (payload) => (dispatch, getState) => {
|
||||
const collectionSnapshotData = payload;
|
||||
return new Promise((resolve, reject) => {
|
||||
const state = getState();
|
||||
|
@ -74,17 +74,17 @@ const interpolateVars = (request, envVars = {}, runtimeVariables = {}, processEn
|
||||
} else if (contentType === 'application/x-www-form-urlencoded') {
|
||||
if (typeof request.data === 'object') {
|
||||
try {
|
||||
let parsed = JSON.stringify(request.data);
|
||||
parsed = _interpolate(parsed);
|
||||
request.data = JSON.parse(parsed);
|
||||
forOwn(request?.data, (value, key) => {
|
||||
request.data[key] = _interpolate(value);
|
||||
});
|
||||
} catch (err) {}
|
||||
}
|
||||
} else if (contentType === 'multipart/form-data') {
|
||||
if (typeof request.data === 'object' && !(request?.data instanceof FormData)) {
|
||||
try {
|
||||
let parsed = JSON.stringify(request.data);
|
||||
parsed = _interpolate(parsed);
|
||||
request.data = JSON.parse(parsed);
|
||||
forOwn(request?.data, (value, key) => {
|
||||
request.data[key] = _interpolate(value);
|
||||
});
|
||||
} catch (err) {}
|
||||
}
|
||||
} else {
|
||||
|
@ -76,17 +76,17 @@ const prepareRequest = (request, collectionRoot) => {
|
||||
const password = get(request, 'auth.wsse.password', '');
|
||||
|
||||
const ts = new Date().toISOString();
|
||||
const nonce = crypto.randomBytes(16).toString('base64');
|
||||
const nonce = crypto.randomBytes(16).toString('hex');
|
||||
|
||||
// Create the password digest using SHA-256
|
||||
const hash = crypto.createHash('sha256');
|
||||
// Create the password digest using SHA-1 as required for WSSE
|
||||
const hash = crypto.createHash('sha1');
|
||||
hash.update(nonce + ts + password);
|
||||
const digest = hash.digest('base64');
|
||||
const digest = Buffer.from(hash.digest('hex').toString('utf8')).toString('base64');
|
||||
|
||||
// Construct the WSSE header
|
||||
axiosRequest.headers[
|
||||
'X-WSSE'
|
||||
] = `UsernameToken Username="${username}", PasswordDigest="${digest}", Created="${ts}", Nonce="${nonce}"`;
|
||||
] = `UsernameToken Username="${username}", PasswordDigest="${digest}", Nonce="${nonce}", Created="${ts}"`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "v1.31.0",
|
||||
"version": "v1.32.1",
|
||||
"name": "bruno",
|
||||
"description": "Opensource API Client for Exploring and Testing APIs",
|
||||
"homepage": "https://www.usebruno.com",
|
||||
|
@ -25,11 +25,11 @@ const { moveRequestUid, deleteRequestUid } = require('../cache/requestUids');
|
||||
const { deleteCookiesForDomain, getDomainsWithCookies } = require('../utils/cookies');
|
||||
const EnvironmentSecretsStore = require('../store/env-secrets');
|
||||
const CollectionSecurityStore = require('../store/collection-security');
|
||||
const UiStateSnapshot = require('../store/ui-state-snapshot');
|
||||
const UiStateSnapshotStore = require('../store/ui-state-snapshot');
|
||||
|
||||
const environmentSecretsStore = new EnvironmentSecretsStore();
|
||||
const collectionSecurityStore = new CollectionSecurityStore();
|
||||
const UiStateSnapshotStore = new UiStateSnapshot();
|
||||
const uiStateSnapshotStore = new UiStateSnapshotStore();
|
||||
|
||||
const envHasSecrets = (environment = {}) => {
|
||||
const secrets = _.filter(environment.variables, (v) => v.secret);
|
||||
@ -700,7 +700,7 @@ const registerRendererEventHandlers = (mainWindow, watcher, lastOpenedCollection
|
||||
|
||||
ipcMain.handle('renderer:update-ui-state-snapshot', (event, { type, data }) => {
|
||||
try {
|
||||
UiStateSnapshotStore.update({ type, data });
|
||||
uiStateSnapshotStore.update({ type, data });
|
||||
} catch (error) {
|
||||
throw new Error(error.message);
|
||||
}
|
||||
|
@ -78,17 +78,17 @@ const interpolateVars = (request, envVariables = {}, runtimeVariables = {}, proc
|
||||
} else if (contentType === 'application/x-www-form-urlencoded') {
|
||||
if (typeof request.data === 'object') {
|
||||
try {
|
||||
let parsed = JSON.stringify(request.data);
|
||||
parsed = _interpolate(parsed);
|
||||
request.data = JSON.parse(parsed);
|
||||
forOwn(request?.data, (value, key) => {
|
||||
request.data[key] = _interpolate(value);
|
||||
});
|
||||
} catch (err) {}
|
||||
}
|
||||
} else if (contentType === 'multipart/form-data') {
|
||||
if (typeof request.data === 'object' && !(request.data instanceof FormData)) {
|
||||
try {
|
||||
let parsed = JSON.stringify(request.data);
|
||||
parsed = _interpolate(parsed);
|
||||
request.data = JSON.parse(parsed);
|
||||
forOwn(request?.data, (value, key) => {
|
||||
request.data[key] = _interpolate(value);
|
||||
});
|
||||
} catch (err) {}
|
||||
}
|
||||
} else {
|
||||
|
@ -224,17 +224,17 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
|
||||
const password = get(request, 'auth.wsse.password', '');
|
||||
|
||||
const ts = new Date().toISOString();
|
||||
const nonce = crypto.randomBytes(16).toString('base64');
|
||||
const nonce = crypto.randomBytes(16).toString('hex');
|
||||
|
||||
// Create the password digest using SHA-256
|
||||
const hash = crypto.createHash('sha256');
|
||||
// Create the password digest using SHA-1 as required for WSSE
|
||||
const hash = crypto.createHash('sha1');
|
||||
hash.update(nonce + ts + password);
|
||||
const digest = hash.digest('base64');
|
||||
const digest = Buffer.from(hash.digest('hex').toString('utf8')).toString('base64');
|
||||
|
||||
// Construct the WSSE header
|
||||
axiosRequest.headers[
|
||||
'X-WSSE'
|
||||
] = `UsernameToken Username="${username}", PasswordDigest="${digest}", Created="${ts}", Nonce="${nonce}"`;
|
||||
] = `UsernameToken Username="${username}", PasswordDigest="${digest}", Nonce="${nonce}", Created="${ts}"`;
|
||||
break;
|
||||
case 'apikey':
|
||||
const apiKeyAuth = get(collectionAuth, 'apikey');
|
||||
@ -318,17 +318,17 @@ const setAuthHeaders = (axiosRequest, request, collectionRoot) => {
|
||||
const password = get(request, 'auth.wsse.password', '');
|
||||
|
||||
const ts = new Date().toISOString();
|
||||
const nonce = crypto.randomBytes(16).toString('base64');
|
||||
const nonce = crypto.randomBytes(16).toString('hex');
|
||||
|
||||
// Create the password digest using SHA-256
|
||||
const hash = crypto.createHash('sha256');
|
||||
// Create the password digest using SHA-1 as required for WSSE
|
||||
const hash = crypto.createHash('sha1');
|
||||
hash.update(nonce + ts + password);
|
||||
const digest = hash.digest('base64');
|
||||
const digest = Buffer.from(hash.digest('hex').toString('utf8')).toString('base64');
|
||||
|
||||
// Construct the WSSE header
|
||||
axiosRequest.headers[
|
||||
'X-WSSE'
|
||||
] = `UsernameToken Username="${username}", PasswordDigest="${digest}", Created="${ts}", Nonce="${nonce}"`;
|
||||
] = `UsernameToken Username="${username}", PasswordDigest="${digest}", Nonce="${nonce}", Created="${ts}"`;
|
||||
break;
|
||||
case 'apikey':
|
||||
const apiKeyAuth = get(request, 'auth.apikey');
|
||||
|
@ -1,6 +1,6 @@
|
||||
const Store = require('electron-store');
|
||||
|
||||
class UiStateSnapshot {
|
||||
class UiStateSnapshotStore {
|
||||
constructor() {
|
||||
this.store = new Store({
|
||||
name: 'ui-state-snapshot',
|
||||
@ -57,4 +57,4 @@ class UiStateSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UiStateSnapshot;
|
||||
module.exports = UiStateSnapshotStore;
|
||||
|
@ -97,6 +97,14 @@ class Bru {
|
||||
delete this.runtimeVariables[key];
|
||||
}
|
||||
|
||||
deleteAllVars() {
|
||||
for (let key in this.runtimeVariables) {
|
||||
if (this.runtimeVariables.hasOwnProperty(key)) {
|
||||
delete this.runtimeVariables[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getCollectionVar(key) {
|
||||
return this._interpolate(this.collectionVariables[key]);
|
||||
}
|
||||
|
@ -50,7 +50,9 @@ class VarsRuntime {
|
||||
_.each(enabledVars, (v) => {
|
||||
try {
|
||||
const value = evaluateJsExpressionBasedOnRuntime(v.value, context, this.runtime);
|
||||
bru.setVar(v.name, value);
|
||||
if (v.name) {
|
||||
bru.setVar(v.name, value);
|
||||
}
|
||||
} catch (error) {
|
||||
errors.set(v.name, error);
|
||||
}
|
||||
|
@ -21,6 +21,12 @@ const addBruShimToContext = (vm, bru) => {
|
||||
vm.setProp(bruObject, 'getProcessEnv', getProcessEnv);
|
||||
getProcessEnv.dispose();
|
||||
|
||||
let hasEnvVar = vm.newFunction('hasEnvVar', function (key) {
|
||||
return marshallToVm(bru.hasEnvVar(vm.dump(key)), vm);
|
||||
});
|
||||
vm.setProp(bruObject, 'hasEnvVar', hasEnvVar);
|
||||
hasEnvVar.dispose();
|
||||
|
||||
let getEnvVar = vm.newFunction('getEnvVar', function (key) {
|
||||
return marshallToVm(bru.getEnvVar(vm.dump(key)), vm);
|
||||
});
|
||||
@ -33,6 +39,12 @@ const addBruShimToContext = (vm, bru) => {
|
||||
vm.setProp(bruObject, 'setEnvVar', setEnvVar);
|
||||
setEnvVar.dispose();
|
||||
|
||||
let hasVar = vm.newFunction('hasVar', function (key) {
|
||||
return marshallToVm(bru.hasVar(vm.dump(key)), vm);
|
||||
});
|
||||
vm.setProp(bruObject, 'hasVar', hasVar);
|
||||
hasVar.dispose();
|
||||
|
||||
let getVar = vm.newFunction('getVar', function (key) {
|
||||
return marshallToVm(bru.getVar(vm.dump(key)), vm);
|
||||
});
|
||||
@ -45,6 +57,18 @@ const addBruShimToContext = (vm, bru) => {
|
||||
vm.setProp(bruObject, 'setVar', setVar);
|
||||
setVar.dispose();
|
||||
|
||||
let deleteVar = vm.newFunction('deleteVar', function (key) {
|
||||
bru.deleteVar(vm.dump(key));
|
||||
});
|
||||
vm.setProp(bruObject, 'deleteVar', deleteVar);
|
||||
deleteVar.dispose();
|
||||
|
||||
let deleteAllVars = vm.newFunction('deleteAllVars', function () {
|
||||
bru.deleteAllVars();
|
||||
});
|
||||
vm.setProp(bruObject, 'deleteAllVars', deleteAllVars);
|
||||
deleteAllVars.dispose();
|
||||
|
||||
let setNextRequest = vm.newFunction('setNextRequest', function (nextRequest) {
|
||||
bru.setNextRequest(vm.dump(nextRequest));
|
||||
});
|
||||
|
@ -12,12 +12,15 @@ post {
|
||||
|
||||
body:form-urlencoded {
|
||||
form-data-key: {{form-data-key}}
|
||||
}
|
||||
|
||||
script:pre-request {
|
||||
bru.setVar('form-data-key', 'form-data-value');
|
||||
form-data-stringified-object: {{form-data-stringified-object}}
|
||||
}
|
||||
|
||||
assert {
|
||||
res.body: eq form-data-key=form-data-value
|
||||
res.body: eq form-data-key=form-data-value&form-data-stringified-object=%7B%22foo%22%3A123%7D
|
||||
}
|
||||
|
||||
script:pre-request {
|
||||
let obj = JSON.stringify({foo:123});
|
||||
bru.setVar('form-data-key', 'form-data-value');
|
||||
bru.setVar('form-data-stringified-object', obj);
|
||||
}
|
||||
|
@ -11,14 +11,18 @@ post {
|
||||
}
|
||||
|
||||
body:multipart-form {
|
||||
foo: {{form-data-key}}
|
||||
form-data-key: {{form-data-key}}
|
||||
form-data-stringified-object: {{form-data-stringified-object}}
|
||||
file: @file(bruno.png)
|
||||
}
|
||||
|
||||
assert {
|
||||
res.body: contains form-data-value
|
||||
res.body: contains {"foo":123}
|
||||
}
|
||||
|
||||
script:pre-request {
|
||||
let obj = JSON.stringify({foo:123});
|
||||
bru.setVar('form-data-key', 'form-data-value');
|
||||
bru.setVar('form-data-stringified-object', obj);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user