diff --git a/package-lock.json b/package-lock.json index 385fc649b..1803c16d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ "packages/bruno-js", "packages/bruno-lang", "packages/bruno-tests", - "packages/bruno-testbench", "packages/bruno-toml", "packages/bruno-graphql-docs" ], @@ -5418,10 +5417,6 @@ "resolved": "packages/bruno-schema", "link": true }, - "node_modules/@usebruno/testbench": { - "resolved": "packages/bruno-testbench", - "link": true - }, "node_modules/@usebruno/tests": { "resolved": "packages/bruno-tests", "link": true @@ -7392,16 +7387,6 @@ "version": "4.0.0", "license": "ISC" }, - "node_modules/config": { - "version": "3.3.9", - "license": "MIT", - "dependencies": { - "json5": "^2.2.3" - }, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/config-chain": { "version": "1.1.13", "dev": true, @@ -11914,6 +11899,7 @@ }, "node_modules/json5": { "version": "2.2.3", + "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" @@ -17928,6 +17914,7 @@ "@reduxjs/toolkit": "^1.8.0", "@tabler/icons": "^1.46.0", "@tippyjs/react": "^4.2.6", + "@usebruno/common": "0.1.0", "@usebruno/graphql-docs": "0.1.0", "@usebruno/schema": "0.6.0", "axios": "^1.5.1", @@ -17944,7 +17931,6 @@ "graphiql": "^1.5.9", "graphql": "^16.6.0", "graphql-request": "^3.7.0", - "handlebars": "^4.7.8", "httpsnippet": "^3.0.1", "idb": "^7.0.0", "immer": "^9.0.15", @@ -18145,6 +18131,7 @@ "version": "1.3.0", "license": "MIT", "dependencies": { + "@usebruno/common": "0.1.0", "@usebruno/js": "0.9.4", "@usebruno/lang": "0.9.0", "axios": "^1.5.1", @@ -18153,7 +18140,6 @@ "decomment": "^0.9.5", "form-data": "^4.0.0", "fs-extra": "^10.1.0", - "handlebars": "^4.7.8", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", "inquirer": "^9.1.4", @@ -18253,6 +18239,7 @@ "dependencies": { "@aws-sdk/credential-providers": "^3.425.0", "@n8n/vm2": "^3.9.23", + "@usebruno/common": "0.1.0", "@usebruno/js": "0.9.4", "@usebruno/lang": "0.9.0", "@usebruno/schema": "0.6.0", @@ -18271,7 +18258,6 @@ "form-data": "^4.0.0", "fs-extra": "^10.1.0", "graphql": "^16.6.0", - "handlebars": "^4.7.8", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", "is-valid-path": "^0.1.1", @@ -18603,6 +18589,7 @@ "packages/bruno-testbench": { "name": "@usebruno/testbench", "version": "1.0.0", + "extraneous": true, "license": "ISC", "dependencies": { "body-parser": "^1.20.0", @@ -18615,21 +18602,8 @@ "multer": "^1.4.5-lts.1" } }, - "packages/bruno-testbench/node_modules/argparse": { - "version": "2.0.1", - "license": "Python-2.0" - }, - "packages/bruno-testbench/node_modules/js-yaml": { - "version": "4.1.0", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "packages/bruno-tests": { + "name": "@usebruno/tests", "version": "0.0.1", "license": "MIT", "dependencies": { @@ -22559,6 +22533,7 @@ "@reduxjs/toolkit": "^1.8.0", "@tabler/icons": "^1.46.0", "@tippyjs/react": "^4.2.6", + "@usebruno/common": "0.1.0", "@usebruno/graphql-docs": "0.1.0", "@usebruno/schema": "0.6.0", "axios": "^1.5.1", @@ -22579,7 +22554,6 @@ "graphiql": "^1.5.9", "graphql": "^16.6.0", "graphql-request": "^3.7.0", - "handlebars": "^4.7.8", "html-loader": "^3.0.1", "html-webpack-plugin": "^5.5.0", "httpsnippet": "^3.0.1", @@ -22709,6 +22683,7 @@ "@usebruno/cli": { "version": "file:packages/bruno-cli", "requires": { + "@usebruno/common": "0.1.0", "@usebruno/js": "0.9.4", "@usebruno/lang": "0.9.0", "axios": "^1.5.1", @@ -22717,7 +22692,6 @@ "decomment": "^0.9.5", "form-data": "^4.0.0", "fs-extra": "^10.1.0", - "handlebars": "^4.7.8", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", "inquirer": "^9.1.4", @@ -22885,30 +22859,6 @@ "version": "file:packages/bruno-schema", "requires": {} }, - "@usebruno/testbench": { - "version": "file:packages/bruno-testbench", - "requires": { - "body-parser": "^1.20.0", - "config": "^3.3.8", - "cors": "^2.8.5", - "express": "^4.18.1", - "express-xml-bodyparser": "^0.3.0", - "js-yaml": "^4.1.0", - "lodash": "^4.17.21", - "multer": "^1.4.5-lts.1" - }, - "dependencies": { - "argparse": { - "version": "2.0.1" - }, - "js-yaml": { - "version": "4.1.0", - "requires": { - "argparse": "^2.0.1" - } - } - } - }, "@usebruno/tests": { "version": "file:packages/bruno-tests", "requires": { @@ -23753,6 +23703,7 @@ "requires": { "@aws-sdk/credential-providers": "^3.425.0", "@n8n/vm2": "^3.9.23", + "@usebruno/common": "0.1.0", "@usebruno/js": "0.9.4", "@usebruno/lang": "0.9.0", "@usebruno/schema": "0.6.0", @@ -23775,7 +23726,6 @@ "form-data": "^4.0.0", "fs-extra": "^10.1.0", "graphql": "^16.6.0", - "handlebars": "^4.7.8", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", "is-valid-path": "^0.1.1", @@ -24373,12 +24323,6 @@ } } }, - "config": { - "version": "3.3.9", - "requires": { - "json5": "^2.2.3" - } - }, "config-chain": { "version": "1.1.13", "dev": true, @@ -27310,7 +27254,8 @@ "version": "5.0.1" }, "json5": { - "version": "2.2.3" + "version": "2.2.3", + "dev": true }, "jsonfile": { "version": "6.1.0", diff --git a/packages/bruno-app/package.json b/packages/bruno-app/package.json index 31a3da61e..63708ce04 100644 --- a/packages/bruno-app/package.json +++ b/packages/bruno-app/package.json @@ -18,6 +18,7 @@ "@reduxjs/toolkit": "^1.8.0", "@tabler/icons": "^1.46.0", "@tippyjs/react": "^4.2.6", + "@usebruno/common": "0.1.0", "@usebruno/graphql-docs": "0.1.0", "@usebruno/schema": "0.6.0", "axios": "^1.5.1", @@ -34,7 +35,6 @@ "graphiql": "^1.5.9", "graphql": "^16.6.0", "graphql-request": "^3.7.0", - "handlebars": "^4.7.8", "httpsnippet": "^3.0.1", "idb": "^7.0.0", "immer": "^9.0.15", diff --git a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/index.js b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/index.js index 3b16d99cf..45ab20bbe 100644 --- a/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/index.js +++ b/packages/bruno-app/src/components/Sidebar/Collections/Collection/CollectionItem/GenerateCodeItem/index.js @@ -4,17 +4,19 @@ import CodeView from './CodeView'; import StyledWrapper from './StyledWrapper'; import { isValidUrl } from 'utils/url/index'; import get from 'lodash/get'; -import handlebars from 'handlebars'; import { findEnvironmentInCollection } from 'utils/collections'; +// Todo: Fix this +// import { interpolate } from '@usebruno/common'; +const brunoCommon = require('@usebruno/common'); +const { interpolate } = brunoCommon.default; + const interpolateUrl = ({ url, envVars, collectionVariables, processEnvVars }) => { if (!url || !url.length || typeof url !== 'string') { return; } - const template = handlebars.compile(url, { noEscape: true }); - - return template({ + return interpolate(url, { ...envVars, ...collectionVariables, process: { diff --git a/packages/bruno-cli/package.json b/packages/bruno-cli/package.json index 9151d1534..bc8ccf166 100644 --- a/packages/bruno-cli/package.json +++ b/packages/bruno-cli/package.json @@ -24,6 +24,7 @@ "package.json" ], "dependencies": { + "@usebruno/common": "0.1.0", "@usebruno/js": "0.9.4", "@usebruno/lang": "0.9.0", "axios": "^1.5.1", @@ -32,7 +33,6 @@ "decomment": "^0.9.5", "form-data": "^4.0.0", "fs-extra": "^10.1.0", - "handlebars": "^4.7.8", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", "inquirer": "^9.1.4", diff --git a/packages/bruno-cli/src/runner/interpolate-string.js b/packages/bruno-cli/src/runner/interpolate-string.js index 33701dd0b..052041670 100644 --- a/packages/bruno-cli/src/runner/interpolate-string.js +++ b/packages/bruno-cli/src/runner/interpolate-string.js @@ -1,21 +1,5 @@ -const Handlebars = require('handlebars'); const { forOwn, cloneDeep } = require('lodash'); - -const interpolateEnvVars = (str, processEnvVars) => { - if (!str || !str.length || typeof str !== 'string') { - return str; - } - - const template = Handlebars.compile(str, { noEscape: true }); - - return template({ - process: { - env: { - ...processEnvVars - } - } - }); -}; +const { interpolate } = require('@usebruno/common'); const interpolateString = (str, { envVars, collectionVariables, processEnvVars }) => { if (!str || !str.length || typeof str !== 'string') { @@ -31,11 +15,15 @@ const interpolateString = (str, { envVars, collectionVariables, processEnvVars } // envVars can inturn have values as {{process.env.VAR_NAME}} // so we need to interpolate envVars first with processEnvVars forOwn(envVars, (value, key) => { - envVars[key] = interpolateEnvVars(value, processEnvVars); + envVars[key] = interpolate(value, { + process: { + env: { + ...processEnvVars + } + } + }); }); - const template = Handlebars.compile(str, { noEscape: true }); - // collectionVariables take precedence over envVars const combinedVars = { ...envVars, @@ -47,7 +35,7 @@ const interpolateString = (str, { envVars, collectionVariables, processEnvVars } } }; - return template(combinedVars); + return interpolate(str, combinedVars); }; module.exports = { diff --git a/packages/bruno-cli/src/runner/interpolate-vars.js b/packages/bruno-cli/src/runner/interpolate-vars.js index a764e791d..2585c1e3c 100644 --- a/packages/bruno-cli/src/runner/interpolate-vars.js +++ b/packages/bruno-cli/src/runner/interpolate-vars.js @@ -1,4 +1,4 @@ -const Handlebars = require('handlebars'); +const { interpolate } = require('@usebruno/common'); const { each, forOwn, cloneDeep } = require('lodash'); const getContentType = (headers = {}) => { @@ -12,24 +12,6 @@ const getContentType = (headers = {}) => { return contentType; }; -const interpolateEnvVars = (str, processEnvVars) => { - if (!str || !str.length || typeof str !== 'string') { - return str; - } - - const template = Handlebars.compile(str, { noEscape: true }); - - return template({ - process: { - env: { - ...processEnvVars - } - } - }); -}; - -const varsRegex = /(? { // we clone envVars because we don't want to modify the original object envVars = cloneDeep(envVars); @@ -37,20 +19,20 @@ const interpolateVars = (request, envVars = {}, collectionVariables = {}, proces // envVars can inturn have values as {{process.env.VAR_NAME}} // so we need to interpolate envVars first with processEnvVars forOwn(envVars, (value, key) => { - envVars[key] = interpolateEnvVars(value, processEnvVars); + envVars[key] = interpolate(value, { + process: { + env: { + ...processEnvVars + } + } + }); }); - const interpolate = (str) => { + const _interpolate = (str) => { if (!str || !str.length || typeof str !== 'string') { return str; } - if (varsRegex.test(str)) { - // Handlebars doesn't allow dots as identifiers, so we need to use literal segments - str = str.replaceAll(varsRegex, '{{[$1]}}'); - } - const template = Handlebars.compile(str, { noEscape: true }); - // collectionVariables take precedence over envVars const combinedVars = { ...envVars, @@ -62,14 +44,14 @@ const interpolateVars = (request, envVars = {}, collectionVariables = {}, proces } }; - return template(combinedVars); + return interpolate(str, combinedVars); }; - request.url = interpolate(request.url); + request.url = _interpolate(request.url); forOwn(request.headers, (value, key) => { delete request.headers[key]; - request.headers[interpolate(key)] = interpolate(value); + request.headers[_interpolate(key)] = _interpolate(value); }); const contentType = getContentType(request.headers); @@ -78,40 +60,40 @@ const interpolateVars = (request, envVars = {}, collectionVariables = {}, proces if (typeof request.data === 'object') { try { let parsed = JSON.stringify(request.data); - parsed = interpolate(parsed); + parsed = _interpolate(parsed); request.data = JSON.parse(parsed); } catch (err) {} } if (typeof request.data === 'string') { if (request.data.length) { - request.data = interpolate(request.data); + request.data = _interpolate(request.data); } } } else if (contentType === 'application/x-www-form-urlencoded') { if (typeof request.data === 'object') { try { let parsed = JSON.stringify(request.data); - parsed = interpolate(parsed); + parsed = _interpolate(parsed); request.data = JSON.parse(parsed); } catch (err) {} } } else { - request.data = interpolate(request.data); + request.data = _interpolate(request.data); } each(request.params, (param) => { - param.value = interpolate(param.value); + 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); + 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); + request.proxy.auth.username = _interpolate(request.proxy.auth.username); + request.proxy.auth.password = _interpolate(request.proxy.auth.password); } } @@ -119,8 +101,8 @@ const interpolateVars = (request, envVars = {}, collectionVariables = {}, proces // need to refactor this in the future // the request.auth (basic auth) object gets set inside the prepare-request.js file if (request.auth) { - const username = interpolate(request.auth.username) || ''; - const password = interpolate(request.auth.password) || ''; + const username = _interpolate(request.auth.username) || ''; + const password = _interpolate(request.auth.password) || ''; // use auth header based approach and delete the request.auth object request.headers['authorization'] = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`; diff --git a/packages/bruno-common/src/interpolate/index.spec.ts b/packages/bruno-common/src/interpolate/index.spec.ts index ca2384ba4..9779021ee 100644 --- a/packages/bruno-common/src/interpolate/index.spec.ts +++ b/packages/bruno-common/src/interpolate/index.spec.ts @@ -82,3 +82,90 @@ describe('interpolate', () => { expect(result).toBe('Hello, my name is Not Bruno and I am 4 years old'); }); }); + +describe('interpolate - template edge cases', () => { + it('should return the input string if the template is not a string', () => { + const inputString = 123; + const inputObject = { + user: 'Bruno' + }; + + const result = interpolate(inputString as any, inputObject); + expect(result).toBe(inputString); + }); + + it('should return the input string if the template is null', () => { + const inputString = null; + const inputObject = { + user: 'Bruno' + }; + + const result = interpolate(inputString as any, inputObject); + expect(result).toBe(inputString); + }); + + it('should return the input string if the template is undefined', () => { + const inputString = undefined; + const inputObject = { + user: 'Bruno' + }; + + const result = interpolate(inputString as any, inputObject); + expect(result).toBe(inputString); + }); + + it('should return the input string if the template is empty', () => { + const inputString = ''; + const inputObject = { + user: 'Bruno' + }; + + const result = interpolate(inputString, inputObject); + expect(result).toBe(inputString); + }); + + it('should return preserve whitespaces', () => { + const inputString = ' '; + const inputObject = { + user: 'Bruno' + }; + + const result = interpolate(inputString, inputObject); + + expect(result).toBe(inputString); + }); +}); + +describe('interpolate - value edge cases', () => { + it('should return the input string if the value is not an object', () => { + const inputString = 'Hello, my name is {{user.name}}'; + const inputObject = 123; + + const result = interpolate(inputString, inputObject as any); + expect(result).toBe(inputString); + }); + + it('should return the input string if the value is null', () => { + const inputString = 'Hello, my name is {{user.name}}'; + const inputObject = null; + + const result = interpolate(inputString, inputObject as any); + expect(result).toBe(inputString); + }); + + it('should return the input string if the value is undefined', () => { + const inputString = 'Hello, my name is {{user.name}}'; + const inputObject = undefined; + + const result = interpolate(inputString, inputObject as any); + expect(result).toBe(inputString); + }); + + it('should return the input string if the value is empty', () => { + const inputString = 'Hello, my name is {{user.name}}'; + const inputObject = {}; + + const result = interpolate(inputString, inputObject); + expect(result).toBe(inputString); + }); +}); diff --git a/packages/bruno-electron/package.json b/packages/bruno-electron/package.json index a61e2e848..45c2d88a9 100644 --- a/packages/bruno-electron/package.json +++ b/packages/bruno-electron/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@aws-sdk/credential-providers": "^3.425.0", + "@usebruno/common": "0.1.0", "@usebruno/js": "0.9.4", "@usebruno/lang": "0.9.0", "@usebruno/schema": "0.6.0", @@ -38,7 +39,6 @@ "form-data": "^4.0.0", "fs-extra": "^10.1.0", "graphql": "^16.6.0", - "handlebars": "^4.7.8", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", "is-valid-path": "^0.1.1", diff --git a/packages/bruno-electron/src/ipc/network/interpolate-string.js b/packages/bruno-electron/src/ipc/network/interpolate-string.js index 33701dd0b..052041670 100644 --- a/packages/bruno-electron/src/ipc/network/interpolate-string.js +++ b/packages/bruno-electron/src/ipc/network/interpolate-string.js @@ -1,21 +1,5 @@ -const Handlebars = require('handlebars'); const { forOwn, cloneDeep } = require('lodash'); - -const interpolateEnvVars = (str, processEnvVars) => { - if (!str || !str.length || typeof str !== 'string') { - return str; - } - - const template = Handlebars.compile(str, { noEscape: true }); - - return template({ - process: { - env: { - ...processEnvVars - } - } - }); -}; +const { interpolate } = require('@usebruno/common'); const interpolateString = (str, { envVars, collectionVariables, processEnvVars }) => { if (!str || !str.length || typeof str !== 'string') { @@ -31,11 +15,15 @@ const interpolateString = (str, { envVars, collectionVariables, processEnvVars } // envVars can inturn have values as {{process.env.VAR_NAME}} // so we need to interpolate envVars first with processEnvVars forOwn(envVars, (value, key) => { - envVars[key] = interpolateEnvVars(value, processEnvVars); + envVars[key] = interpolate(value, { + process: { + env: { + ...processEnvVars + } + } + }); }); - const template = Handlebars.compile(str, { noEscape: true }); - // collectionVariables take precedence over envVars const combinedVars = { ...envVars, @@ -47,7 +35,7 @@ const interpolateString = (str, { envVars, collectionVariables, processEnvVars } } }; - return template(combinedVars); + return interpolate(str, combinedVars); }; module.exports = { diff --git a/packages/bruno-electron/src/ipc/network/interpolate-vars.js b/packages/bruno-electron/src/ipc/network/interpolate-vars.js index 8c165e1b0..417f2df71 100644 --- a/packages/bruno-electron/src/ipc/network/interpolate-vars.js +++ b/packages/bruno-electron/src/ipc/network/interpolate-vars.js @@ -1,4 +1,4 @@ -const Handlebars = require('handlebars'); +const { interpolate } = require('@usebruno/common'); const { each, forOwn, cloneDeep } = require('lodash'); const getContentType = (headers = {}) => { @@ -12,24 +12,6 @@ const getContentType = (headers = {}) => { return contentType; }; -const interpolateEnvVars = (str, processEnvVars) => { - if (!str || !str.length || typeof str !== 'string') { - return str; - } - - const template = Handlebars.compile(str, { noEscape: true }); - - return template({ - process: { - env: { - ...processEnvVars - } - } - }); -}; - -const varsRegex = /(? { // we clone envVars because we don't want to modify the original object envVars = cloneDeep(envVars); @@ -37,20 +19,20 @@ const interpolateVars = (request, envVars = {}, collectionVariables = {}, proces // envVars can inturn have values as {{process.env.VAR_NAME}} // so we need to interpolate envVars first with processEnvVars forOwn(envVars, (value, key) => { - envVars[key] = interpolateEnvVars(value, processEnvVars); + envVars[key] = interpolate(value, { + process: { + env: { + ...processEnvVars + } + } + }); }); - const interpolate = (str) => { + const _interpolate = (str) => { if (!str || !str.length || typeof str !== 'string') { return str; } - if (varsRegex.test(str)) { - // Handlebars doesn't allow dots as identifiers, so we need to use literal segments - str = str.replaceAll(varsRegex, '{{[$1]}}'); - } - const template = Handlebars.compile(str, { noEscape: true }); - // collectionVariables take precedence over envVars const combinedVars = { ...envVars, @@ -62,14 +44,14 @@ const interpolateVars = (request, envVars = {}, collectionVariables = {}, proces } }; - return template(combinedVars); + return interpolate(str, combinedVars); }; - request.url = interpolate(request.url); + request.url = _interpolate(request.url); forOwn(request.headers, (value, key) => { delete request.headers[key]; - request.headers[interpolate(key)] = interpolate(value); + request.headers[_interpolate(key)] = _interpolate(value); }); const contentType = getContentType(request.headers); @@ -78,40 +60,40 @@ const interpolateVars = (request, envVars = {}, collectionVariables = {}, proces if (typeof request.data === 'object') { try { let parsed = JSON.stringify(request.data); - parsed = interpolate(parsed); + parsed = _interpolate(parsed); request.data = JSON.parse(parsed); } catch (err) {} } if (typeof request.data === 'string') { if (request.data.length) { - request.data = interpolate(request.data); + request.data = _interpolate(request.data); } } } else if (contentType === 'application/x-www-form-urlencoded') { if (typeof request.data === 'object') { try { let parsed = JSON.stringify(request.data); - parsed = interpolate(parsed); + parsed = _interpolate(parsed); request.data = JSON.parse(parsed); } catch (err) {} } } else { - request.data = interpolate(request.data); + request.data = _interpolate(request.data); } each(request.params, (param) => { - param.value = interpolate(param.value); + 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); + 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); + request.proxy.auth.username = _interpolate(request.proxy.auth.username); + request.proxy.auth.password = _interpolate(request.proxy.auth.password); } } @@ -119,8 +101,8 @@ const interpolateVars = (request, envVars = {}, collectionVariables = {}, proces // need to refactor this in the future // the request.auth (basic auth) object gets set inside the prepare-request.js file if (request.auth) { - const username = interpolate(request.auth.username) || ''; - const password = interpolate(request.auth.password) || ''; + const username = _interpolate(request.auth.username) || ''; + const password = _interpolate(request.auth.password) || ''; // use auth header based approach and delete the request.auth object request.headers['authorization'] = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`; @@ -129,18 +111,18 @@ const interpolateVars = (request, envVars = {}, collectionVariables = {}, proces // interpolate vars for aws sigv4 auth if (request.awsv4config) { - request.awsv4config.accessKeyId = interpolate(request.awsv4config.accessKeyId) || ''; - request.awsv4config.secretAccessKey = interpolate(request.awsv4config.secretAccessKey) || ''; - request.awsv4config.sessionToken = interpolate(request.awsv4config.sessionToken) || ''; - request.awsv4config.service = interpolate(request.awsv4config.service) || ''; - request.awsv4config.region = interpolate(request.awsv4config.region) || ''; - request.awsv4config.profileName = interpolate(request.awsv4config.profileName) || ''; + request.awsv4config.accessKeyId = _interpolate(request.awsv4config.accessKeyId) || ''; + request.awsv4config.secretAccessKey = _interpolate(request.awsv4config.secretAccessKey) || ''; + request.awsv4config.sessionToken = _interpolate(request.awsv4config.sessionToken) || ''; + request.awsv4config.service = _interpolate(request.awsv4config.service) || ''; + request.awsv4config.region = _interpolate(request.awsv4config.region) || ''; + request.awsv4config.profileName = _interpolate(request.awsv4config.profileName) || ''; } // interpolate vars for digest auth if (request.digestConfig) { - request.digestConfig.username = interpolate(request.digestConfig.username) || ''; - request.digestConfig.password = interpolate(request.digestConfig.password) || ''; + request.digestConfig.username = _interpolate(request.digestConfig.username) || ''; + request.digestConfig.password = _interpolate(request.digestConfig.password) || ''; } return request; diff --git a/packages/bruno-electron/src/ipc/network/prepare-gql-introspection-request.js b/packages/bruno-electron/src/ipc/network/prepare-gql-introspection-request.js index a448d9106..1efb146c7 100644 --- a/packages/bruno-electron/src/ipc/network/prepare-gql-introspection-request.js +++ b/packages/bruno-electron/src/ipc/network/prepare-gql-introspection-request.js @@ -1,10 +1,10 @@ -const Handlebars = require('handlebars'); +const { interpolate } = require('@usebruno/common'); const { getIntrospectionQuery } = require('graphql'); const { setAuthHeaders } = require('./prepare-request'); const prepareGqlIntrospectionRequest = (endpoint, envVars, request, collectionRoot) => { if (endpoint && endpoint.length) { - endpoint = Handlebars.compile(endpoint, { noEscape: true })(envVars); + endpoint = interpolate(endpoint, envVars); } const queryParams = { diff --git a/packages/bruno-electron/tests/network/interpolate-vars.spec.js b/packages/bruno-electron/tests/network/interpolate-vars.spec.js index d33a49bf4..7a769ea3a 100644 --- a/packages/bruno-electron/tests/network/interpolate-vars.spec.js +++ b/packages/bruno-electron/tests/network/interpolate-vars.spec.js @@ -70,13 +70,6 @@ describe('interpolate-vars: interpolateVars', () => { describe('Does NOT interpolate string', () => { describe('With environment variables', () => { - it('If the var is escaped', async () => { - const request = { method: 'GET', url: `\\{{test.url}}` }; - - const result = interpolateVars(request, { 'test.url': 'test.com' }, null, null); - expect(result.url).toEqual('{{test.url}}'); - }); - it("If it's not a var (no braces)", async () => { const request = { method: 'GET', url: 'test' }; @@ -106,25 +99,5 @@ describe('interpolate-vars: interpolateVars', () => { expect(result.data).toEqual(gqlBody); }); }); - - describe('With process environment variables', () => { - it("If there's a var that doesn't start with 'process.env.'", async () => { - const request = { method: 'GET', url: '{{process-env-TEST_VAR}}' }; - - const result = interpolateVars(request, null, null, { TEST_VAR: 'test.com' }); - expect(result.url).toEqual(''); - }); - }); - }); - - describe('Throws an error', () => { - it("If there's a var with an invalid character in its name", async () => { - '!@#%^&*()[{]}=+,<>;\\|'.split('').forEach((character) => { - const request = { method: 'GET', url: `{{test${character}Url}}` }; - expect(() => interpolateVars(request, { [`test${character}Url`]: 'test.com' }, null, null)).toThrow( - /Parse error.*/ - ); - }); - }); }); }); diff --git a/packages/bruno-tests/collection/.env b/packages/bruno-tests/collection/.env new file mode 100644 index 000000000..0c7267404 --- /dev/null +++ b/packages/bruno-tests/collection/.env @@ -0,0 +1 @@ +PROC_ENV_VAR=woof \ No newline at end of file diff --git a/packages/bruno-tests/collection/.gitignore b/packages/bruno-tests/collection/.gitignore new file mode 100644 index 000000000..1e18f275e --- /dev/null +++ b/packages/bruno-tests/collection/.gitignore @@ -0,0 +1 @@ +!.env \ No newline at end of file diff --git a/packages/bruno-tests/collection/echo/echo plaintext.bru b/packages/bruno-tests/collection/echo/echo plaintext.bru index 56a23d345..e6c9b3fdc 100644 --- a/packages/bruno-tests/collection/echo/echo plaintext.bru +++ b/packages/bruno-tests/collection/echo/echo plaintext.bru @@ -1,7 +1,7 @@ meta { name: echo plaintext type: http - seq: 3 + seq: 2 } post { diff --git a/packages/bruno-tests/collection/echo/echo xml parsed.bru b/packages/bruno-tests/collection/echo/echo xml parsed.bru index 586541664..a8ff5e26a 100644 --- a/packages/bruno-tests/collection/echo/echo xml parsed.bru +++ b/packages/bruno-tests/collection/echo/echo xml parsed.bru @@ -1,7 +1,7 @@ meta { name: echo xml parsed type: http - seq: 4 + seq: 3 } post { diff --git a/packages/bruno-tests/collection/echo/echo xml raw.bru b/packages/bruno-tests/collection/echo/echo xml raw.bru index 6a02ac238..9773d4a3d 100644 --- a/packages/bruno-tests/collection/echo/echo xml raw.bru +++ b/packages/bruno-tests/collection/echo/echo xml raw.bru @@ -1,7 +1,7 @@ meta { name: echo xml raw type: http - seq: 5 + seq: 4 } post { diff --git a/packages/bruno-tests/collection/environments/Local.bru b/packages/bruno-tests/collection/environments/Local.bru index 113502138..26d3a6575 100644 --- a/packages/bruno-tests/collection/environments/Local.bru +++ b/packages/bruno-tests/collection/environments/Local.bru @@ -2,4 +2,7 @@ vars { host: http://localhost:80 bearer_auth_token: your_secret_token basic_auth_password: della + env.var1: envVar1 + env-var2: envVar2 + bark: {{process.env.PROC_ENV_VAR}} } diff --git a/packages/bruno-tests/collection/string interpolation/env vars.bru b/packages/bruno-tests/collection/string interpolation/env vars.bru new file mode 100644 index 000000000..f4bf47300 --- /dev/null +++ b/packages/bruno-tests/collection/string interpolation/env vars.bru @@ -0,0 +1,41 @@ +meta { + name: env vars + type: http + seq: 2 +} + +post { + url: {{host}}/api/echo/json + body: json + auth: none +} + +auth:basic { + username: asd + password: j +} + +auth:bearer { + token: +} + +body:json { + { + "envVar1": "{{env.var1}}", + "envVar2": "{{env-var2}}" + } +} + +assert { + res.status: eq 200 +} + +tests { + test("should return json", function() { + expect(res.getBody()).to.eql({ + "envVar1": "envVar1", + "envVar2": "envVar2" + }); + }); + +} diff --git a/packages/bruno-tests/collection/string interpolation/missing values.bru b/packages/bruno-tests/collection/string interpolation/missing values.bru new file mode 100644 index 000000000..2f310bc15 --- /dev/null +++ b/packages/bruno-tests/collection/string interpolation/missing values.bru @@ -0,0 +1,47 @@ +meta { + name: missing values + type: http + seq: 1 +} + +post { + url: {{host}}/api/echo/json?foo={{undefinedVar}} + body: json + auth: none +} + +query { + foo: {{undefinedVar}} +} + +auth:basic { + username: asd + password: j +} + +auth:bearer { + token: +} + +body:json { + { + "hello": "{{undefinedVar2}}" + } +} + +assert { + res.status: eq 200 +} + +tests { + test("should return json", function() { + const url = req.getUrl(); + expect(url).to.equal("http://localhost:80/api/echo/json?foo={{undefinedVar}}"); + + const data = res.getBody(); + expect(res.getBody()).to.eql({ + "hello": "{{undefinedVar2}}" + }); + }); + +} diff --git a/packages/bruno-tests/collection/string interpolation/process env vars.bru b/packages/bruno-tests/collection/string interpolation/process env vars.bru new file mode 100644 index 000000000..52146389b --- /dev/null +++ b/packages/bruno-tests/collection/string interpolation/process env vars.bru @@ -0,0 +1,41 @@ +meta { + name: process env vars + type: http + seq: 4 +} + +post { + url: {{host}}/api/echo/json + body: json + auth: none +} + +auth:basic { + username: asd + password: j +} + +auth:bearer { + token: +} + +body:json { + { + "bark": "{{bark}}", + "bark2": "{{process.env.PROC_ENV_VAR}}" + } +} + +assert { + res.status: eq 200 +} + +tests { + test("should return json", function() { + expect(res.getBody()).to.eql({ + "bark": "woof", + "bark2": "woof" + }); + }); + +} diff --git a/packages/bruno-tests/collection/string interpolation/runtime vars.bru b/packages/bruno-tests/collection/string interpolation/runtime vars.bru new file mode 100644 index 000000000..6cda713e8 --- /dev/null +++ b/packages/bruno-tests/collection/string interpolation/runtime vars.bru @@ -0,0 +1,58 @@ +meta { + name: runtime vars + type: http + seq: 3 +} + +post { + url: {{host}}/api/echo/text + body: text + auth: none +} + +auth:basic { + username: asd + password: j +} + +auth:bearer { + token: +} + +body:json { + { + "envVar1": "{{env.var1}}", + "envVar2": "{{env-var2}}" + } +} + +body:text { + Hi, I am {{rUser.full_name}}, + I am {{rUser.age}} years old. + My favorite food is {{rUser.fav-food[0]}} and {{rUser.fav-food[1]}}. + I like attention: {{rUser.want.attention}} +} + +assert { + res.status: eq 200 +} + +script:pre-request { + bru.setVar("rUser", { + full_name: 'Bruno', + age: 4, + 'fav-food': ['egg', 'meat'], + 'want.attention': true + }); +} + +tests { + test("should return json", function() { + const expectedResponse = `Hi, I am Bruno, + I am 4 years old. + My favorite food is egg and meat. + I like attention: true`; + expect(res.getBody()).to.equal(expectedResponse); + }); + +}