powerplatform-review-tool
Version:
Evaluate Power Platform solution zip files based on best practice patterns
144 lines (143 loc) • 8.37 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.reviewPowerAutomateComponents = reviewPowerAutomateComponents;
const powerautomatePatterns_1 = require("./powerautomatePatterns");
const ManifestConstant_1 = require("../ManifestConstant");
// eslint-disable-next-line sonarjs/cognitive-complexity
async function reviewPowerAutomateComponents(zipContent, patternDetails) {
const workflowsFolder = zipContent.folder("Workflows");
const powerAutomateResults = [];
const flowPatternCount = Object.keys(ManifestConstant_1.FlowPatternInfo).length;
const errorSummary = [];
let totalScore = 0;
if (!workflowsFolder) {
console.error("No Workflows folder found in the solution.");
return { items: [], totalFlows: 0, totalScore: "0%" };
}
const processingPromises = [];
workflowsFolder.forEach((relativePath, file) => {
if (relativePath.endsWith(".json")) {
processingPromises.push(file
.async("string")
.then((jsonContent) => {
const parsedJson = JSON.parse(jsonContent);
const powerAutomateName = relativePath.split("/").pop()?.replace(".json", "");
// We collect all results in patternResults
const patternResults = [];
let flowScore = 0;
// Prepare to track which patternIDs have been processed, so we don't double-process them.
// Track processed pattern IDs
const processedPatternIDs = new Set();
// List of all patterns to check
const patternsToCheck = [
{
fn: powerautomatePatterns_1.patternCheckDataRetrievalParameters,
name: ManifestConstant_1.FlowPatternDetails[ManifestConstant_1.FlowPatternInfo.DataRetrievalParameters].patternName,
},
{
fn: powerautomatePatterns_1.patternCheckScopeUsage,
name: ManifestConstant_1.FlowPatternDetails[ManifestConstant_1.FlowPatternInfo.ScopeUsage].patternName,
},
{
fn: powerautomatePatterns_1.patternCheckNestedLoops,
name: ManifestConstant_1.FlowPatternDetails[ManifestConstant_1.FlowPatternInfo.NestedLoops].patternName,
},
{
fn: powerautomatePatterns_1.patternCheckConcurrencySettings,
name: ManifestConstant_1.FlowPatternDetails[ManifestConstant_1.FlowPatternInfo.Concurrency].patternName,
},
{
fn: powerautomatePatterns_1.patternCheckConsistentNaming,
name: ManifestConstant_1.FlowPatternDetails[ManifestConstant_1.FlowPatternInfo.NamingConventions].patternName,
},
{
fn: powerautomatePatterns_1.patternCheckTriggerCondition,
name: ManifestConstant_1.FlowPatternDetails[ManifestConstant_1.FlowPatternInfo.TriggerCondition].patternName,
},
{
fn: powerautomatePatterns_1.patternCheckPowerAppResponse,
name: ManifestConstant_1.FlowPatternDetails[ManifestConstant_1.FlowPatternInfo.PowerAppResponse].patternName,
},
{
fn: powerautomatePatterns_1.patternCheckStaticVariables,
name: ManifestConstant_1.FlowPatternDetails[ManifestConstant_1.FlowPatternInfo.StaticVariables].patternName,
},
{
fn: powerautomatePatterns_1.patternCheckMultipleInitializeVariable,
name: ManifestConstant_1.FlowPatternDetails[ManifestConstant_1.FlowPatternInfo.MultipleInitializeVariableActions].patternName,
},
{
fn: powerautomatePatterns_1.patternCheckUnusedVariables,
name: ManifestConstant_1.FlowPatternDetails[ManifestConstant_1.FlowPatternInfo.UnusedVariables].patternName,
},
{
fn: powerautomatePatterns_1.patternOptimizeActionCounts,
name: ManifestConstant_1.FlowPatternDetails[ManifestConstant_1.FlowPatternInfo.ActionCount].patternName,
},
{
fn: powerautomatePatterns_1.patternCheckForActionNote,
name: ManifestConstant_1.FlowPatternDetails[ManifestConstant_1.FlowPatternInfo.ActionNote].patternName,
},
{
fn: powerautomatePatterns_1.patternCheckCreateUpdateInsideLoop,
name: ManifestConstant_1.FlowPatternDetails[ManifestConstant_1.FlowPatternInfo.CreateUpdateInsideLoop].patternName,
},
];
// Execute each pattern check in a try/catch block
for (const patternDefinition of patternsToCheck) {
const { fn, name } = patternDefinition;
try {
const result = fn(parsedJson);
if (patternDetails && patternDetails.length > 0 && !processedPatternIDs.has(result.patternID)) {
// Merge extra pattern details from patternDetails if it matches the same patternID
const matchingPatternDetail = patternDetails.find((detail) => detail.data.patternID === result.patternID);
if (matchingPatternDetail) {
const data = matchingPatternDetail.data;
result.patternName = data.patternName || result.patternName;
result.description = data.description || result.description;
result.docLinks = data.docLinks || result.docLinks;
result.recommendation = data.recommendation || result.recommendation;
result.severity = data.severity || result.severity;
result.category = data.category || result.category;
}
}
patternResults.push(result);
processedPatternIDs.add(result.patternID);
if (result.status === "Pass" /* ManifestPropertyStatusNames.Pass */) {
flowScore += 1;
}
if (result.status === "Warning" /* ManifestPropertyStatusNames.Warning */) {
flowScore += 0.5;
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
errorSummary.push({ pattern: name, error: errorMessage });
}
}
totalScore += flowScore;
powerAutomateResults.push({
name: powerAutomateName,
displayName: powerAutomateName?.split(/-[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}/)[0],
result: patternResults,
score: flowScore > 0 ? Math.round((flowScore / flowPatternCount) * 100) + "%" : "0%",
});
})
.catch((error) => {
const errorMessage = error instanceof Error ? error.message : String(error);
console.error(`Failed to process file ${relativePath}:`, errorMessage);
errorSummary.push({ file: relativePath, error: errorMessage });
}));
}
});
console.log(errorSummary);
await Promise.all(processingPromises);
// Return the final results
return {
items: powerAutomateResults,
totalFlows: powerAutomateResults.length,
totalScore: powerAutomateResults.length > 0
? Math.round((totalScore / (powerAutomateResults.length * flowPatternCount)) * 100).toString() + "%"
: "0%",
};
}