Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
"author": "Salesforce",
"bugs": "https://github.com/forcedotcom/cli/issues",
"dependencies": {
"@inquirer/prompts": "^7.3.3",
"@oclif/core": "^4.2.10",
"@oclif/multi-stage-output": "^0.8.12",
"@salesforce/apex-node": "^8.1.19",
"@salesforce/core": "^8.8.5",
"@salesforce/kit": "^3.2.3",
"@salesforce/plugin-info": "^3.4.47",
"@salesforce/sf-plugins-core": "^12.2.1",
"@salesforce/source-deploy-retrieve": "12.16.9",
"@salesforce/source-deploy-retrieve": "^12.16.10",
"@salesforce/source-tracking": "^7.3.19",
"@salesforce/ts-types": "^2.0.12",
"ansis": "^3.17.0",
Expand Down
27 changes: 26 additions & 1 deletion src/commands/project/delete/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
import { Interfaces } from '@oclif/core';
import * as inquirerPrompts from '@inquirer/prompts';
import { Lifecycle, Messages, Org, SfError } from '@salesforce/core';
import {
ComponentSet,
Expand Down Expand Up @@ -136,6 +137,7 @@ export class Source extends SfCommand<DeleteSourceJson> {
private org!: Org;
private componentSet!: ComponentSet;
private deployResult!: DeployResult;
private inquirer = inquirerPrompts;

public async run(): Promise<DeleteSourceJson> {
this.flags = (await this.parse(Source)).flags;
Expand Down Expand Up @@ -189,6 +191,29 @@ export class Source extends SfCommand<DeleteSourceJson> {
fsPaths: await getPackageDirs(),
include: this.componentSet,
});
// Confirm if the user wants to delete GenAiPlugins as part of an agent
if (!this.flags['no-prompt']) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so if --no-prompt it will delete all genAiFuncs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, only GenAiPlugins. GenAiFuncs are not included in the delete.

const genAiPlugins = this.componentSet.toArray().filter((comp) => comp.type.name === 'GenAiPlugin');
if (genAiPlugins?.length) {
const funcsToDelete = await this.inquirer.checkbox<string | null>({
message: 'Select related topics to delete',
choices: genAiPlugins.map((plugin) => ({ name: plugin.fullName, value: plugin.fullName })),
});
if (funcsToDelete?.length !== genAiPlugins?.length) {
// Create a new ComponentSet with selected GenAiPlugins and all non-GenAiPlugins
const compSetNoPlugins = new ComponentSet();
for (const comp of this.componentSet) {
if (
comp.type.name !== 'GenAiPlugin' ||
(comp.type.name === 'GenAiPlugin' && funcsToDelete.includes(comp.fullName))
) {
compSetNoPlugins.add(comp);
}
}
this.componentSet = compSetNoPlugins;
}
}
}
}

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

return this.confirm({ message: message.join('\n') });
return this.confirm({ message: message.join('\n'), ms: 30_000 });
}
return true;
}
Expand Down
90 changes: 86 additions & 4 deletions test/commands/delete/source.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
ComponentSet,
ComponentSetBuilder,
ComponentSetOptions,
RegistryAccess,
SourceComponent,
} from '@salesforce/source-deploy-retrieve';
import { Lifecycle, SfProject } from '@salesforce/core';
Expand Down Expand Up @@ -40,6 +41,30 @@ export const exampleSourceComponent: ComponentProperties = {
content: '/dreamhouse-lwc/force-app/main/default/classes/GeocodingService.cls',
};

const registry = new RegistryAccess();
const agentComponents: SourceComponent[] = [
new SourceComponent({
name: 'My_Agent',
type: registry.getTypeByName('Bot'),
xml: '/dreamhouse-lwc/force-app/main/default/bots/My_Agent.bot-meta.xml',
}),
new SourceComponent({
name: 'Test_Planner',
type: registry.getTypeByName('GenAiPlanner'),
xml: '/dreamhouse-lwc/force-app/main/default/genAiPlanners/Test_Planner.genAiPlanner-meta.xml',
}),
new SourceComponent({
name: 'Test_Plugin1',
type: registry.getTypeByName('GenAiPlugin'),
xml: '/dreamhouse-lwc/force-app/main/default/genAiPlugins/Test_Plugin1.genAiPlugin-meta.xml',
}),
new SourceComponent({
name: 'Test_Plugin2',
type: registry.getTypeByName('GenAiPlugin'),
xml: '/dreamhouse-lwc/force-app/main/default/genAiPlugins/Test_Plugin2.genAiPlugin-meta.xml',
}),
];

