diff --git a/src/client/extension.ts b/src/client/extension.ts index b0763d24..5cda823c 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -44,7 +44,7 @@ import { EXTENSION_ID, SUCCESS } from "../common/constants"; import { AadIdKey, EnvIdKey, TenantIdKey } from "../common/OneDSLoggerTelemetry/telemetryConstants"; import { PowerPagesAppName, PowerPagesClientName } from "../common/ecs-features/constants"; import { ECSFeaturesClient } from "../common/ecs-features/ecsFeatureClient"; -import { getECSOrgLocationValue } from "../common/utilities/Utils"; +import { getECSOrgLocationValue, getWorkspaceFolders } from "../common/utilities/Utils"; import { CliAcquisitionContext } from "./lib/CliAcquisitionContext"; import { PreviewSite, SITE_PREVIEW_COMMAND_ID } from "./runtime-site-preview/PreviewSite"; @@ -190,13 +190,8 @@ export async function activate( let copilotNotificationShown = false; - const workspaceFolders = - vscode.workspace.workspaceFolders?.map( - (fl) => ({ ...fl, uri: fl.uri.fsPath } as WorkspaceFolder) - ) || []; - + const workspaceFolders = getWorkspaceFolders(); - let websiteURL: string | undefined = undefined; const isSiteRuntimePreviewEnabled = PreviewSite.isSiteRuntimePreviewEnabled(); vscode.commands.executeCommand("setContext", "microsoft.powerplatform.pages.siteRuntimePreviewEnabled", isSiteRuntimePreviewEnabled); @@ -257,7 +252,7 @@ export async function activate( } if (artemisResponse !== null && isSiteRuntimePreviewEnabled) { - websiteURL = await PreviewSite.getWebSiteURL(workspaceFolders, artemisResponse?.stamp, orgDetails.EnvironmentId, _telemetry); + await PreviewSite.loadSiteUrl(workspaceFolders, artemisResponse?.stamp, orgDetails.EnvironmentId, _telemetry); } }) @@ -282,17 +277,17 @@ export async function activate( _telemetry.sendTelemetryEvent("EnableSiteRuntimePreview", { isEnabled: isSiteRuntimePreviewEnabled.toString(), - websiteURL: websiteURL || "undefined" + websiteURL: PreviewSite.getSiteUrl() || "undefined" }); oneDSLoggerWrapper.getLogger().traceInfo("EnableSiteRuntimePreview", { isEnabled: isSiteRuntimePreviewEnabled.toString(), - websiteURL: websiteURL || "undefined" + websiteURL: PreviewSite.getSiteUrl() || "undefined" }); _context.subscriptions.push( vscode.commands.registerCommand( SITE_PREVIEW_COMMAND_ID, - async () => await PreviewSite.handlePreviewRequest(isSiteRuntimePreviewEnabled, websiteURL, _telemetry) + async () => await PreviewSite.handlePreviewRequest(isSiteRuntimePreviewEnabled, _telemetry, pacTerminal) ) ); diff --git a/src/client/runtime-site-preview/PreviewSite.ts b/src/client/runtime-site-preview/PreviewSite.ts index 034eb68b..b94fa815 100644 --- a/src/client/runtime-site-preview/PreviewSite.ts +++ b/src/client/runtime-site-preview/PreviewSite.ts @@ -14,11 +14,16 @@ import { PPAPIService } from '../../common/services/PPAPIService'; import { VSCODE_EXTENSION_GET_WEBSITE_RECORD_ID_EMPTY } from '../../common/services/TelemetryConstants'; import { EDGE_TOOLS_EXTENSION_ID } from '../../common/constants'; import { oneDSLoggerWrapper } from "../../common/OneDSLoggerTelemetry/oneDSLoggerWrapper"; -import { showProgressWithNotification } from '../../common/utilities/Utils'; +import { getWorkspaceFolders, showProgressWithNotification } from '../../common/utilities/Utils'; +import { PacTerminal } from '../lib/PacTerminal'; +import { initializeOrgDetails } from '../../common/utilities/OrgHandlerUtils'; +import { ArtemisService } from '../../common/services/ArtemisService'; export const SITE_PREVIEW_COMMAND_ID = "microsoft.powerplatform.pages.preview-site"; export class PreviewSite { + private static _websiteUrl: string | undefined = undefined; + static isSiteRuntimePreviewEnabled(): boolean { const enableSiteRuntimePreview = ECSFeaturesClient.getConfig(EnableSiteRuntimePreview).enableSiteRuntimePreview @@ -26,10 +31,25 @@ export class PreviewSite { return false; } - return enableSiteRuntimePreview; + return true; + } + + static async loadSiteUrl( + workspaceFolders: WorkspaceFolder[], + stamp: ServiceEndpointCategory, + envId: string, + telemetry: ITelemetry) + : Promise { + const websiteUrl = await PreviewSite.getWebSiteUrl(workspaceFolders, stamp, envId, telemetry); + + this._websiteUrl = websiteUrl; } - static async getWebSiteURL(workspaceFolders: WorkspaceFolder[], stamp: ServiceEndpointCategory, envId: string, telemetry: ITelemetry): Promise { + static getSiteUrl(): string | undefined { + return this._websiteUrl; + } + + private static async getWebSiteUrl(workspaceFolders: WorkspaceFolder[], stamp: ServiceEndpointCategory, envId: string, telemetry: ITelemetry): Promise { const websiteRecordId = getWebsiteRecordId(workspaceFolders, telemetry); if (!websiteRecordId) { telemetry.sendTelemetryEvent(VSCODE_EXTENSION_GET_WEBSITE_RECORD_ID_EMPTY, { @@ -74,9 +94,15 @@ export class PreviewSite { await settings.update('defaultUrl', currentDefaultUrl); } ); + + await vscode.window.showInformationMessage(vscode.l10n.t('The preview shown is for published changes.')); } - static async handlePreviewRequest(isSiteRuntimePreviewEnabled: boolean, websiteURL: string | undefined, telemetry: ITelemetry) { + static async handlePreviewRequest( + isSiteRuntimePreviewEnabled: boolean, + telemetry: ITelemetry, + pacTerminal: PacTerminal) { + telemetry.sendTelemetryEvent("StartCommand", { commandId: SITE_PREVIEW_COMMAND_ID, }); @@ -94,25 +120,66 @@ export class PreviewSite { return; } - if (websiteURL === undefined) { + if (this._websiteUrl === undefined) { await vscode.window.showWarningMessage(vscode.l10n.t("Initializing site preview. Please try again after few seconds.")); return; } - if (websiteURL === "") { - const shouldInitiateLogin = await vscode.window.showErrorMessage( - vscode.l10n.t( - `Website not found in the environment. Please check the credentials and login with correct account.` - ), - vscode.l10n.t('Login') - ); + if (this._websiteUrl === "") { + let shouldRepeatLoginFlow = true; - if (shouldInitiateLogin === vscode.l10n.t('Login')) { - await vscode.authentication.getSession(PROVIDER_ID, [], { }) + while (shouldRepeatLoginFlow) { + shouldRepeatLoginFlow = await PreviewSite.handleEmptyWebsiteUrl(pacTerminal, telemetry); } - return; } - await PreviewSite.launchBrowserAndDevToolsWithinVsCode(websiteURL); + await PreviewSite.launchBrowserAndDevToolsWithinVsCode(this._websiteUrl); + } + + private static async handleEmptyWebsiteUrl(pacTerminal: PacTerminal, telemetry: ITelemetry): Promise { + const shouldInitiateLogin = await vscode.window.showErrorMessage( + vscode.l10n.t( + `Website not found in the environment. Please check the credentials and login with correct account.` + ), + vscode.l10n.t('Login'), + vscode.l10n.t('Cancel') + ); + + let shouldRepeatLoginFlow = false; + + if (shouldInitiateLogin === vscode.l10n.t('Login')) { + await vscode.authentication.getSession(PROVIDER_ID, [], { forceNewSession: true, clearSessionPreference: true }); + + await showProgressWithNotification( + vscode.l10n.t('Initializing site preview'), + async (progress) => { + progress.report({ message: vscode.l10n.t('Getting org details...') }); + + const orgDetails = await initializeOrgDetails(false, pacTerminal.getWrapper()); + + progress.report({ message: vscode.l10n.t('Getting region information...') }); + + const artemisResponse = await ArtemisService.getArtemisResponse(orgDetails.orgID, telemetry, ""); + + if (artemisResponse === null || artemisResponse.response === null) { + vscode.window.showErrorMessage(vscode.l10n.t("Failed to get website endpoint. Please try again later")); + return; + } + + progress.report({ message: vscode.l10n.t('Getting website endpoint...') }); + + const websiteUrl = await PreviewSite.getWebSiteUrl(getWorkspaceFolders(), artemisResponse?.stamp, orgDetails.environmentID, telemetry); + + if (websiteUrl === "") { + shouldRepeatLoginFlow = true; + } + else { + this._websiteUrl = websiteUrl; + } + } + ); + } + + return shouldRepeatLoginFlow; } } diff --git a/src/common/utilities/Utils.ts b/src/common/utilities/Utils.ts index c53080eb..810d8a15 100644 --- a/src/common/utilities/Utils.ts +++ b/src/common/utilities/Utils.ts @@ -17,6 +17,8 @@ import { oneDSLoggerWrapper } from "../OneDSLoggerTelemetry/oneDSLoggerWrapper"; import { bapServiceAuthentication } from "../services/AuthenticationProvider"; import { BAP_API_VERSION, BAP_ENVIRONMENT_LIST_URL, BAP_SERVICE_ENDPOINT, ServiceEndpointCategory } from "../services/Constants"; import { VSCODE_EXTENSION_GET_ENV_LIST_SUCCESS, VSCODE_EXTENSION_GET_ENV_LIST_FAILED, VSCODE_EXTENSION_GET_BAP_ENDPOINT_UNSUPPORTED_REGION } from "../services/TelemetryConstants"; +import { WorkspaceFolder } from "vscode-languageserver"; +import { Progress } from "vscode"; export function getSelectedCode(editor: vscode.TextEditor): string { if (!editor) { @@ -104,13 +106,16 @@ export async function showInputBoxAndGetOrgUrl() { }); } -export async function showProgressWithNotification(title: string, task: () => Promise): Promise { +export async function showProgressWithNotification(title: string, task: (progress: Progress<{ + message?: string; + increment?: number; +}>) => Promise): Promise { return await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: title, cancellable: false - }, async () => { - return await task(); + }, async (progress) => { + return await task(progress); }); } @@ -397,3 +402,9 @@ export function getBAPEndpoint(serviceEndpointStamp: ServiceEndpointCategory, te return BAP_SERVICE_ENDPOINT.replace('{rootURL}', bapEndpoint) } + +export function getWorkspaceFolders(): WorkspaceFolder[] { + return vscode.workspace.workspaceFolders?.map( + (fl) => ({ ...fl, uri: fl.uri.fsPath } as WorkspaceFolder) + ) || []; +}