Skip to content

Commit 7350488

Browse files
fix: retrieve package names output (#149)
* fix: retrieve package names output * chore: throw error with non-existant packagename requested * chore: move warnings to bottom of output * chore: enable retrieve packagename NUTs * chore: delete zipFile entry in JSON * chore: delete zipFile entry in JSON * chore: debug NUTs * chore: extend base type * chore: install package in NUTs to try and fix windows * chore: install packages per test * chore: full exec package install * chore: silence exec
1 parent 08fbbdd commit 7350488

File tree

6 files changed

+78
-21
lines changed

6 files changed

+78
-21
lines changed

src/commands/force/source/retrieve.ts

+16-3
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,19 @@
66
*/
77

88
import * as os from 'os';
9+
import { join } from 'path';
910
import { flags, FlagsConfig } from '@salesforce/command';
10-
import { Messages } from '@salesforce/core';
11+
import { Messages, SfdxProject } from '@salesforce/core';
1112
import { Duration } from '@salesforce/kit';
1213
import { getString } from '@salesforce/ts-types';
1314
import { RetrieveResult } from '@salesforce/source-deploy-retrieve';
1415
import { RequestStatus } from '@salesforce/source-deploy-retrieve/lib/src/client/types';
1516
import { SourceCommand } from '../../../sourceCommand';
16-
import { RetrieveResultFormatter, RetrieveCommandResult } from '../../../formatters/retrieveResultFormatter';
17+
import {
18+
RetrieveResultFormatter,
19+
RetrieveCommandResult,
20+
PackageRetrieval,
21+
} from '../../../formatters/retrieveResultFormatter';
1722
import { ComponentSetBuilder } from '../../../componentSetBuilder';
1823

1924
Messages.importMessagesDirectory(__dirname);
@@ -105,10 +110,18 @@ export class Retrieve extends SourceCommand {
105110
}
106111
}
107112

