From 7efd18b61f13d3d07d37ad49a584972ede0d7c26 Mon Sep 17 00:00:00 2001 From: Nathan Abondance Date: Thu, 12 Oct 2023 11:38:31 +0200 Subject: [PATCH 1/2] picklist:unrestrict handle every objects paths --- src/commands/texei/picklist/unrestrict.ts | 77 +++++++++++++---------- src/shared/sfdxProjectFolder.ts | 25 ++++++++ 2 files changed, 68 insertions(+), 34 deletions(-) diff --git a/src/commands/texei/picklist/unrestrict.ts b/src/commands/texei/picklist/unrestrict.ts index 4a42103..1d153e2 100644 --- a/src/commands/texei/picklist/unrestrict.ts +++ b/src/commands/texei/picklist/unrestrict.ts @@ -17,6 +17,7 @@ import { } from '@salesforce/sf-plugins-core'; import { Messages, SfError } from '@salesforce/core'; import xml2js = require('xml2js'); +import { findObjectsFolders } from '../../../shared/sfdxProjectFolder'; // Initialize Messages with the current plugin directory Messages.importMessagesDirectory(__dirname); @@ -58,41 +59,49 @@ export default class Unrestrict extends SfCommand { const picklistMetadata = []; - const filesPath = path.join(process.cwd(), 'force-app', 'main', 'default', 'objects'); - const objectFolders = await fs.promises.readdir(filesPath, 'utf8'); - - for (const folder of objectFolders) { - // Excluse Custom Metadata - if (!folder.endsWith('__mdt')) { - const fieldsPath = path.join(filesPath, folder, 'fields'); - if (fs.existsSync(fieldsPath)) { - const fieldsFolder = await fs.promises.readdir(fieldsPath, 'utf8'); - for (const fieldFile of fieldsFolder) { - // Read File file - const fieldFilePath = path.join(fieldsPath, fieldFile); - const fieldData = await fs.promises.readFile(fieldFilePath, 'utf8'); - - // Parsing file - // According to xml2js doc it's better to recreate a parser for each file - // https://www.npmjs.com/package/xml2js#user-content-parsing-multiple-files - const parser = new xml2js.Parser({ explicitArray: false }); - // eslint-disable-next-line @typescript-eslint/unbound-method - const parseString = util.promisify(parser.parseString); - // @ts-ignore: TODO: working code, but look at TS warning - const fieldJson = JSON.parse(JSON.stringify(await parseString(fieldData))); - if ( - (fieldJson.CustomField.type === 'Picklist' || fieldJson.CustomField.type === 'MultiselectPicklist') && - fieldJson.CustomField.valueSet?.valueSetName === undefined && - fieldJson.CustomField.valueSet?.restricted === 'true' - ) { - // Clean Json for update - const fieldMetadata = fieldJson.CustomField; - fieldMetadata.fullName = `${folder}.${fieldJson.CustomField.fullName}`; - fieldMetadata.valueSet.restricted = 'false'; - delete fieldMetadata['$']; - delete fieldMetadata['@xsi:type']; + const objectsFolderPaths: string[] = []; + const EXCLUDED_DIRS = ['node_modules', '.git']; + + // Get all the objects folder paths + for (const recType of findObjectsFolders(process.cwd(), EXCLUDED_DIRS)) { + objectsFolderPaths.push(recType); + } + + for (const objectsFolderPath of objectsFolderPaths) { + const objectFolders = await fs.promises.readdir(objectsFolderPath, 'utf8'); + for (const folder of objectFolders) { + // Excluse Custom Metadata + if (!folder.endsWith('__mdt')) { + const fieldsPath = path.join(objectsFolderPath, folder, 'fields'); + if (fs.existsSync(fieldsPath)) { + const fieldsFolder = await fs.promises.readdir(fieldsPath, 'utf8'); + for (const fieldFile of fieldsFolder) { + // Read File file + const fieldFilePath = path.join(fieldsPath, fieldFile); + const fieldData = await fs.promises.readFile(fieldFilePath, 'utf8'); + + // Parsing file + // According to xml2js doc it's better to recreate a parser for each file + // https://www.npmjs.com/package/xml2js#user-content-parsing-multiple-files + const parser = new xml2js.Parser({ explicitArray: false }); + // eslint-disable-next-line @typescript-eslint/unbound-method + const parseString = util.promisify(parser.parseString); // @ts-ignore: TODO: working code, but look at TS warning - picklistMetadata.push(fieldMetadata); + const fieldJson = JSON.parse(JSON.stringify(await parseString(fieldData))); + if ( + (fieldJson.CustomField.type === 'Picklist' || fieldJson.CustomField.type === 'MultiselectPicklist') && + fieldJson.CustomField.valueSet?.valueSetName === undefined && + fieldJson.CustomField.valueSet?.restricted === 'true' + ) { + // Clean Json for update + const fieldMetadata = fieldJson.CustomField; + fieldMetadata.fullName = `${folder}.${fieldJson.CustomField.fullName}`; + fieldMetadata.valueSet.restricted = 'false'; + delete fieldMetadata['$']; + delete fieldMetadata['@xsi:type']; + // @ts-ignore: TODO: working code, but look at TS warning + picklistMetadata.push(fieldMetadata); + } } } } diff --git a/src/shared/sfdxProjectFolder.ts b/src/shared/sfdxProjectFolder.ts index 5c7bf88..b36c767 100644 --- a/src/shared/sfdxProjectFolder.ts +++ b/src/shared/sfdxProjectFolder.ts @@ -23,6 +23,31 @@ export function getMetadata(metadata: string): string[] { return metadatas; } +// Get all paths of objects folders with the possibility to exclude specific folders +export function findObjectsFolders(startPath: string, excludedDirs: string[] = []): string[] { + const result: string[] = []; + const filesAndDirs = fs.readdirSync(startPath); + + for (const fileOrDir of filesAndDirs) { + const fullPath = path.join(startPath, fileOrDir); + + if (fs.statSync(fullPath).isDirectory()) { + // If the directory is in the exclusion list, skip it. + if (excludedDirs.includes(fileOrDir)) { + continue; + } + + if (fileOrDir === 'objects') { + result.push(fullPath); + } + + result.push(...findObjectsFolders(fullPath, excludedDirs)); + } + } + + return result; +} + export function getFieldsForObject(objectName: string): string[] { const fieldsPath = path.join('force-app', 'main', 'default', 'objects', objectName, 'fields'); From 82d4ae4eed522458ce0be208e9ec0f5a653003e6 Mon Sep 17 00:00:00 2001 From: Nathan Abondance Date: Tue, 17 Oct 2023 12:27:41 +0200 Subject: [PATCH 2/2] use SfProject to get packages paths --- src/commands/texei/picklist/unrestrict.ts | 9 +++-- src/shared/sfdxProjectFolder.ts | 44 +++++++++++++++-------- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/commands/texei/picklist/unrestrict.ts b/src/commands/texei/picklist/unrestrict.ts index 1d153e2..c7b9ce1 100644 --- a/src/commands/texei/picklist/unrestrict.ts +++ b/src/commands/texei/picklist/unrestrict.ts @@ -17,7 +17,7 @@ import { } from '@salesforce/sf-plugins-core'; import { Messages, SfError } from '@salesforce/core'; import xml2js = require('xml2js'); -import { findObjectsFolders } from '../../../shared/sfdxProjectFolder'; +import { getPackagesPaths, findObjectsFolders } from '../../../shared/sfdxProjectFolder'; // Initialize Messages with the current plugin directory Messages.importMessagesDirectory(__dirname); @@ -62,8 +62,11 @@ export default class Unrestrict extends SfCommand { const objectsFolderPaths: string[] = []; const EXCLUDED_DIRS = ['node_modules', '.git']; - // Get all the objects folder paths - for (const recType of findObjectsFolders(process.cwd(), EXCLUDED_DIRS)) { + // Get packages paths + const packagesPaths: string[] = await getPackagesPaths(); + + // Get all the objects folder paths inside the package ones + for (const recType of findObjectsFolders(packagesPaths, EXCLUDED_DIRS)) { objectsFolderPaths.push(recType); } diff --git a/src/shared/sfdxProjectFolder.ts b/src/shared/sfdxProjectFolder.ts index b36c767..569956c 100644 --- a/src/shared/sfdxProjectFolder.ts +++ b/src/shared/sfdxProjectFolder.ts @@ -4,7 +4,7 @@ // ex. for record type: MyRecordTypeForAccount, MyRecordTypeForAccount.recordType-meta.xml, Account.MyRecordTypeForAccount import * as path from 'path'; import * as fs from 'fs'; -import { SfProjectJson } from '@salesforce/core'; +import { NamedPackageDir, SfProject, SfProjectJson } from '@salesforce/core'; import { JsonArray, JsonMap } from '@salesforce/ts-types'; const defaultProjectFolder = 'force-app'; const defaultPackageFolder: string = path.join('force-app', 'main', 'default'); @@ -23,25 +23,41 @@ export function getMetadata(metadata: string): string[] { return metadatas; } +export async function getPackagesPaths(): Promise { + try { + const project: SfProject = await SfProject.resolve(); + const packageDirectories: NamedPackageDir[] = project.getPackageDirectories(); + + return packageDirectories.map((dir) => dir.path); + } catch (error) { + /* eslint-disable no-console */ + console.error('Error retrieving package paths: ', error); + return []; + } +} + // Get all paths of objects folders with the possibility to exclude specific folders -export function findObjectsFolders(startPath: string, excludedDirs: string[] = []): string[] { +export function findObjectsFolders(startPaths: string[], excludedDirs: string[] = []): string[] { const result: string[] = []; - const filesAndDirs = fs.readdirSync(startPath); + for (const iPath of startPaths) { + // the new loop on the new startPaths parameter + const filesAndDirs = fs.readdirSync(iPath); - for (const fileOrDir of filesAndDirs) { - const fullPath = path.join(startPath, fileOrDir); + for (const fileOrDir of filesAndDirs) { + const fullPath = path.join(iPath, fileOrDir); - if (fs.statSync(fullPath).isDirectory()) { - // If the directory is in the exclusion list, skip it. - if (excludedDirs.includes(fileOrDir)) { - continue; - } + if (fs.statSync(fullPath).isDirectory()) { + // If the directory is in the exclusion list, skip it. + if (excludedDirs.includes(fileOrDir)) { + continue; + } - if (fileOrDir === 'objects') { - result.push(fullPath); - } + if (fileOrDir === 'objects') { + result.push(fullPath); + } - result.push(...findObjectsFolders(fullPath, excludedDirs)); + result.push(...findObjectsFolders([fullPath], excludedDirs)); + } } }