Skip to content

Commit e895e0b

Browse files
Merge pull request #1301 from salesforcecli/sh/confirm-agent-delete
W-17909761 - feat: confirm agent delete
2 parents ca09afd + 05adf15 commit e895e0b

File tree

4 files changed

+120
-28
lines changed

4 files changed

+120
-28
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55
"author": "Salesforce",
66
"bugs": "https://github.com/forcedotcom/cli/issues",
77
"dependencies": {
8+
"@inquirer/prompts": "^7.3.3",
89
"@oclif/core": "^4.2.10",
910
"@oclif/multi-stage-output": "^0.8.12",
1011
"@salesforce/apex-node": "^8.1.19",
1112
"@salesforce/core": "^8.8.5",
1213
"@salesforce/kit": "^3.2.3",
1314
"@salesforce/plugin-info": "^3.4.47",
1415
"@salesforce/sf-plugins-core": "^12.2.1",
15-
"@salesforce/source-deploy-retrieve": "12.16.9",
16+
"@salesforce/source-deploy-retrieve": "^12.16.10",
1617
"@salesforce/source-tracking": "^7.3.19",
1718
"@salesforce/ts-types": "^2.0.12",
1819
"ansis": "^3.17.0",

src/commands/project/delete/source.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import fs from 'node:fs';
99
import path from 'node:path';
1010
import os from 'node:os';
1111
import { Interfaces } from '@oclif/core';
12+
import * as inquirerPrompts from '@inquirer/prompts';
1213
import { Lifecycle, Messages, Org, SfError } from '@salesforce/core';
1314
import {
1415
ComponentSet,
@@ -136,6 +137,7 @@ export class Source extends SfCommand<DeleteSourceJson> {
136137
private org!: Org;
137138
private componentSet!: ComponentSet;
138139
private deployResult!: DeployResult;
140+
private inquirer = inquirerPrompts;
139141

140142
public async run(): Promise<DeleteSourceJson> {
141143
this.flags = (await this.parse(Source)).flags;
@@ -189,6 +191,29 @@ export class Source extends SfCommand<DeleteSourceJson> {
189191
fsPaths: await getPackageDirs(),
190192
include: this.componentSet,
191193
});
194+
// Confirm if the user wants to delete GenAiPlugins as part of an agent
195+
if (!this.flags['no-prompt']) {
196+
const genAiPlugins = this.componentSet.toArray().filter((comp) => comp.type.name === 'GenAiPlugin');
197+
if (genAiPlugins?.length) {
198+
const funcsToDelete = await this.inquirer.checkbox<string | null>({
199+
message: 'Select related topics to delete',
200+
choices: genAiPlugins.map((plugin) => ({ name: plugin.fullName, value: plugin.fullName })),
201+
});
202+
if (funcsToDelete?.length !== genAiPlugins?.length) {
203+
// Create a new ComponentSet with selected GenAiPlugins and all non-GenAiPlugins
204+
const compSetNoPlugins = new ComponentSet();
205+
for (const comp of this.componentSet) {
206+
if (
207+
comp.type.name !== 'GenAiPlugin' ||
208+
(comp.type.name === 'GenAiPlugin' && funcsToDelete.includes(comp.fullName))
209+
) {
210+
compSetNoPlugins.add(comp);
211+
}
212+
}
213+
this.componentSet = compSetNoPlugins;
214+
}
215+
}
216+
}
192217
}
193218

194219
if (this.flags['track-source'] && !this.flags['force-overwrite']) {
@@ -441,7 +466,7 @@ Update the .forceignore file and try again.`);
441466
: messages.getMessage('areYouSure'),
442467
];
443468

444-
return this.confirm({ message: message.join('\n') });
469+
return this.confirm({ message: message.join('\n'), ms: 30_000 });
445470
}
446471
return true;
447472
}

test/commands/delete/source.test.ts

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
ComponentSet,
1313
ComponentSetBuilder,
1414
ComponentSetOptions,
15+
RegistryAccess,
1516
SourceComponent,
1617
} from '@salesforce/source-deploy-retrieve';
1718
import { Lifecycle, SfProject } from '@salesforce/core';
@@ -40,6 +41,30 @@ export const exampleSourceComponent: ComponentProperties = {
4041
content: '/dreamhouse-lwc/force-app/main/default/classes/GeocodingService.cls',
4142
};
4243

44+
const registry = new RegistryAccess();
45+
const agentComponents: SourceComponent[] = [
46+
new SourceComponent({
47+
name: 'My_Agent',
48+
type: registry.getTypeByName('Bot'),
49+
xml: '/dreamhouse-lwc/force-app/main/default/bots/My_Agent.bot-meta.xml',
50+
}),
51+
new SourceComponent({
52+
name: 'Test_Planner',
53+
type: registry.getTypeByName('GenAiPlanner'),
54+
xml: '/dreamhouse-lwc/force-app/main/default/genAiPlanners/Test_Planner.genAiPlanner-meta.xml',
55+
}),
56+
new SourceComponent({
57+
name: 'Test_Plugin1',
58+
type: registry.getTypeByName('GenAiPlugin'),
59+
xml: '/dreamhouse-lwc/force-app/main/default/genAiPlugins/Test_Plugin1.genAiPlugin-meta.xml',
60+
}),
61+
new SourceComponent({
62+
name: 'Test_Plugin2',
63+
type: registry.getTypeByName('GenAiPlugin'),
64+
xml: '/dreamhouse-lwc/force-app/main/default/genAiPlugins/Test_Plugin2.genAiPlugin-meta.xml',
65+
}),
66+
];
67+
4368
export const exampleDeleteResponse = {
4469
// required but ignored by the delete UT
4570
getFileResponses: (): void => {},
@@ -118,6 +143,7 @@ describe('project delete source', () => {
118143
let resolveProjectConfigStub: sinon.SinonStub;
119144
let rmStub: sinon.SinonStub;
120145
let compSetFromSourceStub: sinon.SinonStub;
146+
let handlePromptStub: sinon.SinonStub;
121147

122148
class TestDelete extends Source {
123149
public async runIt() {
@@ -128,7 +154,13 @@ describe('project delete source', () => {
128154
}
129155
}
130156

131-
const runDeleteCmd = async (params: string[], options?: { sourceApiVersion?: string }) => {
157+
const runDeleteCmd = async (
158+
params: string[],
159+
options?: {
160+
sourceApiVersion?: string;
161+
inquirerMock?: { checkbox: sinon.SinonStub };
162+
}
163+
) => {
132164
const cmd = new TestDelete(params, oclifConfigStub);
133165
cmd.project = SfProject.getInstance();
134166
$$.SANDBOX.stub(cmd.project, 'getDefaultPackage').returns({ name: '', path: '', fullPath: defaultPackagePath });
@@ -151,7 +183,14 @@ describe('project delete source', () => {
151183
onCancel: () => {},
152184
onError: () => {},
153185
});
154-
stubMethod($$.SANDBOX, cmd, 'handlePrompt').returns(confirm);
186+
handlePromptStub = stubMethod($$.SANDBOX, cmd, 'handlePrompt').returns(confirm);
187+
if (options?.inquirerMock) {
188+
// @ts-expect-error stubbing private member of the command
189+
cmd.inquirer = options.inquirerMock;
190+
} else {
191+
// @ts-expect-error stubbing private member of the command
192+
cmd.inquirer = { checkbox: $$.SANDBOX.stub().resolves([]) };
193+
}
155194
rmStub = stubMethod($$.SANDBOX, fs.promises, 'rm').resolves();
156195
stubMethod($$.SANDBOX, DeployCache, 'update').resolves();
157196

@@ -233,9 +272,45 @@ describe('project delete source', () => {
233272
ensureHookArgs();
234273
});
235274

236-
it('should pass along metadata and org for pseudo-type matching', async () => {
275+
it('should pass along metadata and org for pseudo-type matching with plugins', async () => {
276+
const agentCompSet = new ComponentSet();
277+
const pluginNames = [agentComponents[2].name, agentComponents[3].name];
278+
agentComponents.map((comp) => agentCompSet.add(comp));
279+
compSetFromSourceStub = compSetFromSourceStub.returns(agentCompSet);
280+
const inquirerCheckboxStub = $$.SANDBOX.stub().resolves(pluginNames);
281+
const inquirerMock = { checkbox: inquirerCheckboxStub };
282+
const metadata = ['Agent:My_Agent'];
283+
await runDeleteCmd(['--metadata', metadata[0], '--json'], { inquirerMock });
284+
ensureCreateComponentSetArgs({
285+
metadata: {
286+
metadataEntries: metadata,
287+
directoryPaths: [defaultPackagePath],
288+
},
289+
org: {
290+
username: testOrg.username,
291+
exclude: [],
292+
},
293+
});
294+
ensureHookArgs();
295+
expect(compSetFromSourceStub.calledOnce).to.be.true;
296+
expect(inquirerCheckboxStub.calledOnce).to.be.true;
297+
expect(inquirerCheckboxStub.firstCall.firstArg).has.property('message', 'Select related topics to delete');
298+
expect(inquirerCheckboxStub.firstCall.firstArg).has.deep.property('choices', [
299+
{ name: 'Test_Plugin1', value: 'Test_Plugin1' },
300+
{ name: 'Test_Plugin2', value: 'Test_Plugin2' },
301+
]);
302+
expect(handlePromptStub.calledOnce).to.be.true;
303+
expect(lifecycleEmitStub.firstCall.args[1]).to.deep.equal(agentComponents);
304+
});
305+
306+
it('should pass along metadata and org for pseudo-type matching without plugins', async () => {
307+
const agentCompSet = new ComponentSet();
308+
agentComponents.map((comp) => agentCompSet.add(comp));
309+
compSetFromSourceStub = compSetFromSourceStub.returns(agentCompSet);
310+
const inquirerCheckboxStub = $$.SANDBOX.stub().resolves([]);
311+
const inquirerMock = { checkbox: inquirerCheckboxStub };
237312
const metadata = ['Agent:My_Agent'];
238-
await runDeleteCmd(['--metadata', metadata[0], '--json']);
313+
await runDeleteCmd(['--metadata', metadata[0], '--json'], { inquirerMock });
239314
ensureCreateComponentSetArgs({
240315
metadata: {
241316
metadataEntries: metadata,
@@ -248,6 +323,13 @@ describe('project delete source', () => {
248323
});
249324
ensureHookArgs();
250325
expect(compSetFromSourceStub.calledOnce).to.be.true;
326+
expect(inquirerCheckboxStub.calledOnce).to.be.true;
327+
expect(inquirerCheckboxStub.firstCall.firstArg).has.deep.property('choices', [
328+
{ name: 'Test_Plugin1', value: 'Test_Plugin1' },
329+
{ name: 'Test_Plugin2', value: 'Test_Plugin2' },
330+
]);
331+
expect(handlePromptStub.calledOnce).to.be.true;
332+
expect(lifecycleEmitStub.firstCall.args[1]).to.deep.equal([agentComponents[0], agentComponents[1]]);
251333
});
252334

253335
it('should pass along apiversion', async () => {

yarn.lock

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1294,7 +1294,7 @@
12941294
"@nodelib/fs.scandir" "2.1.5"
12951295
fastq "^1.6.0"
12961296

1297-
"@oclif/core@^4", "@oclif/core@^4.0.27", "@oclif/core@^4.2.10", "@oclif/core@^4.2.4", "@oclif/core@^4.2.8", "@oclif/core@^4.2.9":
1297+
"@oclif/core@^4", "@oclif/core@^4.0.27", "@oclif/core@^4.2.10", "@oclif/core@^4.2.8", "@oclif/core@^4.2.9":
12981298
version "4.2.10"
12991299
resolved "https://registry.yarnpkg.com/@oclif/core/-/core-4.2.10.tgz#31dfb7481c79887c3e672e10c981fcc01fcbaeb3"
13001300
integrity sha512-fAqcXgqkUm4v5FYy7qWP4w1HaOlVSVJveah+yVTo5Nm5kTiXhmD5mQQ7+knGeBaStyrtQy6WardoC2xSic9rlQ==
@@ -1581,23 +1581,7 @@
15811581
string-width "^7.2.0"
15821582
terminal-link "^3.0.0"
15831583

1584-
"@salesforce/sf-plugins-core@^12":
1585-
version "12.2.0"
1586-
resolved "https://registry.yarnpkg.com/@salesforce/sf-plugins-core/-/sf-plugins-core-12.2.0.tgz#c53f5342841cc490752b78f2707e84d8946dd740"
1587-
integrity sha512-aGNk74rMt8I+HTP7hRsX6kxiGTuun9ONrWkX7JvWDdtIoO9TsEbNVZENH8GFxHFalWPFCj31IMUQD/bGbxMFbg==
1588-
dependencies:
1589-
"@inquirer/confirm" "^3.1.22"
1590-
"@inquirer/password" "^2.2.0"
1591-
"@oclif/core" "^4.2.4"
1592-
"@oclif/table" "^0.4.6"
1593-
"@salesforce/core" "^8.5.1"
1594-
"@salesforce/kit" "^3.2.3"
1595-
"@salesforce/ts-types" "^2.0.12"
1596-
ansis "^3.3.2"
1597-
cli-progress "^3.12.0"
1598-
terminal-link "^3.0.0"
1599-
1600-
"@salesforce/sf-plugins-core@^12.2.1":
1584+
"@salesforce/sf-plugins-core@^12", "@salesforce/sf-plugins-core@^12.2.1":
16011585
version "12.2.1"
16021586
resolved "https://registry.yarnpkg.com/@salesforce/sf-plugins-core/-/sf-plugins-core-12.2.1.tgz#1048a5d1245f07f0e864f0f76e8818fd21a84fe6"
16031587
integrity sha512-b3eRSzGO0weBLL1clHaJNgNP1aKkD1Qy2DQEc0ieteEm+fh1FfPA0QpJ9rh/hdmkJRip2x2R2zz9tflJ0wflbg==
@@ -1613,10 +1597,10 @@
16131597
cli-progress "^3.12.0"
16141598
terminal-link "^3.0.0"
16151599

1616-
"@salesforce/[email protected].9", "@salesforce/source-deploy-retrieve@^12.16.6", "@salesforce/source-deploy-retrieve@^12.16.9":
1617-
version "12.16.9"
1618-
resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-12.16.9.tgz#f86f7dd8de10efe533f1b5737b2a4047d21fdc5a"
1619-
integrity sha512-1Ms7ULjaSnzJ+KJu7jFmEaYN4krLXFUvZQfB1UuUoCvcnNrMuP5zj7TqyfnsVN8CiTlIn3Xap9vT2yYC3CE2uw==
1600+
"@salesforce/source-deploy-retrieve@^12.16.10", "@salesforce/source-deploy-retrieve@^12.16.6", "@salesforce/source-deploy-retrieve@^12.16.9":
1601+
version "12.16.10"
1602+
resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-12.16.10.tgz#b38ee71f9e5691212beae6575078ec8459b59a00"
1603+
integrity sha512-O1K5I5ZmMTfCZ4SV+/w5iWMjGsHSU+PtHCx7gJVcgH8emY25OUhxrQPqb7BS2jupKQZy6U6hhVhkcD9yYqClag==
16201604
dependencies:
16211605
"@salesforce/core" "^8.8.5"
16221606
"@salesforce/kit" "^3.2.3"

0 commit comments

Comments
 (0)