From 3f74178c8146db5ebb5cd142817b50436537fdca Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Tue, 7 Feb 2023 21:01:35 +0530 Subject: [PATCH] feat: bru cli - specify env + completed vars runtime --- packages/bruno-cli/src/commands/run.js | 46 +++++++++---- .../src/runner/run-single-request.js | 67 ++++++++----------- .../src/{runner/bru/index.js => utils/bru.js} | 40 +++++++++-- .../bruno-electron/src/ipc/network/index.js | 5 +- packages/bruno-js/src/bru.js | 10 +-- packages/bruno-js/src/utils.js | 17 +++++ packages/bruno-js/src/vars-runtime.js | 66 +++++++++++------- 7 files changed, 162 insertions(+), 89 deletions(-) rename packages/bruno-cli/src/{runner/bru/index.js => utils/bru.js} (67%) create mode 100644 packages/bruno-js/src/utils.js diff --git a/packages/bruno-cli/src/commands/run.js b/packages/bruno-cli/src/commands/run.js index 92404ea1f..9b4fe1114 100644 --- a/packages/bruno-cli/src/commands/run.js +++ b/packages/bruno-cli/src/commands/run.js @@ -1,27 +1,34 @@ +const fs = require('fs'); const chalk = require('chalk'); -const { - exists, - isFile, - isDirectory -} = require('../utils/filesystem'); -const { - runSingleRequest -} = require('../runner/run-single-request'); +const path = require('path'); +const { exists, isFile } = require('../utils/filesystem'); +const { runSingleRequest } = require('../runner/run-single-request'); +const { bruToEnvJson, getEnvVars } = require('../utils/bru'); const command = 'run '; const desc = 'Run a request'; const builder = async (yargs) => { - yargs.example('$0 run request.bru', 'Run a request'); + yargs + .option('env', { + describe: 'Environment variables', + type: 'string', + }) + .example('$0 run request.bru', 'Run a request') + .example('$0 run request.bru --env local', 'Run a request with the environment set to local'); }; const handler = async function (argv) { try { - const { filename } = argv; + const { + filename, + env + } = argv; const pathExists = await exists(filename); if(!pathExists) { console.error(chalk.red(`File or directory ${filename} does not exist`)); + return; } // todo @@ -30,14 +37,29 @@ const handler = async function (argv) { const collectionPath = process.cwd(); const collectionVariables = {}; + let envVars = {}; + if(env) { + const envFile = path.join(collectionPath, 'environments', `${env}.bru`); + const envPathExists = await exists(envFile); + + if(!envPathExists) { + console.error(chalk.red(`Environment file not found: `) + chalk.dim(`environments/${env}.bru`)); + return; + } + + const envBruContent = fs.readFileSync(envFile, 'utf8'); + const envJson = bruToEnvJson(envBruContent); + envVars = getEnvVars(envJson); + } + const _isFile = await isFile(filename); if(_isFile) { console.log(chalk.yellow('Running Request \n')); - await runSingleRequest(filename, collectionPath, collectionVariables); + await runSingleRequest(filename, collectionPath, collectionVariables, envVars); console.log(chalk.green('\nDone!')); } } catch (err) { - console.error(err); + // console.error(err.message); } }; diff --git a/packages/bruno-cli/src/runner/run-single-request.js b/packages/bruno-cli/src/runner/run-single-request.js index a53befe45..b03805489 100644 --- a/packages/bruno-cli/src/runner/run-single-request.js +++ b/packages/bruno-cli/src/runner/run-single-request.js @@ -1,41 +1,15 @@ -const Mustache = require('mustache'); const fs = require('fs'); +const chalk = require('chalk'); const { forOwn, each, extend, get } = require('lodash'); const FormData = require('form-data'); -const path = require('path'); const axios = require('axios'); const prepareRequest = require('./prepare-request'); +const interpolateVars = require('./interpolate-vars'); const { ScriptRuntime, TestRuntime, VarsRuntime } = require('@usebruno/js'); -const { - bruToJson -} = require('./bru'); -const { - stripExtension -} = require('../utils/filesystem'); -const chalk = require('chalk'); +const { bruToJson } = require('../utils/bru'); +const { stripExtension } = require('../utils/filesystem'); -// override the default escape function to prevent escaping -Mustache.escape = function (value) { - return value; -}; - -const getEnvVars = (environment = {}) => { - const variables = environment.variables; - if (!variables || !variables.length) { - return {}; - } - - const envVars = {}; - each(variables, (variable) => { - if(variable.enabled) { - envVars[variable.name] = Mustache.escape(variable.value); - } - }); - - return envVars; -}; - -const runSingleRequest = async function (filename, collectionPath, collectionVariables) { +const runSingleRequest = async function (filename, collectionPath, collectionVariables, envVariables) { try { const bruContent = fs.readFileSync(filename, 'utf8'); @@ -53,30 +27,46 @@ const runSingleRequest = async function (filename, collectionPath, collectionVar request.data = form; } - const envVars = getEnvVars({}); + // run pre-request vars + const preRequestVars = get(bruJson, 'request.vars.req'); + if(preRequestVars && preRequestVars.length) { + const varsRuntime = new VarsRuntime(); + varsRuntime.runPreRequestVars(preRequestVars, request, envVariables, collectionVariables, collectionPath); + } + // run pre request script const requestScriptFile = get(bruJson, 'request.script.req'); if(requestScriptFile && requestScriptFile.length) { const scriptRuntime = new ScriptRuntime(); - const result = scriptRuntime.runRequestScript(requestScriptFile, request, envVars, collectionVariables, collectionPath); + scriptRuntime.runRequestScript(requestScriptFile, request, envVariables, collectionVariables, collectionPath); } + // interpolate variables inside request + interpolateVars(request, envVariables, collectionVariables); + + // run request const response = await axios(request); - const varsRuntime = new VarsRuntime(); - varsRuntime.runResponseVars(bruJson.request.vars.res, request, response, envVars, collectionVariables); + // run post-response vars + const postResponseVars = get(bruJson, 'request.vars.res'); + if(postResponseVars && postResponseVars.length) { + const varsRuntime = new VarsRuntime(); + varsRuntime.runPostResponseVars(postResponseVars, request, response, envVariables, collectionVariables, collectionPath); + } + // run post response script const responseScriptFile = get(bruJson, 'request.script.res'); if(responseScriptFile && responseScriptFile.length) { const scriptRuntime = new ScriptRuntime(); - const result = scriptRuntime.runResponseScript(responseScriptFile, response, envVars, collectionVariables, collectionPath); + scriptRuntime.runResponseScript(responseScriptFile, response, envVariables, collectionVariables, collectionPath); } + // run tests let testResults = []; const testFile = get(bruJson, 'request.tests'); if(testFile && testFile.length) { const testRuntime = new TestRuntime(); - const result = testRuntime.runTests(testFile, request, response, envVars, collectionVariables, collectionPath); + const result = testRuntime.runTests(testFile, request, response, envVariables, collectionVariables, collectionPath); testResults = get(result, 'results', []); } @@ -91,8 +81,7 @@ const runSingleRequest = async function (filename, collectionPath, collectionVar }); } } catch (err) { - console.log(err.response); - Promise.reject(err); + console.log(chalk.red(stripExtension(filename)) + chalk.dim(` (${err.message})`)); } }; diff --git a/packages/bruno-cli/src/runner/bru/index.js b/packages/bruno-cli/src/utils/bru.js similarity index 67% rename from packages/bruno-cli/src/runner/bru/index.js rename to packages/bruno-cli/src/utils/bru.js index d8a6e76fc..9b98e2ad8 100644 --- a/packages/bruno-cli/src/runner/bru/index.js +++ b/packages/bruno-cli/src/utils/bru.js @@ -1,7 +1,11 @@ const _ = require('lodash'); -const { - bruToJsonV2 -} = require('@usebruno/lang'); +const Mustache = require('mustache'); +const { bruToEnvJsonV2, bruToJsonV2 } = require('@usebruno/lang'); + +// override the default escape function to prevent escaping +Mustache.escape = function (value) { + return value; +}; /** * The transformer function for converting a BRU file to JSON. @@ -52,6 +56,32 @@ const bruToJson = (bru) => { } }; -module.exports = { - bruToJson +const bruToEnvJson = (bru) => { + try { + return bruToEnvJsonV2(bru); + } catch (err) { + return Promise.reject(err); + } +}; + +const getEnvVars = (environment = {}) => { + const variables = environment.variables; + if (!variables || !variables.length) { + return {}; + } + + const envVars = {}; + _.each(variables, (variable) => { + if(variable.enabled) { + envVars[variable.name] = Mustache.escape(variable.value); + } + }); + + return envVars; +}; + +module.exports = { + bruToJson, + bruToEnvJson, + getEnvVars }; diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js index 3f7695cf4..b675ae11b 100644 --- a/packages/bruno-electron/src/ipc/network/index.js +++ b/packages/bruno-electron/src/ipc/network/index.js @@ -9,10 +9,7 @@ const prepareGqlIntrospectionRequest = require('./prepare-gql-introspection-requ const { cancelTokens, saveCancelToken, deleteCancelToken } = require('../../utils/cancel-token'); const { uuid } = require('../../utils/common'); const interpolateVars = require('./interpolate-vars'); -const { - sortFolder, - getAllRequestsInFolderRecursively -} = require('./helper'); +const { sortFolder, getAllRequestsInFolderRecursively } = require('./helper'); // override the default escape function to prevent escaping Mustache.escape = function (value) { diff --git a/packages/bruno-js/src/bru.js b/packages/bruno-js/src/bru.js index 663a9a1c0..b3a84e931 100644 --- a/packages/bruno-js/src/bru.js +++ b/packages/bruno-js/src/bru.js @@ -1,12 +1,12 @@ class Bru { - constructor(environment, collectionVariables) { - this._environment = environment; + constructor(envVariables, collectionVariables) { + this._envVariables = envVariables; this._collectionVariables = collectionVariables; } getEnvVar(key) { - return this._environment[key]; + return this._envVariables[key]; } setEnvVar(key, value) { @@ -15,11 +15,11 @@ class Bru { } // gracefully ignore if key is not present in environment - if(!this._environment.hasOwnProperty(key)) { + if(!this._envVariables.hasOwnProperty(key)) { return; } - this._environment[key] = value; + this._envVariables[key] = value; } setVar(key, value) { diff --git a/packages/bruno-js/src/utils.js b/packages/bruno-js/src/utils.js new file mode 100644 index 000000000..193558623 --- /dev/null +++ b/packages/bruno-js/src/utils.js @@ -0,0 +1,17 @@ +const jsonQuery = require('json-query'); + +const evaluateJsExpression = (expression, context) => { + const fn = new Function(...Object.keys(context), `return ${expression}`); + return fn(...Object.values(context)); +}; + +const createResponseParser = (res = {}) => (expr) => { + const output = jsonQuery(expr, { data: res.data }); + + return output ? output.value : null; +}; + +module.exports = { + evaluateJsExpression, + createResponseParser +}; \ No newline at end of file diff --git a/packages/bruno-js/src/vars-runtime.js b/packages/bruno-js/src/vars-runtime.js index e39d225a2..f4d5e0c8a 100644 --- a/packages/bruno-js/src/vars-runtime.js +++ b/packages/bruno-js/src/vars-runtime.js @@ -1,43 +1,61 @@ +const _ = require('lodash'); const Bru = require('./bru'); const BrunoRequest = require('./bruno-request'); -const BrunoResponse = require('./bruno-response'); -const _ = require('lodash'); -const jsonQuery = require('json-query'); - +const { evaluateJsExpression, createResponseParser } = require('./utils'); class VarsRuntime { - constructor() { - } - - runResponseVars(vars, request, response, environment, collectionVariables, collectionPath) { - const bru = new Bru(environment, collectionVariables); - const req = new BrunoRequest(request); - const res = new BrunoResponse(response); - - res.q = function(expr) { - const output = jsonQuery(expr, {data: res.body}); - - return output.value; + runPreRequestVars(vars, request, envVariables, collectionVariables, collectionPath) { + const enabledVars = _.filter(vars, (v) => v.enabled); + if(!enabledVars.length) { + return; } - const result = { + const bru = new Bru(envVariables, collectionVariables); + const req = new BrunoRequest(request); + const bruContext = { + bru, + req }; const context = { - bru, - req, - res + ...envVariables, + ...collectionVariables, + ...bruContext } - console.log(JSON.stringify(vars, null, 2)); - _.each(vars, (v) => { - result[v.name] = eval(v.value, context); + _.each(enabledVars, (v) => { + const value = evaluateJsExpression(v.value, context); + bru.setVar(v.name, value); }); + } - console.log(result); + runPostResponseVars(vars, request, response, envVariables, collectionVariables, collectionPath) { + const enabledVars = _.filter(vars, (v) => v.enabled); + if(!enabledVars.length) { + return; + } + const bru = new Bru(envVariables, collectionVariables); + const req = new BrunoRequest(request); + const res = createResponseParser(response); + const bruContext = { + bru, + req, + res + }; + + const context = { + ...envVariables, + ...collectionVariables, + ...bruContext + } + + _.each(enabledVars, (v) => { + const value = evaluateJsExpression(v.value, context); + bru.setVar(v.name, value); + }); } }