Skip to content

Commit 0c2135b

Browse files
committed
feat: event subscription and convenience method for push
1 parent f38a7c3 commit 0c2135b

File tree

2 files changed

+53
-20
lines changed

2 files changed

+53
-20
lines changed

src/shared/populateFilePaths.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,7 @@ export const populateFilePaths = (elements: ChangeResult[], packageDirPaths: str
6969
);
7070

7171
// make it simpler to find things later
72-
const elementMap = new Map<string, ChangeResult>();
73-
elements.map((element) => {
74-
elementMap.set(getKeyFromObject(element), element);
75-
});
72+
const elementMap = new Map<string, ChangeResult>(elements.map((e) => [getKeyFromObject(e), e]));
7673

7774
// iterates the local components and sets their filenames
7875
for (const matchingComponent of matchingLocalSourceComponentsSet.getSourceComponents().toArray()) {

src/sourceTracking.ts

+52-16
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ import {
1919
DestructiveChangesType,
2020
VirtualTreeContainer,
2121
DeployResult,
22+
ScopedPreDeploy,
23+
ScopedPostRetrieve,
24+
ScopedPreRetrieve,
25+
ScopedPostDeploy,
26+
RetrieveResult,
2227
} from '@salesforce/source-deploy-retrieve';
2328
import { RemoteSourceTrackingService, remoteChangeElementToChangeResult } from './shared/remoteSourceTrackingService';
2429
import { ShadowRepo } from './shared/localShadowRepo';
@@ -42,8 +47,8 @@ import { populateTypesAndNames } from './shared/populateTypesAndNames';
4247
export interface SourceTrackingOptions {
4348
org: Org;
4449
project: SfProject;
45-
ignoreConflicts?: boolean;
4650
subscribeSDREvents?: boolean;
51+
ignoreConflicts?: boolean;
4752
}
4853

4954
/**
@@ -65,17 +70,19 @@ export class SourceTracking extends AsyncCreatable {
6570
private hasSfdxTrackingFiles: boolean;
6671
private ignoreConflicts: boolean;
6772
private subscribeSDREvents: boolean;
73+
private orgId: string;
6874

6975
public constructor(options: SourceTrackingOptions) {
7076
super(options);
7177
this.org = options.org;
78+
this.orgId = this.org.getOrgId();
7279
this.projectPath = options.project.getPath();
7380
this.packagesDirs = options.project.getPackageDirectories();
7481
this.logger = Logger.childFromRoot('SourceTracking');
7582
this.project = options.project;
7683
this.ignoreConflicts = options.ignoreConflicts ?? false;
7784
this.subscribeSDREvents = options.subscribeSDREvents ?? false;
78-
this.hasSfdxTrackingFiles = hasSfdxTrackingFiles(this.org.getOrgId(), this.projectPath);
85+
this.hasSfdxTrackingFiles = hasSfdxTrackingFiles(this.orgId, this.projectPath);
7986
this.maybeSubscribeLifecycleEvents();
8087
}
8188

@@ -296,7 +303,7 @@ export class SourceTracking extends AsyncCreatable {
296303
this.logger.debug('remoteChanges', remoteChanges);
297304
const filteredChanges = remoteChanges
298305
.filter(remoteFilterByState[options.state])
299-
// skip any remote types not in the registry. Will emit node warnings
306+
// skip any remote types not in the registry. Will emit warnings
300307
.filter((rce) => registrySupportsType(rce.type));
301308
if (options.format === 'ChangeResult') {
302309
return filteredChanges.map((change) => remoteChangeElementToChangeResult(change));
@@ -330,6 +337,20 @@ export class SourceTracking extends AsyncCreatable {
330337
throw new Error(`unsupported options: ${JSON.stringify(options)}`);
331338
}
332339

340+
/**
341+
*
342+
* Convenience method to reduce duplicated steps required to do a fka pull
343+
* It's full of side effects: retrieving remote deletes, deleting those files locall, and then updating tracking files
344+
* Most bizarrely, it then returns a ComponentSet of the remote nonDeletes.
345+
*
346+
* @returns the ComponentSet for what you would retrieve now that the deletes are done
347+
*/
348+
349+
public async maybeApplyRemoteDeletesToLocal(): Promise<ComponentSet> {
350+
const changesToDelete = await this.getChanges({ origin: 'remote', state: 'delete', format: 'SourceComponent' });
351+
await this.deleteFilesAndUpdateTracking(changesToDelete);
352+
return this.remoteNonDeletesAsComponentSet();
353+
}
333354
/**
334355
*
335356
* returns immediately if there are no changesToDelete
@@ -441,7 +462,7 @@ export class SourceTracking extends AsyncCreatable {
441462
return;
442463
}
443464
this.localRepo = await ShadowRepo.getInstance({
444-
orgId: this.org.getOrgId(),
465+
orgId: this.orgId,
445466
projectPath: normalize(this.projectPath),
446467
packageDirs: this.packagesDirs,
447468
hasSfdxTrackingFiles: this.hasSfdxTrackingFiles,
@@ -501,7 +522,7 @@ export class SourceTracking extends AsyncCreatable {
501522
* Deletes the remote tracking files
502523
*/
503524
public async clearRemoteTracking(): Promise<string> {
504-
return RemoteSourceTrackingService.delete(this.org.getOrgId(), this.hasSfdxTrackingFiles);
525+
return RemoteSourceTrackingService.delete(this.orgId, this.hasSfdxTrackingFiles);
505526
}
506527

507528
/**
@@ -573,8 +594,7 @@ export class SourceTracking extends AsyncCreatable {
573594
.map((fileResponse) => fileResponse.filePath as string),
574595
}),
575596
this.updateRemoteTracking(
576-
successes.map(({ state, fullName, type, filePath }) => ({ state, fullName, type, filePath })),
577-
true // retrieves don't need to poll for SourceMembers
597+
successes.map(({ state, fullName, type, filePath }) => ({ state, fullName, type, filePath }))
578598
),
579599
]);
580600
}
@@ -584,15 +604,17 @@ export class SourceTracking extends AsyncCreatable {
584604
*
585605
* @param result FileResponse[]
586606
*/
587-
public async updateTrackingFromRetrieve(responses: FileResponse[]): Promise<void> {
588-
const successes = responses.filter((fileResponse) => fileResponse.state !== ComponentStatus.Failed);
607+
public async updateTrackingFromRetrieve(retrieveResult: RetrieveResult): Promise<void> {
608+
const successes = retrieveResult
609+
.getFileResponses()
610+
.filter((fileResponse) => fileResponse.state !== ComponentStatus.Failed);
589611
if (!successes.length) {
590612
return;
591613
}
592614

593615
await Promise.all([
594616
this.updateLocalTracking({
595-
// assertion allowed because it's filtering out undefined on the next line
617+
// assertion allowed because it's filtering out undefined
596618
files: successes.map((fileResponse) => fileResponse.filePath as string).filter(Boolean),
597619
}),
598620
this.updateRemoteTracking(
@@ -615,21 +637,35 @@ export class SourceTracking extends AsyncCreatable {
615637
private maybeSubscribeLifecycleEvents(): void {
616638
if (this.subscribeSDREvents && this.org.tracksSource) {
617639
const lifecycle = Lifecycle.getInstance();
640+
// the only thing STL uses pre events for is to check conflicts. So if you don't care about conflicts, don't listen!
618641
if (!this.ignoreConflicts) {
619642
this.logger.debug('subscribing to predeploy/retrieve events');
620643
// subscribe to SDR `pre` events to handle conflicts before deploy/retrieve
621-
lifecycle.on('predeploy', async (components: SourceComponent[]) => {
622-
throwIfConflicts(findConflictsInComponentSet(components, await this.getConflicts()));
644+
lifecycle.on('scopedPreDeploy', async (e: ScopedPreDeploy) => {
645+
if (e.orgId === this.orgId) {
646+
throwIfConflicts(findConflictsInComponentSet(e.componentSet, await this.getConflicts()));
647+
}
623648
});
624-
lifecycle.on('preretrieve', async (components: SourceComponent[]) => {
625-
throwIfConflicts(findConflictsInComponentSet(components, await this.getConflicts()));
649+
lifecycle.on('scopedPreRetrieve', async (e: ScopedPreRetrieve) => {
650+
if (e.orgId === this.orgId) {
651+
throwIfConflicts(findConflictsInComponentSet(e.componentSet, await this.getConflicts()));
652+
}
626653
});
627654
}
628655
// subscribe to SDR post-deploy event
629656
this.logger.debug('subscribing to postdeploy/retrieve events');
657+
630658
// yes, the post hooks really have different payloads!
631-
lifecycle.on('postdeploy', async (result: DeployResult) => this.updateTrackingFromDeploy(result));
632-
lifecycle.on('postretrieve', async (result: FileResponse[]) => this.updateTrackingFromRetrieve(result));
659+
lifecycle.on('scopedPostDeploy', async (e: ScopedPostDeploy) => {
660+
if (e.orgId === this.orgId) {
661+
await this.updateTrackingFromDeploy(e.deployResult);
662+
}
663+
});
664+
lifecycle.on('scopedPostRetrieve', async (e: ScopedPostRetrieve) => {
665+
if (e.orgId === this.orgId) {
666+
await this.updateTrackingFromRetrieve(e.retrieveResult);
667+
}
668+
});
633669
}
634670
}
635671

0 commit comments

Comments
 (0)