Skip to content

Commit 62658c9

Browse files
feat: support --version flag with --api-name in org open agent
Allow --version to work with both --api-name and --authoring-bundle. When used with --api-name, queries BotVersion to get the version ID and adds it to the URL as versionId parameter.
1 parent 885f0f9 commit 62658c9

2 files changed

Lines changed: 49 additions & 25 deletions

File tree

src/commands/org/open/agent.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ export class OrgOpenAgent extends OrgOpenCommandBase<OrgOpenOutput> {
6060
version: Flags.string({
6161
summary: messages.getMessage('flags.version.summary'),
6262
description: messages.getMessage('flags.version.description'),
63-
dependsOn: ['authoring-bundle'],
6463
}),
6564
};
6665

@@ -71,7 +70,7 @@ export class OrgOpenAgent extends OrgOpenCommandBase<OrgOpenOutput> {
7170

7271
let path: string;
7372
if (flags['api-name']) {
74-
path = await buildRetUrl(this.connection, flags['api-name']);
73+
path = await buildRetUrl(this.connection, flags['api-name'], flags.version);
7574
} else {
7675
// authoring-bundle is provided
7776
const queryParams = new URLSearchParams({
@@ -89,8 +88,14 @@ export class OrgOpenAgent extends OrgOpenCommandBase<OrgOpenOutput> {
8988
}
9089

9190
// Build the URL part to the Agent Builder given a Bot API name.
92-
const buildRetUrl = async (conn: Connection, botName: string): Promise<string> => {
91+
const buildRetUrl = async (conn: Connection, botName: string, version?: string): Promise<string> => {
9392
const query = `SELECT id FROM BotDefinition WHERE DeveloperName='${botName}'`;
9493
const botId = (await conn.singleRecordQuery<{ Id: string }>(query)).Id;
95-
return `AiCopilot/copilotStudio.app#/copilot/builder?copilotId=${botId}`;
94+
const queryParams = new URLSearchParams({ copilotId: botId });
95+
if (version) {
96+
const versionQuery = `SELECT Id FROM BotVersion WHERE BotDefinitionId='${botId}' AND VersionNumber=${version}`;
97+
const versionId = (await conn.singleRecordQuery<{ Id: string }>(versionQuery)).Id;
98+
queryParams.set('versionId', versionId);
99+
}
100+
return `AiCopilot/copilotStudio.app#/copilot/builder?${queryParams.toString()}`;
96101
};

test/unit/org/open/agent.test.ts

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ describe('org:open:agent', () => {
3333

3434
const mockBotId = '0Xx1234567890ABCD';
3535
const mockBotName = 'TestAgent';
36+
const mockVersionId = '0X9DD0000000032s0AA';
3637

3738
let sfCommandUxStubs: ReturnType<typeof stubSfCommandUx>;
3839

@@ -107,27 +108,6 @@ describe('org:open:agent', () => {
107108
expect((error as Error).message).to.match(/exactly one|cannot also be provided/i);
108109
}
109110
});
110-
111-
it('requires authoring-bundle when version is provided', async () => {
112-
try {
113-
await OrgOpenAgent.run([
114-
'--json',
115-
'--target-org',
116-
testOrg.username,
117-
'--url-only',
118-
'--api-name',
119-
mockBotName,
120-
'--version',
121-
'1',
122-
]);
123-
assert.fail('Should have thrown an error');
124-
} catch (error) {
125-
expect(error).to.exist;
126-
expect((error as Error).message).to.include('--version');
127-
expect((error as Error).message).to.include('--authoring-bundle');
128-
expect((error as Error).message).to.match(/All of the following must be provided|depends on/i);
129-
}
130-
});
131111
});
132112

133113
describe('url generation with api-name', () => {
@@ -168,6 +148,45 @@ describe('org:open:agent', () => {
168148
expect(spies.get('singleRecordQuery').callCount).to.equal(1);
169149
expect(spies.get('singleRecordQuery').firstCall.args[0]).to.include(specialName);
170150
});
151+
152+
it('builds URL with api-name and version using BotVersion query', async () => {
153+
const version = '2';
154+
// Override the singleRecordQuery stub to return different results for BotDefinition and BotVersion
155+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
156+
const singleRecordQueryStub = spies.get('singleRecordQuery');
157+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
158+
singleRecordQueryStub.onFirstCall().resolves({ Id: mockBotId }); // BotDefinition query
159+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
160+
singleRecordQueryStub.onSecondCall().resolves({ Id: mockVersionId }); // BotVersion query
161+
162+
const response = await OrgOpenAgent.run([
163+
'--json',
164+
'--target-org',
165+
testOrg.username,
166+
'--url-only',
167+
'--api-name',
168+
mockBotName,
169+
'--version',
170+
version,
171+
]);
172+
assert(response);
173+
testJsonStructure(response);
174+
175+
// Verify both queries were made
176+
expect(singleRecordQueryStub.callCount).to.equal(2);
177+
178+
// Verify BotDefinition query
179+
expect(singleRecordQueryStub.firstCall.args[0]).to.include(mockBotName);
180+
expect(singleRecordQueryStub.firstCall.args[0]).to.include('BotDefinition');
181+
182+
// Verify BotVersion query
183+
expect(singleRecordQueryStub.secondCall.args[0]).to.include('BotVersion');
184+
expect(singleRecordQueryStub.secondCall.args[0]).to.include(mockBotId);
185+
expect(singleRecordQueryStub.secondCall.args[0]).to.include(`VersionNumber=${version}`);
186+
187+
const expectedPath = `AiCopilot/copilotStudio.app#/copilot/builder?copilotId=${mockBotId}&versionId=${mockVersionId}`;
188+
expect(response.url).to.equal(getExpectedUrlWithPath(expectedPath));
189+
});
171190
});
172191

173192
describe('url generation with authoring-bundle', () => {

0 commit comments

Comments
 (0)