Skip to content

Commit

Permalink
add profile:clean
Browse files Browse the repository at this point in the history
  • Loading branch information
Ahmed BENSAAD committed Dec 15, 2021
1 parent 8e7e09a commit 8112c79
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 2 deletions.
128 changes: 128 additions & 0 deletions src/commands/mdt/profile/clean.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { flags, SfdxCommand } from "@salesforce/command";
import { AnyJson } from "@salesforce/ts-types";
import * as chalk from "chalk";
import * as x2jParser from "fast-xml-parser";
import * as path from "path";
import { j2xParser } from "fast-xml-parser";

import { j2xOptions, x2jOptions } from "../../../config/fastXMLOptions";

import {
substringBefore,
readFile,
writeXMLFile,
profileAccessFilenamesCompare,
} from "../../../utils/utilities";

export default class Cleaner extends SfdxCommand {
public static examples = [
`$ sfdx mdt:profile:clean -p {sourcepath} -l {cleansourcepath} [-d {outputdirectory}]
Clean Profile xml file
`,
];

protected static flagsConfig = {
sourcepath: flags.string({
char: "p",
required: true,
description: "The path to the source metadata file",
}),
cleansourcepath: flags.string({
char: "l",
description: "The path to the file that list the profile access to clean",
}),
outputdir: flags.string({
char: "d",
description: "The output directory that stores metadata files",
}),
};

public async run(): Promise<AnyJson> {
this.ux.startSpinner(chalk.yellowBright("Cleaning Profile"));
try {
await this.clean(
this.flags.sourcepath,
this.flags.cleansourcepath,
this.flags.outputdir
);
} catch (e) {
// output error
this.ux.stopSpinner("❌");
this.ux.error(chalk.redBright(e));
}
this.ux.stopSpinner("✔️");
// Return an object to be displayed with --json
return { success: true };
}

public async clean(
sourcepath: string,
cleansourcepath: string,
outputdir: string
) {
const profileXML: string = await readFile(sourcepath);
const cleanXML: string = await readFile(cleansourcepath);
const profileName: string = substringBefore(path.basename(sourcepath), ".");
const destpath: string = outputdir
? `${outputdir}/${profileName}.profile-meta.xml`
: sourcepath;

if (x2jParser.validate(cleanXML) === true) {
if (x2jParser.validate(profileXML) === true) {
// parse xml to json
const profileJSON = x2jParser.parse(profileXML, x2jOptions);
const cleanJSON = x2jParser.parse(cleanXML, x2jOptions);

for (const accessTypeToClean in cleanJSON.Profile) {
const accessList = profileJSON.Profile[accessTypeToClean];
if (Array.isArray(accessList)) {
profileJSON.Profile[accessTypeToClean] = accessList.filter(
(access) => {
const cleanList = cleanJSON.Profile[accessTypeToClean];
if (Array.isArray(cleanList)) {
for (const accessToClean of cleanJSON.Profile[
accessTypeToClean
]) {
if (
profileAccessFilenamesCompare(
accessTypeToClean,
access,
accessToClean
) === 0
) {
return false;
}
}
} else {
if (
profileAccessFilenamesCompare(
accessTypeToClean,
access,
cleanList
) === 0
) {
return false;
}
}
return true;
}
);
}
}

// init the json to xml parser
const json2xmlParser = new j2xParser(j2xOptions);

// parse json to xml
const formattedXml: string = json2xmlParser.parse(profileJSON);

// write xml version & encoding
await writeXMLFile(`${destpath}`, formattedXml);
} else {
throw `${sourcepath} is an invalid xml file`;
}
} else {
throw `${cleansourcepath} is an invalid xml file`;
}
}
}
31 changes: 29 additions & 2 deletions src/utils/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,37 @@ const profileAccessFilenamesCompare = (
) => {
switch (accessType) {
case "applicationVisibilities":
if (access1["application"] === access2["application"]) return 0;
return access1["application"] > access2["application"] ? 1 : -1;
case "categoryGroupVisibilities":
if (access1["dataCategoryGroup"] === access2["dataCategoryGroup"])
return 0;
return access1["dataCategoryGroup"] > access2["dataCategoryGroup"]
? 1
: -1;
case "customMetadataTypeAccesses":
if (access1["name"] === access2["name"]) return 0;
return access1["name"] > access2["name"] ? 1 : -1;
case "classAccesses":
if (access1["apexClass"] === access2["apexClass"]) return 0;
return access1["apexClass"] > access2["apexClass"] ? 1 : -1;
case "customPermissions":
if (access1["name"] === access2["name"]) return 0;
return access1["name"] > access2["name"] ? 1 : -1;
case "customSettingAccesses":
if (access1["name"] === access2["name"]) return 0;
return access1["name"] > access2["name"] ? 1 : -1;
case "externalDataSourceAccess":
if (access1["externalDataSource"] === access2["externalDataSource"])
return 0;
return access1["externalDataSource"] > access2["externalDataSource"]
? 1
: -1;
case "fieldPermissions":
if (access1["field"] === access2["field"]) return 0;
return access1["field"] > access2["field"] ? 1 : -1;
case "flowAccesses":
if (access1["flow"] === access2["flow"]) return 0;
return access1["flow"] > access2["flow"] ? 1 : -1;
case "layoutAssignments":
const layoutName1 = access1["layout"];
Expand All @@ -46,9 +57,19 @@ const profileAccessFilenamesCompare = (
} else {
if (!recordTypeName1) return -1;
if (!recordTypeName2) return 1;
if (recordTypeName1 === recordTypeName2) return 0;
return recordTypeName1 > recordTypeName2 ? 1 : -1;
}
case "loginIpRanges":
if (
normalizeIP(access1["startAddress"]) +
"." +
normalizeIP(access1["endAddress"]) ===
normalizeIP(access2["startAddress"]) +
"." +
normalizeIP(access2["endAddress"])
)
return 0;
return normalizeIP(access1["startAddress"]) +
"." +
normalizeIP(access1["endAddress"]) >
Expand All @@ -58,16 +79,22 @@ const profileAccessFilenamesCompare = (
? 1
: -1;
case "objectPermissions":
if (access1["object"] === access2["object"]) return 0;
return access1["object"] > access2["object"] ? 1 : -1;
case "pageAccesses":
if (access1["apexPage"] === access2["apexPage"]) return 0;
return access1["apexPage"] > access2["apexPage"] ? 1 : -1;
case "profileActionOverrides":
if (access1["actionName"] === access2["actionName"]) return 0;
return access1["actionName"] > access2["actionName"] ? 1 : -1;
case "recordTypeVisibilities":
if (access1["recordType"] === access2["recordType"]) return 0;
return access1["recordType"] > access2["recordType"] ? 1 : -1;
case "tabVisibilities":
if (access1["tab"] === access2["tab"]) return 0;
return access1["tab"] > access2["tab"] ? 1 : -1;
case "userPermissions":
if (access1["name"] === access2["name"]) return 0;
return access1["name"] > access2["name"] ? 1 : -1;
default:
return 0;
Expand Down Expand Up @@ -190,8 +217,8 @@ const copyFile = async (

/**
* copy folder source recusively to folder target
* @param source
* @param target
* @param source
* @param target
*/
const copyFolderRecursive = async (source, target) => {
let files = [];
Expand Down

0 comments on commit 8112c79

Please sign in to comment.