diff --git a/command-snapshot.json b/command-snapshot.json index 7e29bd18..44ee8527 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -286,6 +286,7 @@ "api-version", "flags-dir", "ignore-conflicts", + "root-type-with-dependencies", "json", "manifest", "metadata", diff --git a/messages/retrieve.start.md b/messages/retrieve.start.md index f03725cb..1b7a3245 100644 --- a/messages/retrieve.start.md +++ b/messages/retrieve.start.md @@ -37,19 +37,23 @@ To retrieve multiple metadata components, either use multiple --metadata - Retrieve specific Apex classes that match a pattern; in this example, retrieve Apex classes whose names contain the string "MyApex": - <%= config.bin %> <%= command.id %> --metadata 'ApexClass:MyApex*' + <%= config.bin %> <%= command.id %> --metadata 'ApexClass:MyApex*' + +- Retrieve the source files in the "force-app" directory; if any of the retrieved metadata is of type "Bot", also retrieve all dependent components of the Bot, such as GenAiPlannerBundles, GenAiPlugins, and GenAiFunctions: + + <%= config.bin %> <%= command.id %> --source-dir force-app -root-type-with-dependencies Bot - Retrieve a custom object called ExcitingObject that's in the SBQQ namespace: - sf <%= command.id %> --metadata CustomObject:SBQQ__ExcitingObject + <%= config.bin %> <%= command.id %> --metadata CustomObject:SBQQ__ExcitingObject - Retrieve all custom objects in the SBQQ namespace by using a wildcard and quotes: - sf <%= command.id %> --metadata 'CustomObject:SBQQ__*' + <%= config.bin %> <%= command.id %> --metadata 'CustomObject:SBQQ__*' - Retrieve all list views for the Case standard object: - sf <%= command.id %> --metadata 'ListView:Case*' + <%= config.bin %> <%= command.id %> --metadata 'ListView:Case*' - Retrieve all custom objects and Apex classes found in all defined package directories (both examples are equivalent): @@ -113,6 +117,10 @@ Package names to retrieve. Use of this flag is for reference only; don't use it The metadata of the supplied package name(s) will be retrieved into a child directory of the project. The name of that child directory matches the name of the package. The retrieved metadata is meant for your reference only, don't add it to a source control system for development and deployment. For package development, retrieve the metadata using a manifest (`--manifest` flag) or by targeting a source controlled package directory within your project (`--source-dir` flag). +# flags.root-type-with-dependencies.summary + +Metadata type whose dependent types should also be retrieved. + # flags.source-dir.summary File paths for source to retrieve from the org. diff --git a/package.json b/package.json index 227aba49..134f03d7 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "@salesforce/kit": "^4.0.0", "@salesforce/plugin-info": "^4.0.0", "@salesforce/sf-plugins-core": "^13.0.0", - "@salesforce/source-deploy-retrieve": "^13.0.0", + "@salesforce/source-deploy-retrieve": "^13.1.0", "@salesforce/source-tracking": "^8.0.0", "@salesforce/ts-types": "^3.0.0", "ansis": "^3.17.0", diff --git a/src/commands/project/retrieve/start.ts b/src/commands/project/retrieve/start.ts index 438abd96..8c3d0c5a 100644 --- a/src/commands/project/retrieve/start.ts +++ b/src/commands/project/retrieve/start.ts @@ -108,6 +108,11 @@ export default class RetrieveMetadata extends SfCommand { description: messages.getMessage('flags.package-name.description'), multiple: true, }), + 'root-type-with-dependencies': Flags.string({ + summary: messages.getMessage('flags.root-type-with-dependencies.summary'), + multiple: true, + options: ['Bot', 'AiAgentDefinitionVersion'], + }), 'output-dir': Flags.directory({ char: 'r', summary: messages.getMessage('flags.output-dir.summary'), @@ -556,7 +561,12 @@ const buildRetrieveOptions = async ( merge: true, packageOptions: flags['package-name'], format, - ...(hasRootTypesWithDependencies(flags, apiVersion) ? { rootTypesWithDependencies: ['Bot'] } : {}), + ...((): { rootTypesWithDependencies?: string[] } => { + const auto = hasRootTypesWithDependencies(flags, apiVersion) ? ['Bot'] : []; + const explicit = flags['root-type-with-dependencies'] ?? []; + const merged = [...new Set([...auto, ...explicit])]; + return merged.length ? { rootTypesWithDependencies: merged } : {}; + })(), ...(format === 'metadata' ? { singlePackage: flags['single-package'], diff --git a/test/commands/retrieve/start.test.ts b/test/commands/retrieve/start.test.ts index 0074151e..3635974a 100644 --- a/test/commands/retrieve/start.test.ts +++ b/test/commands/retrieve/start.test.ts @@ -253,6 +253,37 @@ describe('project retrieve start', () => { ensureRetrieveArgs({ format: 'source', rootTypesWithDependencies: ['Bot'] }); }); + it('should pass along rootTypesWithDependencies from the --root-type-with-dependencies flag', async () => { + const metadata = ['ApexClass:MyClass']; + const result = await RetrieveMetadata.run([ + '--metadata', + metadata[0], + '--root-type-with-dependencies', + 'Bot', + '--root-type-with-dependencies', + 'AiAgentDefinitionVersion', + '--json', + ]); + expect(result).to.deep.equal(expectedResults); + ensureRetrieveArgs({ format: 'source', rootTypesWithDependencies: ['Bot', 'AiAgentDefinitionVersion'] }); + }); + + it('should merge --root-type-with-dependencies with auto-detected Bot for pseudo-types', async () => { + const metadata = ['Agent:My_Agent']; + const apiversion = '64.0'; + const result = await RetrieveMetadata.run([ + '--metadata', + metadata[0], + '--api-version', + apiversion, + '--root-type-with-dependencies', + 'AiAgentDefinitionVersion', + '--json', + ]); + expect(result).to.deep.equal(expectedResults); + ensureRetrieveArgs({ format: 'source', rootTypesWithDependencies: ['Bot', 'AiAgentDefinitionVersion'] }); + }); + it('should pass along metadata and org for pseudo-type wildcard matching', async () => { const metadata = ['ApexClass', 'Agent']; const result = await RetrieveMetadata.run(['--metadata', metadata[0], '--metadata', metadata[1], '--json']); diff --git a/test/nuts/manifest/manifestCreate.nut.ts b/test/nuts/manifest/manifestCreate.nut.ts index 898bf39d..3b0d599c 100644 --- a/test/nuts/manifest/manifestCreate.nut.ts +++ b/test/nuts/manifest/manifestCreate.nut.ts @@ -147,27 +147,13 @@ describe('project generate manifest', () => { ); const manifestContents = fs.readFileSync(join(session.project.dir, manifestName), 'utf-8'); - const expectedManifestContents = - '\n' + - '\n' + - ' \n' + - ' FileUtilities\n' + - ' FileUtilitiesTest\n' + - ' ApexClass\n' + - ' \n' + - ' \n' + - ' Create_property\n' + - ' Flow\n' + - ' \n' + - ' \n' + - ' dreamhouse\n' + - ' sfdcInternalInt__sfdc_nc_constraints_engine_deploy\n' + - ' sfdcInternalInt__sfdc_scrt2\n' + - ' PermissionSet\n' + - ' \n' + - ' 64.0\n' + - '\n'; - expect(manifestContents).to.equal(expectedManifestContents); + expect(manifestContents).to.include('ApexClass'); + expect(manifestContents).to.include('FileUtilities'); + expect(manifestContents).to.include('FileUtilitiesTest'); + expect(manifestContents).to.include('Flow'); + expect(manifestContents).to.include('Create_property'); + expect(manifestContents).to.include('PermissionSet'); + expect(manifestContents).to.include('dreamhouse'); }); it('should produce a manifest from an excluded list of metadata in an org', async () => { diff --git a/yarn.lock b/yarn.lock index 94c8455e..cce8f548 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1027,22 +1027,7 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@jsforce/jsforce-node@^3.10.17": - version "3.10.17" - resolved "https://registry.yarnpkg.com/@jsforce/jsforce-node/-/jsforce-node-3.10.17.tgz#c8f0a9d3b88925d45b297f2a9dac8af2a17c47ee" - integrity sha512-gYWiK0Y68dMUTdlPjkF+Bw3r7pmfTbqzs7/6ZChg2Md14NyzXk/usp+y8hHj6vc1SF3s7e6j5OUzUasYF5RJWQ== - dependencies: - "@sindresorhus/is" "^4" - base64url "^3.0.1" - csv-parse "^5.5.2" - csv-stringify "^6.6.0" - faye "^1.4.0" - form-data "^4.0.4" - multistream "^3.1.0" - undici "^8.5.0" - xml2js "^0.6.2" - -"@jsforce/jsforce-node@^3.10.19": +"@jsforce/jsforce-node@^3.10.17", "@jsforce/jsforce-node@^3.10.19": version "3.10.19" resolved "https://registry.yarnpkg.com/@jsforce/jsforce-node/-/jsforce-node-3.10.19.tgz#ccbc539c12f4f7dff9cfdcc6cfb8f07bd840f731" integrity sha512-k7i2Tntu1fLvkMtRcKDFU64/Fr2M692ECtbwIGX6hcOh5mj+jrMa1tlvcdwffxAMl+lPYCXnY2bjErxWmP84zA== @@ -1487,10 +1472,10 @@ proxy-agent "^6.5.0" yaml "^2.9.0" -"@salesforce/source-deploy-retrieve@^13.0.0": - version "13.0.0" - resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-13.0.0.tgz#dcdaf8461d6ad7af937f5b9daf38844e3933247f" - integrity sha512-MBqBM9Waewh+tCR89KlDHEGaUJ9cAjwS2FIyIn+Zq/NwW7aRucyCF25MWs4gSHeFsBiNUGyGtxlIt+2oWNUIlw== +"@salesforce/source-deploy-retrieve@^13.0.0", "@salesforce/source-deploy-retrieve@^13.1.0": + version "13.1.0" + resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-13.1.0.tgz#f9f0427e46bcca60bd39974779bf816e17357ca1" + integrity sha512-oSOuinhSMkP1t1F0IzwX6NKgcirSl2ROV2v4GcYb1QOp0tFS9NGpofNMuViTFNoxmBdvrKv5mUT19OdrBMyeWw== dependencies: "@salesforce/core" "^9.0.0" "@salesforce/kit" "^4.0.0" @@ -6481,12 +6466,7 @@ path-exists@^4.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== -path-expression-matcher@^1.5.0: - version "1.6.1" - resolved "https://registry.yarnpkg.com/path-expression-matcher/-/path-expression-matcher-1.6.1.tgz#fb190513954875d7e1b05c8caabe7fdd0a4cd75b" - integrity sha512-h7bxdzhHk8Knyc4Tj+jMaa7fEEoUJy7p1qtbVgkYg1Uhpe5Np5VuGXCRZnkZvU+Q42M1vStt0ifa3ueykRJPmQ== - -path-expression-matcher@^1.6.2: +path-expression-matcher@^1.5.0, path-expression-matcher@^1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz#567c73c07197e9dcef24e90edcdc571056599168" integrity sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==