Skip to content

Commit e80d372

Browse files
Merge pull request #646 from salesforcecli/wr/fixDefaultAlias
fix: can delete default being an alias
2 parents 2a19822 + cd55cce commit e80d372

File tree

6 files changed

+72
-55
lines changed

6 files changed

+72
-55
lines changed

src/commands/force/org/delete.ts

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
import { Flags, loglevel, orgApiVersionFlagWithDeprecations, SfCommand } from '@salesforce/sf-plugins-core';
88
import { AuthInfo, AuthRemover, Messages, Org, StateAggregator } from '@salesforce/core';
9+
import { orgThatMightBeDeleted } from '../../../shared/flags';
910

1011
Messages.importMessagesDirectory(__dirname);
1112
const messages = Messages.loadMessages('@salesforce/plugin-org', 'delete');
@@ -24,13 +25,8 @@ export class Delete extends SfCommand<DeleteResult> {
2425
message: messages.getMessage('deprecation'),
2526
};
2627
public static readonly flags = {
27-
'target-org': Flags.string({
28-
// not required because the user could be assuming the default config
29-
aliases: ['targetusername', 'u'],
30-
deprecateAliases: true,
31-
// we're recreating the flag without all the validation
32-
// eslint-disable-next-line sf-plugin/dash-o
33-
char: 'o',
28+
'target-org': orgThatMightBeDeleted({
29+
required: true,
3430
summary: messages.getMessage('flags.target-org.summary'),
3531
}),
3632
targetdevhubusername: Flags.string({
@@ -54,15 +50,7 @@ export class Delete extends SfCommand<DeleteResult> {
5450

5551
public async run(): Promise<DeleteResult> {
5652
const { flags } = await this.parse(Delete);
57-
const resolvedUsername =
58-
// from -o alias -> -o username -> [default username]
59-
(await StateAggregator.getInstance()).aliases.getUsername(flags['target-org'] ?? '') ??
60-
flags['target-org'] ??
61-
(this.configAggregator.getPropertyValue('target-org') as string);
62-
63-
if (!resolvedUsername) {
64-
throw messages.createError('missingUsername');
65-
}
53+
const resolvedUsername = flags['target-org'];
6654

6755
const orgId = (await AuthInfo.create({ username: resolvedUsername })).getFields().orgId as string;
6856
const isSandbox = await (await StateAggregator.getInstance()).sandboxes.hasFile(orgId);

src/commands/org/delete/sandbox.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
* Licensed under the BSD 3-Clause license.
55
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
7-
import { AuthInfo, AuthRemover, Messages, Org, SfError, StateAggregator } from '@salesforce/core';
7+
import { AuthInfo, AuthRemover, Messages, Org, StateAggregator } from '@salesforce/core';
88
import { Flags, SfCommand } from '@salesforce/sf-plugins-core';
9+
import { orgThatMightBeDeleted } from '../../../shared/flags';
910

1011
Messages.importMessagesDirectory(__dirname);
1112
const messages = Messages.loadMessages('@salesforce/plugin-org', 'delete_sandbox');
@@ -22,10 +23,7 @@ export default class EnvDeleteSandbox extends SfCommand<SandboxDeleteResponse> {
2223
public static readonly aliases = ['env:delete:sandbox'];
2324
public static readonly deprecateAliases = true;
2425
public static readonly flags = {
25-
'target-org': Flags.string({
26-
// we're recreating the flag without all the validation
27-
// eslint-disable-next-line sf-plugin/dash-o
28-
char: 'o',
26+
'target-org': orgThatMightBeDeleted({
2927
summary: messages.getMessage('flags.target-org.summary'),
3028
required: true,
3129
}),
@@ -37,13 +35,7 @@ export default class EnvDeleteSandbox extends SfCommand<SandboxDeleteResponse> {
3735

3836
public async run(): Promise<SandboxDeleteResponse> {
3937
const flags = (await this.parse(EnvDeleteSandbox)).flags;
40-
const username = // from -o alias -> -o username -> [default username]
41-
(await StateAggregator.getInstance()).aliases.getUsername(flags['target-org'] ?? '') ??
42-
flags['target-org'] ??
43-
(this.configAggregator.getPropertyValue('target-org') as string);
44-
if (!username) {
45-
throw new SfError('The org does not have a username.');
46-
}
38+
const username = flags['target-org'];
4739

4840
const orgId = (await AuthInfo.create({ username })).getFields().orgId as string;
4941
const isSandbox = await (await StateAggregator.getInstance()).sandboxes.hasFile(orgId);

src/commands/org/delete/scratch.ts

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
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 { AuthInfo, AuthRemover, Messages, Org, StateAggregator } from '@salesforce/core';
9-
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
8+
import { AuthInfo, AuthRemover, Messages, Org } from '@salesforce/core';
9+
import { Flags, SfCommand } from '@salesforce/sf-plugins-core';
10+
import { orgThatMightBeDeleted } from '../../../shared/flags';
1011

1112
Messages.importMessagesDirectory(__dirname);
1213
const messages = Messages.loadMessages('@salesforce/plugin-org', 'delete_scratch');
@@ -23,14 +24,9 @@ export default class EnvDeleteScratch extends SfCommand<ScratchDeleteResponse> {
2324
public static readonly aliases = ['env:delete:scratch'];
2425
public static readonly deprecateAliases = true;
2526
public static readonly flags = {
26-
'target-org': Flags.string({
27-
// not required because the user could be assuming the default config
28-
aliases: ['targetusername', 'u'],
29-
deprecateAliases: true,
30-
// we're recreating the flag without all the validation
31-
// eslint-disable-next-line sf-plugin/dash-o
32-
char: 'o',
27+
'target-org': orgThatMightBeDeleted({
3328
summary: messages.getMessage('flags.target-org.summary'),
29+
required: true,
3430
}),
3531
'no-prompt': Flags.boolean({
3632
char: 'p',
@@ -40,11 +36,7 @@ export default class EnvDeleteScratch extends SfCommand<ScratchDeleteResponse> {
4036

4137
public async run(): Promise<ScratchDeleteResponse> {
4238
const flags = (await this.parse(EnvDeleteScratch)).flags;
43-
const resolvedUsername =
44-
// from -o alias -> -o username -> [default username]
45-
(await StateAggregator.getInstance()).aliases.getUsername(flags['target-org'] ?? '') ??
46-
flags['target-org'] ??
47-
(this.configAggregator.getPropertyValue('target-org') as string);
39+
const resolvedUsername = flags['target-org'];
4840
const orgId = (await AuthInfo.create({ username: resolvedUsername })).getFields().orgId as string;
4941

5042
if (flags['no-prompt'] || (await this.confirm(messages.getMessage('prompt.confirm', [resolvedUsername])))) {

src/shared/flags.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (c) 2023, salesforce.com, inc.
3+
* All rights reserved.
4+
* Licensed under the BSD 3-Clause license.
5+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
8+
import { Flags } from '@oclif/core';
9+
import { ConfigAggregator, StateAggregator, Messages, SfError } from '@salesforce/core';
10+
11+
Messages.importMessagesDirectory(__dirname);
12+
const messages = Messages.loadMessages('@salesforce/plugin-org', 'delete');
13+
14+
const resolveUsername = async (usernameOrAlias?: string): Promise<string> => {
15+
const stateAggregator = await StateAggregator.getInstance();
16+
// we have a value, but don't know if it's a username or an alias
17+
if (usernameOrAlias) return stateAggregator.aliases.resolveUsername(usernameOrAlias);
18+
// we didn't get a value, so let's see if the config has a default target org
19+
const configAggregator = await ConfigAggregator.create();
20+
const defaultUsernameOrAlias = configAggregator.getPropertyValue('target-org') as string | undefined;
21+
if (defaultUsernameOrAlias) return stateAggregator.aliases.resolveUsername(defaultUsernameOrAlias);
22+
throw new SfError(messages.getMessage('missingUsername'), 'MissingUsernameError');
23+
};
24+
25+
/**
26+
* Almost like the use case for the normal optional org flag,
27+
* but delete commands need to handle the situation where connecting to the org fails because it's expired.
28+
*
29+
* Returns the username so you can construct your own org.
30+
*/
31+
export const orgThatMightBeDeleted = Flags.custom({
32+
char: 'o',
33+
required: true,
34+
deprecateAliases: true,
35+
aliases: ['targetusername', 'u'],
36+
parse: async (input: string | undefined) => resolveUsername(input),
37+
default: async () => resolveUsername(),
38+
defaultHelp: async () => resolveUsername(),
39+
});

test/nut/scratchDelete.nut.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { ScratchDeleteResponse } from '../../src/commands/org/delete/scratch';
1414
describe('env:delete:scratch NUTs', () => {
1515
const scratchOrgAlias = 'scratch-org';
1616
const scratchOrgAlias2 = 'scratch-org-2';
17+
const scratchOrgAlias3 = 'scratch-org-3';
1718
let session: TestSession;
1819

1920
before(async () => {
@@ -33,6 +34,7 @@ describe('env:delete:scratch NUTs', () => {
3334
},
3435
{
3536
setDefault: true,
37+
alias: scratchOrgAlias3,
3638
duration: 1,
3739
config: path.join('config', 'project-scratch-def.json'),
3840
},

test/unit/force/org/delete.test.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@
44
* Licensed under the BSD 3-Clause license.
55
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
7-
import { ConfigAggregator, Messages, Org, SfError } from '@salesforce/core';
7+
import { Messages, Org, SfError } from '@salesforce/core';
88
import { MockTestOrgData, TestContext } from '@salesforce/core/lib/testSetup';
99

10-
import { expect, config } from 'chai';
10+
import { config, expect } from 'chai';
1111
import { stubPrompter, stubSfCommandUx } from '@salesforce/sf-plugins-core';
1212
import { SandboxAccessor } from '@salesforce/core/lib/stateAggregator/accessors/sandboxAccessor';
13-
import { Config } from '@oclif/core';
1413
import { Delete } from '../../../../src/commands/force/org/delete';
1514

1615
config.truncateThreshold = 0;
@@ -29,18 +28,14 @@ describe('org:delete', () => {
2928

3029
beforeEach(async () => {
3130
await $$.stubAuths(testOrg, testHub);
32-
await $$.stubConfig({ 'target-org': testOrg.username });
3331
prompterStubs = stubPrompter($$.SANDBOX);
3432
sfCommandUxStubs = stubSfCommandUx($$.SANDBOX);
3533
});
3634

3735
it('will throw an error when no default set', async () => {
38-
const deleteCommand = new Delete([], {} as Config);
39-
deleteCommand.configAggregator = await ConfigAggregator.create();
40-
$$.SANDBOX.stub(deleteCommand.configAggregator, 'getPropertyValue').onSecondCall().returns(undefined);
41-
36+
await $$.stubConfig({});
4237
try {
43-
await deleteCommand.run();
38+
await Delete.run();
4439
expect.fail('should have thrown an error');
4540
} catch (e) {
4641
const err = e as SfError;
@@ -50,10 +45,19 @@ describe('org:delete', () => {
5045
});
5146

5247
it('will prompt before attempting to delete', async () => {
53-
const deleteCommand = new Delete([], {} as Config);
54-
deleteCommand.configAggregator = await ConfigAggregator.create();
55-
$$.SANDBOX.stub(deleteCommand.configAggregator, 'getPropertyValue').onSecondCall().returns(testOrg.username);
56-
const res = await deleteCommand.run();
48+
await $$.stubConfig({ 'target-org': testOrg.username });
49+
const res = await Delete.run([]);
50+
expect(prompterStubs.confirm.calledOnce).to.equal(true);
51+
expect(prompterStubs.confirm.firstCall.args[0]).to.equal(
52+
messages.getMessage('confirmDelete', ['scratch', testOrg.username])
53+
);
54+
expect(res).to.deep.equal({ orgId: testOrg.orgId, username: testOrg.username });
55+
});
56+
57+
it('will resolve a default alias', async () => {
58+
await $$.stubConfig({ 'target-org': 'myAlias' });
59+
$$.stubAliases({ myAlias: testOrg.username });
60+
const res = await Delete.run([]);
5761
expect(prompterStubs.confirm.calledOnce).to.equal(true);
5862
expect(prompterStubs.confirm.firstCall.args[0]).to.equal(
5963
messages.getMessage('confirmDelete', ['scratch', testOrg.username])

0 commit comments

Comments
 (0)