Skip to content
This repository has been archived by the owner on Mar 18, 2024. It is now read-only.

Commit

Permalink
feat(profiles): add profile merge and retrieve to sfp (#1353)
Browse files Browse the repository at this point in the history
* move from sfpowerkit

* fix: remove useWorkspaces from lerna

removing deprecated configuration value for lerna 7 compat

* feat(profiles): merge

* feat(profiles): include merge and retrieve

* Revert "fix: remove useWorkspaces from lerna"

This reverts commit a090f91.

* fix(profiles):  update profile commands to latest libs

---------

Co-authored-by: Azlam <[email protected]>
Co-authored-by: azlam <[email protected]>
  • Loading branch information
3 people authored Sep 20, 2023
1 parent 6ca7222 commit d783041
Show file tree
Hide file tree
Showing 5 changed files with 289 additions and 5 deletions.
3 changes: 2 additions & 1 deletion packages/sfpowerscripts-cli/messages/profile_retrieve.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"commandDescription": "Retrieve profiles from the salesforce org with all its associated permissions. Common use case for this command is to migrate profile changes from a integration environment to other higher environments [overcomes SFDX CLI Profile retrieve issue where it doesnt fetch the full profile unless the entire metadata is present in source], or retrieving profiles from production to lower environments for testing.",
"folderFlagDescription": "retrieve only updated versions of profiles found in this directory, If ignored, all profiles will be retrieved.",
"profileListFlagDescription": "comma separated list of profiles to be retrieved. Use it for selectively retrieving an existing profile or retrieving a new profile",
"deleteFlagDescription": "set this flag to delete profile files that does not exist in the org, when retrieving in bulk"
"deleteFlagDescription": "set this flag to delete profile files that does not exist in the org, when retrieving in bulk",
"retriveDelayWarning":"Retrieving profiles may take a significant time depending on the number of profiles \nand managed package components installed in your org,Please be patient"
}
2 changes: 1 addition & 1 deletion packages/sfpowerscripts-cli/src/SfpowerscriptsCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export default abstract class SfpowerscriptsCommand extends Command {
SFPLogger.printHeaderLine('',COLOR_HEADER,LoggerLevel.INFO);
SFPLogger.log(
COLOR_HEADER(
`sfpowerscripts -- The DX@Scale CI/CD Orchestrator -Version:${this.config.version} -Release:${this.config.pjson.release}`
`sfp -- The DX@Scale CLI -Version:${this.config.version} -Release:${this.config.pjson.release}`
)
);

Expand Down
157 changes: 157 additions & 0 deletions packages/sfpowerscripts-cli/src/commands/profile/merge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { Messages, Org } from '@salesforce/core';
import { isNil } from 'lodash';
import { Sfpowerkit } from '@dxatscale/sfprofiles/lib/utils/sfpowerkit';
import SFPLogger, { LoggerLevel } from '@dxatscale/sfp-logger';
import ProfileRetriever from '@dxatscale/sfprofiles/lib/impl/metadata/retriever/profileRetriever';
import ProfileMerge from '@dxatscale/sfprofiles/lib/impl/source/profileMerge';
import SfpowerscriptsCommand from '../../SfpowerscriptsCommand';
import Table from 'cli-table';
import { ZERO_BORDER_TABLE } from '../../ui/TableConstants';
import { arrayFlagSfdxStyle, loglevel, orgApiVersionFlagSfdxStyle, requiredUserNameFlag } from '../../flags/sfdxflags';
import { Flags } from '@oclif/core';

Messages.importMessagesDirectory(__dirname);

const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'profile_merge');

