feat: bru cli - specify env + completed vars runtime

This commit is contained in:
Anoop M D 2023-02-07 21:01:35 +05:30
parent 78ca6c5e96
commit 3f74178c81
7 changed files with 162 additions and 89 deletions

View File

@ -1,27 +1,34 @@
const fs = require('fs');
const chalk = require('chalk'); const chalk = require('chalk');
const { const path = require('path');
exists, const { exists, isFile } = require('../utils/filesystem');
isFile, const { runSingleRequest } = require('../runner/run-single-request');
isDirectory const { bruToEnvJson, getEnvVars } = require('../utils/bru');
} = require('../utils/filesystem');
const {
runSingleRequest
} = require('../runner/run-single-request');
const command = 'run <filename>'; const command = 'run <filename>';
const desc = 'Run a request'; const desc = 'Run a request';
const builder = async (yargs) => { 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) { const handler = async function (argv) {
try { try {
const { filename } = argv; const {
filename,
env
} = argv;
const pathExists = await exists(filename); const pathExists = await exists(filename);
if(!pathExists) { if(!pathExists) {
console.error(chalk.red(`File or directory ${filename} does not exist`)); console.error(chalk.red(`File or directory ${filename} does not exist`));
return;
} }
// todo // todo
@ -30,14 +37,29 @@ const handler = async function (argv) {
const collectionPath = process.cwd(); const collectionPath = process.cwd();
const collectionVariables = {}; 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); const _isFile = await isFile(filename);
if(_isFile) { if(_isFile) {
console.log(chalk.yellow('Running Request \n')); console.log(chalk.yellow('Running Request \n'));
await runSingleRequest(filename, collectionPath, collectionVariables); await runSingleRequest(filename, collectionPath, collectionVariables, envVars);
console.log(chalk.green('\nDone!')); console.log(chalk.green('\nDone!'));
} }
} catch (err) { } catch (err) {
console.error(err); // console.error(err.message);
} }
}; };

View File

@ -1,41 +1,15 @@
const Mustache = require('mustache');
const fs = require('fs'); const fs = require('fs');
const chalk = require('chalk');
const { forOwn, each, extend, get } = require('lodash'); const { forOwn, each, extend, get } = require('lodash');
const FormData = require('form-data'); const FormData = require('form-data');
const path = require('path');
const axios = require('axios'); const axios = require('axios');
const prepareRequest = require('./prepare-request'); const prepareRequest = require('./prepare-request');
const interpolateVars = require('./interpolate-vars');
const { ScriptRuntime, TestRuntime, VarsRuntime } = require('@usebruno/js'); const { ScriptRuntime, TestRuntime, VarsRuntime } = require('@usebruno/js');
const { const { bruToJson } = require('../utils/bru');
bruToJson const { stripExtension } = require('../utils/filesystem');
} = require('./bru');
const {
stripExtension
} = require('../utils/filesystem');
const chalk = require('chalk');
// override the default escape function to prevent escaping const runSingleRequest = async function (filename, collectionPath, collectionVariables, envVariables) {
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) {
try { try {
const bruContent = fs.readFileSync(filename, 'utf8'); const bruContent = fs.readFileSync(filename, 'utf8');
@ -53,30 +27,46 @@ const runSingleRequest = async function (filename, collectionPath, collectionVar
request.data = form; 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'); const requestScriptFile = get(bruJson, 'request.script.req');
if(requestScriptFile && requestScriptFile.length) { if(requestScriptFile && requestScriptFile.length) {
const scriptRuntime = new ScriptRuntime(); 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 response = await axios(request);
const varsRuntime = new VarsRuntime(); // run post-response vars
varsRuntime.runResponseVars(bruJson.request.vars.res, request, response, envVars, collectionVariables); 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'); const responseScriptFile = get(bruJson, 'request.script.res');
if(responseScriptFile && responseScriptFile.length) { if(responseScriptFile && responseScriptFile.length) {
const scriptRuntime = new ScriptRuntime(); const scriptRuntime = new ScriptRuntime();
const result = scriptRuntime.runResponseScript(responseScriptFile, response, envVars, collectionVariables, collectionPath); scriptRuntime.runResponseScript(responseScriptFile, response, envVariables, collectionVariables, collectionPath);
} }
// run tests
let testResults = []; let testResults = [];
const testFile = get(bruJson, 'request.tests'); const testFile = get(bruJson, 'request.tests');
if(testFile && testFile.length) { if(testFile && testFile.length) {
const testRuntime = new TestRuntime(); 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', []); testResults = get(result, 'results', []);
} }
@ -91,8 +81,7 @@ const runSingleRequest = async function (filename, collectionPath, collectionVar
}); });
} }
} catch (err) { } catch (err) {
console.log(err.response); console.log(chalk.red(stripExtension(filename)) + chalk.dim(` (${err.message})`));
Promise.reject(err);
} }
}; };

