From b4f3392877d496c9720a6039447074fd76c383dc Mon Sep 17 00:00:00 2001 From: Vincent FINET Date: Fri, 28 Aug 2026 00:18:22 +0200 Subject: [PATCH 1/3] Simplification when retrieving sharing rules #724 --- .../orgcheck-api-salesforcemanager-impl.ts | 5 +++-- .../orgcheck-api-salesforcemanager.ts | 3 ++- ...heck-api-dataset-profilepasswordpolicies.ts | 5 +---- .../orgcheck-api-dataset-sharingrules.ts | 15 +-------------- ...orgcheck-api-salesforcemanager.unit.test.ts | 18 ++++++++++++++---- .../orgcheck-api-salesforce-mock.utility.ts | 2 +- 6 files changed, 22 insertions(+), 26 deletions(-) diff --git a/packages/orgcheck-api/src/api/core/salesforce/orgcheck-api-salesforcemanager-impl.ts b/packages/orgcheck-api/src/api/core/salesforce/orgcheck-api-salesforcemanager-impl.ts index d55613ef..8fadab35 100644 --- a/packages/orgcheck-api/src/api/core/salesforce/orgcheck-api-salesforcemanager-impl.ts +++ b/packages/orgcheck-api/src/api/core/salesforce/orgcheck-api-salesforcemanager-impl.ts @@ -754,13 +754,14 @@ export class SalesforceManager implements SalesforceManagerIntf { /** * @see SalesforceManagerIntf.readMetadata * @param {SalesforceMetadataRequest[]} metadatas - Information of what metadata you want to retrieve + * @param {boolean} expandWildcard - true if you want to expand the wildcard, false otherwise * @param {SimpleLoggerIntf} logger - Logger * @returns {Promise} Information by metadata type * @throws {SalesforceError} If an error occurs during the metadata read * @public * @async */ - public async readMetadata(metadatas: SalesforceMetadataRequest[], logger: SimpleLoggerIntf): Promise>>> { + public async readMetadata(metadatas: SalesforceMetadataRequest[], expandWildcard: boolean, logger: SimpleLoggerIntf): Promise>>> { // Let's start to check if we are 'allowed' to use the Salesforce API... this._watchDog?.beforeRequest(); // if limit has been reached, an error will be thrown here // Now we can start, log some message @@ -768,7 +769,7 @@ export class SalesforceManager implements SalesforceManagerIntf { // First, if the metadatas contains an item with member='*' we want to list for this type and substitute the '*' with the fullNames await LargeProcessor.runAll( // only get the types that have at least '*' once - metadatas.filter((m) => m.members?.includes('*')) + metadatas.filter((m) => expandWildcard === true && m.members?.includes('*')) // then turn this filtered list into a list of promises .map((metadata) => async () => { // using async as we just want to run parallel processes without manipulating their return values try { diff --git a/packages/orgcheck-api/src/api/core/salesforce/orgcheck-api-salesforcemanager.ts b/packages/orgcheck-api/src/api/core/salesforce/orgcheck-api-salesforcemanager.ts index 70fa7dab..151a7cb3 100644 --- a/packages/orgcheck-api/src/api/core/salesforce/orgcheck-api-salesforcemanager.ts +++ b/packages/orgcheck-api/src/api/core/salesforce/orgcheck-api-salesforcemanager.ts @@ -159,13 +159,14 @@ export interface SalesforceManagerIntf { /** * @description Method to retrieve a list of metadata types * @param {SalesforceMetadataRequest[]} metadatas - Information of what metadata you want to retrieve + * @param {boolean} expandWildcard - true if you want to expand the wildcard, false otherwise * @param {SimpleLoggerIntf} logger - Logger to use * @returns {Promise} Information by metadata type * @throws {SalesforceError} If an error occurs during the query * @async * @public */ - readMetadata(metadatas: SalesforceMetadataRequest[], logger: SimpleLoggerIntf): Promise>>>; + readMetadata(metadatas: SalesforceMetadataRequest[], expandWildcard: boolean, logger: SimpleLoggerIntf): Promise>>>; /** * @description Method to retrieve a list of metadata types by at Scale (using composite tooling api) diff --git a/packages/orgcheck-api/src/api/dataset/orgcheck-api-dataset-profilepasswordpolicies.ts b/packages/orgcheck-api/src/api/dataset/orgcheck-api-dataset-profilepasswordpolicies.ts index 3e7cd839..2b60b62d 100644 --- a/packages/orgcheck-api/src/api/dataset/orgcheck-api-dataset-profilepasswordpolicies.ts +++ b/packages/orgcheck-api/src/api/dataset/orgcheck-api-dataset-profilepasswordpolicies.ts @@ -19,10 +19,7 @@ export class DatasetProfilePasswordPolicies implements Dataset { // First Metadata API query logger?.log(`Querying Metadata API about ProfilePasswordPolicy...`); - const results = await sfdcManager.readMetadata([{ - type: 'ProfilePasswordPolicy', - members: [ '*' ] - }], logger); + const results = await sfdcManager.readMetadata([{ type: 'ProfilePasswordPolicy', members: [ '*' ] }], true, logger); // List of policies const profilePasswordPolicies = results?.get('ProfilePasswordPolicy') || []; diff --git a/packages/orgcheck-api/src/api/dataset/orgcheck-api-dataset-sharingrules.ts b/packages/orgcheck-api/src/api/dataset/orgcheck-api-dataset-sharingrules.ts index bf5b64f7..aad8bed0 100644 --- a/packages/orgcheck-api/src/api/dataset/orgcheck-api-dataset-sharingrules.ts +++ b/packages/orgcheck-api/src/api/dataset/orgcheck-api-dataset-sharingrules.ts @@ -20,22 +20,9 @@ export class DatasetSharingRules implements Dataset { */ async run(sfdcManager: SalesforceManagerIntf, dataFactory: DataFactoryIntf, logger: SimpleLoggerIntf): Promise> { - // List all custom object which can have a sharing model - logger?.log(`Listing all custom objects that can have a sharing model...`); - const customObjectsResults = await sfdcManager.soqlQuery([{ - tooling: true, - string: 'SELECT NamespacePrefix, DeveloperName ' + - 'FROM CustomObject ' + - `WHERE SharingModel != ''` - }], logger); - const customObjectNames: Record[] = customObjectsResults[0]; - // Read all SharingRules metadata logger?.log(`Reading sharing rules metadata via Metadata API...`); - const results = await sfdcManager.readMetadata([{ - type: SalesforceMetadataTypes.SHARING_RULE, - members: ['*', ...(customObjectNames?.map((r) => `${r.NamespacePrefix ? `${r.NamespacePrefix}__`: ''}${r.DeveloperName}__c`) ?? [])] - }], logger); + const results = await sfdcManager.readMetadata([{ type: SalesforceMetadataTypes.SHARING_RULE, members: ['*'] }], true, logger); // Init the factory and records const sharingRuleDataFactory = dataFactory.getInstance(DataAliases.SfdcSharingRule); diff --git a/packages/orgcheck-api/tests/unit/orgcheck-api-salesforcemanager.unit.test.ts b/packages/orgcheck-api/tests/unit/orgcheck-api-salesforcemanager.unit.test.ts index 0523bb18..d1397466 100644 --- a/packages/orgcheck-api/tests/unit/orgcheck-api-salesforcemanager.unit.test.ts +++ b/packages/orgcheck-api/tests/unit/orgcheck-api-salesforcemanager.unit.test.ts @@ -121,7 +121,7 @@ describe('tests.api.unit.SalesforceManager', () => { describe('metadataApi use cases', () => { it('checks if the salesforce manager implementation runs readMetadata correctly with explicit members', async () => { - const results = await manager.readMetadata([{ type: 'ProfilePasswordPolicy #Members=4#', members: [ 'member0', 'member999' ] }], simpleLogger); + const results = await manager.readMetadata([{ type: 'ProfilePasswordPolicy #Members=4#', members: [ 'member0', 'member999' ] }], false,simpleLogger); expect(results).toBeDefined(); expect(results instanceof Map).toBeTruthy(); expect(results.size).toBe(1); @@ -130,14 +130,24 @@ describe('tests.api.unit.SalesforceManager', () => { expect(policies?.length).toBe(1); // member0 exists, but member999 does not }); - it('checks if the salesforce manager implementation runs readMetadata correctly with star (*)', async () => { - const results = await manager.readMetadata([{ type: 'ProfilePasswordPolicy #Members=4#', members: [ '*' ] }], simpleLogger); + it('checks if the salesforce manager implementation runs readMetadata correctly with star (*) with expandWildcard=true', async () => { + const results = await manager.readMetadata([{ type: 'ProfilePasswordPolicy #Members=4#', members: [ '*' ] }], true, simpleLogger); expect(results).toBeDefined(); expect(results instanceof Map).toBeTruthy(); expect(results.size).toBe(1); const policies: any[] | undefined = results.get('ProfilePasswordPolicy #Members=4#'); expect(policies instanceof Array).toBeTruthy(); - expect(policies?.length).toBe(4); // you put '*' ! + expect(policies?.length).toBe(4); // you put '*' with 4 members expected when expanding wildcards + }); + + it('checks if the salesforce manager implementation runs readMetadata correctly with star (*) with expandWildcard=false', async () => { + const results = await manager.readMetadata([{ type: 'ProfilePasswordPolicy #Members=4#', members: [ '*' ] }], false, simpleLogger); + expect(results).toBeDefined(); + expect(results instanceof Map).toBeTruthy(); + expect(results.size).toBe(1); + const policies: any[] | undefined = results.get('ProfilePasswordPolicy #Members=4#'); + expect(policies instanceof Array).toBeTruthy(); + expect(policies?.length).toBe(0); // you put '*' with 4 members expected when expanding wildcards, but expandWildcard=false, so we should have zero! }); }); diff --git a/packages/orgcheck-api/tests/utils/orgcheck-api-salesforce-mock.utility.ts b/packages/orgcheck-api/tests/utils/orgcheck-api-salesforce-mock.utility.ts index 1b8aded6..4dfc3be0 100644 --- a/packages/orgcheck-api/tests/utils/orgcheck-api-salesforce-mock.utility.ts +++ b/packages/orgcheck-api/tests/utils/orgcheck-api-salesforce-mock.utility.ts @@ -13,7 +13,7 @@ export class SalesforceManagerMock_DoingNothing implements SalesforceManagerIntf async soqlQuery(_queries: SalesforceQueryRequest | any[], _logger: SimpleLoggerIntf): Promise>> { return [[]]; } async soslQuery(_queries: SalesforceQueryRequest | any[], _logger: SimpleLoggerIntf): Promise>> { return [[]]; } async dependenciesQuery(_ids: string[], _logger: SimpleLoggerIntf): Promise<{ records: Array; errors: Array; }> { return { records: [], errors: [] }; } - async readMetadata(_metadatas: SalesforceMetadataRequest[], _logger: SimpleLoggerIntf): Promise>> { return new Map(); } + async readMetadata(_metadatas: SalesforceMetadataRequest[], _expandWildcard: boolean, _logger: SimpleLoggerIntf): Promise>> { return new Map(); } async readMetadataAtScale(_type: string, _ids: any[], _byPasses: string[], _logger: SimpleLoggerIntf): Promise { return []; } async describeGlobal(_logger: SimpleLoggerIntf): Promise { return []; } async describe(_sobjectDevName: string, _logger: SimpleLoggerIntf): Promise { return {}; } From 6f8ea0cd8e89cf233a7aed478b82c428435ac6e0 Mon Sep 17 00:00:00 2001 From: Vincent FINET Date: Fri, 28 Aug 2026 00:24:29 +0200 Subject: [PATCH 2/3] Using DurableId -- you dont need namespace and devname anymore #750 --- .../src/api/dataset/orgcheck-api-dataset-object.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/packages/orgcheck-api/src/api/dataset/orgcheck-api-dataset-object.ts b/packages/orgcheck-api/src/api/dataset/orgcheck-api-dataset-object.ts index 7dc42038..e2c75fa3 100644 --- a/packages/orgcheck-api/src/api/dataset/orgcheck-api-dataset-object.ts +++ b/packages/orgcheck-api/src/api/dataset/orgcheck-api-dataset-object.ts @@ -36,10 +36,6 @@ export class DatasetObject implements Dataset { throw new Error(`DatasetObject: No object were provided in the parameters.`); } - // split name and namespace frpm object api name - const splittedApiName = fullObjectApiName.split('__'); - const packageName = splittedApiName?.length === 3 ? splittedApiName[0] : ''; - // Init the factories const fieldDataFactory = dataFactory.getInstance(DataAliases.SfdcField); const fieldSetDataFactory = dataFactory.getInstance(DataAliases.SfdcFieldSet); @@ -64,20 +60,17 @@ export class DatasetObject implements Dataset { '(SELECT Id, Name, Url, LinkType, OpenType, Description, CreatedDate, LastModifiedDate, NamespacePrefix FROM WebLinks) ' + 'FROM EntityDefinition ' + `WHERE QualifiedApiName = '${fullObjectApiName}' ` + - (packageName ? `AND NamespacePrefix = '${packageName}' ` : '') + 'LIMIT 1' // We should get zero or one record, not more! }, { tooling: true, string: 'SELECT DurableId, QualifiedApiName, Description, IsIndexed ' + 'FROM FieldDefinition '+ - `WHERE EntityDefinition.QualifiedApiName = '${fullObjectApiName}' ` + - (packageName ? `AND EntityDefinition.NamespacePrefix = '${packageName}' ` : ''), + `WHERE EntityDefinition.QualifiedApiName = '${fullObjectApiName}' `, queryMoreField: 'DurableId' // FieldDefinition does not support calling QueryMore, use the custom instead }, { string: 'SELECT TableEnumOrId, Id ' + // TableEnumOrId = EntityDefinition.QualifiedApiName 'FROM WorkflowRule ' + - `WHERE TableEnumOrId = '${fullObjectApiName}' ` + - (packageName ? `AND EntityDefinition.NamespacePrefix = '${packageName}' ` : ''), + `WHERE TableEnumOrId = '${fullObjectApiName}' `, tooling: true, }], logger), sfdcManager.recordCount(fullObjectApiName, logger) From a99d9542d5195624648eb0a3624bd5a934763fcd Mon Sep 17 00:00:00 2001 From: Vincent FINET Date: Fri, 28 Aug 2026 15:07:57 +0200 Subject: [PATCH 3/3] 8.9 update --- build/package.readme | 6 +++--- packages/orgcheck-salesforce-app/sfdx-project.json | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/build/package.readme b/build/package.readme index 59752efd..7afd8dcf 100644 --- a/build/package.readme +++ b/build/package.readme @@ -44,8 +44,8 @@ sf data query --query "SELECT InstanceName, MetadataPackageId, MetadataPackageVe ## script to update subscribers using a version (or lower) to another version targetOrg=vincent@orgcheck.com -oldVersion=04tQy000000VPrl -newVersion=04tQy000000VhYX +oldVersion=04tQy000000VhYX +newVersion=04tQy000000Z76f sf data create record --target-org "${targetOrg}" --sobjecttype=PackagePushRequest "--values=PackageVersionId=${newVersion}" --json > /tmp/out.txt if [[ $(grep -c 'success": true' /tmp/out.txt) -ne 1 ]]; then echo 'PackagePushRequest record not created.' @@ -53,7 +53,7 @@ else id=$(grep 'id": ' /tmp/out.txt | cut -d'"' -f4) ; csvFile=/tmp/packagePushJob-${id}.csv ; echo "PackagePushRequestId,SubscriberOrganizationKey" > "${csvFile}" ; - sf data query --query "SELECT OrgKey FROM PackageSubscriber WHERE MetadataPackageVersionId <= '${oldVersion}' " --resultformat=csv | awk 'NR > 1 { print "'${id}'," $1 }' >> "${csvFile}" ; + sf data query --query "SELECT OrgKey FROM PackageSubscriber WHERE MetadataPackageVersionId <= '${oldVersion}' LIMIT 9000 " --resultformat=csv | awk 'NR > 1 { print "'${id}'," $1 }' >> "${csvFile}" ; sf data upsert bulk --target-org "${targetOrg}" --sobject PackagePushJob --file "${csvFile}" -i Id ; echo "When bulk job if done, please run the following command:" ; echo "sf data update record --target-org ${targetOrg} --sobject PackagePushRequest --record-id ${id} \"--values=Status=Pending\""; diff --git a/packages/orgcheck-salesforce-app/sfdx-project.json b/packages/orgcheck-salesforce-app/sfdx-project.json index e35c7b27..1b13564e 100755 --- a/packages/orgcheck-salesforce-app/sfdx-project.json +++ b/packages/orgcheck-salesforce-app/sfdx-project.json @@ -5,7 +5,7 @@ "default": false, "package": "Org Check", "versionName": "Oxygen", - "versionNumber": "8.8.0.NEXT", + "versionNumber": "8.9.0.NEXT", "versionDescription": "Org Check is an easy-to-install and easy-to-use Salesforce application in order to quickly analyze your org and its technical debt.", "ancestorVersion": "HIGHEST" }, @@ -14,7 +14,7 @@ "default": true, "package": "Org Check (unlocked)", "versionName": "Oxygen", - "versionNumber": "8.8.0.NEXT", + "versionNumber": "8.9.0.NEXT", "versionDescription": "Org Check is an easy-to-install and easy-to-use Salesforce application in order to quickly analyze your org and its technical debt." } ], @@ -32,6 +32,7 @@ "Org Check (unlocked)@8.3.0-1": "04tKB000000DdYGYA0", "Org Check (unlocked)@8.6.0-1": "04tKB000000De1KYAS", "Org Check (unlocked)@8.7.0-1": "04tKB000000DeYWYA0", - "Org Check (unlocked)@8.8.0-1": "04tQy000000Z753IAC" + "Org Check (unlocked)@8.8.0-1": "04tQy000000Z753IAC", + "Org Check (unlocked)@8.9.0-1": "04tQy000000ZEEbIAO" } } \ No newline at end of file