export default class Merge extends SfpowerscriptsCommand {
public static description = messages.getMessage('commandDescription');

public static examples = [
`$ sfp profile:merge -u sandbox`,
`$ sfp profile:merge -f force-app -n "My Profile" -u sandbox`,
`$ sfp profile:merge -f "module1, module2, module3" -n "My Profile1, My profile2" -u sandbox`,
];

public static flags = {
folder: arrayFlagSfdxStyle({
char: 'f',
description: messages.getMessage('folderFlagDescription'),
required: false,
}),
profilelist: arrayFlagSfdxStyle({
char: 'n',
description: messages.getMessage('profileListFlagDescription'),
required: false,
}),
metadata: arrayFlagSfdxStyle({
char: 'm',
description: messages.getMessage('metadataFlagDescription'),
required: false,
}),
delete: Flags.boolean({
char: 'd',
description: messages.getMessage('deleteFlagDescription'),
required: false,
}),
targetorg: requiredUserNameFlag,
'apiversion': orgApiVersionFlagSfdxStyle,
loglevel,
};

// Comment this out if your command does not require an org username
protected static requiresUsername = true

// Set this to true if your command requires a project workspace; 'requiresProject' is false by default
protected static requiresProject = true;

public async execute(): Promise<any> {
let argFolder = this.flags.folder;
let argProfileList = this.flags.profilelist;
let argMetadatas = this.flags.metadata;

// argMetadatas = (val: string) => {
// let parts = val.split(':');
// return {
// MetadataType: parts[0].trim(),
// ApiName: parts.length >= 2 ? parts[1].trim() : '*',
// };
// };

Sfpowerkit.initCache();

let metadatas = undefined;
let invalidArguments = [];

if (argMetadatas !== undefined) {
metadatas = {};
ProfileRetriever.supportedMetadataTypes.forEach((val) => {
metadatas[val] = [];
});
for (let i = 0; i < argMetadatas.length; i++) {
if (ProfileRetriever.supportedMetadataTypes.includes(argMetadatas[i].MetadataType)) {
metadatas[argMetadatas[i].MetadataType].push(argMetadatas[i].ApiName);
} else {
invalidArguments.push(argMetadatas[i].MetadataType);
}
}
if (invalidArguments.length > 0) {
throw new Error(
'Metadata(s) ' + invalidArguments.join(', ') + ' is/are not supported.'
);
}
}

if (!isNil(argFolder) && argFolder.length !== 0) {
Sfpowerkit.setDefaultFolder(argFolder[0]);
}
``;


this.org = await Org.create({ aliasOrUsername: this.flags.targetorg });
const profileUtils = new ProfileMerge(this.org);

let mergedProfiles = await profileUtils.merge(argFolder, argProfileList || [], metadatas, this.flags.delete);

const table = new Table({
head: ['State', 'Full Name', 'Type', 'Path'],
chars: ZERO_BORDER_TABLE,
});
if (mergedProfiles.added) {
mergedProfiles.added.forEach((profile) => {
table.push({
state: 'Add',
fullName: profile.name,
type: 'Profile',
path: profile.path,
});
});
}
if (mergedProfiles.updated) {
mergedProfiles.updated.forEach((profile) => {
table.push({
state: 'Merged',
fullName: profile.name,
type: 'Profile',
path: profile.path,
});
});
}
if (this.flags.delete) {
if (mergedProfiles.deleted) {
mergedProfiles.deleted.forEach((profile) => {
table.push({
state: 'Deleted',
fullName: profile.name,
type: 'Profile',
path: profile.path,
});
});
}
} else {
if (mergedProfiles.deleted) {
mergedProfiles.deleted.forEach((profile) => {
table.push({
state: 'Skipped',
fullName: profile.name,
type: 'Profile',
path: profile.path,
});
});
}
}
SFPLogger.log(table.toString(), LoggerLevel.INFO);

return mergedProfiles;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,9 @@ import { ZERO_BORDER_TABLE } from '../../ui/TableConstants';
import { Flags } from '@oclif/core';
import { arrayFlagSfdxStyle, loglevel, orgApiVersionFlagSfdxStyle, requiredUserNameFlag } from '../../flags/sfdxflags';

// Initialize Messages with the current plugin directory
Messages.importMessagesDirectory(__dirname);

// Load the specific messages for this file. Messages from @salesforce/command, @salesforce/core,
// or any library that is using the messages framework can also be loaded this way.

const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'profile_reconcile');

export default class Reconcile extends SfpowerscriptsCommand {
Expand Down
128 changes: 128 additions & 0 deletions packages/sfpowerscripts-cli/src/commands/profile/retrieve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { Messages, Org } from '@salesforce/core';
import * as fs from 'fs-extra';
import { isNil } from 'lodash';
import { Sfpowerkit } from '@dxatscale/sfprofiles/lib/utils/sfpowerkit';
import ProfileSync from '@dxatscale/sfprofiles/lib/impl/source/profileSync';
import SfpowerscriptsCommand from '../../SfpowerscriptsCommand';
import Table from 'cli-table';
import { ZERO_BORDER_TABLE } from '../../ui/TableConstants';
import { arrayFlagSfdxStyle, loglevel, orgApiVersionFlagSfdxStyle, requiredUserNameFlag } from '../../flags/sfdxflags';
import { Flags } from '@oclif/core';
import SFPLogger, { COLOR_KEY_MESSAGE, COLOR_WARNING, LoggerLevel } from '@dxatscale/sfp-logger';


Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@dxatscale/sfpowerscripts', 'profile_retrieve');

export default class Retrieve extends SfpowerscriptsCommand {
public static description = messages.getMessage('commandDescription');

public static examples = [
`$ sfp profile:retrieve -u prod`,
`$ sfp profile:retrieve -f force-app -n "My Profile" -u prod`,
`$ sfp profile:retrieve -f "module1, module2, module3" -n "My Profile1, My profile2" -u prod`,
];


public static flags = {
folder: arrayFlagSfdxStyle({
char: 'f',
description: messages.getMessage('folderFlagDescription'),
required: false,
}),
profilelist: arrayFlagSfdxStyle({
char: 'n',
description: messages.getMessage('profileListFlagDescription'),
required: false,
}),
delete: Flags.boolean({
char: 'd',
description: messages.getMessage('deleteFlagDescription'),
required: false,
}),
targetorg: requiredUserNameFlag,
'apiversion': orgApiVersionFlagSfdxStyle,
loglevel,
};

// Comment this out if your command does not require an org username
protected static requiresUsername = true;

// Set this to true if your command requires a project workspace; 'requiresProject' is false by default
protected static requiresProject = true;

public async execute(): Promise<any> {
let argFolder: string = this.flags.folder;
let argProfileList: string[] = this.flags.profilelist;

let folders: string[] = [];
if (!isNil(argFolder) && argFolder.length !== 0) {
for (let dir of argFolder) {
if (!fs.existsSync(dir)) {
throw new Error(`The profile path ${dir} does not exist.`);
}
}
folders.push(...argFolder);
}

Sfpowerkit.initCache();

SFPLogger.log(COLOR_WARNING(messages.getMessage('retriveDelayWarning')),LoggerLevel.INFO);
SFPLogger.log(COLOR_KEY_MESSAGE(`Retrieving profiles from ${this.flags.targetorg}`),LoggerLevel.INFO );

this.org = await Org.create({ aliasOrUsername: this.flags.targetorg });
const profileUtils = new ProfileSync(this.org);

let syncProfiles = await profileUtils.sync(folders, argProfileList || [], this.flags.delete);

const table = new Table({
head: ['State', 'Full Name', 'Type', 'Path'],
chars: ZERO_BORDER_TABLE,
});
if (syncProfiles.added) {
syncProfiles.added.forEach((profile) => {
table.push({
state: 'Add',
fullName: profile.name,
type: 'Profile',
path: profile.path,
});
});
}
if (syncProfiles.updated) {
syncProfiles.updated.forEach((profile) => {
table.push({
state: 'Updated',
fullName: profile.name,
type: 'Profile',
path: profile.path,
});
});
}
if (this.flags.delete) {
if (syncProfiles.deleted) {
syncProfiles.deleted.forEach((profile) => {
table.push({
state: 'Deleted',
fullName: profile.name,
type: 'Profile',
path: profile.path,
});
});
}
} else {
if (syncProfiles.deleted) {
syncProfiles.deleted.forEach((profile) => {
table.push({
state: 'Skipped',
fullName: profile.name,
type: 'Profile',
path: profile.path,
});
});
}
}

return syncProfiles;
}
}

0 comments on commit d783041

Please sign in to comment.