diff --git a/packages/bruno-electron/package.json b/packages/bruno-electron/package.json index 3af73d9d..2fc36671 100644 --- a/packages/bruno-electron/package.json +++ b/packages/bruno-electron/package.json @@ -18,7 +18,7 @@ "@usebruno/lang": "0.4.0", "@usebruno/schema": "0.4.0", "about-window": "^1.15.2", - "axios": "^0.26.0", + "axios": "^1.5.1", "chai": "^4.3.7", "chokidar": "^3.5.3", "dotenv": "^16.0.3", diff --git a/packages/bruno-electron/src/app/collections.js b/packages/bruno-electron/src/app/collections.js index d8c5716b..f3c5b574 100644 --- a/packages/bruno-electron/src/app/collections.js +++ b/packages/bruno-electron/src/app/collections.js @@ -5,20 +5,12 @@ const Yup = require('yup'); const { isDirectory, normalizeAndResolvePath } = require('../utils/filesystem'); const { generateUidBasedOnHash } = require('../utils/common'); -// uid inside collections is deprecated, but we still need to validate it -// for backward compatibility -const uidSchema = Yup.string() - .length(21, 'uid must be 21 characters in length') - .matches(/^[a-zA-Z0-9]*$/, 'uid must be alphanumeric'); - +// todo: bruno.json config schema validation errors must be propagated to the UI const configSchema = Yup.object({ - uid: uidSchema, - name: Yup.string().nullable().max(256, 'name must be 256 characters or less'), + name: Yup.string().max(256, 'name must be 256 characters or less').required('name is required'), type: Yup.string().oneOf(['collection']).required('type is required'), version: Yup.string().oneOf(['1']).required('type is required') -}) - .noUnknown(true) - .strict(); +}); const readConfigFile = async (pathname) => { try { diff --git a/packages/bruno-electron/src/app/watcher.js b/packages/bruno-electron/src/app/watcher.js index 3fd2b08a..c2388d43 100644 --- a/packages/bruno-electron/src/app/watcher.js +++ b/packages/bruno-electron/src/app/watcher.js @@ -12,6 +12,7 @@ const { uuid } = require('../utils/common'); const { getRequestUid } = require('../cache/requestUids'); const { decryptString } = require('../utils/encryption'); const { setDotEnvVars } = require('../store/process-env'); +const { setBrunoConfig } = require('../store/bruno-config'); const EnvironmentSecretsStore = require('../store/env-secrets'); const environmentSecretsStore = new EnvironmentSecretsStore(); @@ -30,6 +31,13 @@ const isDotEnvFile = (pathname, collectionPath) => { return dirname === collectionPath && basename === '.env'; }; +const isBrunoConfigFile = (pathname, collectionPath) => { + const dirname = path.dirname(pathname); + const basename = path.basename(pathname); + + return dirname === collectionPath && basename === 'bruno.json'; +}; + const isBruEnvironmentConfig = (pathname, collectionPath) => { const dirname = path.dirname(pathname); const envDirectory = path.join(collectionPath, 'environments'); @@ -167,6 +175,17 @@ const unlinkEnvironmentFile = async (win, pathname, collectionUid) => { const add = async (win, pathname, collectionUid, collectionPath) => { console.log(`watcher add: ${pathname}`); + if (isBrunoConfigFile(pathname, collectionPath)) { + try { + const content = fs.readFileSync(pathname, 'utf8'); + const jsonData = JSON.parse(content); + + setBrunoConfig(collectionUid, jsonData); + } catch (err) { + console.error(err); + } + } + if (isDotEnvFile(pathname, collectionPath)) { try { const content = fs.readFileSync(pathname, 'utf8'); @@ -281,6 +300,17 @@ const addDirectory = (win, pathname, collectionUid, collectionPath) => { }; const change = async (win, pathname, collectionUid, collectionPath) => { + if (isBrunoConfigFile(pathname, collectionPath)) { + try { + const content = fs.readFileSync(pathname, 'utf8'); + const jsonData = JSON.parse(content); + + setBrunoConfig(collectionUid, jsonData); + } catch (err) { + console.error(err); + } + } + if (isDotEnvFile(pathname, collectionPath)) { try { const content = fs.readFileSync(pathname, 'utf8'); @@ -378,7 +408,7 @@ class Watcher { const watcher = chokidar.watch(watchPath, { ignoreInitial: false, usePolling: false, - ignored: (path) => ['node_modules', '.git', 'bruno.json'].some((s) => path.includes(s)), + ignored: (path) => ['node_modules', '.git'].some((s) => path.includes(s)), persistent: true, ignorePermissionErrors: true, awaitWriteFinish: { diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index bc8deb73..df8bfea0 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -14,6 +14,7 @@ const interpolateVars = require('./interpolate-vars'); const { sortFolder, getAllRequestsInFolderRecursively } = require('./helper'); const { getPreferences } = require('../../store/preferences'); const { getProcessEnvVars } = require('../../store/process-env'); +const { getBrunoConfig } = require('../../store/bruno-config'); // override the default escape function to prevent escaping Mustache.escape = function (value) { @@ -163,6 +164,31 @@ const registerNetworkIpc = (mainWindow) => { const processEnvVars = getProcessEnvVars(collectionUid); + const brunoConfig = getBrunoConfig(collectionUid); + const proxyEnabled = get(brunoConfig, 'proxy.enabled', false); + if (proxyEnabled) { + const proxyProtocol = get(brunoConfig, 'proxy.protocol'); + const proxyHostname = get(brunoConfig, 'proxy.hostname'); + const proxyPort = get(brunoConfig, 'proxy.port'); + const proxyAuthEnabled = get(brunoConfig, 'proxy.auth.enabled', false); + + const proxyConfig = { + protocol: proxyProtocol, + hostname: proxyHostname, + port: proxyPort + }; + if (proxyAuthEnabled) { + const proxyAuthUsername = get(brunoConfig, 'proxy.auth.username'); + const proxyAuthPassword = get(brunoConfig, 'proxy.auth.password'); + proxyConfig.auth = { + username: proxyAuthUsername, + password: proxyAuthPassword + }; + } + + request.proxy = proxyConfig; + } + interpolateVars(request, envVars, collectionVariables, processEnvVars); // stringify the request url encoded params diff --git a/packages/bruno-electron/src/ipc/network/interpolate-vars.js b/packages/bruno-electron/src/ipc/network/interpolate-vars.js index bfb0601d..7cdbcd25 100644 --- a/packages/bruno-electron/src/ipc/network/interpolate-vars.js +++ b/packages/bruno-electron/src/ipc/network/interpolate-vars.js @@ -84,6 +84,17 @@ const interpolateVars = (request, envVars = {}, collectionVariables = {}, proces param.value = interpolate(param.value); }); + if (request.proxy) { + request.proxy.protocol = interpolate(request.proxy.protocol); + request.proxy.hostname = interpolate(request.proxy.hostname); + request.proxy.port = interpolate(request.proxy.port); + + if (request.proxy.auth) { + request.proxy.auth.username = interpolate(request.proxy.auth.username); + request.proxy.auth.password = interpolate(request.proxy.auth.password); + } + } + return request; }; diff --git a/packages/bruno-electron/src/store/bruno-config.js b/packages/bruno-electron/src/store/bruno-config.js new file mode 100644 index 00000000..a092e935 --- /dev/null +++ b/packages/bruno-electron/src/store/bruno-config.js @@ -0,0 +1,19 @@ +/** + * This modules stores the configs loaded from bruno.json + */ + +const config = {}; + +// collectionUid is a hash based on the collection path) +const getBrunoConfig = (collectionUid) => { + return config[collectionUid] || {}; +}; + +const setBrunoConfig = (collectionUid, brunoConfig) => { + config[collectionUid] = brunoConfig; +}; + +module.exports = { + getBrunoConfig, + setBrunoConfig +};