Skip to content

Commit c115f29

Browse files
amitjoshi438amitjoshipriyanshu92
authored
[PowerPages] [Create-Site] Implement multi-step site creation with enhanced input handling (#1063)
* Enhance CreateSiteCommand to include extension context and add ReadonlyFileSystemProvider for site page previews * Implement EditableFileSystemProvider for site page editing and update CreateSiteHelper to utilize it * Integrate CreateSiteCommand into CommandRegistry and update related components for site creation functionality * Disable copy functionality in EditableFileSystemProvider implementation * Remove ReadonlyFileSystemProvider implementation * Add telemetry constant for previewing site pages and refactor related components * Refactor CommandRegistry and add command registration utility for chat participants * Add constants for site creation parameters and refactor NL2SiteService to use them * Refactor CreateSiteCommand and CreateSiteHelper to use structured options and improve readability; add CreateSiteTypes for better type management * Add error telemetry constant for previewing site pages and handle errors in previewSitePagesContent function * Rename fileContentMap to _fileContentMap for consistency and clarity in EditableFileSystemProvider * Implement multi-step input for site creation and register command for user inputs in CreateSiteHelper * Enhance copilot availability checks and update response structure in ArtemisService * Add site creation inputs and environment info interfaces; refactor CreateSiteCommand * Update src/common/chat-participants/powerpages/commands/create-site/CreateSiteTypes.ts Co-authored-by: Priyanshu Agrawal <priyanshuagrawal@outlook.com> * Update src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts Co-authored-by: Priyanshu Agrawal <priyanshuagrawal@outlook.com> * Update src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts Co-authored-by: Priyanshu Agrawal <priyanshuagrawal@outlook.com> * Add eslint directives to suppress no-explicit-any warnings in site page handling --------- Co-authored-by: amitjoshi <amitjoshi@microsoft.com> Co-authored-by: Priyanshu Agrawal <priyanshuagrawal@outlook.com>
1 parent 46d8cc0 commit c115f29

File tree

11 files changed

+150
-23
lines changed

11 files changed

+150
-23
lines changed

src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { intelligenceAPIAuthentication } from '../../services/AuthenticationProv
1414
import { ActiveOrgOutput } from '../../../client/pac/PacTypes';
1515
import { AUTHENTICATION_FAILED_MSG, COPILOT_NOT_AVAILABLE_MSG, COPILOT_NOT_RELEASED_MSG, DISCLAIMER_MESSAGE, INVALID_RESPONSE, NO_PROMPT_MESSAGE, PAC_AUTH_INPUT, PAC_AUTH_NOT_FOUND, POWERPAGES_CHAT_PARTICIPANT_ID, POWERPAGES_COMMANDS, RESPONSE_AWAITED_MSG, RESPONSE_SCENARIOS, SKIP_CODES, STATER_PROMPTS, WELCOME_MESSAGE, WELCOME_PROMPT } from './PowerPagesChatParticipantConstants';
1616
import { ORG_DETAILS_KEY, handleOrgChangeSuccess, initializeOrgDetails } from '../../utilities/OrgHandlerUtils';
17-
import { createAndReferenceLocation, getComponentInfo, getEndpoint, provideChatParticipantFollowups, handleChatParticipantFeedback, createErrorResult, createSuccessResult, removeChatVariables } from './PowerPagesChatParticipantUtils';
17+
import { createAndReferenceLocation, getComponentInfo, getEndpoint, provideChatParticipantFollowups, handleChatParticipantFeedback, createErrorResult, createSuccessResult, removeChatVariables, registerButtonCommands } from './PowerPagesChatParticipantUtils';
1818
import { checkCopilotAvailability, fetchRelatedFiles, getActiveEditorContent } from '../../utilities/Utils';
1919
import { IIntelligenceAPIEndpointInformation } from '../../services/Interfaces';
2020
import { v4 as uuidv4 } from 'uuid';
@@ -63,6 +63,8 @@ export class PowerPagesChatParticipant {
6363

6464
this._pacWrapper = pacWrapper;
6565

66+
registerButtonCommands();
67+
6668
this._disposables.push(orgChangeEvent(async (orgDetails: ActiveOrgOutput) => {
6769
await this.handleOrgChangeSuccess(orgDetails);
6870
}));
@@ -131,9 +133,13 @@ export class PowerPagesChatParticipant {
131133
const userId = intelligenceApiAuthResponse.userId;
132134
const intelligenceAPIEndpointInfo = await getEndpoint(this.orgID, this.environmentID, this.telemetry, this.cachedEndpoint, this.powerPagesAgentSessionId);
133135

136+
if (!intelligenceAPIEndpointInfo.intelligenceEndpoint) {
137+
return createErrorResult(COPILOT_NOT_AVAILABLE_MSG, RESPONSE_SCENARIOS.COPILOT_NOT_AVAILABLE, this.orgID);
138+
}
139+
134140
const copilotAvailabilityStatus = checkCopilotAvailability(intelligenceAPIEndpointInfo.intelligenceEndpoint, this.orgID, this.telemetry, this.powerPagesAgentSessionId);
135141

136-
if (!copilotAvailabilityStatus || !intelligenceAPIEndpointInfo.intelligenceEndpoint) {
142+
if (!copilotAvailabilityStatus) {
137143
return createErrorResult(COPILOT_NOT_AVAILABLE_MSG, RESPONSE_SCENARIOS.COPILOT_NOT_AVAILABLE, this.orgID);
138144
}
139145

src/common/chat-participants/powerpages/PowerPagesChatParticipantUtils.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import { ITelemetry } from "../../OneDSLoggerTelemetry/telemetry/ITelemetry";
1111
import { ArtemisService } from "../../services/ArtemisService";
1212
import { dataverseAuthentication } from "../../services/AuthenticationProvider";
1313
import { IIntelligenceAPIEndpointInformation } from "../../services/Interfaces";
14+
import { EditableFileSystemProvider } from "../../utilities/EditableFileSystemProvider";
15+
import { CREATE_SITE_BTN_CMD } from "./commands/create-site/CreateSiteConstants";
16+
import { collectSiteCreationInputs, getUpdatedPageContent } from "./commands/create-site/CreateSiteHelper";
1417
import { SUPPORTED_ENTITIES, EXPLAIN_CODE_PROMPT, FORM_PROMPT, LIST_PROMPT, STATER_PROMPTS, WEB_API_PROMPT } from "./PowerPagesChatParticipantConstants";
1518
import { VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO_FEEDBACK_THUMBSUP, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO_FEEDBACK_THUMBSDOWN } from "./PowerPagesChatParticipantTelemetryConstants";
1619
import { IComponentInfo, IPowerPagesChatResult } from "./PowerPagesChatParticipantTypes";
@@ -127,3 +130,22 @@ export function removeChatVariables(userPrompt: string): string {
127130

128131
return userPrompt.replace(regex, '').trim();
129132
}
133+
134+
export function registerButtonCommands() {
135+
vscode.commands.registerCommand(CREATE_SITE_BTN_CMD, async (siteName: string, sitePages, envList, contentProvider: EditableFileSystemProvider, isCreateSiteInputsReceived) => {
136+
if (!isCreateSiteInputsReceived) {
137+
//Update Page Content will be used for the site creation
138+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
139+
sitePages.map((page: any) => {
140+
return {
141+
...page,
142+
code: getUpdatedPageContent(contentProvider, page.metadata.pageTitle)
143+
}
144+
});
145+
const siteCreateInputs = await collectSiteCreationInputs(siteName, envList);
146+
if (siteCreateInputs) {
147+
isCreateSiteInputsReceived = true;
148+
}
149+
}
150+
});
151+
}

src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export class CreateSiteCommand implements Command {
1919
try {
2020
// eslint-disable-next-line @typescript-eslint/no-unused-vars
2121
const result = await createSite({
22-
intelligenceEndpoint: intelligenceAPIEndpointInfo.intelligenceEndpoint,
22+
intelligenceAPIEndpointInfo,
2323
intelligenceApiToken,
2424
userPrompt: request.prompt,
2525
sessionId: powerPagesAgentSessionId,

src/common/chat-participants/powerpages/commands/create-site/CreateSiteConstants.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,10 @@ export const EDITABLE_SCHEME = 'editable';
77
export const ENGLISH = "English";
88
export const MIN_PAGES = 7;
99
export const MAX_PAGES = 7;
10+
export const SITE_CREATE_INPUTS = 'New Power Pages Site';
11+
export const ENVIRONMENT_FOR_SITE_CREATION = 'Select Environment for Site Creation';
12+
export const SITE_NAME = 'Enter Site Name';
13+
export const SITE_NAME_REQUIRED = 'Site Name is required';
14+
export const CREATE_SITE_BTN_CMD = 'create-site-inputs';
15+
export const CREATE_SITE_BTN_TITLE = 'Create Site';
16+
export const CREATE_SITE_BTN_TOOLTIP = 'Create a new Power Pages site';

src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ import { NL2SITE_REQUEST_FAILED, NL2PAGE_GENERATING_WEBPAGES, NL2PAGE_RESPONSE_F
1111
import { oneDSLoggerWrapper } from '../../../../OneDSLoggerTelemetry/oneDSLoggerWrapper';
1212
import { VSCODE_EXTENSION_NL2PAGE_REQUEST, VSCODE_EXTENSION_NL2SITE_REQUEST, VSCODE_EXTENSION_PREVIEW_SITE_PAGES, VSCODE_EXTENSION_PREVIEW_SITE_PAGES_ERROR } from '../../PowerPagesChatParticipantTelemetryConstants';
1313
import { EditableFileSystemProvider } from '../../../../utilities/EditableFileSystemProvider';
14-
import { HTML_FILE_EXTENSION, UTF8_ENCODING } from '../../../../constants';
15-
import { EDITABLE_SCHEME } from './CreateSiteConstants';
16-
import { ICreateSiteOptions, IPreviewSitePagesContentOptions } from './CreateSiteTypes';
14+
import { HTML_FILE_EXTENSION, IEnvInfo, UTF8_ENCODING } from '../../../../constants';
15+
import { CREATE_SITE_BTN_CMD, CREATE_SITE_BTN_TITLE, CREATE_SITE_BTN_TOOLTIP, EDITABLE_SCHEME, ENVIRONMENT_FOR_SITE_CREATION, SITE_CREATE_INPUTS, SITE_NAME, SITE_NAME_REQUIRED } from './CreateSiteConstants';
16+
import { ICreateSiteOptions, IPreviewSitePagesContentOptions, ISiteInputState } from './CreateSiteTypes';
17+
import { MultiStepInput } from '../../../../utilities/MultiStepInput';
18+
import { getEnvList } from '../../../../utilities/Utils';
1719

1820
export const createSite = async (createSiteOptions: ICreateSiteOptions) => {
1921
const {
20-
intelligenceEndpoint,
22+
intelligenceAPIEndpointInfo,
2123
intelligenceApiToken,
2224
userPrompt,
2325
sessionId,
@@ -29,12 +31,22 @@ export const createSite = async (createSiteOptions: ICreateSiteOptions) => {
2931
extensionContext
3032
} = createSiteOptions;
3133

32-
const { siteName, siteDescription, sitePages } = await fetchSiteAndPageData(intelligenceEndpoint, intelligenceApiToken, userPrompt, sessionId, telemetry, stream, orgId, envId, userId);
34+
if (!intelligenceAPIEndpointInfo.intelligenceEndpoint) {
35+
return;
36+
}
37+
const { siteName, siteDescription, sitePages } = await fetchSiteAndPageData(intelligenceAPIEndpointInfo.intelligenceEndpoint, intelligenceApiToken, userPrompt, sessionId, telemetry, stream, orgId, envId, userId);
3338

3439
// eslint-disable-next-line @typescript-eslint/no-unused-vars
35-
const contentProvider = previewSitePagesContent({sitePages, stream, extensionContext, telemetry, sessionId, orgId, envId, userId});
40+
const contentProvider = previewSitePagesContent({ sitePages, stream, extensionContext, telemetry, sessionId, orgId, envId, userId });
41+
42+
const envList = await getEnvList(telemetry, intelligenceAPIEndpointInfo.endpointStamp);
3643

37-
// TODO: Implement the create site button click handler
44+
stream.button({
45+
command: CREATE_SITE_BTN_CMD,
46+
title: CREATE_SITE_BTN_TITLE,
47+
tooltip: CREATE_SITE_BTN_TOOLTIP,
48+
arguments: [siteName, envList, contentProvider, false],
49+
});
3850

3951
return {
4052
siteName,
@@ -117,4 +129,63 @@ function previewSitePagesContent(
117129
throw error;
118130
}
119131
}
132+
// Function to get updated content
133+
export function getUpdatedPageContent(contentProvider: EditableFileSystemProvider, pageName: string): string {
134+
const pageUri = vscode.Uri.parse(`${EDITABLE_SCHEME}:/${pageName}${HTML_FILE_EXTENSION}`);
135+
return contentProvider.getFileContent(pageUri);
136+
}
137+
138+
export async function collectSiteCreationInputs(siteName: string, envList: IEnvInfo[]) {
139+
const envNames: vscode.QuickPickItem[] = envList.map((env: IEnvInfo) => {
140+
return {
141+
label: env.envDisplayName,
142+
description: env.orgUrl,
143+
};
144+
});
145+
146+
const title = vscode.l10n.t(SITE_CREATE_INPUTS);
147+
148+
async function collectInputs() {
149+
const state = {} as Partial<ISiteInputState>;
150+
await MultiStepInput.run((input) => selectEnvName(input, state));
151+
return state as ISiteInputState;
152+
}
120153

154+
async function selectEnvName(
155+
input: MultiStepInput,
156+
state: Partial<ISiteInputState>
157+
) {
158+
const pick = await input.showQuickPick({
159+
title,
160+
step: 1,
161+
totalSteps: 2,
162+
placeholder: vscode.l10n.t(ENVIRONMENT_FOR_SITE_CREATION),
163+
items: envNames,
164+
activeItem:
165+
typeof state.envName !== "string"
166+
? state.envName
167+
: undefined,
168+
});
169+
state.envName = pick.label;
170+
state.OrgUrl = pick.description;
171+
return (input: MultiStepInput) => inputSiteName(input, state);
172+
}
173+
174+
async function inputSiteName(
175+
input: MultiStepInput,
176+
state: Partial<ISiteInputState>
177+
) {
178+
state.siteName = await input.showInputBox({
179+
title,
180+
step: 2,
181+
totalSteps: 2,
182+
value: state.siteName || siteName,
183+
placeholder: vscode.l10n.t(SITE_NAME),
184+
validate: async (value) => (value ? undefined : vscode.l10n.t(SITE_NAME_REQUIRED)),
185+
});
186+
}
187+
188+
const siteInputState = await collectInputs();
189+
// Return the collected site creation inputs including site name, environment name, and domain name
190+
return siteInputState;
191+
}

src/common/chat-participants/powerpages/commands/create-site/CreateSiteTypes.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55

66
import { ITelemetry } from "../../../../OneDSLoggerTelemetry/telemetry/ITelemetry";
77
import * as vscode from 'vscode';
8+
import { IIntelligenceAPIEndpointInformation } from "../../../../services/Interfaces";
89

910
export interface ICreateSiteOptions {
10-
intelligenceEndpoint: string;
11+
intelligenceAPIEndpointInfo: IIntelligenceAPIEndpointInformation;
1112
intelligenceApiToken: string;
1213
userPrompt: string;
1314
sessionId: string;
@@ -31,3 +32,13 @@ export interface IPreviewSitePagesContentOptions {
3132
envId: string;
3233
userId: string;
3334
}
35+
36+
export interface ISiteInputState {
37+
siteName: string;
38+
envName: string;
39+
orgUrl: string;
40+
domainName: string;
41+
title: string;
42+
step: number;
43+
totalSteps: number;
44+
}

src/common/constants.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ export interface IApiRequestParams {
8585
relatedFiles?: IRelatedFiles[];
8686
}
8787

88+
export interface IEnvInfo {
89+
orgUrl: string;
90+
envDisplayName: string;
91+
}
92+
8893
export const VSCODE_EXTENSION_COPILOT_CONTEXT_RELATED_FILES_FETCH_FAILED = "VSCodeExtensionCopilotContextRelatedFilesFetchFailed";
8994
export const ADX_WEBPAGE = 'adx_webpage'
9095
export const HTML_FILE_EXTENSION = '.html';

src/common/copilot/PowerPagesCopilot.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,10 @@ export class PowerPagesCopilot implements vscode.WebviewViewProvider {
426426
sendTelemetryEvent(this.telemetry, { eventName: CopilotOrgChangedEvent, copilotSessionId: sessionID, orgId: orgID });
427427

428428
const intelligenceAPIEndpointInfo = await ArtemisService.getIntelligenceEndpoint(orgID, this.telemetry, sessionID, environmentId);
429+
if (!intelligenceAPIEndpointInfo.intelligenceEndpoint) {
430+
this.sendMessageToWebview({ type: 'Unavailable' });
431+
return;
432+
}
429433
this.aibEndpoint = intelligenceAPIEndpointInfo.intelligenceEndpoint;
430434
this.geoName = intelligenceAPIEndpointInfo.geoName;
431435
this.crossGeoDataMovementEnabledPPACFlag = intelligenceAPIEndpointInfo.crossGeoDataMovementEnabledPPACFlag;

src/common/services/ArtemisService.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ export class ArtemisService {
2121
if (artemisResponse === null) {
2222
return { intelligenceEndpoint: null, geoName: null, crossGeoDataMovementEnabledPPACFlag: false };
2323
}
24-
const { geoName, environment, clusterNumber } = artemisResponse.response as unknown as IArtemisAPIOrgResponse;
24+
25+
const endpointStamp = artemisResponse.stamp;
26+
const { geoName, environment, clusterNumber } = artemisResponse.response as IArtemisAPIOrgResponse;
2527
sendTelemetryEvent(telemetry, { eventName: CopilotArtemisSuccessEvent, copilotSessionId: sessionID, geoName: String(geoName), orgId: orgId });
2628

2729
const crossGeoDataMovementEnabledPPACFlag = await BAPService.getCrossGeoCopilotDataMovementEnabledFlag(artemisResponse.stamp, telemetry, environmentId);
@@ -38,7 +40,7 @@ export class ArtemisService {
3840

3941
const intelligenceEndpoint = `https://aibuildertextapiservice.${geoName}-${'il' + clusterNumber}.gateway.${environment}.island.powerapps.com/v1.0/${orgId}/appintelligence/chat`
4042

41-
return { intelligenceEndpoint: intelligenceEndpoint, geoName: geoName, crossGeoDataMovementEnabledPPACFlag: crossGeoDataMovementEnabledPPACFlag };
43+
return { intelligenceEndpoint: intelligenceEndpoint, geoName: geoName, crossGeoDataMovementEnabledPPACFlag: crossGeoDataMovementEnabledPPACFlag, endpointStamp: endpointStamp };
4244
}
4345

4446
// Function to fetch Artemis response

src/common/services/Interfaces.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,11 @@ export interface IArtemisAPIOrgResponse {
2525
clusterType: string,
2626
}
2727

28-
export interface IArtemisServiceResponse {
29-
stamp: ServiceEndpointCategory;
30-
response: IArtemisAPIOrgResponse;
31-
}
32-
3328
export interface IIntelligenceAPIEndpointInformation {
3429
intelligenceEndpoint: string | null,
3530
geoName: string | null,
36-
crossGeoDataMovementEnabledPPACFlag: boolean
31+
crossGeoDataMovementEnabledPPACFlag: boolean,
32+
endpointStamp?: ServiceEndpointCategory,
3733
}
3834

3935
export interface IWebsiteDetails {

0 commit comments

Comments
 (0)