diff --git a/.gitignore b/.gitignore index e6fd29f2b..cd87b7054 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ telemetry-generated # tasks copy them to the correct location, but leave the originals where # we do not want them checked in l10n/package.nls.*.json + +# Code Coverage Output +.nyc_output/ diff --git a/package.json b/package.json index 679829610..ecadf0b2c 100644 --- a/package.json +++ b/package.json @@ -191,10 +191,6 @@ "title": "Open in Power Pages", "when": "never" }, - { - "command": "extension.createChatView", - "title": "Create Chat View" - }, { "command": "pacCLI.openDocumentation", "category": "Power Platform CLI", diff --git a/src/client/extension.ts b/src/client/extension.ts index 249f1ef7a..0a49712a3 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -9,10 +9,9 @@ import * as vscode from "vscode"; import { AppTelemetryConfigUtility } from "../common/pp-tooling-telemetry-node"; import { vscodeExtAppInsightsResourceProvider } from "../common/telemetry-generated/telemetryConfiguration"; import { ITelemetryData } from "../common/TelemetryData"; -import { CliAcquisition, ICliAcquisitionContext } from "./lib/CliAcquisition"; +import { CliAcquisition } from "./lib/CliAcquisition"; import { PacTerminal } from "./lib/PacTerminal"; import { PortalWebView } from "./PortalWebView"; -import { ITelemetry } from "../common/OneDSLoggerTelemetry/telemetry/ITelemetry"; import { LanguageClient, @@ -46,6 +45,7 @@ import { AadIdKey, EnvIdKey, TenantIdKey } from "../common/OneDSLoggerTelemetry/ import { PowerPagesAppName, PowerPagesClientName } from "../common/ecs-features/constants"; import { ECSFeaturesClient } from "../common/ecs-features/ecsFeatureClient"; import { getECSOrgLocationValue } from "../common/utilities/Utils"; +import { CliAcquisitionContext } from "./lib/CliAcquisitionContext"; import { PreviewSite } from "./runtime-site-preview/PreviewSite"; let client: LanguageClient; @@ -493,70 +493,3 @@ function showNotificationForCopilot(telemetry: TelemetryReporter, telemetryData: } } - -// allow for DI without direct reference to vscode's d.ts file: that definintions file is being generated at VS Code runtime -class CliAcquisitionContext implements ICliAcquisitionContext { - public constructor( - private readonly _context: vscode.ExtensionContext, - private readonly _telemetry: ITelemetry - ) { } - - public get extensionPath(): string { - return this._context.extensionPath; - } - public get globalStorageLocalPath(): string { - return this._context.globalStorageUri.fsPath; - } - public get telemetry(): ITelemetry { - return this._telemetry; - } - - showInformationMessage(message: string, ...items: string[]): void { - vscode.window.showInformationMessage(message, ...items); - } - - showErrorMessage(message: string, ...items: string[]): void { - vscode.window.showErrorMessage(message, ...items); - } - - showCliPreparingMessage(version: string): void { - vscode.window.showInformationMessage( - vscode.l10n.t({ - message: "Preparing pac CLI (v{0})...", - args: [version], - comment: ["{0} represents the version number"] - }) - ); - } - - showCliReadyMessage(): void { - vscode.window.showInformationMessage( - vscode.l10n.t('The pac CLI is ready for use in your VS Code terminal!')); - } - - showCliInstallFailedError(err: string): void { - vscode.window.showErrorMessage( - vscode.l10n.t({ - message: "Cannot install pac CLI: {0}", - args: [err], - comment: ["{0} represents the error message returned from the exception"] - }) - ); - } - - showGeneratorInstallingMessage(version: string): void { - vscode.window.showInformationMessage( - vscode.l10n.t({ - message: "Installing Power Pages generator(v{0})...", - args: [version], - comment: ["{0} represents the version number"] - })) - } - - locDotnetNotInstalledOrInsufficient(): string { - return vscode.l10n.t({ - message: "dotnet sdk 6.0 or greater must be installed", - comment: ["Do not translate 'dotnet' or 'sdk'"] - }); - } -} diff --git a/src/client/lib/CliAcquisition.ts b/src/client/lib/CliAcquisition.ts index 531e7ef2a..2642f0127 100644 --- a/src/client/lib/CliAcquisition.ts +++ b/src/client/lib/CliAcquisition.ts @@ -10,31 +10,17 @@ import * as fs from 'fs-extra'; import * as glob from 'glob'; import * as os from 'os'; import { Extract } from 'unzip-stream' -import { ITelemetry } from '../../common/OneDSLoggerTelemetry/telemetry/ITelemetry'; import find from 'find-process'; import { spawnSync } from 'child_process'; import commandExists from 'command-exists'; import { oneDSLoggerWrapper } from '../../common/OneDSLoggerTelemetry/oneDSLoggerWrapper'; - -// allow for DI without direct reference to vscode's d.ts file: that definintions file is being generated at VS Code runtime -export interface ICliAcquisitionContext { - readonly extensionPath: string; - readonly globalStorageLocalPath: string; - readonly telemetry: ITelemetry; - showInformationMessage(message: string, ...items: string[]): void; - showErrorMessage(message: string, ...items: string[]): void; - showCliPreparingMessage(version: string): void; - showCliReadyMessage(): void; - showCliInstallFailedError(err: string): void; - locDotnetNotInstalledOrInsufficient(): string; -} +import { ICliAcquisitionContext } from './CliAcquisitionContext'; export interface IDisposable { dispose(): void; } export class CliAcquisition implements IDisposable { - private readonly _context: ICliAcquisitionContext; private readonly _cliPath: string; private readonly _cliVersion: string; @@ -241,4 +227,3 @@ export class CliAcquisition implements IDisposable { } } } - diff --git a/src/client/lib/CliAcquisitionContext.ts b/src/client/lib/CliAcquisitionContext.ts new file mode 100644 index 000000000..77a165d23 --- /dev/null +++ b/src/client/lib/CliAcquisitionContext.ts @@ -0,0 +1,86 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import * as vscode from 'vscode'; +import { ITelemetry } from '../../common/OneDSLoggerTelemetry/telemetry/ITelemetry'; + +export interface ICliAcquisitionContext { + readonly extensionPath: string; + readonly globalStorageLocalPath: string; + readonly telemetry: ITelemetry; + showInformationMessage(message: string, ...items: string[]): void; + showErrorMessage(message: string, ...items: string[]): void; + showCliPreparingMessage(version: string): void; + showCliReadyMessage(): void; + showCliInstallFailedError(err: string): void; + locDotnetNotInstalledOrInsufficient(): string; +} + +// allow for DI without direct reference to vscode's d.ts file: that definintions file is being generated at VS Code runtime +export class CliAcquisitionContext implements ICliAcquisitionContext { + public constructor( + private readonly _context: vscode.ExtensionContext, + private readonly _telemetry: ITelemetry + ) { } + + public get extensionPath(): string { + return this._context.extensionPath; + } + public get globalStorageLocalPath(): string { + return this._context.globalStorageUri.fsPath; + } + public get telemetry(): ITelemetry { + return this._telemetry; + } + + showInformationMessage(message: string, ...items: string[]): void { + vscode.window.showInformationMessage(message, ...items); + } + + showErrorMessage(message: string, ...items: string[]): void { + vscode.window.showErrorMessage(message, ...items); + } + + showCliPreparingMessage(version: string): void { + vscode.window.showInformationMessage( + vscode.l10n.t({ + message: "Preparing pac CLI (v{0})...", + args: [version], + comment: ["{0} represents the version number"] + }) + ); + } + + showCliReadyMessage(): void { + vscode.window.showInformationMessage( + vscode.l10n.t('The pac CLI is ready for use in your VS Code terminal!')); + } + + showCliInstallFailedError(err: string): void { + vscode.window.showErrorMessage( + vscode.l10n.t({ + message: "Cannot install pac CLI: {0}", + args: [err], + comment: ["{0} represents the error message returned from the exception"] + }) + ); + } + + showGeneratorInstallingMessage(version: string): void { + vscode.window.showInformationMessage( + vscode.l10n.t({ + message: "Installing Power Pages generator(v{0})...", + args: [version], + comment: ["{0} represents the version number"] + })) + } + + locDotnetNotInstalledOrInsufficient(): string { + return vscode.l10n.t({ + message: "dotnet sdk 6.0 or greater must be installed", + comment: ["Do not translate 'dotnet' or 'sdk'"] + }); + } +} diff --git a/src/client/lib/GeneratorAcquisition.ts b/src/client/lib/GeneratorAcquisition.ts index 9321d673d..e07c9aeb5 100644 --- a/src/client/lib/GeneratorAcquisition.ts +++ b/src/client/lib/GeneratorAcquisition.ts @@ -11,7 +11,7 @@ import * as fs from 'fs-extra'; import * as os from 'os'; import * as path from 'path'; import { PORTAL_YEOMAN_GENERATOR_PACKAGE_NAME, PORTAL_YEOMAN_GENERATOR_PACKAGE_TARBALL_NAME } from '../../common/constants'; -import { ICliAcquisitionContext } from './CliAcquisition'; +import { ICliAcquisitionContext } from './CliAcquisitionContext'; import { glob } from 'glob'; import commandExists from 'command-exists'; import { oneDSLoggerWrapper } from '../../common/OneDSLoggerTelemetry/oneDSLoggerWrapper'; diff --git a/src/client/test/Integration/CliAcquisition.test.ts b/src/client/test/Integration/CliAcquisition.test.ts index 0280144f3..7b2368710 100644 --- a/src/client/test/Integration/CliAcquisition.test.ts +++ b/src/client/test/Integration/CliAcquisition.test.ts @@ -5,7 +5,8 @@ import * as fs from 'fs-extra'; import * as path from 'path'; -import { CliAcquisition, ICliAcquisitionContext } from '../../lib/CliAcquisition'; +import { CliAcquisition } from '../../lib/CliAcquisition'; +import { ICliAcquisitionContext } from '../../lib/CliAcquisitionContext'; import { expect } from 'chai'; import { ITelemetry } from '../../../common/OneDSLoggerTelemetry/telemetry/ITelemetry'; import { NoopTelemetryInstance } from '../../telemetry/NoopTelemetry'; diff --git a/src/client/test/Integration/CliAcquisitionContext.test.ts b/src/client/test/Integration/CliAcquisitionContext.test.ts new file mode 100644 index 000000000..b6f7b8089 --- /dev/null +++ b/src/client/test/Integration/CliAcquisitionContext.test.ts @@ -0,0 +1,125 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import * as vscode from 'vscode'; +import { ITelemetry } from '../../../common/OneDSLoggerTelemetry/telemetry/ITelemetry'; +import { CliAcquisitionContext } from '../../lib/CliAcquisitionContext'; +import { expect } from 'chai'; +import sinon from 'sinon'; + +describe('CliAcquisitionContext', () => { + let context: vscode.ExtensionContext; + let telemetry: ITelemetry; + let showInformationMessageSpy: sinon.SinonSpy; + let showErrorMessageSpy: sinon.SinonSpy; + + beforeEach(() => { + context = { + extensionPath: 'testExtensionPath', + globalStorageUri: { + fsPath: 'testGlobalStorageUri' + } + } as vscode.ExtensionContext; + + telemetry = {} as ITelemetry; + + showInformationMessageSpy = sinon.spy(vscode.window, "showInformationMessage"); + showErrorMessageSpy = sinon.spy(vscode.window, "showErrorMessage"); + }); + + afterEach(() => { + showInformationMessageSpy.restore(); + showErrorMessageSpy.restore(); + }); + + it('should return the extension path', () => { + const cliAcquisitionContext = new CliAcquisitionContext(context, telemetry); + + expect(cliAcquisitionContext.extensionPath).to.equal('testExtensionPath'); + }); + + it('should return the global storage local path', () => { + const cliAcquisitionContext = new CliAcquisitionContext(context, telemetry); + + expect(cliAcquisitionContext.globalStorageLocalPath).to.equal('testGlobalStorageUri'); + }); + + it('should return the telemetry', () => { + const cliAcquisitionContext = new CliAcquisitionContext(context, telemetry); + + expect(cliAcquisitionContext.telemetry).to.equal(telemetry); + }); + + it('should show information message', () => { + const message = 'testMessage'; + const cliAcquisitionContext = new CliAcquisitionContext(context, telemetry); + + cliAcquisitionContext.showInformationMessage(message); + + const showInformationMessageArgs = showInformationMessageSpy.getCalls()[0].args; + + expect(showInformationMessageArgs[0]).eq("testMessage"); + }); + + it('should show error message', () => { + const message = 'testMessage'; + const cliAcquisitionContext = new CliAcquisitionContext(context, telemetry); + + cliAcquisitionContext.showErrorMessage(message); + + const showErrorMessageArgs = showErrorMessageSpy.getCalls()[0].args; + + expect(showErrorMessageArgs[0]).eq("testMessage"); + }); + + it('should show cli preparing message', () => { + const version = 'testVersion'; + const cliAcquisitionContext = new CliAcquisitionContext(context, telemetry); + + cliAcquisitionContext.showCliPreparingMessage(version); + + const showInformationMessageArgs = showInformationMessageSpy.getCalls()[0].args; + + expect(showInformationMessageArgs[0]).eq("Preparing pac CLI (vtestVersion)..."); + }); + + it('should show cli ready message', () => { + const cliAcquisitionContext = new CliAcquisitionContext(context, telemetry); + + cliAcquisitionContext.showCliReadyMessage(); + + const showInformationMessageArgs = showInformationMessageSpy.getCalls()[0].args; + + expect(showInformationMessageArgs[0]).eq("The pac CLI is ready for use in your VS Code terminal!"); + }); + + it('should show cli install failed error', () => { + const err = 'testError'; + const cliAcquisitionContext = new CliAcquisitionContext(context, telemetry); + + cliAcquisitionContext.showCliInstallFailedError(err); + + const showErrorMessageArgs = showErrorMessageSpy.getCalls()[0].args; + + expect(showErrorMessageArgs[0]).eq("Cannot install pac CLI: testError"); + }); + + it('should show generator installing message', () => { + const version = 'testVersion'; + const cliAcquisitionContext = new CliAcquisitionContext(context, telemetry); + + cliAcquisitionContext.showGeneratorInstallingMessage(version); + + const showInformationMessageArgs = showInformationMessageSpy.getCalls()[0].args; + + expect(showInformationMessageArgs[0]).eq("Installing Power Pages generator(vtestVersion)..."); + }); + + it('should return loc dotnet not installed or insufficient', () => { + const cliAcquisitionContext = new CliAcquisitionContext(context, telemetry); + + expect(cliAcquisitionContext.locDotnetNotInstalledOrInsufficient()).eq("dotnet sdk 6.0 or greater must be installed"); + }); +}); diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts index f7a150776..eb0a98820 100644 --- a/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts +++ b/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts @@ -14,7 +14,7 @@ import { intelligenceAPIAuthentication } from '../../services/AuthenticationProv import { ActiveOrgOutput } from '../../../client/pac/PacTypes'; 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'; import { ORG_DETAILS_KEY, handleOrgChangeSuccess, initializeOrgDetails } from '../../utilities/OrgHandlerUtils'; -import { createAndReferenceLocation, getComponentInfo, getEndpoint, provideChatParticipantFollowups, handleChatParticipantFeedback, createErrorResult, createSuccessResult, removeChatVariables } from './PowerPagesChatParticipantUtils'; +import { createAndReferenceLocation, getComponentInfo, getEndpoint, provideChatParticipantFollowups, handleChatParticipantFeedback, createErrorResult, createSuccessResult, removeChatVariables, registerButtonCommands } from './PowerPagesChatParticipantUtils'; import { checkCopilotAvailability, fetchRelatedFiles, getActiveEditorContent } from '../../utilities/Utils'; import { IIntelligenceAPIEndpointInformation } from '../../services/Interfaces'; import { v4 as uuidv4 } from 'uuid'; @@ -63,6 +63,8 @@ export class PowerPagesChatParticipant { this._pacWrapper = pacWrapper; + registerButtonCommands(); + this._disposables.push(orgChangeEvent(async (orgDetails: ActiveOrgOutput) => { await this.handleOrgChangeSuccess(orgDetails); })); @@ -131,9 +133,13 @@ export class PowerPagesChatParticipant { const userId = intelligenceApiAuthResponse.userId; const intelligenceAPIEndpointInfo = await getEndpoint(this.orgID, this.environmentID, this.telemetry, this.cachedEndpoint, this.powerPagesAgentSessionId); + if (!intelligenceAPIEndpointInfo.intelligenceEndpoint) { + return createErrorResult(COPILOT_NOT_AVAILABLE_MSG, RESPONSE_SCENARIOS.COPILOT_NOT_AVAILABLE, this.orgID); + } + const copilotAvailabilityStatus = checkCopilotAvailability(intelligenceAPIEndpointInfo.intelligenceEndpoint, this.orgID, this.telemetry, this.powerPagesAgentSessionId); - if (!copilotAvailabilityStatus || !intelligenceAPIEndpointInfo.intelligenceEndpoint) { + if (!copilotAvailabilityStatus) { return createErrorResult(COPILOT_NOT_AVAILABLE_MSG, RESPONSE_SCENARIOS.COPILOT_NOT_AVAILABLE, this.orgID); } diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipantUtils.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipantUtils.ts index 94749968a..8fd130f78 100644 --- a/src/common/chat-participants/powerpages/PowerPagesChatParticipantUtils.ts +++ b/src/common/chat-participants/powerpages/PowerPagesChatParticipantUtils.ts @@ -11,6 +11,9 @@ import { ITelemetry } from "../../OneDSLoggerTelemetry/telemetry/ITelemetry"; import { ArtemisService } from "../../services/ArtemisService"; import { dataverseAuthentication } from "../../services/AuthenticationProvider"; import { IIntelligenceAPIEndpointInformation } from "../../services/Interfaces"; +import { EditableFileSystemProvider } from "../../utilities/EditableFileSystemProvider"; +import { CREATE_SITE_BTN_CMD } from "./commands/create-site/CreateSiteConstants"; +import { collectSiteCreationInputs, getUpdatedPageContent } from "./commands/create-site/CreateSiteHelper"; import { SUPPORTED_ENTITIES, EXPLAIN_CODE_PROMPT, FORM_PROMPT, LIST_PROMPT, STATER_PROMPTS, WEB_API_PROMPT } from "./PowerPagesChatParticipantConstants"; import { VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO_FEEDBACK_THUMBSUP, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO_FEEDBACK_THUMBSDOWN } from "./PowerPagesChatParticipantTelemetryConstants"; import { IComponentInfo, IPowerPagesChatResult } from "./PowerPagesChatParticipantTypes"; @@ -127,3 +130,22 @@ export function removeChatVariables(userPrompt: string): string { return userPrompt.replace(regex, '').trim(); } + +export function registerButtonCommands() { + vscode.commands.registerCommand(CREATE_SITE_BTN_CMD, async (siteName: string, sitePages, envList, contentProvider: EditableFileSystemProvider, isCreateSiteInputsReceived) => { + if (!isCreateSiteInputsReceived) { + //Update Page Content will be used for the site creation + // eslint-disable-next-line @typescript-eslint/no-explicit-any + sitePages.map((page: any) => { + return { + ...page, + code: getUpdatedPageContent(contentProvider, page.metadata.pageTitle) + } + }); + const siteCreateInputs = await collectSiteCreationInputs(siteName, envList); + if (siteCreateInputs) { + isCreateSiteInputsReceived = true; + } + } + }); +} diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts index f6212e79f..61bd117ba 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts @@ -19,7 +19,7 @@ export class CreateSiteCommand implements Command { try { // eslint-disable-next-line @typescript-eslint/no-unused-vars const result = await createSite({ - intelligenceEndpoint: intelligenceAPIEndpointInfo.intelligenceEndpoint, + intelligenceAPIEndpointInfo, intelligenceApiToken, userPrompt: request.prompt, sessionId: powerPagesAgentSessionId, diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteConstants.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteConstants.ts index eac8c5826..3ebbfd0af 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteConstants.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteConstants.ts @@ -7,3 +7,10 @@ export const EDITABLE_SCHEME = 'editable'; export const ENGLISH = "English"; export const MIN_PAGES = 7; export const MAX_PAGES = 7; +export const SITE_CREATE_INPUTS = 'New Power Pages Site'; +export const ENVIRONMENT_FOR_SITE_CREATION = 'Select Environment for Site Creation'; +export const SITE_NAME = 'Enter Site Name'; +export const SITE_NAME_REQUIRED = 'Site Name is required'; +export const CREATE_SITE_BTN_CMD = 'create-site-inputs'; +export const CREATE_SITE_BTN_TITLE = 'Create Site'; +export const CREATE_SITE_BTN_TOOLTIP = 'Create a new Power Pages site'; diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts index 77d04bc60..488d486fa 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts @@ -11,13 +11,15 @@ import { NL2SITE_REQUEST_FAILED, NL2PAGE_GENERATING_WEBPAGES, NL2PAGE_RESPONSE_F import { oneDSLoggerWrapper } from '../../../../OneDSLoggerTelemetry/oneDSLoggerWrapper'; import { VSCODE_EXTENSION_NL2PAGE_REQUEST, VSCODE_EXTENSION_NL2SITE_REQUEST, VSCODE_EXTENSION_PREVIEW_SITE_PAGES, VSCODE_EXTENSION_PREVIEW_SITE_PAGES_ERROR } from '../../PowerPagesChatParticipantTelemetryConstants'; import { EditableFileSystemProvider } from '../../../../utilities/EditableFileSystemProvider'; -import { HTML_FILE_EXTENSION, UTF8_ENCODING } from '../../../../constants'; -import { EDITABLE_SCHEME } from './CreateSiteConstants'; -import { ICreateSiteOptions, IPreviewSitePagesContentOptions } from './CreateSiteTypes'; +import { HTML_FILE_EXTENSION, IEnvInfo, UTF8_ENCODING } from '../../../../constants'; +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'; +import { ICreateSiteOptions, IPreviewSitePagesContentOptions, ISiteInputState } from './CreateSiteTypes'; +import { MultiStepInput } from '../../../../utilities/MultiStepInput'; +import { getEnvList } from '../../../../utilities/Utils'; export const createSite = async (createSiteOptions: ICreateSiteOptions) => { const { - intelligenceEndpoint, + intelligenceAPIEndpointInfo, intelligenceApiToken, userPrompt, sessionId, @@ -29,12 +31,22 @@ export const createSite = async (createSiteOptions: ICreateSiteOptions) => { extensionContext } = createSiteOptions; - const { siteName, siteDescription, sitePages } = await fetchSiteAndPageData(intelligenceEndpoint, intelligenceApiToken, userPrompt, sessionId, telemetry, stream, orgId, envId, userId); + if (!intelligenceAPIEndpointInfo.intelligenceEndpoint) { + return; + } + const { siteName, siteDescription, sitePages } = await fetchSiteAndPageData(intelligenceAPIEndpointInfo.intelligenceEndpoint, intelligenceApiToken, userPrompt, sessionId, telemetry, stream, orgId, envId, userId); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const contentProvider = previewSitePagesContent({sitePages, stream, extensionContext, telemetry, sessionId, orgId, envId, userId}); + const contentProvider = previewSitePagesContent({ sitePages, stream, extensionContext, telemetry, sessionId, orgId, envId, userId }); + + const envList = await getEnvList(telemetry, intelligenceAPIEndpointInfo.endpointStamp); - // TODO: Implement the create site button click handler + stream.button({ + command: CREATE_SITE_BTN_CMD, + title: CREATE_SITE_BTN_TITLE, + tooltip: CREATE_SITE_BTN_TOOLTIP, + arguments: [siteName, envList, contentProvider, false], + }); return { siteName, @@ -117,4 +129,63 @@ function previewSitePagesContent( throw error; } } +// Function to get updated content +export function getUpdatedPageContent(contentProvider: EditableFileSystemProvider, pageName: string): string { + const pageUri = vscode.Uri.parse(`${EDITABLE_SCHEME}:/${pageName}${HTML_FILE_EXTENSION}`); + return contentProvider.getFileContent(pageUri); +} + +export async function collectSiteCreationInputs(siteName: string, envList: IEnvInfo[]) { + const envNames: vscode.QuickPickItem[] = envList.map((env: IEnvInfo) => { + return { + label: env.envDisplayName, + description: env.orgUrl, + }; + }); + + const title = vscode.l10n.t(SITE_CREATE_INPUTS); + + async function collectInputs() { + const state = {} as Partial; + await MultiStepInput.run((input) => selectEnvName(input, state)); + return state as ISiteInputState; + } + async function selectEnvName( + input: MultiStepInput, + state: Partial + ) { + const pick = await input.showQuickPick({ + title, + step: 1, + totalSteps: 2, + placeholder: vscode.l10n.t(ENVIRONMENT_FOR_SITE_CREATION), + items: envNames, + activeItem: + typeof state.envName !== "string" + ? state.envName + : undefined, + }); + state.envName = pick.label; + state.orgUrl = pick.description; + return (input: MultiStepInput) => inputSiteName(input, state); + } + + async function inputSiteName( + input: MultiStepInput, + state: Partial + ) { + state.siteName = await input.showInputBox({ + title, + step: 2, + totalSteps: 2, + value: state.siteName || siteName, + placeholder: vscode.l10n.t(SITE_NAME), + validate: async (value) => (value ? undefined : vscode.l10n.t(SITE_NAME_REQUIRED)), + }); + } + + const siteInputState = await collectInputs(); + // Return the collected site creation inputs including site name, environment name, and domain name + return siteInputState; +} diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteTypes.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteTypes.ts index 30d8f358d..6d43ea2e7 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteTypes.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteTypes.ts @@ -5,9 +5,10 @@ import { ITelemetry } from "../../../../OneDSLoggerTelemetry/telemetry/ITelemetry"; import * as vscode from 'vscode'; +import { IIntelligenceAPIEndpointInformation } from "../../../../services/Interfaces"; export interface ICreateSiteOptions { - intelligenceEndpoint: string; + intelligenceAPIEndpointInfo: IIntelligenceAPIEndpointInformation; intelligenceApiToken: string; userPrompt: string; sessionId: string; @@ -31,3 +32,13 @@ export interface IPreviewSitePagesContentOptions { envId: string; userId: string; } + +export interface ISiteInputState { + siteName: string; + envName: string; + orgUrl: string; + domainName: string; + title: string; + step: number; + totalSteps: number; +} diff --git a/src/common/constants.ts b/src/common/constants.ts index dfb5f8fba..b7c365381 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -85,6 +85,11 @@ export interface IApiRequestParams { relatedFiles?: IRelatedFiles[]; } +export interface IEnvInfo { + orgUrl: string; + envDisplayName: string; +} + export const VSCODE_EXTENSION_COPILOT_CONTEXT_RELATED_FILES_FETCH_FAILED = "VSCodeExtensionCopilotContextRelatedFilesFetchFailed"; export const ADX_WEBPAGE = 'adx_webpage' export const HTML_FILE_EXTENSION = '.html'; diff --git a/src/common/copilot/PowerPagesCopilot.ts b/src/common/copilot/PowerPagesCopilot.ts index 4adfdeb3d..229a8735e 100644 --- a/src/common/copilot/PowerPagesCopilot.ts +++ b/src/common/copilot/PowerPagesCopilot.ts @@ -426,6 +426,10 @@ export class PowerPagesCopilot implements vscode.WebviewViewProvider { sendTelemetryEvent(this.telemetry, { eventName: CopilotOrgChangedEvent, copilotSessionId: sessionID, orgId: orgID }); const intelligenceAPIEndpointInfo = await ArtemisService.getIntelligenceEndpoint(orgID, this.telemetry, sessionID, environmentId); + if (!intelligenceAPIEndpointInfo.intelligenceEndpoint) { + this.sendMessageToWebview({ type: 'Unavailable' }); + return; + } this.aibEndpoint = intelligenceAPIEndpointInfo.intelligenceEndpoint; this.geoName = intelligenceAPIEndpointInfo.geoName; this.crossGeoDataMovementEnabledPPACFlag = intelligenceAPIEndpointInfo.crossGeoDataMovementEnabledPPACFlag; @@ -501,7 +505,7 @@ export class PowerPagesCopilot implements vscode.WebviewViewProvider {
-