const { get, each, filter, find, compact } = require('lodash'); const fs = require('fs'); const os = require('os'); var JSONbig = require('json-bigint'); const decomment = require('decomment'); const crypto = require('node:crypto'); const mergeHeaders = (collection, request, requestTreePath) => { let headers = new Map(); let collectionHeaders = get(collection, 'root.request.headers', []); collectionHeaders.forEach((header) => { if (header.enabled) { headers.set(header.name, header.value); } }); for (let i of requestTreePath) { if (i.type === 'folder') { let _headers = get(i, 'root.request.headers', []); _headers.forEach((header) => { if (header.enabled) { headers.set(header.name, header.value); } }); } else { const _headers = i?.draft ? get(i, 'draft.request.headers', []) : get(i, 'request.headers', []); _headers.forEach((header) => { if (header.enabled) { headers.set(header.name, header.value); } }); } } request.headers = Array.from(headers, ([name, value]) => ({ name, value, enabled: true })); }; const mergeVars = (collection, request, requestTreePath) => { let reqVars = new Map(); let collectionRequestVars = get(collection, 'root.request.vars.req', []); let collectionVariables = {}; collectionRequestVars.forEach((_var) => { if (_var.enabled) { reqVars.set(_var.name, _var.value); collectionVariables[_var.name] = _var.value; } }); let folderVariables = {}; let requestVariables = {}; for (let i of requestTreePath) { if (i.type === 'folder') { let vars = get(i, 'root.request.vars.req', []); vars.forEach((_var) => { if (_var.enabled) { reqVars.set(_var.name, _var.value); folderVariables[_var.name] = _var.value; } }); } else { const vars = i?.draft ? get(i, 'draft.request.vars.req', []) : get(i, 'request.vars.req', []); vars.forEach((_var) => { if (_var.enabled) { reqVars.set(_var.name, _var.value); requestVariables[_var.name] = _var.value; } }); } } request.collectionVariables = collectionVariables; request.folderVariables = folderVariables; request.requestVariables = requestVariables; if(request?.vars) { request.vars.req = Array.from(reqVars, ([name, value]) => ({ name, value, enabled: true, type: 'request' })); } let resVars = new Map(); let collectionResponseVars = get(collection, 'root.request.vars.res', []); collectionResponseVars.forEach((_var) => { if (_var.enabled) { resVars.set(_var.name, _var.value); } }); for (let i of requestTreePath) { if (i.type === 'folder') { let vars = get(i, 'root.request.vars.res', []); vars.forEach((_var) => { if (_var.enabled) { resVars.set(_var.name, _var.value); } }); } else { const vars = i?.draft ? get(i, 'draft.request.vars.res', []) : get(i, 'request.vars.res', []); vars.forEach((_var) => { if (_var.enabled) { resVars.set(_var.name, _var.value); } }); } } if(request?.vars) { request.vars.res = Array.from(resVars, ([name, value]) => ({ name, value, enabled: true, type: 'response' })); } }; const mergeScripts = (collection, request, requestTreePath, scriptFlow) => { let collectionPreReqScript = get(collection, 'root.request.script.req', ''); let collectionPostResScript = get(collection, 'root.request.script.res', ''); let collectionTests = get(collection, 'root.request.tests', ''); let combinedPreReqScript = []; let combinedPostResScript = []; let combinedTests = []; for (let i of requestTreePath) { if (i.type === 'folder') { let preReqScript = get(i, 'root.request.script.req', ''); if (preReqScript && preReqScript.trim() !== '') { combinedPreReqScript.push(preReqScript); } let postResScript = get(i, 'root.request.script.res', ''); if (postResScript && postResScript.trim() !== '') { combinedPostResScript.push(postResScript); } let tests = get(i, 'root.request.tests', ''); if (tests && tests?.trim?.() !== '') { combinedTests.push(tests); } } } request.script.req = compact([collectionPreReqScript, ...combinedPreReqScript, request?.script?.req || '']).join(os.EOL); if (scriptFlow === 'sequential') { request.script.res = compact([collectionPostResScript, ...combinedPostResScript, request?.script?.res || '']).join(os.EOL); } else { request.script.res = compact([request?.script?.res || '', ...combinedPostResScript.reverse(), collectionPostResScript]).join(os.EOL); } if (scriptFlow === 'sequential') { request.tests = compact([collectionTests, ...combinedTests, request?.tests || '']).join(os.EOL); } else { request.tests = compact([request?.tests || '', ...combinedTests.reverse(), collectionTests]).join(os.EOL); } }; const findItem = (items = [], pathname) => { return find(items, (i) => i.pathname === pathname); }; const findItemInCollection = (collection, pathname) => { let flattenedItems = flattenItems(collection.items); return findItem(flattenedItems, pathname); }; const findParentItemInCollection = (collection, pathname) => { let flattenedItems = flattenItems(collection.items); return find(flattenedItems, (item) => { return item.items && find(item.items, (i) => i.pathname === pathname); }); }; const flattenItems = (items = []) => { const flattenedItems = []; const flatten = (itms, flattened) => { each(itms, (i) => { flattened.push(i); if (i.items && i.items.length) { flatten(i.items, flattened); } }); }; flatten(items, flattenedItems); return flattenedItems; }; const getTreePathFromCollectionToItem = (collection, _item) => { let path = []; let item = findItemInCollection(collection, _item.pathname); while (item) { path.unshift(item); item = findParentItemInCollection(collection, item.pathname); } return path; }; const prepareRequest = (item = {}, collection = {}) => { const request = item?.request; const brunoConfig = get(collection, 'brunoConfig', {}); const headers = {}; let contentTypeDefined = false; const scriptFlow = brunoConfig?.scripts?.flow ?? 'sandwich'; const requestTreePath = getTreePathFromCollectionToItem(collection, item); if (requestTreePath && requestTreePath.length > 0) { mergeHeaders(collection, request, requestTreePath); mergeScripts(collection, request, requestTreePath, scriptFlow); mergeVars(collection, request, requestTreePath); } each(get(request, 'headers', []), (h) => { if (h.enabled) { headers[h.name] = h.value; if (h.name.toLowerCase() === 'content-type') { contentTypeDefined = true; } } }); let axiosRequest = { method: request.method, url: request.url, headers: headers, pathParams: request?.params?.filter((param) => param.type === 'path') }; const collectionAuth = get(collection, 'root.request.auth'); if (collectionAuth && request.auth.mode === 'inherit') { 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 = { username: get(request, 'auth.basic.username'), password: get(request, 'auth.basic.password') }; } if (request.auth.mode === 'awsv4') { axiosRequest.awsv4config = { accessKeyId: get(request, 'auth.awsv4.accessKeyId'), secretAccessKey: get(request, 'auth.awsv4.secretAccessKey'), sessionToken: get(request, 'auth.awsv4.sessionToken'), service: get(request, 'auth.awsv4.service'), region: get(request, 'auth.awsv4.region'), profileName: get(request, 'auth.awsv4.profileName') }; } if (request.auth.mode === 'bearer') { axiosRequest.headers['Authorization'] = `Bearer ${get(request, 'auth.bearer.token')}`; } if (request.auth.mode === 'wsse') { const username = get(request, 'auth.wsse.username', ''); const password = get(request, 'auth.wsse.password', ''); const ts = new Date().toISOString(); const nonce = crypto.randomBytes(16).toString('hex'); // Create the password digest using SHA-1 as required for WSSE const hash = crypto.createHash('sha1'); hash.update(nonce + ts + password); const digest = Buffer.from(hash.digest('hex').toString('utf8')).toString('base64'); // Construct the WSSE header axiosRequest.headers[ 'X-WSSE' ] = `UsernameToken Username="${username}", PasswordDigest="${digest}", Nonce="${nonce}", Created="${ts}"`; } } request.body = request.body || {}; if (request.body.mode === 'json') { if (!contentTypeDefined) { axiosRequest.headers['content-type'] = 'application/json'; } let jsonBody; try { jsonBody = decomment(request?.body?.json); } catch (error) { jsonBody = request?.body?.json; } try { axiosRequest.data = JSONbig.parse(jsonBody); } catch (error) { axiosRequest.data = jsonBody; } } if (request.body.mode === 'text') { if (!contentTypeDefined) { axiosRequest.headers['content-type'] = 'text/plain'; } axiosRequest.data = request.body.text; } if (request.body.mode === 'xml') { if (!contentTypeDefined) { axiosRequest.headers['content-type'] = 'text/xml'; } axiosRequest.data = request.body.xml; } if (request.body.mode === 'sparql') { if (!contentTypeDefined) { axiosRequest.headers['content-type'] = 'application/sparql-query'; } axiosRequest.data = request.body.sparql; } if (request.body.mode === 'formUrlEncoded') { axiosRequest.headers['content-type'] = 'application/x-www-form-urlencoded'; const params = {}; const enabledParams = filter(request.body.formUrlEncoded, (p) => p.enabled); each(enabledParams, (p) => (params[p.name] = p.value)); axiosRequest.data = params; } if (request.body.mode === 'multipartForm') { axiosRequest.headers['content-type'] = 'multipart/form-data'; const params = {}; const enabledParams = filter(request.body.multipartForm, (p) => p.enabled); each(enabledParams, (p) => (params[p.name] = p.value)); axiosRequest.data = params; } if (request.body.mode === 'graphql') { const graphqlQuery = { query: get(request, 'body.graphql.query'), variables: JSON.parse(decomment(get(request, 'body.graphql.variables') || '{}')) }; if (!contentTypeDefined) { axiosRequest.headers['content-type'] = 'application/json'; } axiosRequest.data = graphqlQuery; } if (request.script) { axiosRequest.script = request.script; } if (request.tests) { axiosRequest.tests = request.tests; } axiosRequest.vars = request.vars; axiosRequest.collectionVariables = request.collectionVariables; axiosRequest.folderVariables = request.folderVariables; axiosRequest.requestVariables = request.requestVariables; return axiosRequest; }; module.exports = prepareRequest;