From 362289b7cd70e3b2b976977fbd19538b51b7f9e8 Mon Sep 17 00:00:00 2001 From: Anoop M D Date: Mon, 9 Oct 2023 08:26:10 +0530 Subject: [PATCH] feat(#334): bru cli support for collection headers, auth, scripts and tests --- package-lock.json | 12 ++++---- packages/bruno-cli/package.json | 2 +- packages/bruno-cli/src/commands/run.js | 22 ++++++++++++-- .../bruno-cli/src/runner/prepare-request.js | 30 ++++++++++++++++++- .../src/runner/run-single-request.js | 19 ++++++++---- packages/bruno-cli/src/utils/bru.js | 26 ++++++++++++++-- packages/bruno-electron/package.json | 2 +- packages/bruno-lang/package.json | 2 +- 8 files changed, 96 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index a1bf6956a..162eaae6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16691,7 +16691,7 @@ "license": "MIT", "dependencies": { "@usebruno/js": "0.8.0", - "@usebruno/lang": "0.5.0", + "@usebruno/lang": "0.6.0", "axios": "^1.5.1", "chai": "^4.3.7", "chalk": "^3.0.0", @@ -16771,10 +16771,10 @@ }, "packages/bruno-electron": { "name": "bruno", - "version": "v0.21.1", + "version": "v0.22.0", "dependencies": { "@usebruno/js": "0.8.0", - "@usebruno/lang": "0.5.0", + "@usebruno/lang": "0.6.0", "@usebruno/schema": "0.5.0", "about-window": "^1.15.2", "axios": "^1.5.1", @@ -17021,7 +17021,7 @@ }, "packages/bruno-lang": { "name": "@usebruno/lang", - "version": "0.5.0", + "version": "0.6.0", "license": "MIT", "dependencies": { "arcsecond": "^5.0.0", @@ -20011,7 +20011,7 @@ "version": "file:packages/bruno-cli", "requires": { "@usebruno/js": "0.8.0", - "@usebruno/lang": "0.5.0", + "@usebruno/lang": "0.6.0", "axios": "^1.5.1", "chai": "^4.3.7", "chalk": "^3.0.0", @@ -21135,7 +21135,7 @@ "version": "file:packages/bruno-electron", "requires": { "@usebruno/js": "0.8.0", - "@usebruno/lang": "0.5.0", + "@usebruno/lang": "0.6.0", "@usebruno/schema": "0.5.0", "about-window": "^1.15.2", "axios": "^1.5.1", diff --git a/packages/bruno-cli/package.json b/packages/bruno-cli/package.json index 77d7bc596..78814900f 100644 --- a/packages/bruno-cli/package.json +++ b/packages/bruno-cli/package.json @@ -25,7 +25,7 @@ ], "dependencies": { "@usebruno/js": "0.8.0", - "@usebruno/lang": "0.5.0", + "@usebruno/lang": "0.6.0", "axios": "^1.5.1", "chai": "^4.3.7", "chalk": "^3.0.0", diff --git a/packages/bruno-cli/src/commands/run.js b/packages/bruno-cli/src/commands/run.js index 7866425ec..78b7226ca 100644 --- a/packages/bruno-cli/src/commands/run.js +++ b/packages/bruno-cli/src/commands/run.js @@ -6,7 +6,7 @@ const { exists, isFile, isDirectory } = require('../utils/filesystem'); const { runSingleRequest } = require('../runner/run-single-request'); const { bruToEnvJson, getEnvVars } = require('../utils/bru'); const { rpad } = require('../utils/common'); -const { bruToJson, getOptions } = require('../utils/bru'); +const { bruToJson, getOptions, collectionBruToJson } = require('../utils/bru'); const { dotenvToJson } = require('@usebruno/lang'); const command = 'run [filename]'; @@ -121,6 +121,9 @@ const getBruFilesRecursively = (dir) => { const currentDirBruJsons = []; for (const file of filesInCurrentDir) { + if (['collection.bru', 'folder.bru'].includes(file)) { + continue; + } const filePath = path.join(currentPath, file); const stats = fs.lstatSync(filePath); @@ -151,6 +154,19 @@ const getBruFilesRecursively = (dir) => { return getFilesInOrder(dir); }; +const getCollectionRoot = (dir) => { + const collectionRootPath = path.join(dir, 'collection.bru'); + const exists = fs.existsSync(collectionRootPath); + if (!exists) { + return {}; + } + + const content = fs.readFileSync(collectionRootPath, 'utf8'); + const json = collectionBruToJson(content); + + return json; +}; + const builder = async (yargs) => { yargs .option('r', { @@ -210,6 +226,7 @@ const handler = async function (argv) { const brunoConfigFile = fs.readFileSync(brunoJsonPath, 'utf8'); const brunoConfig = JSON.parse(brunoConfigFile); + const collectionRoot = getCollectionRoot(collectionPath); if (filename && filename.length) { const pathExists = await exists(filename); @@ -349,7 +366,8 @@ const handler = async function (argv) { collectionVariables, envVars, processEnvVars, - brunoConfig + brunoConfig, + collectionRoot ); results.push(result); diff --git a/packages/bruno-cli/src/runner/prepare-request.js b/packages/bruno-cli/src/runner/prepare-request.js index e52cb5418..5568ae311 100644 --- a/packages/bruno-cli/src/runner/prepare-request.js +++ b/packages/bruno-cli/src/runner/prepare-request.js @@ -1,9 +1,20 @@ const { get, each, filter } = require('lodash'); const decomment = require('decomment'); -const prepareRequest = (request) => { +const prepareRequest = (request, collectionRoot) => { const headers = {}; let contentTypeDefined = false; + + // collection headers + each(get(collectionRoot, 'request.headers', []), (h) => { + if (h.enabled) { + headers[h.name] = h.value; + if (h.name.toLowerCase() === 'content-type') { + contentTypeDefined = true; + } + } + }); + each(request.headers, (h) => { if (h.enabled) { headers[h.name] = h.value; @@ -20,6 +31,23 @@ const prepareRequest = (request) => { }; // Authentication + // A request can override the collection auth with another auth + // But it cannot override the collection auth with no auth + // We will provide support for disabling the auth via scripting in the future + const collectionAuth = get(collectionRoot, 'request.auth'); + if (collectionAuth) { + if (collectionAuth.mode === 'basic') { + axiosRequest.auth = { + username: get(collectionAuth, 'basic.username'), + password: get(collectionAuth, 'basic.password') + }; + } + + if (collectionAuth.mode === 'bearer') { + axiosRequest.headers['authorization'] = `Bearer ${get(collectionAuth, 'bearer.token')}`; + } + } + if (request.auth) { if (request.auth.mode === 'basic') { axiosRequest.auth = { diff --git a/packages/bruno-cli/src/runner/run-single-request.js b/packages/bruno-cli/src/runner/run-single-request.js index 3e56bf0a5..082c3d416 100644 --- a/packages/bruno-cli/src/runner/run-single-request.js +++ b/packages/bruno-cli/src/runner/run-single-request.js @@ -1,8 +1,9 @@ +const os = require('os'); const qs = require('qs'); const chalk = require('chalk'); const decomment = require('decomment'); const fs = require('fs'); -const { forOwn, each, extend, get } = require('lodash'); +const { forOwn, each, extend, get, compact } = require('lodash'); const FormData = require('form-data'); const prepareRequest = require('./prepare-request'); const interpolateVars = require('./interpolate-vars'); @@ -23,7 +24,8 @@ const runSingleRequest = async function ( collectionVariables, envVariables, processEnvVars, - brunoConfig + brunoConfig, + collectionRoot ) { try { let request; @@ -58,7 +60,10 @@ const runSingleRequest = async function ( } // run pre request script - const requestScriptFile = get(bruJson, 'request.script.req'); + const requestScriptFile = compact([ + get(collectionRoot, 'request.script.req'), + get(bruJson, 'request.script.req') + ]).join(os.EOL); if (requestScriptFile && requestScriptFile.length) { const scriptRuntime = new ScriptRuntime(); await scriptRuntime.runRequestScript( @@ -208,7 +213,10 @@ const runSingleRequest = async function ( } // run post response script - const responseScriptFile = get(bruJson, 'request.script.res'); + const responseScriptFile = compact([ + get(collectionRoot, 'request.script.res'), + get(bruJson, 'request.script.res') + ]).join(os.EOL); if (responseScriptFile && responseScriptFile.length) { const scriptRuntime = new ScriptRuntime(); await scriptRuntime.runResponseScript( @@ -250,7 +258,7 @@ const runSingleRequest = async function ( // run tests let testResults = []; - const testFile = get(bruJson, 'request.tests'); + const testFile = compact([get(collectionRoot, 'request.tests'), get(bruJson, 'request.tests')]).join(os.EOL); if (typeof testFile === 'string') { const testRuntime = new TestRuntime(); const result = await testRuntime.runTests( @@ -296,6 +304,7 @@ const runSingleRequest = async function ( testResults }; } catch (err) { + console.log(chalk.red(stripExtension(filename)) + chalk.dim(` (${err.message})`)); return { request: { method: null, diff --git a/packages/bruno-cli/src/utils/bru.js b/packages/bruno-cli/src/utils/bru.js index 684106399..34fb09c6b 100644 --- a/packages/bruno-cli/src/utils/bru.js +++ b/packages/bruno-cli/src/utils/bru.js @@ -1,12 +1,33 @@ const _ = require('lodash'); const Mustache = require('mustache'); -const { bruToEnvJsonV2, bruToJsonV2 } = require('@usebruno/lang'); +const { bruToEnvJsonV2, bruToJsonV2, collectionBruToJson: _collectionBruToJson } = require('@usebruno/lang'); // override the default escape function to prevent escaping Mustache.escape = function (value) { return value; }; +const collectionBruToJson = (bru) => { + try { + const json = _collectionBruToJson(bru); + + const transformedJson = { + request: { + params: _.get(json, 'query', []), + headers: _.get(json, 'headers', []), + auth: _.get(json, 'auth', {}), + script: _.get(json, 'script', {}), + vars: _.get(json, 'vars', {}), + tests: _.get(json, 'tests', '') + } + }; + + return transformedJson; + } catch (error) { + return Promise.reject(error); + } +}; + /** * The transformer function for converting a BRU file to JSON. * @@ -91,5 +112,6 @@ module.exports = { bruToJson, bruToEnvJson, getEnvVars, - getOptions + getOptions, + collectionBruToJson }; diff --git a/packages/bruno-electron/package.json b/packages/bruno-electron/package.json index b4de839d5..fdbd339c9 100644 --- a/packages/bruno-electron/package.json +++ b/packages/bruno-electron/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@usebruno/js": "0.8.0", - "@usebruno/lang": "0.5.0", + "@usebruno/lang": "0.6.0", "@usebruno/schema": "0.5.0", "about-window": "^1.15.2", "axios": "^1.5.1", diff --git a/packages/bruno-lang/package.json b/packages/bruno-lang/package.json index fffde571f..11ed26349 100644 --- a/packages/bruno-lang/package.json +++ b/packages/bruno-lang/package.json @@ -1,6 +1,6 @@ { "name": "@usebruno/lang", - "version": "0.5.0", + "version": "0.6.0", "license": "MIT", "main": "src/index.js", "files": [