export const exampleDeleteResponse = {
// required but ignored by the delete UT
getFileResponses: (): void => {},
Expand Down Expand Up @@ -118,6 +143,7 @@ describe('project delete source', () => {
let resolveProjectConfigStub: sinon.SinonStub;
let rmStub: sinon.SinonStub;
let compSetFromSourceStub: sinon.SinonStub;
let handlePromptStub: sinon.SinonStub;

class TestDelete extends Source {
public async runIt() {
Expand All @@ -128,7 +154,13 @@ describe('project delete source', () => {
}
}

const runDeleteCmd = async (params: string[], options?: { sourceApiVersion?: string }) => {
const runDeleteCmd = async (
params: string[],
options?: {
sourceApiVersion?: string;
inquirerMock?: { checkbox: sinon.SinonStub };
}
) => {
const cmd = new TestDelete(params, oclifConfigStub);
cmd.project = SfProject.getInstance();
$$.SANDBOX.stub(cmd.project, 'getDefaultPackage').returns({ name: '', path: '', fullPath: defaultPackagePath });
Expand All @@ -151,7 +183,14 @@ describe('project delete source', () => {
onCancel: () => {},
onError: () => {},
});
stubMethod($$.SANDBOX, cmd, 'handlePrompt').returns(confirm);
handlePromptStub = stubMethod($$.SANDBOX, cmd, 'handlePrompt').returns(confirm);
if (options?.inquirerMock) {
// @ts-expect-error stubbing private member of the command
cmd.inquirer = options.inquirerMock;
} else {
// @ts-expect-error stubbing private member of the command
cmd.inquirer = { checkbox: $$.SANDBOX.stub().resolves([]) };
}
rmStub = stubMethod($$.SANDBOX, fs.promises, 'rm').resolves();
stubMethod($$.SANDBOX, DeployCache, 'update').resolves();

Expand Down Expand Up @@ -233,9 +272,45 @@ describe('project delete source', () => {
ensureHookArgs();
});

it('should pass along metadata and org for pseudo-type matching', async () => {
it('should pass along metadata and org for pseudo-type matching with plugins', async () => {
const agentCompSet = new ComponentSet();
const pluginNames = [agentComponents[2].name, agentComponents[3].name];
agentComponents.map((comp) => agentCompSet.add(comp));
compSetFromSourceStub = compSetFromSourceStub.returns(agentCompSet);
const inquirerCheckboxStub = $$.SANDBOX.stub().resolves(pluginNames);
const inquirerMock = { checkbox: inquirerCheckboxStub };
const metadata = ['Agent:My_Agent'];
await runDeleteCmd(['--metadata', metadata[0], '--json'], { inquirerMock });
ensureCreateComponentSetArgs({
metadata: {
metadataEntries: metadata,
directoryPaths: [defaultPackagePath],
},
org: {
username: testOrg.username,
exclude: [],
},
});
ensureHookArgs();
expect(compSetFromSourceStub.calledOnce).to.be.true;
expect(inquirerCheckboxStub.calledOnce).to.be.true;
expect(inquirerCheckboxStub.firstCall.firstArg).has.property('message', 'Select related topics to delete');
expect(inquirerCheckboxStub.firstCall.firstArg).has.deep.property('choices', [
{ name: 'Test_Plugin1', value: 'Test_Plugin1' },
{ name: 'Test_Plugin2', value: 'Test_Plugin2' },
]);
expect(handlePromptStub.calledOnce).to.be.true;
expect(lifecycleEmitStub.firstCall.args[1]).to.deep.equal(agentComponents);
});

it('should pass along metadata and org for pseudo-type matching without plugins', async () => {
const agentCompSet = new ComponentSet();
agentComponents.map((comp) => agentCompSet.add(comp));
compSetFromSourceStub = compSetFromSourceStub.returns(agentCompSet);
const inquirerCheckboxStub = $$.SANDBOX.stub().resolves([]);
const inquirerMock = { checkbox: inquirerCheckboxStub };
const metadata = ['Agent:My_Agent'];
await runDeleteCmd(['--metadata', metadata[0], '--json']);
await runDeleteCmd(['--metadata', metadata[0], '--json'], { inquirerMock });
ensureCreateComponentSetArgs({
metadata: {
metadataEntries: metadata,
Expand All @@ -248,6 +323,13 @@ describe('project delete source', () => {
});
ensureHookArgs();
expect(compSetFromSourceStub.calledOnce).to.be.true;
expect(inquirerCheckboxStub.calledOnce).to.be.true;
expect(inquirerCheckboxStub.firstCall.firstArg).has.deep.property('choices', [
{ name: 'Test_Plugin1', value: 'Test_Plugin1' },
{ name: 'Test_Plugin2', value: 'Test_Plugin2' },
]);
expect(handlePromptStub.calledOnce).to.be.true;
expect(lifecycleEmitStub.firstCall.args[1]).to.deep.equal([agentComponents[0], agentComponents[1]]);
});

it('should pass along apiversion', async () => {
Expand Down
28 changes: 6 additions & 22 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1294,7 +1294,7 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"

"@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":
"@oclif/core@^4", "@oclif/core@^4.0.27", "@oclif/core@^4.2.10", "@oclif/core@^4.2.8", "@oclif/core@^4.2.9":
version "4.2.10"
resolved "https://registry.yarnpkg.com/@oclif/core/-/core-4.2.10.tgz#31dfb7481c79887c3e672e10c981fcc01fcbaeb3"
integrity sha512-fAqcXgqkUm4v5FYy7qWP4w1HaOlVSVJveah+yVTo5Nm5kTiXhmD5mQQ7+knGeBaStyrtQy6WardoC2xSic9rlQ==
Expand Down Expand Up @@ -1581,23 +1581,7 @@
string-width "^7.2.0"
terminal-link "^3.0.0"

"@salesforce/sf-plugins-core@^12":
version "12.2.0"
resolved "https://registry.yarnpkg.com/@salesforce/sf-plugins-core/-/sf-plugins-core-12.2.0.tgz#c53f5342841cc490752b78f2707e84d8946dd740"
integrity sha512-aGNk74rMt8I+HTP7hRsX6kxiGTuun9ONrWkX7JvWDdtIoO9TsEbNVZENH8GFxHFalWPFCj31IMUQD/bGbxMFbg==
dependencies:
"@inquirer/confirm" "^3.1.22"
"@inquirer/password" "^2.2.0"
"@oclif/core" "^4.2.4"
"@oclif/table" "^0.4.6"
"@salesforce/core" "^8.5.1"
"@salesforce/kit" "^3.2.3"
"@salesforce/ts-types" "^2.0.12"
ansis "^3.3.2"
cli-progress "^3.12.0"
terminal-link "^3.0.0"

"@salesforce/sf-plugins-core@^12.2.1":
"@salesforce/sf-plugins-core@^12", "@salesforce/sf-plugins-core@^12.2.1":
version "12.2.1"
resolved "https://registry.yarnpkg.com/@salesforce/sf-plugins-core/-/sf-plugins-core-12.2.1.tgz#1048a5d1245f07f0e864f0f76e8818fd21a84fe6"
integrity sha512-b3eRSzGO0weBLL1clHaJNgNP1aKkD1Qy2DQEc0ieteEm+fh1FfPA0QpJ9rh/hdmkJRip2x2R2zz9tflJ0wflbg==
Expand All @@ -1613,10 +1597,10 @@
cli-progress "^3.12.0"
terminal-link "^3.0.0"

"@salesforce/[email protected].9", "@salesforce/source-deploy-retrieve@^12.16.6", "@salesforce/source-deploy-retrieve@^12.16.9":
version "12.16.9"
resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-12.16.9.tgz#f86f7dd8de10efe533f1b5737b2a4047d21fdc5a"
integrity sha512-1Ms7ULjaSnzJ+KJu7jFmEaYN4krLXFUvZQfB1UuUoCvcnNrMuP5zj7TqyfnsVN8CiTlIn3Xap9vT2yYC3CE2uw==
"@salesforce/source-deploy-retrieve@^12.16.10", "@salesforce/source-deploy-retrieve@^12.16.6", "@salesforce/source-deploy-retrieve@^12.16.9":
version "12.16.10"
resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-12.16.10.tgz#b38ee71f9e5691212beae6575078ec8459b59a00"
integrity sha512-O1K5I5ZmMTfCZ4SV+/w5iWMjGsHSU+PtHCx7gJVcgH8emY25OUhxrQPqb7BS2jupKQZy6U6hhVhkcD9yYqClag==
dependencies:
"@salesforce/core" "^8.8.5"
"@salesforce/kit" "^3.2.3"
Expand Down
Loading