View File

@ -1,7 +1,11 @@
const _ = require('lodash'); const _ = require('lodash');
const { const Mustache = require('mustache');
bruToJsonV2 const { bruToEnvJsonV2, bruToJsonV2 } = require('@usebruno/lang');
} = 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. * The transformer function for converting a BRU file to JSON.
@ -52,6 +56,32 @@ const bruToJson = (bru) => {
} }
}; };
module.exports = { const bruToEnvJson = (bru) => {
bruToJson 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
}; };

View File

@ -9,10 +9,7 @@ const prepareGqlIntrospectionRequest = require('./prepare-gql-introspection-requ
const { cancelTokens, saveCancelToken, deleteCancelToken } = require('../../utils/cancel-token'); const { cancelTokens, saveCancelToken, deleteCancelToken } = require('../../utils/cancel-token');
const { uuid } = require('../../utils/common'); const { uuid } = require('../../utils/common');
const interpolateVars = require('./interpolate-vars'); const interpolateVars = require('./interpolate-vars');
const { const { sortFolder, getAllRequestsInFolderRecursively } = require('./helper');
sortFolder,
getAllRequestsInFolderRecursively
} = require('./helper');
// override the default escape function to prevent escaping // override the default escape function to prevent escaping
Mustache.escape = function (value) { Mustache.escape = function (value) {

View File

@ -1,12 +1,12 @@
class Bru { class Bru {
constructor(environment, collectionVariables) { constructor(envVariables, collectionVariables) {
this._environment = environment; this._envVariables = envVariables;
this._collectionVariables = collectionVariables; this._collectionVariables = collectionVariables;
} }
getEnvVar(key) { getEnvVar(key) {
return this._environment[key]; return this._envVariables[key];
} }
setEnvVar(key, value) { setEnvVar(key, value) {
@ -15,11 +15,11 @@ class Bru {
} }
// gracefully ignore if key is not present in environment // gracefully ignore if key is not present in environment
if(!this._environment.hasOwnProperty(key)) { if(!this._envVariables.hasOwnProperty(key)) {
return; return;
} }
this._environment[key] = value; this._envVariables[key] = value;
} }
setVar(key, value) { setVar(key, value) {

View File

@ -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
};

View File

@ -1,43 +1,61 @@
const _ = require('lodash');
const Bru = require('./bru'); const Bru = require('./bru');
const BrunoRequest = require('./bruno-request'); const BrunoRequest = require('./bruno-request');
const BrunoResponse = require('./bruno-response'); const { evaluateJsExpression, createResponseParser } = require('./utils');
const _ = require('lodash');
const jsonQuery = require('json-query');
class VarsRuntime { class VarsRuntime {
constructor() { runPreRequestVars(vars, request, envVariables, collectionVariables, collectionPath) {
} const enabledVars = _.filter(vars, (v) => v.enabled);
if(!enabledVars.length) {
runResponseVars(vars, request, response, environment, collectionVariables, collectionPath) { return;
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;
} }
const result = { const bru = new Bru(envVariables, collectionVariables);
const req = new BrunoRequest(request);
const bruContext = {
bru,
req
}; };
const context = { const context = {
bru, ...envVariables,
req, ...collectionVariables,
res ...bruContext
} }
console.log(JSON.stringify(vars, null, 2));
_.each(vars, (v) => { _.each(enabledVars, (v) => {
result[v.name] = eval(v.value, context); 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);
});
} }
} }