Skip to content

Commit d101833

Browse files
committed
refactor: deletable org flag
1 parent 38eed36 commit d101833

File tree

6 files changed

+59
-74
lines changed

6 files changed

+59
-74
lines changed

src/commands/force/org/delete.ts

Lines changed: 4 additions & 19 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,18 +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 resolved an alias] -> [default username]
59-
(await StateAggregator.getInstance()).aliases.getUsername(flags['target-org'] ?? '') ??
60-
flags['target-org'] ??
61-
(await StateAggregator.getInstance()).aliases.getUsername(
62-
this.configAggregator.getPropertyValue('target-org') as string
63-
) ??
64-
(this.configAggregator.getPropertyValue('target-org') as string);
65-
66-
if (!resolvedUsername) {
67-
throw messages.createError('missingUsername');
68-
}
53+
const resolvedUsername = flags['target-org'];
6954

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

src/commands/org/delete/sandbox.ts

Lines changed: 4 additions & 16 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,17 +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 =
41-
// from -o alias -> -o username -> [default username resolved an alias] -> [default username]
42-
(await StateAggregator.getInstance()).aliases.getUsername(flags['target-org'] ?? '') ??
43-
flags['target-org'] ??
44-
(await StateAggregator.getInstance()).aliases.getUsername(
45-
this.configAggregator.getPropertyValue('target-org') as string
46-
) ??
47-
(this.configAggregator.getPropertyValue('target-org') as string);
48-
if (!username) {
49-
throw new SfError('The org does not have a username.');
50-
}
38+
const username = flags['target-org'];
5139

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

src/commands/org/delete/scratch.ts

Lines changed: 5 additions & 16 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';
8+
import { AuthInfo, AuthRemover, Messages, Org } from '@salesforce/core';
99
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,14 +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 resolved an alias] -> [default username]
45-
(await StateAggregator.getInstance()).aliases.getUsername(flags['target-org'] ?? '') ??
46-
flags['target-org'] ??
47-
(await StateAggregator.getInstance()).aliases.getUsername(
48-
this.configAggregator.getPropertyValue('target-org') as string
49-
) ??
50-
(this.configAggregator.getPropertyValue('target-org') as string);
39+
const resolvedUsername = flags['target-org'];
5140
const orgId = (await AuthInfo.create({ username: resolvedUsername })).getFields().orgId as string;
5241

5342
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/unit/force/org/delete.test.ts

Lines changed: 6 additions & 19 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

1010
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,8 @@ 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').onThirdCall().returns(testOrg.username);
56-
const res = await deleteCommand.run();
48+
await $$.stubConfig({ 'target-org': testOrg.username });
49+
const res = await Delete.run([]);
5750
expect(prompterStubs.confirm.calledOnce).to.equal(true);
5851
expect(prompterStubs.confirm.firstCall.args[0]).to.equal(
5952
messages.getMessage('confirmDelete', ['scratch', testOrg.username])
@@ -62,19 +55,13 @@ describe('org:delete', () => {
6255
});
6356

6457
it('will resolve a default alias', async () => {
65-
const deleteCommand = new Delete([], {} as Config);
66-
deleteCommand.configAggregator = await ConfigAggregator.create();
6758
await $$.stubConfig({ 'target-org': 'myAlias' });
6859
$$.stubAliases({ myAlias: testOrg.username });
69-
const getPropertyValueStub = $$.SANDBOX.stub(deleteCommand.configAggregator, 'getPropertyValue')
70-
.onSecondCall()
71-
.returns('myAlias');
72-
const res = await deleteCommand.run();
60+
const res = await Delete.run([]);
7361
expect(prompterStubs.confirm.calledOnce).to.equal(true);
7462
expect(prompterStubs.confirm.firstCall.args[0]).to.equal(
7563
messages.getMessage('confirmDelete', ['scratch', testOrg.username])
7664
);
77-
expect(getPropertyValueStub.calledTwice).to.be.true;
7865
expect(res).to.deep.equal({ orgId: testOrg.orgId, username: testOrg.username });
7966
});
8067

test/unit/org/list.test.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
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 { expect, use as ChaiUse } from 'chai';
8-
import * as chaiAsPromised from 'chai-as-promised';
7+
import { expect } from 'chai';
98
import { TestContext } from '@salesforce/core/lib/testSetup';
109
import { AuthInfo, Connection, Org } from '@salesforce/core';
1110
import { stubMethod } from '@salesforce/ts-sinon';
@@ -14,8 +13,6 @@ import OrgListMock = require('../../shared/orgListMock');
1413
import { OrgListCommand } from '../../../src/commands/org/list';
1514
import { OrgListUtil } from '../../../src/shared/orgListUtil';
1615

17-
ChaiUse(chaiAsPromised);
18-
1916
describe('org:list', () => {
2017
// Create new TestContext, which automatically creates and restores stubs
2118
// pertaining to authorization, orgs, config files, etc...

0 commit comments

Comments
 (0)