108-
protected formatResult(): RetrieveCommandResult {
113+
protected async formatResult(): Promise<RetrieveCommandResult> {
114+
const packages: PackageRetrieval[] = [];
115+
const projectPath = await SfdxProject.resolveProjectPath();
116+
117+
this.getFlag<string[]>('packagenames', []).forEach((name) => {
118+
packages.push({ name, path: join(projectPath, name) });
119+
});
120+
109121
const formatterOptions = {
110122
waitTime: this.getFlag<Duration>('wait').quantity,
111123
verbose: this.getFlag<boolean>('verbose', false),
124+
packages,
112125
};
113126
const formatter = new RetrieveResultFormatter(this.logger, this.ux, formatterOptions, this.retrieveResult);
114127

src/formatters/retrieveResultFormatter.ts

+26-13
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import { blue, yellow } from 'chalk';
99
import { UX } from '@salesforce/command';
10-
import { Logger, Messages } from '@salesforce/core';
10+
import { Logger, Messages, SfdxError } from '@salesforce/core';
1111
import { get, getString, getNumber } from '@salesforce/ts-types';
1212
import { RetrieveResult, MetadataApiRetrieveStatus } from '@salesforce/source-deploy-retrieve';
1313
import {
@@ -26,6 +26,10 @@ export interface PackageRetrieval {
2626
path: string;
2727
}
2828

29+
export interface RetrieveResultFormatterOptions extends ResultFormatterOptions {
30+
packages?: PackageRetrieval[];
31+
}
32+
2933
export interface RetrieveCommandResult {
3034
inboundFiles: FileResponse[];
3135
packages: PackageRetrieval[];
@@ -34,16 +38,20 @@ export interface RetrieveCommandResult {
3438
}
3539

3640
export class RetrieveResultFormatter extends ResultFormatter {
41+
protected packages: PackageRetrieval[] = [];
3742
protected result: RetrieveResult;
3843
protected fileResponses: FileResponse[];
3944
protected warnings: RetrieveMessage[];
4045

41-
public constructor(logger: Logger, ux: UX, options: ResultFormatterOptions, result: RetrieveResult) {
46+
public constructor(logger: Logger, ux: UX, options: RetrieveResultFormatterOptions, result: RetrieveResult) {
4247
super(logger, ux, options);
4348
this.result = result;
4449
this.fileResponses = result?.getFileResponses ? result.getFileResponses() : [];
4550
const warnMessages = get(result, 'response.messages', []) as RetrieveMessage | RetrieveMessage[];
4651
this.warnings = Array.isArray(warnMessages) ? warnMessages : [warnMessages];
52+
this.packages = options.packages || [];
53+
// zipFile can become massive and unweildy with JSON parsing/terminal output and, isn't useful
54+
delete this.result.response.zipFile;
4755
}
4856

4957
/**
@@ -54,7 +62,7 @@ export class RetrieveResultFormatter extends ResultFormatter {
5462
public getJson(): RetrieveCommandResult {
5563
return {
5664
inboundFiles: this.fileResponses,
57-
packages: [],
65+
packages: this.packages,
5866
warnings: this.warnings,
5967
response: this.result.response,
6068
};
@@ -71,28 +79,28 @@ export class RetrieveResultFormatter extends ResultFormatter {
7179
}
7280

7381
if (this.isSuccess()) {
74-
if (this.warnings.length) {
75-
this.displayWarnings();
76-
}
7782
this.ux.styledHeader(blue(messages.getMessage('retrievedSourceHeader')));
7883
const retrievedFiles = this.fileResponses.filter((fr) => fr.state !== ComponentStatus.Failed);
7984
if (retrievedFiles?.length) {
8085
this.displaySuccesses(retrievedFiles);
8186
} else {
8287
this.ux.log(messages.getMessage('NoResultsFound'));
8388
}
89+
if (this.warnings.length) {
90+
this.displayWarnings();
91+
}
8492
} else {
8593
this.displayErrors();
8694
}
8795

8896
// Display any package retrievals
89-
// if (results.packages && results.packages.length) {
90-
// this.logger.styledHeader(this.logger.color.blue('Retrieved Packages'));
91-
// results.packages.forEach(pkg => {
92-
// this.logger.log(`${pkg.name} package converted and retrieved to: ${pkg.path}`);
93-
// });
94-
// this.logger.log('');
95-
// }
97+
if (this.packages && this.packages.length) {
98+
this.ux.styledHeader(blue('Retrieved Packages'));
99+
this.packages.forEach((pkg) => {
100+
this.ux.log(`${pkg.name} package converted and retrieved to: ${pkg.path}`);
101+
});
102+
this.ux.log('');
103+
}
96104
}
97105

98106
protected hasStatus(status: RequestStatus): boolean {
@@ -125,6 +133,11 @@ export class RetrieveResultFormatter extends ResultFormatter {
125133
}
126134

127135
private displayErrors(): void {
136+
// an invalid packagename retrieval will end up with a message in the `errorMessage` entry
137+
const errorMessage = get(this.result.response, 'errorMessage') as string;
138+
if (errorMessage) {
139+
throw new SfdxError(errorMessage);
140+
}
128141
const unknownMsg: RetrieveMessage[] = [{ fileName: 'unknown', problem: 'unknown' }];
129142
const responseMsgs = get(this.result, 'response.messages', unknownMsg) as RetrieveMessage | RetrieveMessage[];
130143
const errMsgs = Array.isArray(responseMsgs) ? responseMsgs : [responseMsgs];

test/commands/source/retrieve.test.ts

+26
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
77

8+
import { join } from 'path';
89
import * as sinon from 'sinon';
910
import { expect } from 'chai';
1011
import { RetrieveOptions } from '@salesforce/source-deploy-retrieve';
@@ -56,6 +57,7 @@ describe('force:source:retrieve', () => {
5657

5758
const runRetrieveCmd = async (params: string[]) => {
5859
const cmd = new TestRetrieve(params, oclifConfigStub);
60+
stubMethod(sandbox, SfdxProject, 'resolveProjectPath').resolves(join('path', 'to', 'package'));
5961
stubMethod(sandbox, cmd, 'assignProject').callsFake(() => {
6062
const sfdxProjectStub = fromStub(
6163
stubInterface<SfdxProject>(sandbox, {
@@ -213,6 +215,7 @@ describe('force:source:retrieve', () => {
213215
const manifest = 'package.xml';
214216
const packagenames = ['package1'];
215217
const result = await runRetrieveCmd(['--manifest', manifest, '--packagenames', packagenames[0], '--json']);
218+
expectedResults.packages.push({ name: packagenames[0], path: join('path', 'to', 'package', packagenames[0]) });
216219
expect(result).to.deep.equal(expectedResults);
217220
ensureCreateComponentSetArgs({
218221
packagenames,
@@ -223,6 +226,29 @@ describe('force:source:retrieve', () => {
223226
});
224227
ensureRetrieveArgs({ packageOptions: packagenames });
225228
ensureHookArgs();
229+
// reset the packages for other tests
230+
expectedResults.packages = [];
231+
});
232+
233+
it('should pass along multiple packagenames', async () => {
234+
const manifest = 'package.xml';
235+
const packagenames = ['package1', 'package2'];
236+
const result = await runRetrieveCmd(['--manifest', manifest, '--packagenames', packagenames.join(','), '--json']);
237+
packagenames.forEach((pkg) => {
238+
expectedResults.packages.push({ name: pkg, path: join('path', 'to', 'package', pkg) });
239+
});
240+
expect(result).to.deep.equal(expectedResults);
241+
ensureCreateComponentSetArgs({
242+
packagenames,
243+
manifest: {
244+
manifestPath: manifest,
245+
directoryPaths: [defaultPackagePath],
246+
},
247+
});
248+
ensureRetrieveArgs({ packageOptions: packagenames });
249+
ensureHookArgs();
250+
// reset the packages for other tests
251+
expectedResults.packages = [];
226252
});
227253

228254
it('should display output with no --json', async () => {

test/commands/source/retrieveResponses.ts

-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ const baseRetrieveResponse = {
4343
id: '09S21000002jxznEAA',
4444
status: 'Succeeded',
4545
success: true,
46-
zipFile: 'UEsDBBQA...some_long_string',
4746
};
4847

4948
const warningMessage = "Entity of type 'ApexClass' named 'ProductController' cannot be found";

test/formatters/retrieveResultFormatter.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ describe('RetrieveResultFormatter', () => {
143143
expect(styledHeaderStub.calledTwice).to.equal(true);
144144
expect(logStub.called).to.equal(true);
145145
expect(tableStub.calledOnce).to.equal(true);
146-
expect(styledHeaderStub.firstCall.args[0]).to.contain('Retrieved Source Warnings');
146+
expect(styledHeaderStub.secondCall.args[0]).to.contain('Retrieved Source Warnings');
147147
const warnMessages = retrieveResultWarnings.response.messages;
148148
const warnings = Array.isArray(warnMessages) ? warnMessages : [warnMessages];
149149
expect(tableStub.firstCall.args[0]).to.deep.equal(warnings);

test/nuts/seeds/retrieve.packagenames.seed.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@
77

88
import * as path from 'path';
99
import { SourceTestkit } from '@salesforce/source-testkit';
10+
import { exec } from 'shelljs';
1011

1112
// DO NOT TOUCH. generateNuts.ts will insert these values
1213
const EXECUTABLE = '%EXECUTABLE%';
1314

1415
const ELECTRON = { id: '04t6A000002zgKSQAY', name: 'ElectronBranding' };
1516
const SKUID = { id: '04t4A000000cESSQA2', name: 'Skuid' };
1617

17-
context.skip('Retrieve packagenames NUTs [exec: %EXECUTABLE%]', () => {
18+
context('Retrieve packagenames NUTs [exec: %EXECUTABLE%]', () => {
1819
let testkit: SourceTestkit;
1920

2021
before(async () => {
@@ -23,7 +24,6 @@ context.skip('Retrieve packagenames NUTs [exec: %EXECUTABLE%]', () => {
2324
executable: EXECUTABLE,
2425
nut: __filename,
2526
});
26-
testkit.installPackage(ELECTRON.id);
2727
await testkit.deploy({ args: `--sourcepath ${testkit.packageNames.join(',')}` });
2828
});
2929

@@ -39,17 +39,23 @@ context.skip('Retrieve packagenames NUTs [exec: %EXECUTABLE%]', () => {
3939

4040
describe('--packagenames flag', () => {
4141
it('should retrieve an installed package', async () => {
42+
exec(`sfdx force:package:install --noprompt --package ${ELECTRON.id} --wait 5 --json`, { silent: true });
43+
4244
await testkit.retrieve({ args: `--packagenames "${ELECTRON.name}"` });
4345
await testkit.expect.packagesToBeRetrieved([ELECTRON.name]);
4446
});
4547

4648
it('should retrieve two installed packages', async () => {
47-
testkit.installPackage(SKUID.id);
49+
exec(`sfdx force:package:install --noprompt --package ${ELECTRON.id} --wait 5 --json`, { silent: true });
50+
exec(`sfdx force:package:install --noprompt --package ${SKUID.id} --wait 5 --json`, { silent: true });
51+
4852
await testkit.retrieve({ args: `--packagenames "${ELECTRON.name}, ${SKUID.name}"` });
4953
await testkit.expect.packagesToBeRetrieved([ELECTRON.name, SKUID.name]);
5054
});
5155

5256
it('should retrieve an installed package and sourcepath', async () => {
57+
exec(`sfdx force:package:install --noprompt --package ${ELECTRON.id} --wait 5 --json`, { silent: true });
58+
5359
await testkit.retrieve({
5460
args: `--packagenames "${ELECTRON.name}" --sourcepath "${path.join('force-app', 'main', 'default', 'apex')}"`,
5561
});

0 commit comments

Comments
 (0)