Streamline management of API collections #1
@@ -294,7 +294,7 @@ function getRequestConfigForFile(yamlFile, sourceDir) {
|
||||
return resolved.reduce((result, config) => mergeRequestConfig(result, config), {});
|
||||
}
|
||||
|
||||
function buildRequestContent(request, requestName, requestConfig = {}) {
|
||||
function buildRequestContent(request, requestName, requestConfig = {}, dotenvVariables = new Set()) {
|
||||
const lines = [];
|
||||
const variableDefinitions = [];
|
||||
const seenVariables = new Set();
|
||||
@@ -304,13 +304,32 @@ function buildRequestContent(request, requestName, requestConfig = {}) {
|
||||
return;
|
||||
}
|
||||
const normalized = String(name).trim();
|
||||
if (!normalized || seenVariables.has(normalized)) {
|
||||
if (!normalized || seenVariables.has(normalized) || dotenvVariables.has(normalized)) {
|
||||
return;
|
||||
}
|
||||
seenVariables.add(normalized);
|
||||
variableDefinitions.push({ name: normalized, value });
|
||||
};
|
||||
|
||||
const addReferencedVariables = (value, fallbackValue = 'YOUR_VALUE_HERE') => {
|
||||
for (const placeholder of collectPlaceholders(String(value))) {
|
||||
addVariable(placeholder, fallbackValue);
|
||||
}
|
||||
};
|
||||
|
||||
const renderValue = (value) => {
|
||||
if (typeof value !== 'string') {
|
||||
return value;
|
||||
}
|
||||
return value.replace(/\{\{([^{}]+)\}\}/g, (match, name) => {
|
||||
const normalized = String(name).trim();
|
||||
if (normalized && dotenvVariables.has(normalized)) {
|
||||
return `{{$dotenv ${normalized}}}`;
|
||||
}
|
||||
return match;
|
||||
});
|
||||
};
|
||||
|
||||
const configVariables = Array.isArray(requestConfig.variables) ? requestConfig.variables : [];
|
||||
for (const variable of configVariables) {
|
||||
if (variable && variable.name) {
|
||||
@@ -320,10 +339,9 @@ function buildRequestContent(request, requestName, requestConfig = {}) {
|
||||
|
||||
let url = request.url || '';
|
||||
if (url) {
|
||||
for (const placeholder of collectPlaceholders(url)) {
|
||||
addVariable(placeholder, 'YOUR_VALUE_HERE');
|
||||
}
|
||||
addReferencedVariables(url);
|
||||
url = url.replace(/:([A-Za-z0-9_]+)/g, (_, name) => `{{${name}}}`);
|
||||
url = renderValue(url);
|
||||
// Since Bruno puts enabled params in the URL, this avoids duplicate query params
|
||||
url = url.split('?')[0];
|
||||
}
|
||||
@@ -335,10 +353,8 @@ function buildRequestContent(request, requestName, requestConfig = {}) {
|
||||
if (!header || !header.name) {
|
||||
continue;
|
||||
}
|
||||
for (const placeholder of collectPlaceholders(String(header.value ?? ''))) {
|
||||
addVariable(placeholder, 'YOUR_VALUE_HERE');
|
||||
}
|
||||
headers.push({ name: header.name, value: header.value ?? '' });
|
||||
addReferencedVariables(header.value ?? '');
|
||||
headers.push({ name: header.name, value: renderValue(header.value ?? '') });
|
||||
}
|
||||
|
||||
for (const param of request.params || []) {
|
||||
@@ -351,40 +367,32 @@ function buildRequestContent(request, requestName, requestConfig = {}) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const placeholder of collectPlaceholders(String(value))) {
|
||||
addVariable(placeholder, 'YOUR_VALUE_HERE');
|
||||
}
|
||||
addReferencedVariables(value);
|
||||
|
||||
if (type === 'header') {
|
||||
headers.push({ name, value });
|
||||
headers.push({ name, value: renderValue(value) });
|
||||
} else {
|
||||
queryParams.push({ name, value });
|
||||
queryParams.push({ name, value: renderValue(value) });
|
||||
}
|
||||
}
|
||||
|
||||
if (requestConfig.auth)
|
||||
{
|
||||
if (requestConfig.auth.type === 'bearer') {
|
||||
for (const placeholder of collectPlaceholders(String(requestConfig.auth.token ?? ''))) {
|
||||
addVariable(placeholder, 'YOUR_VALUE_HERE');
|
||||
}
|
||||
addReferencedVariables(requestConfig.auth.token ?? '');
|
||||
headers.push({
|
||||
name: 'Authorization',
|
||||
value: `Bearer ${requestConfig.auth.token ?? ''}`,
|
||||
value: `Bearer ${renderValue(requestConfig.auth.token ?? '')}`,
|
||||
});
|
||||
} else if (requestConfig.auth.type === 'basic') {
|
||||
const username = requestConfig.auth.username ?? '';
|
||||
const password = requestConfig.auth.password ?? '';
|
||||
for (const placeholder of collectPlaceholders(String(username))) {
|
||||
addVariable(placeholder, 'YOUR_VALUE_HERE');
|
||||
}
|
||||
for (const placeholder of collectPlaceholders(String(password))) {
|
||||
addVariable(placeholder, 'YOUR_VALUE_HERE');
|
||||
}
|
||||
addReferencedVariables(username);
|
||||
addReferencedVariables(password);
|
||||
// VSCode REST Client can manage username:password format directly!
|
||||
headers.push({
|
||||
name: 'Authorization',
|
||||
value: `Basic ${username}:${password}`,
|
||||
value: `Basic ${renderValue(username)}:${renderValue(password)}`,
|
||||
});
|
||||
} else {
|
||||
headers.push({
|
||||
@@ -440,8 +448,30 @@ function walkYamlFiles(rootDir) {
|
||||
return results;
|
||||
}
|
||||
|
||||
function getDotenvVariablesForTargetDir(targetDir, outputRoot, dotenvVariablesByTarget) {
|
||||
const variables = new Set();
|
||||
let currentDir = targetDir;
|
||||
|
||||
while (true) {
|
||||
const vars = dotenvVariablesByTarget.get(currentDir);
|
||||
if (vars) {
|
||||
for (const variable of vars) {
|
||||
variables.add(variable);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentDir === outputRoot || path.dirname(currentDir) === currentDir) {
|
||||
break;
|
||||
}
|
||||
currentDir = path.dirname(currentDir);
|
||||
}
|
||||
|
||||
return variables;
|
||||
}
|
||||
|
||||
function writeEnvironmentTemplates(sourceDir, outputRoot) {
|
||||
const targets = [];
|
||||
const dotenvVariablesByTarget = new Map();
|
||||
|
||||
const visit = (currentDir) => {
|
||||
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
||||
@@ -496,7 +526,10 @@ function writeEnvironmentTemplates(sourceDir, outputRoot) {
|
||||
|
||||
const templateContent = variableNames.length > 0 ? `${variableNames.map(v => `${v}={{${v}}}`).join('\n')}\n` : '';
|
||||
fs.writeFileSync(path.join(targetDir, '.env.template'), templateContent, 'utf8');
|
||||
dotenvVariablesByTarget.set(targetDir, new Set(variableNames));
|
||||
}
|
||||
|
||||
return dotenvVariablesByTarget;
|
||||
}
|
||||
|
||||
function main() {
|
||||
@@ -522,7 +555,7 @@ function main() {
|
||||
|
||||
const outputRoot = path.join(workspaceRoot, 'autodocs', 'http', collection.name);
|
||||
ensureDir(outputRoot);
|
||||
writeEnvironmentTemplates(sourceDir, outputRoot);
|
||||
const dotenvVariablesByTarget = writeEnvironmentTemplates(sourceDir, outputRoot);
|
||||
|
||||
const yamlFiles = walkYamlFiles(sourceDir);
|
||||
let processed = 0;
|
||||
@@ -542,7 +575,8 @@ function main() {
|
||||
|
||||
const requestConfig = getRequestConfigForFile(yamlFile, sourceDir);
|
||||
const outputFile = path.join(targetDir, `${parsedPath.name}.http`);
|
||||
const requestContent = buildRequestContent(httpBlock, requestName, requestConfig);
|
||||
const dotenvVariables = getDotenvVariablesForTargetDir(targetDir, outputRoot, dotenvVariablesByTarget);
|
||||
const requestContent = buildRequestContent(httpBlock, requestName, requestConfig, dotenvVariables);
|
||||
fs.writeFileSync(outputFile, `${requestContent}\n`, 'utf8');
|
||||
processed += 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user