Skip to content

Commit

Permalink
feat: add a wildcard expansion to metadata full names when building t…
Browse files Browse the repository at this point in the history
…he CS (#1063)

* feat: add a wildcard expansion to metadata full names when building the CS

* chore: filter out type:*

* docs: add comments discussion issue
  • Loading branch information
WillieRuemmele committed Aug 2, 2023
1 parent 5b7ca0c commit 6a6770b
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 9 deletions.
38 changes: 29 additions & 9 deletions src/collections/componentSetBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,23 +99,43 @@ export class ComponentSetBuilder {
const registry = new RegistryAccess();
const compSetFilter = new ComponentSet();
componentSet ??= new ComponentSet();
const directoryPaths = metadata.directoryPaths;

// Build a Set of metadata entries
metadata.metadataEntries.forEach((rawEntry) => {
const splitEntry = rawEntry.split(':').map((entry) => entry.trim());
// The registry will throw if it doesn't know what this type is.
registry.getTypeByName(splitEntry[0]);
const entry = {
type: splitEntry[0],
fullName: splitEntry.length === 1 ? '*' : splitEntry[1],
};
// Add to the filtered ComponentSet for resolved source paths,
// and the unfiltered ComponentSet to build the correct manifest.
compSetFilter.add(entry);
componentSet?.add(entry);
// this '.*' is a surprisingly valid way to specify a metadata, especially a DEB :sigh:
// https://github.com/salesforcecli/plugin-deploy-retrieve/blob/main/test/nuts/digitalExperienceBundle/constants.ts#L140
// because we're filtering from what we have locally, this won't allow you to retrieve new metadata (on the server only) using the partial wildcard
// to do that, you'd need check the size of the CS created below, see if it's 0, and then query the org for the metadata that matches the regex
// but building a CS from a metadata argument doesn't require an org, so we can't do that here
if (splitEntry[1]?.includes('*') && splitEntry[1]?.length > 1 && !splitEntry[1].includes('.*')) {
// get all components of the type, and then filter by the regex of the fullName
ComponentSet.fromSource({
fsPaths: directoryPaths,
include: new ComponentSet([{ type: splitEntry[0], fullName: ComponentSet.WILDCARD }]),
})
.getSourceComponents()
.toArray()
.filter((cs) => Boolean(cs.fullName.match(new RegExp(splitEntry[1]))))
.map((match) => {
compSetFilter.add(match);
componentSet?.add(match);
});
} else {
const entry = {
type: splitEntry[0],
fullName: splitEntry.length === 1 ? '*' : splitEntry[1],
};
// Add to the filtered ComponentSet for resolved source paths,
// and the unfiltered ComponentSet to build the correct manifest.
compSetFilter.add(entry);
componentSet?.add(entry);
}
});

const directoryPaths = metadata.directoryPaths;
logger.debug(`Searching for matching metadata in directories: ${directoryPaths.join(', ')}`);
const resolvedComponents = ComponentSet.fromSource({ fsPaths: directoryPaths, include: compSetFilter });
componentSet.forceIgnoredPaths = resolvedComponents.forceIgnoredPaths;
Expand Down
44 changes: 44 additions & 0 deletions test/collections/componentSetBuilder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,21 @@ describe('ComponentSetBuilder', () => {
content: 'MyClass.cls',
xml: 'MyClass.cls-meta.xml',
};

const apexClassWildcardMatch = {
type: 'ApexClass',
fullName: 'MyClassIsAwesome',
content: 'MyClassIsAwesome.cls',
xml: 'MyClassIsAwesome.cls-meta.xml',
};

const apexClassWildcardNoMatch = {
type: 'ApexClass',
fullName: 'MyTableIsAwesome',
content: 'MyTableIsAwesome.cls',
xml: 'MyTableIsAwesome.cls-meta.xml',
};

const customObjectComponent = {
type: 'CustomObject',
fullName: 'MyCustomObject__c',
Expand Down Expand Up @@ -275,6 +290,35 @@ describe('ComponentSetBuilder', () => {
expect(compSet.has({ type: 'CustomObject', fullName: '*' })).to.equal(true);
});

it('should create ComponentSet from partial-match fullName (ApexClass:Prop*)', async () => {
componentSet.add(apexClassComponent);
componentSet.add(apexClassWildcardMatch);
fromSourceStub.returns(componentSet);
const packageDir1 = path.resolve('force-app');

const compSet = await ComponentSetBuilder.build({
sourcepath: undefined,
manifest: undefined,
metadata: {
metadataEntries: ['ApexClass:MyClas*'],
directoryPaths: [packageDir1],
},
});
expect(fromSourceStub.calledTwice).to.equal(true);
const fromSourceArgs = fromSourceStub.firstCall.args[0] as FromSourceOptions;
expect(fromSourceArgs).to.have.deep.property('fsPaths', [packageDir1]);
const filter = new ComponentSet();
filter.add({ type: 'ApexClass', fullName: 'MyClass' });
filter.add({ type: 'ApexClass', fullName: 'MyClassIsAwesome' });
filter.add({ type: 'ApexClass', fullName: 'MyTableIsAwesome' });
assert(fromSourceArgs.include instanceof ComponentSet, 'include should be a ComponentSet');
expect(fromSourceArgs.include.getSourceComponents()).to.deep.equal(filter.getSourceComponents());
expect(compSet.size).to.equal(2);
expect(compSet.has(apexClassComponent)).to.equal(true);
expect(compSet.has(apexClassWildcardMatch)).to.equal(true);
expect(compSet.has(apexClassWildcardNoMatch)).to.equal(false);
});

it('should create ComponentSet from metadata and multiple package directories', async () => {
componentSet.add(apexClassComponent);
const apexClassComponent2 = { type: 'ApexClass', fullName: 'MyClass2' };
Expand Down

0 comments on commit 6a6770b

Please sign in to comment.