Skip to content

Commit 56435ca

Browse files
committed
feat: support throttling of listMetadata calls and queries; include and exclude metadata
1 parent 6fabd68 commit 56435ca

File tree

3 files changed

+257
-95
lines changed

3 files changed

+257
-95
lines changed

src/collections/componentSetBuilder.ts

Lines changed: 90 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,18 @@ export type ManifestOption = {
2929
};
3030

3131
type MetadataOption = {
32+
/**
33+
* Array of metadata type:name pairs to include in the ComponentSet.
34+
*/
3235
metadataEntries: string[];
36+
/**
37+
* Array of filesystem directory paths to search for local metadata to include in the ComponentSet.
38+
*/
3339
directoryPaths: string[];
40+
/**
41+
* Array of metadata type:name pairs to exclude from the ComponentSet.
42+
*/
43+
excludedEntries?: string[];
3444
/**
3545
* Array of metadata type:name pairs to delete before the deploy. Use of wildcards is not allowed.
3646
*/
@@ -60,6 +70,14 @@ export type ComponentSetOptions = {
6070

6171
type MetadataMap = Map<string, string[]>;
6272

73+
let logger: Logger;
74+
const getLogger = (): Logger => {
75+
if (!logger) {
76+
logger = Logger.childFromRoot('ComponentSetBuilder');
77+
}
78+
return logger;
79+
};
80+
6381
export class ComponentSetBuilder {
6482
/**
6583
* Builds a ComponentSet that can be used for source conversion,
@@ -71,14 +89,13 @@ export class ComponentSetBuilder {
7189
*/
7290

7391
public static async build(options: ComponentSetOptions): Promise<ComponentSet> {
74-
const logger = Logger.childFromRoot('componentSetBuilder');
7592
let componentSet: ComponentSet | undefined;
7693

7794
const { sourcepath, manifest, metadata, packagenames, org } = options;
7895
const registry = new RegistryAccess(undefined, options.projectDir);
7996

8097
if (sourcepath) {
81-
logger.debug(`Building ComponentSet from sourcepath: ${sourcepath.join(', ')}`);
98+
getLogger().debug(`Building ComponentSet from sourcepath: ${sourcepath.join(', ')}`);
8299
const fsPaths = sourcepath.map(validateAndResolvePath);
83100
componentSet = ComponentSet.fromSource({
84101
fsPaths,
@@ -88,16 +105,16 @@ export class ComponentSetBuilder {
88105

89106
// Return empty ComponentSet and use packageNames in the connection via `.retrieve` options
90107
if (packagenames) {
91-
logger.debug(`Building ComponentSet for packagenames: ${packagenames.toString()}`);
108+
getLogger().debug(`Building ComponentSet for packagenames: ${packagenames.toString()}`);
92109
componentSet ??= new ComponentSet(undefined, registry);
93110
}
94111

95112
// Resolve manifest with source in package directories.
96113
if (manifest) {
97-
logger.debug(`Building ComponentSet from manifest: ${manifest.manifestPath}`);
114+
getLogger().debug(`Building ComponentSet from manifest: ${manifest.manifestPath}`);
98115
assertFileExists(manifest.manifestPath);
99116

100-
logger.debug(`Searching in packageDir: ${manifest.directoryPaths.join(', ')} for matching metadata`);
117+
getLogger().debug(`Searching in packageDir: ${manifest.directoryPaths.join(', ')} for matching metadata`);
101118
componentSet = await ComponentSet.fromManifest({
102119
manifestPath: manifest.manifestPath,
103120
resolveSourcePaths: manifest.directoryPaths,
@@ -108,9 +125,10 @@ export class ComponentSetBuilder {
108125
});
109126
}
110127

111-
// Resolve metadata entries with source in package directories.
112-
if (metadata) {
113-
logger.debug(`Building ComponentSet from metadata: ${metadata.metadataEntries.toString()}`);
128+
// Resolve metadata entries with source in package directories, unless we are building a ComponentSet
129+
// from metadata in an org.
130+
if (metadata && !org) {
131+
getLogger().debug(`Building ComponentSet from metadata: ${metadata.metadataEntries.toString()}`);
114132
const directoryPaths = metadata.directoryPaths;
115133
componentSet ??= new ComponentSet(undefined, registry);
116134
const componentSetFilter = new ComponentSet(undefined, registry);
@@ -122,7 +140,7 @@ export class ComponentSetBuilder {
122140
.map(addToComponentSet(componentSet))
123141
.map(addToComponentSet(componentSetFilter));
124142

125-
logger.debug(`Searching for matching metadata in directories: ${directoryPaths.join(', ')}`);
143+
getLogger().debug(`Searching for matching metadata in directories: ${directoryPaths.join(', ')}`);
126144

127145
// add destructive changes if defined. Because these are deletes, all entries
128146
// are resolved to SourceComponents
@@ -170,25 +188,8 @@ export class ComponentSetBuilder {
170188
// Resolve metadata entries with an org connection
171189
if (org) {
172190
componentSet ??= new ComponentSet(undefined, registry);
173-
174-
logger.debug(
175-
`Building ComponentSet from targetUsername: ${org.username} ${
176-
metadata ? `filtered by metadata: ${metadata.metadataEntries.toString()}` : ''
177-
}`
178-
);
179-
180-
const mdMap = metadata
181-
? buildMapFromComponents(metadata.metadataEntries.map(entryToTypeAndName(registry)))
182-
: (new Map() as MetadataMap);
183-
184-
const fromConnection = await ComponentSet.fromConnection({
185-
usernameOrConnection: (await StateAggregator.getInstance()).aliases.getUsername(org.username) ?? org.username,
186-
componentFilter: getOrgComponentFilter(org, mdMap, metadata),
187-
metadataTypes: mdMap.size ? Array.from(mdMap.keys()) : undefined,
188-
registry,
189-
});
190-
191-
fromConnection.toArray().map(addToComponentSet(componentSet));
191+
const orgComponentSet = await this.resolveOrgComponents(registry, org, metadata);
192+
orgComponentSet.toArray().map(addToComponentSet(componentSet));
192193
}
193194

194195
// there should have been a componentSet created by this point.
@@ -197,9 +198,35 @@ export class ComponentSetBuilder {
197198
componentSet.sourceApiVersion ??= options.sourceapiversion;
198199
componentSet.projectDirectory = options.projectDir;
199200

200-
logComponents(logger, componentSet);
201+
logComponents(componentSet);
201202
return componentSet;
202203
}
204+
205+
private static async resolveOrgComponents(
206+
registry: RegistryAccess,
207+
org: OrgOption,
208+
metadata?: MetadataOption
209+
): Promise<ComponentSet> {
210+
let mdMap = new Map() as MetadataMap;
211+
let debugMsg = `Building ComponentSet from metadata in an org using targetUsername: ${org.username}`;
212+
if (metadata) {
213+
if (metadata.metadataEntries?.length) {
214+
debugMsg += ` filtering on metadata: ${metadata.metadataEntries.toString()}`;
215+
}
216+
if (metadata.excludedEntries?.length) {
217+
debugMsg += ` excluding metadata: ${metadata.excludedEntries.toString()}`;
218+
}
219+
mdMap = buildMapFromMetadata(metadata, registry);
220+
}
221+
getLogger().debug(debugMsg);
222+
223+
return ComponentSet.fromConnection({
224+
usernameOrConnection: (await StateAggregator.getInstance()).aliases.getUsername(org.username) ?? org.username,
225+
componentFilter: getOrgComponentFilter(org, mdMap, metadata),
226+
metadataTypes: mdMap.size ? Array.from(mdMap.keys()) : undefined,
227+
registry,
228+
});
229+
}
203230
}
204231

205232
const addToComponentSet =
@@ -234,27 +261,27 @@ const assertNoWildcardInDestructiveEntries = (mdEntry: MetadataTypeAndMetadataNa
234261

235262
/** This is only for debug output of matched files based on the command flags.
236263
* It will log up to 20 file matches. */
237-
const logComponents = (logger: Logger, componentSet: ComponentSet): void => {
238-
logger.debug(`Matching metadata files (${componentSet.size}):`);
264+
const logComponents = (componentSet: ComponentSet): void => {
265+
getLogger().debug(`Matching metadata files (${componentSet.size}):`);
239266

240267
const components = componentSet.getSourceComponents().toArray();
241268

242269
components
243270
.slice(0, 20)
244271
.map((cmp) => cmp.content ?? cmp.xml ?? cmp.fullName)
245-
.map((m) => logger.debug(m));
246-
if (components.length > 20) logger.debug(`(showing 20 of ${componentSet.size} matches)`);
272+
.map((m) => getLogger().debug(m));
273+
if (components.length > 20) getLogger().debug(`(showing 20 of ${componentSet.size} matches)`);
247274

248-
logger.debug(`ComponentSet apiVersion = ${componentSet.apiVersion ?? '<not set>'}`);
249-
logger.debug(`ComponentSet sourceApiVersion = ${componentSet.sourceApiVersion ?? '<not set>'}`);
275+
getLogger().debug(`ComponentSet apiVersion = ${componentSet.apiVersion ?? '<not set>'}`);
276+
getLogger().debug(`ComponentSet sourceApiVersion = ${componentSet.sourceApiVersion ?? '<not set>'}`);
250277
};
251278

252279
const getOrgComponentFilter = (
253280
org: OrgOption,
254281
mdMap: MetadataMap,
255282
metadata?: MetadataOption
256283
): FromConnectionOptions['componentFilter'] =>
257-
metadata
284+
metadata?.metadataEntries?.length
258285
? (component: Partial<FileProperties>): boolean => {
259286
if (component.type && component.fullName) {
260287
const mdMapEntry = mdMap.get(component.type);
@@ -312,11 +339,33 @@ const typeAndNameToMetadataComponents =
312339
.filter((cs) => minimatch(cs.fullName, metadataName))
313340
: [{ type, fullName: metadataName }];
314341

315-
// TODO: use Map.groupBy when it's available
316-
const buildMapFromComponents = (components: MetadataTypeAndMetadataName[]): MetadataMap => {
342+
const buildMapFromMetadata = (mdOption: MetadataOption, registry: RegistryAccess): MetadataMap => {
317343
const mdMap: MetadataMap = new Map<string, string[]>();
318-
components.map((cmp) => {
319-
mdMap.set(cmp.type.name, [...(mdMap.get(cmp.type.name) ?? []), cmp.metadataName]);
320-
});
344+
345+
// Add metadata type entries we were told to include
346+
if (mdOption.metadataEntries?.length) {
347+
mdOption.metadataEntries.map(entryToTypeAndName(registry)).map((cmp) => {
348+
mdMap.set(cmp.type.name, [...(mdMap.get(cmp.type.name) ?? []), cmp.metadataName]);
349+
});
350+
}
351+
352+
// Build an array of excluded types from the options
353+
if (mdOption.excludedEntries?.length) {
354+
const excludedTypes: string[] = [];
355+
mdOption.excludedEntries.map(entryToTypeAndName(registry)).map((cmp) => {
356+
if (cmp.metadataName === '*') {
357+
excludedTypes.push(cmp.type.name);
358+
}
359+
});
360+
if (mdMap.size === 0) {
361+
// we are excluding specific metadata types from all supported types
362+
Object.values(registry.getRegistry().types).map((t) => {
363+
if (!excludedTypes.includes(t.name)) {
364+
mdMap.set(t.name, []);
365+
}
366+
});
367+
}
368+
}
369+
321370
return mdMap;
322371
};

0 commit comments

Comments
 (0)