Files
bruno/packages/bruno-cli/src/runner/prepare-request.js

377 lines
11 KiB
JavaScript

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;