UNPKG

apim-policy-utils

Version:

An XML file scripts maniputaling and debugging tool targeting to help working with Azure APIM Policy files in xml format.

177 lines (154 loc) 6.57 kB
import * as fs from 'fs'; import path from 'path'; import { separator } from './constants'; export async function combineScript(directoryPath: string, destinationPath?: string) { const filenames = await getFilenamesInDirectory(directoryPath); // Compute the xml filename for storing the result const dirArray = directoryPath.split('/') const xmlFilename = `${dirArray[dirArray.length - 1]}.xml` // Read xmlContent from the generated xmlfile let xmlFileContent = fs.readFileSync(`${directoryPath}/replaced.xml`, 'utf8'); // Get code outside of block-xxx.csx file and inline-xxx.csx file filenames.forEach(filename => { if ((filename.startsWith('inline') || filename.startsWith('block')) && filename.endsWith('.csx')) { let codeSnippet = getCodeInMethod(`${directoryPath}/${filename}`, 'ExtractedScript') codeSnippet = refineCode(filename, codeSnippet); const xmlPlaceholder = filename.slice(0, -4); xmlFileContent = replaceAll(xmlFileContent, xmlPlaceholder, codeSnippet) } // Write the combined XML to a file if (!destinationPath) { fs.writeFileSync(`${directoryPath}/${xmlFilename}`, xmlFileContent); } else { updateFileInDirectory(destinationPath, xmlFilename, xmlFileContent) .then(() => { console.log('File update complete.'); }) .catch((error) => { console.error('An error occurred while updating the file:', error); }); } }); } function refineCode(file: string, codeSnippet: string | null): string | null { if (codeSnippet === null) { return ''; } const inlinePrefix = "return " if (file.startsWith('inline')) { codeSnippet = codeSnippet.trim() if (codeSnippet.startsWith(inlinePrefix)) { codeSnippet = codeSnippet.substring(inlinePrefix.length).trim() // remove "return " prefix return codeSnippet.slice(0, -1) // remove ";" suffix } } if (file.startsWith('block')) { const lines = codeSnippet.split('\n').map(line => line); const nonEmptyLines = lines.filter(line => line !== ''); const indentation = nonEmptyLines[0].match(/^\s*/)?.[0]; const formattedContent = nonEmptyLines.map((line, index) => { if (index === 0 || index === nonEmptyLines.length - 1) { return line; } return `${indentation}${line}`; }).join('\n'); return `${formattedContent}` } return codeSnippet } function getFilenamesInDirectory(directoryPath: string): Promise<string[]> { return new Promise((resolve, reject) => { fs.readdir(directoryPath, (err, files) => { if (err) { reject(err); } else { const filenames: string[] = []; files.forEach((file) => { filenames.push(file); }); resolve(filenames); } }); }); } function getCodeInMethod(csxFilePath: string, methodName: string): string | null { try { // Read the contents of the file at the given path const fileContents: string = fs.readFileSync(csxFilePath, 'utf8'); // Find the starting index of the desired method const startRegex = new RegExp(`(?<=\\b(?:public|private|internal)?\\s+(?:async\\s+)?(?:static\\s+)?(?:readonly\\s+)?(?:partial\\s+)?(?:unsafe\\s+)?(?:virtual\\s+)?(?:override\\s+)?\\w+\\s+${methodName}\\s*\\()`); const startIndex: number = fileContents.search(startRegex); if (startIndex === -1) { console.error(`Method '${methodName}' not found in file at path '${csxFilePath}'`); return null; } // Find the ending index of the desired method let openBraces = 0; let actualStartIndex: number = startIndex; let endIndex: number = startIndex; for (let i = startIndex; i < fileContents.length; i++) { if (fileContents[i] === '{') { openBraces++; if (openBraces === 1) { actualStartIndex = i; } } else if (fileContents[i] === '}') { openBraces--; if (openBraces === 0) { endIndex = i; break; } } } // The code within the method let codeInMethod: string = fileContents.slice(actualStartIndex, endIndex + 1); codeInMethod = removeSurroundingChars(codeInMethod); // Remove everything before the generated sepatators codeInMethod = removeCodeAboveSeparator(codeInMethod); codeInMethod = convertNamedValue(codeInMethod) return codeInMethod; } catch (error: any) { console.error(`Error reading file at path '${csxFilePath}': ${error.message}`); return null; } } function replaceAll(xml: string, toReplace: string, replacement: string | null): string { const regex = new RegExp(toReplace, 'g'); replacement = replacement ? replacement : ''; xml = xml.replace(regex, replacement); return xml; } function removeSurroundingChars(str: string): string { if (str.startsWith('{') && str.endsWith('}')) { str = str.slice(1, -1); } return str; } function removeCodeAboveSeparator(input: string): string { const separatorIndex = input.indexOf(separator); if (separatorIndex === -1) { return ""; } return input.substring(separatorIndex + separator.length); } function convertNamedValue(input: string): string { const regex = /{nv_(\w+)}/g; return input.replace(regex, (match, p1) => `{{${p1.replace(/_/g, "-")}}}`); } async function updateFileInDirectory(destinationPath: string, fileName: string, content: string): Promise<void> { const files = fs.readdirSync(destinationPath); if (files.includes(fileName)) { const filePath = path.join(destinationPath, fileName); fs.writeFileSync(filePath, content); return; } for (const file of files) { const filePath = path.join(destinationPath, file); const isDirectory = fs.statSync(filePath).isDirectory(); if (isDirectory) { await updateFileInDirectory(filePath, fileName, content); } } const rootFilePath = path.join(destinationPath, fileName); fs.writeFileSync(rootFilePath, content); }