|
1 | 1 | import type { MetadataType } from "@jsforce/jsforce-node/lib/api/metadata.js"; |
2 | | -import { Flags, SfCommand, requiredOrgFlagWithDeprecations } from "@salesforce/sf-plugins-core"; |
3 | | -import { ComponentSetBuilder, SourceComponent } from "@salesforce/source-deploy-retrieve"; |
| 2 | +import { |
| 3 | + Flags, |
| 4 | + SfCommand, |
| 5 | + requiredOrgFlagWithDeprecations, |
| 6 | +} from "@salesforce/sf-plugins-core"; |
| 7 | +import { |
| 8 | + ComponentSetBuilder, |
| 9 | + SourceComponent, |
| 10 | +} from "@salesforce/source-deploy-retrieve"; |
4 | 11 | import { filePathsFromMetadataComponent } from "@salesforce/source-deploy-retrieve/lib/src/utils/filePathGenerator.js"; |
5 | 12 | import { mkdir, writeFile } from "fs/promises"; |
6 | 13 | import { dirname, join } from "path"; |
7 | 14 | import { convertToXml, parseCommaSeparatedValues } from "../../../utils.js"; |
8 | 15 |
|
9 | 16 | export class SourceReadCommand extends SfCommand<any> { |
10 | 17 | public static readonly summary = "Read Metadata using the CRUD Metadata API"; |
11 | | - public static readonly description = "Read Metadata using the CRUD Metadata API"; |
| 18 | + public static readonly description = |
| 19 | + "Read Metadata using the CRUD Metadata API"; |
12 | 20 |
|
13 | 21 | public static readonly examples = [ |
14 | 22 | `$ <%= config.bin %> <%= command.id %> -m "Profile:Admin"`, |
@@ -39,29 +47,67 @@ export class SourceReadCommand extends SfCommand<any> { |
39 | 47 | const defaultPackageDirectory = this.project.getDefaultPackage().path; |
40 | 48 | const sourcePaths = packageDirectories.map((dir) => dir.path); |
41 | 49 | const componentSet = await ComponentSetBuilder.build({ |
42 | | - sourcepath: flags.sourcepath && parseCommaSeparatedValues(flags.sourcepath), |
| 50 | + sourcepath: |
| 51 | + flags.sourcepath && parseCommaSeparatedValues(flags.sourcepath), |
43 | 52 | ...(flags.metadata && { |
44 | 53 | metadata: { |
45 | 54 | metadataEntries: parseCommaSeparatedValues(flags.metadata), |
46 | 55 | directoryPaths: sourcePaths, |
47 | | - } |
| 56 | + }, |
48 | 57 | }), |
49 | 58 | }); |
50 | | - for (const component of componentSet) { |
51 | | - this.log("reading", `${component.type.name}:${component.fullName}`, "..."); |
52 | | - const mdJson = await conn.metadata.read(component.type.name as MetadataType, component.fullName); |
53 | | - let filePath; |
54 | | - if (component instanceof SourceComponent) { |
55 | | - filePath = component.xml; |
56 | | - } else { |
57 | | - filePath = filePathsFromMetadataComponent(component, join(defaultPackageDirectory, "main", "default")).find( |
58 | | - (p) => p.endsWith(`.${component.type.suffix}-meta.xml`), |
| 59 | + const manifestObject = await componentSet.getObject(); |
| 60 | + const sourceComponents = componentSet.getSourceComponents(); |
| 61 | + for (const typeMember of manifestObject.Package.types) { |
| 62 | + const typeName = typeMember.name; |
| 63 | + // https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_readMetadata.htm |
| 64 | + const chunkSize = |
| 65 | + typeName === "CustomApplication" || typeName === "CustomMetadata" |
| 66 | + ? 200 |
| 67 | + : 10; |
| 68 | + for (const chunkOfMemberNames of chunk(typeMember.members, chunkSize)) { |
| 69 | + const componentNames = chunkOfMemberNames.map( |
| 70 | + (memberName) => `${typeName}:${memberName}` |
| 71 | + ); |
| 72 | + this.log("reading", `${componentNames.join(", ")}`, "..."); |
| 73 | + const metadataResults = await conn.metadata.read( |
| 74 | + typeName as MetadataType, |
| 75 | + chunkOfMemberNames |
59 | 76 | ); |
60 | | - await mkdir(dirname(filePath), { recursive: true }); |
| 77 | + for (const metadataResult of metadataResults) { |
| 78 | + let filePath; |
| 79 | + const component = |
| 80 | + sourceComponents.find( |
| 81 | + (cmp) => |
| 82 | + cmp.type.name === typeName && |
| 83 | + cmp.fullName === metadataResult.fullName |
| 84 | + ) || |
| 85 | + componentSet.find( |
| 86 | + (cmp) => |
| 87 | + cmp.type.name === typeName && |
| 88 | + cmp.fullName === metadataResult.fullName |
| 89 | + ); |
| 90 | + if (component instanceof SourceComponent) { |
| 91 | + filePath = component.xml; |
| 92 | + } else { |
| 93 | + filePath = filePathsFromMetadataComponent( |
| 94 | + component, |
| 95 | + join(defaultPackageDirectory, "main", "default") |
| 96 | + ).find((p) => p.endsWith(`.${component.type.suffix}-meta.xml`)); |
| 97 | + await mkdir(dirname(filePath), { recursive: true }); |
| 98 | + } |
| 99 | + await writeFile(filePath, convertToXml(component, metadataResult)); |
| 100 | + } |
61 | 101 | } |
62 | | - await writeFile(filePath, convertToXml(component, mdJson)); |
63 | 102 | } |
64 | | - |
65 | 103 | return; |
66 | 104 | } |
67 | 105 | } |
| 106 | + |
| 107 | +const chunk = (input, size) => { |
| 108 | + return input.reduce((arr, item, idx) => { |
| 109 | + return idx % size === 0 |
| 110 | + ? [...arr, [item]] |
| 111 | + : [...arr.slice(0, -1), [...arr.slice(-1)[0], item]]; |
| 112 | + }, []); |
| 113 | +}; |
0 commit comments