From 8c25291bba8bda1706430671057c9bdd58c302ac Mon Sep 17 00:00:00 2001 From: Ashwani Kumar Date: Fri, 25 Oct 2024 04:05:07 +0530 Subject: [PATCH 01/10] Added site runtime preview code behind ECS Config --- src/client/extension.ts | 85 ++++++++++++++---- .../runtimeSitePreview/LaunchJsonHelper.ts | 88 +++++++++++++++++++ src/client/runtimeSitePreview/PreviewSite.ts | 74 ++++++++++++++++ src/common/ecs-features/ecsFeatureGates.ts | 10 +++ src/common/services/PPAPIService.ts | 31 +++++++ .../utilities/WorkspaceInfoFinderUtil.ts | 24 +++++ 6 files changed, 295 insertions(+), 17 deletions(-) create mode 100644 src/client/runtimeSitePreview/LaunchJsonHelper.ts create mode 100644 src/client/runtimeSitePreview/PreviewSite.ts diff --git a/src/client/extension.ts b/src/client/extension.ts index 2f023c292..aff96beee 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -40,12 +40,16 @@ import { ActiveOrgOutput } from "./pac/PacTypes"; import { desktopTelemetryEventNames } from "../common/OneDSLoggerTelemetry/client/desktopExtensionTelemetryEventNames"; import { ArtemisService } from "../common/services/ArtemisService"; import { workspaceContainsPortalConfigFolder } from "../common/utilities/PathFinderUtil"; -import { getPortalsOrgURLs } from "../common/utilities/WorkspaceInfoFinderUtil"; +import { getPortalsOrgURLs, getWebsiteRecordID } from "../common/utilities/WorkspaceInfoFinderUtil"; 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 { ServiceEndpointCategory } from "../common/services/Constants"; +import { PPAPIService } from "../common/services/PPAPIService"; +import { EnableSiteRuntimePreview } from "../common/ecs-features/ecsFeatureGates"; +import { PreviewSite } from "./runtimeSitePreview/PreviewSite"; let client: LanguageClient; let _context: vscode.ExtensionContext; @@ -101,22 +105,6 @@ export async function activate( ); } - // portal web view panel - _context.subscriptions.push( - vscode.commands.registerCommand( - "microsoft-powerapps-portals.preview-show", - () => { - _telemetry.sendTelemetryEvent("StartCommand", { - commandId: "microsoft-powerapps-portals.preview-show", - }); - oneDSLoggerWrapper.getLogger().traceInfo("StartCommand", { - commandId: "microsoft-powerapps-portals.preview-show" - }); - PortalWebView.createOrShow(); - } - ) - ); - // registering bootstrapdiff command _context.subscriptions.push( vscode.commands.registerCommand('microsoft-powerapps-portals.bootstrap-diff', async () => { @@ -195,6 +183,8 @@ export async function activate( ) || []; + let websiteURL = ""; + _context.subscriptions.push( orgChangeEvent(async (orgDetails: ActiveOrgOutput) => { const orgID = orgDetails.OrgId; @@ -249,6 +239,9 @@ export async function activate( copilotNotificationShown = true; } + if(artemisResponse!==null && isSiteRuntimePreviewEnabled()) { + websiteURL = await getWebSiteURL(workspaceFolders, artemisResponse?.stamp, orgDetails.EnvironmentId, _telemetry); + } }) ); @@ -270,6 +263,47 @@ export async function activate( vscode.commands.executeCommand('setContext', 'powerpages.websiteYmlExists', false); } + const registerPreviewShowCommand = async () => { + const isEnabled = isSiteRuntimePreviewEnabled(); + + _telemetry.sendTelemetryEvent("EnableSiteRuntimePreview", { + isEnabled: isEnabled.toString(), + websiteURL: websiteURL + }); + oneDSLoggerWrapper.getLogger().traceInfo("EnableSiteRuntimePreview", { + isEnabled: isEnabled.toString(), + websiteURL: websiteURL + }); + + if (!isEnabled || websiteURL === "") { + // portal web view panel + _context.subscriptions.push( + vscode.commands.registerCommand( + "microsoft-powerapps-portals.preview-show", + () => { + _telemetry.sendTelemetryEvent("StartCommand", { + commandId: "microsoft-powerapps-portals.preview-show", + }); + oneDSLoggerWrapper.getLogger().traceInfo("StartCommand", { + commandId: "microsoft-powerapps-portals.preview-show" + }); + PortalWebView.createOrShow(); + } + ) + ); + } else { + _context.subscriptions.push( + vscode.commands.registerCommand( + "microsoft-powerapps-portals.preview-show", + () => PreviewSite.launchBrowserAndDevToolsWithinVsCode(websiteURL) + ) + ); + } + }; + + await registerPreviewShowCommand(); + + const workspaceFolderWatcher = vscode.workspace.onDidChangeWorkspaceFolders(handleWorkspaceFolderChange); _context.subscriptions.push(workspaceFolderWatcher); @@ -299,6 +333,23 @@ export async function deactivate(): Promise { disposeNotificationPanel(); } +async function getWebSiteURL(workspaceFolders: WorkspaceFolder[], stamp: ServiceEndpointCategory, envId: string, telemetry: ITelemetry): Promise { + + const websiteRecordId = getWebsiteRecordID(workspaceFolders, telemetry); + const websiteDetails = await PPAPIService.getWebsiteDetailsByWebsiteRecordId(stamp, envId, websiteRecordId, _telemetry); + return websiteDetails?.websiteUrl || ""; +} + +function isSiteRuntimePreviewEnabled() { + const enableSiteRuntimePreview = ECSFeaturesClient.getConfig(EnableSiteRuntimePreview).enableSiteRuntimePreview + + if(enableSiteRuntimePreview === undefined) { + return false; + } + + return enableSiteRuntimePreview; +} + function didOpenTextDocument(document: vscode.TextDocument): void { // The debug options for the server // --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging diff --git a/src/client/runtimeSitePreview/LaunchJsonHelper.ts b/src/client/runtimeSitePreview/LaunchJsonHelper.ts new file mode 100644 index 000000000..0ee52f41a --- /dev/null +++ b/src/client/runtimeSitePreview/LaunchJsonHelper.ts @@ -0,0 +1,88 @@ +/* + * 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'; + +export async function updateLaunchJsonConfig(url: string): Promise { + + const workspaceFolders = vscode.workspace.workspaceFolders; + if (!workspaceFolders) { + vscode.window.showErrorMessage('No workspace folder is open.'); + return; + } + + const launchJsonPath = vscode.Uri.joinPath(workspaceFolders[0].uri, '.vscode', 'launch.json'); + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let launchJson: any; + let launchJsonDoc: vscode.TextDocument | undefined; + + try { + launchJsonDoc = await vscode.workspace.openTextDocument(launchJsonPath); + const launchJsonText = launchJsonDoc.getText(); + launchJson = launchJsonText ? JSON.parse(launchJsonText) : { configurations: [], compounds: [] }; + } catch (error) { + // If the file does not exist or is empty, initialize it + launchJson = { configurations: [], compounds: [] }; + } + + // Update or add the configurations for Microsoft Edge + const edgeConfigurations = [ + { + type: 'pwa-msedge', + name: 'Launch Microsoft Edge', + request: 'launch', + runtimeArgs: ['--remote-debugging-port=9222'], + url: url, + presentation: { + hidden: true + } + }, + { + type: 'pwa-msedge', + name: 'Launch Microsoft Edge in headless mode', + request: 'launch', + runtimeArgs: ['--headless', '--remote-debugging-port=9222'], + url: url, + presentation: { + hidden: true + } + }, + { + type: 'vscode-edge-devtools.debug', + name: 'Open Edge DevTools', + request: 'attach', + url: url, + presentation: { + hidden: true + } + } + ]; + + // Update or add the compounds for Microsoft Edge + const edgeCompounds = [ + { + name: 'Launch Edge Headless and attach DevTools', + configurations: ['Launch Microsoft Edge in headless mode', 'Open Edge DevTools'] + }, + { + name: 'Launch Edge and attach DevTools', + configurations: ['Launch Microsoft Edge', 'Open Edge DevTools'] + } + ]; + + // Merge the new configurations and compounds with the existing ones + launchJson.configurations = [...launchJson.configurations, ...edgeConfigurations]; + launchJson.compounds = [...launchJson.compounds, ...edgeCompounds]; + + // Write the updated launch.json file + const launchJsonContent = JSON.stringify(launchJson, null, 4); + await vscode.workspace.fs.writeFile(launchJsonPath, Buffer.from(launchJsonContent, 'utf8')); + } catch (e) { + if(e instanceof Error) { + vscode.window.showErrorMessage(`Failed to update launch.json: ${e.message}`); + } + } +} diff --git a/src/client/runtimeSitePreview/PreviewSite.ts b/src/client/runtimeSitePreview/PreviewSite.ts new file mode 100644 index 000000000..eeac06ab2 --- /dev/null +++ b/src/client/runtimeSitePreview/PreviewSite.ts @@ -0,0 +1,74 @@ +/* + * 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 * as path from 'path'; +import * as fs from 'fs'; +import { updateLaunchJsonConfig } from './LaunchJsonHelper'; + +export class PreviewSite { + + static async launchBrowserAndDevToolsWithinVsCode(webSitePreviewURL: string): Promise { + + const edgeToolsExtensionId = 'ms-edgedevtools.vscode-edge-devtools'; + const edgeToolsExtension = vscode.extensions.getExtension(edgeToolsExtensionId); + + if (edgeToolsExtension) { + // Preserve the original state of the launch.json file and .vscode folder + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; + const vscodeFolderPath = workspaceFolder ? path.join(workspaceFolder.uri.fsPath, '.vscode') : null; + const launchJsonPath = vscodeFolderPath ? path.join(vscodeFolderPath, 'launch.json') : null; + let originalLaunchJsonContent: string | null = null; + let vscodeFolderExisted = false; + + if (vscodeFolderPath && fs.existsSync(vscodeFolderPath)) { + vscodeFolderExisted = true; + if (launchJsonPath && fs.existsSync(launchJsonPath)) { + originalLaunchJsonContent = fs.readFileSync(launchJsonPath, 'utf8'); + } + } + + await updateLaunchJsonConfig(webSitePreviewURL); + + try { + // Added a 2-second delay before executing the launchProject command to handle the case where the launch.json file is not saved yet + await new Promise(resolve => setTimeout(resolve, 2000)); + await vscode.commands.executeCommand('vscode-edge-devtools-view.launchProject'); + + } finally { + // Revert the changes made to the launch.json file and remove the .vscode folder if it was created + + // Added a 2-second delay to ensure that debug session is closed and then launch.json file is removed + await new Promise(resolve => setTimeout(resolve, 2000)); + if (launchJsonPath) { + if (originalLaunchJsonContent !== null) { + fs.writeFileSync(launchJsonPath, originalLaunchJsonContent, 'utf8'); + } else if (fs.existsSync(launchJsonPath)) { + fs.unlinkSync(launchJsonPath); + } + } + + if (vscodeFolderPath && !vscodeFolderExisted && fs.existsSync(vscodeFolderPath)) { + const files = fs.readdirSync(vscodeFolderPath); + if (files.length === 0) { + fs.rmdirSync(vscodeFolderPath); + } + } + } + } else { + const install = await vscode.window.showWarningMessage( + `The extension "${edgeToolsExtensionId}" is required to run this command. Do you want to install it now?`, + 'Install', 'Cancel' + ); + + if (install === 'Install') { + // Open the Extensions view with the specific extension + vscode.commands.executeCommand('workbench.extensions.search', edgeToolsExtensionId); + } + + return; + } + } +} diff --git a/src/common/ecs-features/ecsFeatureGates.ts b/src/common/ecs-features/ecsFeatureGates.ts index b49fe6453..7cacacbf3 100644 --- a/src/common/ecs-features/ecsFeatureGates.ts +++ b/src/common/ecs-features/ecsFeatureGates.ts @@ -48,3 +48,13 @@ export const { enablePowerpagesInGithubCopilot: false, }, }); + +export const { + feature: EnableSiteRuntimePreview +} = getFeatureConfigs({ + teamName: PowerPagesClientName, + description: 'Enable Site Runtime Preview in VS Code Desktop', + fallback: { + enableSiteRuntimePreview: false, + }, +}); diff --git a/src/common/services/PPAPIService.ts b/src/common/services/PPAPIService.ts index 2079ff118..0a1ef9fc5 100644 --- a/src/common/services/PPAPIService.ts +++ b/src/common/services/PPAPIService.ts @@ -33,6 +33,37 @@ export class PPAPIService { return null; } + public static async getWebsiteDetailsByWebsiteRecordId(serviceEndpointStamp: ServiceEndpointCategory, environmentId: string, websiteRecordId: string, telemetry: ITelemetry): Promise { + + const websiteDetailsArray = await PPAPIService.getWebsiteDetails(serviceEndpointStamp, environmentId, telemetry); + const websiteDetails = websiteDetailsArray?.find((website) => website.websiteRecordId === websiteRecordId); + + if (websiteDetails) { + sendTelemetryEvent(telemetry, { eventName: VSCODE_EXTENSION_PPAPI_GET_WEBSITE_BY_ID_COMPLETED, orgUrl: websiteDetails.dataverseInstanceUrl }); + return websiteDetails; + } + return null; + } + + static async getWebsiteDetails(serviceEndpointStamp: ServiceEndpointCategory, environmentId: string, telemetry: ITelemetry): Promise { + try { + const accessToken = await powerPlatformAPIAuthentication(telemetry, true); + const response = await fetch(await PPAPIService.getPPAPIServiceEndpoint(serviceEndpointStamp, telemetry, environmentId), { + method: 'GET', + headers: getCommonHeaders(accessToken) + }); + + if (response.ok) { + const websiteDetailsArray = await response.json() as unknown as IWebsiteDetails[]; + return websiteDetailsArray; + } + } + catch (error) { + sendTelemetryEvent(telemetry, { eventName: VSCODE_EXTENSION_GET_CROSS_GEO_DATA_MOVEMENT_ENABLED_FLAG_FAILED, errorMsg: (error as Error).message }); + } + return null; + } + static async getPPAPIServiceEndpoint(serviceEndpointStamp: ServiceEndpointCategory, telemetry: ITelemetry, environmentId: string, websitePreviewId?: string): Promise { let ppapiEndpoint = ""; diff --git a/src/common/utilities/WorkspaceInfoFinderUtil.ts b/src/common/utilities/WorkspaceInfoFinderUtil.ts index 2b4afc175..ec8256260 100644 --- a/src/common/utilities/WorkspaceInfoFinderUtil.ts +++ b/src/common/utilities/WorkspaceInfoFinderUtil.ts @@ -7,6 +7,9 @@ import { WorkspaceFolder } from 'vscode-languageserver/node'; import { glob } from 'glob'; +import * as path from 'path'; +import * as fs from 'fs'; +import { parse } from 'yaml'; import { ITelemetry } from '../OneDSLoggerTelemetry/telemetry/ITelemetry'; import { sendTelemetryEvent } from '../OneDSLoggerTelemetry/telemetry/telemetry'; @@ -36,3 +39,24 @@ export function getPortalsOrgURLs(workspaceRootFolders: WorkspaceFolder[] | null } return output; } + +export function getWebsiteRecordID(workspaceFolders: { uri: string }[], telemetry: ITelemetry): string { + try { + if (!workspaceFolders || workspaceFolders.length === 0) { + return ""; + } + + const workspaceRootFolder = workspaceFolders[0]; + const websiteYmlPath = path.join(workspaceRootFolder.uri, 'website.yml'); + if (fs.existsSync(websiteYmlPath)) { + const fileContent = fs.readFileSync(websiteYmlPath, 'utf8'); + const parsedYaml = parse(fileContent); + if (parsedYaml && parsedYaml.adx_websiteid) { + return parsedYaml.adx_websiteid; + } + } + } catch (exception) { + sendTelemetryEvent(telemetry, { methodName: getWebsiteRecordID.name, eventName: 'getWebsiteRecordID', exception: exception as Error }); + } + return ""; +} From 5a8ad34cea1962605180a8c748bccfec2bf844a5 Mon Sep 17 00:00:00 2001 From: Ashwani Kumar Date: Tue, 5 Nov 2024 03:16:28 +0530 Subject: [PATCH 02/10] localization added and moved preview site code to separate file --- src/client/extension.ts | 23 +++------------- .../runtimeSitePreview/LaunchJsonHelper.ts | 6 +++-- src/client/runtimeSitePreview/PreviewSite.ts | 27 ++++++++++++++++++- .../telemetry/telemetry.ts | 1 + src/common/services/PPAPIService.ts | 13 ++++++--- src/common/services/TelemetryConstants.ts | 3 +++ .../utilities/WorkspaceInfoFinderUtil.ts | 4 +-- 7 files changed, 48 insertions(+), 29 deletions(-) diff --git a/src/client/extension.ts b/src/client/extension.ts index aff96beee..f023958cf 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -239,8 +239,8 @@ export async function activate( copilotNotificationShown = true; } - if(artemisResponse!==null && isSiteRuntimePreviewEnabled()) { - websiteURL = await getWebSiteURL(workspaceFolders, artemisResponse?.stamp, orgDetails.EnvironmentId, _telemetry); + if(artemisResponse!==null && PreviewSite.isSiteRuntimePreviewEnabled()) { + websiteURL = await PreviewSite.getWebSiteURL(workspaceFolders, artemisResponse?.stamp, orgDetails.EnvironmentId, _telemetry); } }) @@ -264,7 +264,7 @@ export async function activate( } const registerPreviewShowCommand = async () => { - const isEnabled = isSiteRuntimePreviewEnabled(); + const isEnabled = PreviewSite.isSiteRuntimePreviewEnabled(); _telemetry.sendTelemetryEvent("EnableSiteRuntimePreview", { isEnabled: isEnabled.toString(), @@ -333,23 +333,6 @@ export async function deactivate(): Promise { disposeNotificationPanel(); } -async function getWebSiteURL(workspaceFolders: WorkspaceFolder[], stamp: ServiceEndpointCategory, envId: string, telemetry: ITelemetry): Promise { - - const websiteRecordId = getWebsiteRecordID(workspaceFolders, telemetry); - const websiteDetails = await PPAPIService.getWebsiteDetailsByWebsiteRecordId(stamp, envId, websiteRecordId, _telemetry); - return websiteDetails?.websiteUrl || ""; -} - -function isSiteRuntimePreviewEnabled() { - const enableSiteRuntimePreview = ECSFeaturesClient.getConfig(EnableSiteRuntimePreview).enableSiteRuntimePreview - - if(enableSiteRuntimePreview === undefined) { - return false; - } - - return enableSiteRuntimePreview; -} - function didOpenTextDocument(document: vscode.TextDocument): void { // The debug options for the server // --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging diff --git a/src/client/runtimeSitePreview/LaunchJsonHelper.ts b/src/client/runtimeSitePreview/LaunchJsonHelper.ts index 0ee52f41a..f592aef4d 100644 --- a/src/client/runtimeSitePreview/LaunchJsonHelper.ts +++ b/src/client/runtimeSitePreview/LaunchJsonHelper.ts @@ -9,7 +9,8 @@ export async function updateLaunchJsonConfig(url: string): Promise { const workspaceFolders = vscode.workspace.workspaceFolders; if (!workspaceFolders) { - vscode.window.showErrorMessage('No workspace folder is open.'); + vscode.window.showErrorMessage( + vscode.l10n.t('No workspace folder is open.')); return; } @@ -82,7 +83,8 @@ export async function updateLaunchJsonConfig(url: string): Promise { await vscode.workspace.fs.writeFile(launchJsonPath, Buffer.from(launchJsonContent, 'utf8')); } catch (e) { if(e instanceof Error) { - vscode.window.showErrorMessage(`Failed to update launch.json: ${e.message}`); + vscode.window.showErrorMessage( + vscode.l10n.t(`Failed to update launch.json: ${e.message}`)); } } } diff --git a/src/client/runtimeSitePreview/PreviewSite.ts b/src/client/runtimeSitePreview/PreviewSite.ts index eeac06ab2..a4cb1cf11 100644 --- a/src/client/runtimeSitePreview/PreviewSite.ts +++ b/src/client/runtimeSitePreview/PreviewSite.ts @@ -7,9 +7,33 @@ import * as vscode from 'vscode'; import * as path from 'path'; import * as fs from 'fs'; import { updateLaunchJsonConfig } from './LaunchJsonHelper'; +import { ECSFeaturesClient } from '../../common/ecs-features/ecsFeatureClient'; +import { EnableSiteRuntimePreview } from '../../common/ecs-features/ecsFeatureGates'; +import { ITelemetry } from '../../common/OneDSLoggerTelemetry/telemetry/ITelemetry'; +import { WorkspaceFolder } from 'vscode-languageclient/node'; +import { getWebsiteRecordID } from '../../common/utilities/WorkspaceInfoFinderUtil'; +import { ServiceEndpointCategory } from '../../common/services/Constants'; +import { PPAPIService } from '../../common/services/PPAPIService'; export class PreviewSite { + static isSiteRuntimePreviewEnabled(): boolean { + const enableSiteRuntimePreview = ECSFeaturesClient.getConfig(EnableSiteRuntimePreview).enableSiteRuntimePreview + + if(enableSiteRuntimePreview === undefined) { + return false; + } + + return enableSiteRuntimePreview; + } + + static async getWebSiteURL(workspaceFolders: WorkspaceFolder[], stamp: ServiceEndpointCategory, envId: string, telemetry: ITelemetry): Promise { + + const websiteRecordId = getWebsiteRecordID(workspaceFolders, telemetry); + const websiteDetails = await PPAPIService.getWebsiteDetailsByWebsiteRecordId(stamp, envId, websiteRecordId, telemetry); + return websiteDetails?.websiteUrl || ""; + } + static async launchBrowserAndDevToolsWithinVsCode(webSitePreviewURL: string): Promise { const edgeToolsExtensionId = 'ms-edgedevtools.vscode-edge-devtools'; @@ -59,9 +83,10 @@ export class PreviewSite { } } else { const install = await vscode.window.showWarningMessage( + vscode.l10n.t( `The extension "${edgeToolsExtensionId}" is required to run this command. Do you want to install it now?`, 'Install', 'Cancel' - ); + )); if (install === 'Install') { // Open the Extensions view with the specific extension diff --git a/src/common/OneDSLoggerTelemetry/telemetry/telemetry.ts b/src/common/OneDSLoggerTelemetry/telemetry/telemetry.ts index 8275fa0fa..1975fb82f 100644 --- a/src/common/OneDSLoggerTelemetry/telemetry/telemetry.ts +++ b/src/common/OneDSLoggerTelemetry/telemetry/telemetry.ts @@ -18,6 +18,7 @@ export const CleanupRelatedFilesEvent = 'CleanupRelatedFilesEvent'; export const UpdateEntityNameInYmlEvent = 'UpdateEntityNameInYmlEvent'; export const UserFileCreateEvent = 'UserFileCreateEvent'; export const FileCreateEvent = 'FileCreateEvent'; +export const GetWebsiteRecordID = 'getWebsiteRecordID'; interface ITelemetryData { eventName: string, diff --git a/src/common/services/PPAPIService.ts b/src/common/services/PPAPIService.ts index 0a1ef9fc5..e26223b87 100644 --- a/src/common/services/PPAPIService.ts +++ b/src/common/services/PPAPIService.ts @@ -5,8 +5,8 @@ import { ITelemetry } from "../OneDSLoggerTelemetry/telemetry/ITelemetry"; import { getCommonHeaders, powerPlatformAPIAuthentication } from "./AuthenticationProvider"; -import { VSCODE_EXTENSION_GET_CROSS_GEO_DATA_MOVEMENT_ENABLED_FLAG_FAILED, VSCODE_EXTENSION_GET_PPAPI_WEBSITES_ENDPOINT_UNSUPPORTED_REGION, VSCODE_EXTENSION_PPAPI_GET_WEBSITE_BY_ID_COMPLETED } from "./TelemetryConstants"; -import { ServiceEndpointCategory, PPAPI_WEBSITES_ENDPOINT, PPAPI_WEBSITES_API_VERSION } from "./Constants"; +import { VSCODE_EXTENSION_SERVICE_STAMP_NOT_FOUND, VSCODE_EXTENSION_GET_CROSS_GEO_DATA_MOVEMENT_ENABLED_FLAG_FAILED, VSCODE_EXTENSION_GET_PPAPI_WEBSITES_ENDPOINT_UNSUPPORTED_REGION, +VSCODE_EXTENSION_PPAPI_GET_WEBSITE_BY_ID_COMPLETED, VSCODE_EXTENSION_PPAPI_GET_WEBSITE_DETAILS_FAILED, VSCODE_EXTENSION_PPAPI_GET_WEBSITE_BY_RECORD_ID_COMPLETED } from "./TelemetryConstants";import { ServiceEndpointCategory, PPAPI_WEBSITES_ENDPOINT, PPAPI_WEBSITES_API_VERSION } from "./Constants"; import { sendTelemetryEvent } from "../copilot/telemetry/copilotTelemetry"; import { IWebsiteDetails } from "./Interfaces"; @@ -35,11 +35,16 @@ export class PPAPIService { public static async getWebsiteDetailsByWebsiteRecordId(serviceEndpointStamp: ServiceEndpointCategory, environmentId: string, websiteRecordId: string, telemetry: ITelemetry): Promise { + if (!serviceEndpointStamp) { + sendTelemetryEvent(telemetry, { eventName: VSCODE_EXTENSION_SERVICE_STAMP_NOT_FOUND, data: serviceEndpointStamp }); + return null; + } + const websiteDetailsArray = await PPAPIService.getWebsiteDetails(serviceEndpointStamp, environmentId, telemetry); const websiteDetails = websiteDetailsArray?.find((website) => website.websiteRecordId === websiteRecordId); if (websiteDetails) { - sendTelemetryEvent(telemetry, { eventName: VSCODE_EXTENSION_PPAPI_GET_WEBSITE_BY_ID_COMPLETED, orgUrl: websiteDetails.dataverseInstanceUrl }); + sendTelemetryEvent(telemetry, { eventName: VSCODE_EXTENSION_PPAPI_GET_WEBSITE_BY_RECORD_ID_COMPLETED, orgUrl: websiteDetails.dataverseInstanceUrl }); return websiteDetails; } return null; @@ -59,7 +64,7 @@ export class PPAPIService { } } catch (error) { - sendTelemetryEvent(telemetry, { eventName: VSCODE_EXTENSION_GET_CROSS_GEO_DATA_MOVEMENT_ENABLED_FLAG_FAILED, errorMsg: (error as Error).message }); + sendTelemetryEvent(telemetry, { eventName: VSCODE_EXTENSION_PPAPI_GET_WEBSITE_DETAILS_FAILED, errorMsg: (error as Error).message }); } return null; } diff --git a/src/common/services/TelemetryConstants.ts b/src/common/services/TelemetryConstants.ts index 18615c9b2..c8ea054b0 100644 --- a/src/common/services/TelemetryConstants.ts +++ b/src/common/services/TelemetryConstants.ts @@ -26,3 +26,6 @@ export const VSCODE_EXTENSION_PPAPI_GET_WEBSITE_BY_ID_COMPLETED = "VSCodeExtensi export const VSCODE_EXTENSION_PPAPI_GET_WEBSITE_BY_ID_FAILED = "VSCodeExtensionPPAPIGetWebsiteByIdFailed"; export const VSCODE_EXTENSION_GET_ENV_LIST_SUCCESS = "VSCodeExtensionGetEnvListSuccess"; export const VSCODE_EXTENSION_GET_ENV_LIST_FAILED = "VSCodeExtensionGetEnvListFailed"; +export const VSCODE_EXTENSION_SERVICE_STAMP_NOT_FOUND = "VSCodeExtensionServiceStampNotFound"; +export const VSCODE_EXTENSION_PPAPI_GET_WEBSITE_DETAILS_FAILED = "VSCodeExtensionPPAPIGetWebsiteDetailsFailed"; +export const VSCODE_EXTENSION_PPAPI_GET_WEBSITE_BY_RECORD_ID_COMPLETED = "VSCodeExtensionPPAPIGetWebsiteByRecordIdCompleted"; diff --git a/src/common/utilities/WorkspaceInfoFinderUtil.ts b/src/common/utilities/WorkspaceInfoFinderUtil.ts index ec8256260..4d60e91ab 100644 --- a/src/common/utilities/WorkspaceInfoFinderUtil.ts +++ b/src/common/utilities/WorkspaceInfoFinderUtil.ts @@ -11,7 +11,7 @@ import * as path from 'path'; import * as fs from 'fs'; import { parse } from 'yaml'; import { ITelemetry } from '../OneDSLoggerTelemetry/telemetry/ITelemetry'; -import { sendTelemetryEvent } from '../OneDSLoggerTelemetry/telemetry/telemetry'; +import { GetWebsiteRecordID, sendTelemetryEvent } from '../OneDSLoggerTelemetry/telemetry/telemetry'; export function getPortalsOrgURLs(workspaceRootFolders: WorkspaceFolder[] | null, telemetry: ITelemetry) { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -56,7 +56,7 @@ export function getWebsiteRecordID(workspaceFolders: { uri: string }[], telemetr } } } catch (exception) { - sendTelemetryEvent(telemetry, { methodName: getWebsiteRecordID.name, eventName: 'getWebsiteRecordID', exception: exception as Error }); + sendTelemetryEvent(telemetry, { methodName: getWebsiteRecordID.name, eventName: GetWebsiteRecordID, exception: exception as Error }); } return ""; } From e342730e91fbd637604d6669d80266ec44d64205 Mon Sep 17 00:00:00 2001 From: Ashwani Kumar Date: Tue, 5 Nov 2024 12:47:50 +0530 Subject: [PATCH 03/10] fixed build failures and handled empty website recordID --- l10n/bundle.l10n.json | 4 ++ .../vscode-powerplatform.xlf | 37 ++++++++++++------- src/client/extension.ts | 5 +-- .../runtimeSitePreview/LaunchJsonHelper.ts | 2 +- src/client/runtimeSitePreview/PreviewSite.ts | 11 +++++- src/common/services/TelemetryConstants.ts | 1 + 6 files changed, 39 insertions(+), 21 deletions(-) diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index 36ca2d229..9c8ad9da3 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -117,6 +117,10 @@ "Do not translate 'dotnet' or 'sdk'" ] }, + "The extension Microsoft Edge Tools is required to run this command. Do you want to install it now?": "The extension Microsoft Edge Tools is required to run this command. Do you want to install it now?", + "Install": "Install", + "No workspace folder is open.": "No workspace folder is open.", + "Failed to update launch.json: ${0}": "Failed to update launch.json: ${0}", "File might be referenced by name {0} here./{0} represents the name of the file": { "message": "File might be referenced by name {0} here.", "comment": [ diff --git a/loc/translations-export/vscode-powerplatform.xlf b/loc/translations-export/vscode-powerplatform.xlf index 5cba25db7..8ce799cf2 100644 --- a/loc/translations-export/vscode-powerplatform.xlf +++ b/loc/translations-export/vscode-powerplatform.xlf @@ -217,6 +217,9 @@ Return to this chat and @powerpages can help you write and edit your website cod Insert code into editor + + Install + Installing Power Pages generator(v{0})... {0} represents the version number @@ -294,6 +297,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID) No workspace folder found + + No workspace folder is open. + One or more attribute names have been changed or removed. Contact your admin. @@ -391,6 +397,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID) The Power Pages generator is ready for use in your VS Code extension! + + The extension Microsoft Edge Tools is required to run this command. Do you want to install it now? + The name you want to give to this authentication profile @@ -490,8 +499,8 @@ The {3} represents Dataverse Environment's Organization ID (GUID) Auth Profiles - Avoid accidental overwrites when you try to save outdated code in VS Code for the Web. You can compare the changes side-by-side and decide to accept or revert the changes. - + Avoid accidental overwrites when you try to save outdated code in VS Code for the Web. You can compare the changes side-by-side and decide to accept or revert the changes. + To learn more, visit [Prevent accidental overwrites](command:powerplatform-walkthrough.saveConflict-learn-more). This is a Markdown formatted string, and the formatting must persist across translations. The fifth line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.saveConflict-learn-more).', keeping brackets and the text in the parentheses unmodified @@ -554,12 +563,12 @@ The fifth line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.s File explorer - Find your page files organized in folders under your site name. All your pages are arranged into HTML, CSS, and JS files within the respective site component folders. - - You can find site components like forms, content snippets, lists, and more to edit. - - To learn more, visit [Power Pages file structure in VS Code](command:powerplatform-walkthrough.fileSystem-documentation). - + Find your page files organized in folders under your site name. All your pages are arranged into HTML, CSS, and JS files within the respective site component folders. + + You can find site components like forms, content snippets, lists, and more to edit. + + To learn more, visit [Power Pages file structure in VS Code](command:powerplatform-walkthrough.fileSystem-documentation). + [Visit site folder](command:powerplatform-walkthrough.fileSystem-open-folder) This is a Markdown formatted string, and the formatting must persist across translations. The seventh line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.fileSystem-documentation).', keeping brackets and the text in the parentheses unmodified @@ -593,7 +602,7 @@ The second line should be '[TRANSLATION HERE](command:pacCLI.authPanel.newAuthPr Now easily edit code of your Power Pages site. Access code from supported site components like forms, content snippets, lists, and more from within Visual Studio Code. - + To learn more, visit [Edit Power Pages code in VS Code](command:powerplatform-walkthrough.overview-learn-more). This is a Markdown formatted string, and the formatting must persist across translations. The second line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.overview-learn-more)', keeping brackets and the text in the parentheses unmodified @@ -649,11 +658,11 @@ The second line should be '[TRANSLATION HERE](command:powerplatform-walkthrough. Visual Studio Code for Web enables editing and publishing of web pages on your website. - - For a command line interface and more advanced capabilities, install the Power Platform Extension for VS Code, available in the VS Code Marketplace for desktop. - + + For a command line interface and more advanced capabilities, install the Power Platform Extension for VS Code, available in the VS Code Marketplace for desktop. + [Learn More](command:powerplatform-walkthrough.advancedCapabilities-learn-more) about the difference between Visual Studio Code for desktop and web. - + [Start coding](command:powerplatform-walkthrough.advancedCapabilities-start-coding) This is a Markdown formatted string, and the formatting must persist across translations. The fifth line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.advancedCapabilities-learn-more) TRANSLATION', keeping brackets and the text in the parentheses unmodified @@ -663,4 +672,4 @@ The seventh line should be '[TRANSLATION HERE](command:powerplatform-walkthrough Which Azure Cloud Power Platform Tools should use for authentication. - \ No newline at end of file + diff --git a/src/client/extension.ts b/src/client/extension.ts index f023958cf..e46ddeb58 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -40,15 +40,12 @@ import { ActiveOrgOutput } from "./pac/PacTypes"; import { desktopTelemetryEventNames } from "../common/OneDSLoggerTelemetry/client/desktopExtensionTelemetryEventNames"; import { ArtemisService } from "../common/services/ArtemisService"; import { workspaceContainsPortalConfigFolder } from "../common/utilities/PathFinderUtil"; -import { getPortalsOrgURLs, getWebsiteRecordID } from "../common/utilities/WorkspaceInfoFinderUtil"; +import { getPortalsOrgURLs } from "../common/utilities/WorkspaceInfoFinderUtil"; 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 { ServiceEndpointCategory } from "../common/services/Constants"; -import { PPAPIService } from "../common/services/PPAPIService"; -import { EnableSiteRuntimePreview } from "../common/ecs-features/ecsFeatureGates"; import { PreviewSite } from "./runtimeSitePreview/PreviewSite"; let client: LanguageClient; diff --git a/src/client/runtimeSitePreview/LaunchJsonHelper.ts b/src/client/runtimeSitePreview/LaunchJsonHelper.ts index f592aef4d..9fc29b20f 100644 --- a/src/client/runtimeSitePreview/LaunchJsonHelper.ts +++ b/src/client/runtimeSitePreview/LaunchJsonHelper.ts @@ -84,7 +84,7 @@ export async function updateLaunchJsonConfig(url: string): Promise { } catch (e) { if(e instanceof Error) { vscode.window.showErrorMessage( - vscode.l10n.t(`Failed to update launch.json: ${e.message}`)); + vscode.l10n.t("Failed to update launch.json: ${0}", e.message)); } } } diff --git a/src/client/runtimeSitePreview/PreviewSite.ts b/src/client/runtimeSitePreview/PreviewSite.ts index a4cb1cf11..f55f08d2f 100644 --- a/src/client/runtimeSitePreview/PreviewSite.ts +++ b/src/client/runtimeSitePreview/PreviewSite.ts @@ -14,6 +14,7 @@ import { WorkspaceFolder } from 'vscode-languageclient/node'; import { getWebsiteRecordID } from '../../common/utilities/WorkspaceInfoFinderUtil'; import { ServiceEndpointCategory } from '../../common/services/Constants'; import { PPAPIService } from '../../common/services/PPAPIService'; +import { VSCODE_EXTENSION_GET_WEBSITE_RECORD_ID_EMPTY } from '../../common/services/TelemetryConstants'; export class PreviewSite { @@ -30,6 +31,12 @@ export class PreviewSite { 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, { + websiteRecordId: websiteRecordId + }); + return ""; + } const websiteDetails = await PPAPIService.getWebsiteDetailsByWebsiteRecordId(stamp, envId, websiteRecordId, telemetry); return websiteDetails?.websiteUrl || ""; } @@ -84,11 +91,11 @@ export class PreviewSite { } else { const install = await vscode.window.showWarningMessage( vscode.l10n.t( - `The extension "${edgeToolsExtensionId}" is required to run this command. Do you want to install it now?`, + `The extension Microsoft Edge Tools is required to run this command. Do you want to install it now?`, 'Install', 'Cancel' )); - if (install === 'Install') { + if (install === vscode.l10n.t('Install')) { // Open the Extensions view with the specific extension vscode.commands.executeCommand('workbench.extensions.search', edgeToolsExtensionId); } diff --git a/src/common/services/TelemetryConstants.ts b/src/common/services/TelemetryConstants.ts index c8ea054b0..2db83b5d0 100644 --- a/src/common/services/TelemetryConstants.ts +++ b/src/common/services/TelemetryConstants.ts @@ -29,3 +29,4 @@ export const VSCODE_EXTENSION_GET_ENV_LIST_FAILED = "VSCodeExtensionGetEnvListFa export const VSCODE_EXTENSION_SERVICE_STAMP_NOT_FOUND = "VSCodeExtensionServiceStampNotFound"; export const VSCODE_EXTENSION_PPAPI_GET_WEBSITE_DETAILS_FAILED = "VSCodeExtensionPPAPIGetWebsiteDetailsFailed"; export const VSCODE_EXTENSION_PPAPI_GET_WEBSITE_BY_RECORD_ID_COMPLETED = "VSCodeExtensionPPAPIGetWebsiteByRecordIdCompleted"; +export const VSCODE_EXTENSION_GET_WEBSITE_RECORD_ID_EMPTY = "VSCodeExtensionGetWebsiteRecordIdEmpty"; From 9fd0f9279ef40a10b99294e918bafc3452779f3d Mon Sep 17 00:00:00 2001 From: Ashwani Kumar Date: Fri, 8 Nov 2024 17:01:27 +0530 Subject: [PATCH 04/10] Corrected website details ppapi service for runtime preview --- src/common/services/PPAPIService.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/common/services/PPAPIService.ts b/src/common/services/PPAPIService.ts index e26223b87..7862cdf5e 100644 --- a/src/common/services/PPAPIService.ts +++ b/src/common/services/PPAPIService.ts @@ -33,15 +33,17 @@ export class PPAPIService { return null; } - public static async getWebsiteDetailsByWebsiteRecordId(serviceEndpointStamp: ServiceEndpointCategory, environmentId: string, websiteRecordId: string, telemetry: ITelemetry): Promise { + public static async getWebsiteDetailsByWebsiteRecordId(serviceEndpointStamp: ServiceEndpointCategory | undefined, environmentId: string, websiteRecordId: string, telemetry: ITelemetry): Promise { if (!serviceEndpointStamp) { sendTelemetryEvent(telemetry, { eventName: VSCODE_EXTENSION_SERVICE_STAMP_NOT_FOUND, data: serviceEndpointStamp }); return null; } - const websiteDetailsArray = await PPAPIService.getWebsiteDetails(serviceEndpointStamp, environmentId, telemetry); - const websiteDetails = websiteDetailsArray?.find((website) => website.websiteRecordId === websiteRecordId); + const websiteDetailsResponse = await PPAPIService.getWebsiteDetails(serviceEndpointStamp, environmentId, telemetry); + const websiteDetailsArray = websiteDetailsResponse?.value; // Access all the websites + const websiteDetails = websiteDetailsArray?.find((website) => website.websiteRecordId === websiteRecordId); // selecting 1st websiteDetails whose websiteRecordId matches + if (websiteDetails) { sendTelemetryEvent(telemetry, { eventName: VSCODE_EXTENSION_PPAPI_GET_WEBSITE_BY_RECORD_ID_COMPLETED, orgUrl: websiteDetails.dataverseInstanceUrl }); @@ -50,16 +52,16 @@ export class PPAPIService { return null; } - static async getWebsiteDetails(serviceEndpointStamp: ServiceEndpointCategory, environmentId: string, telemetry: ITelemetry): Promise { + static async getWebsiteDetails(serviceEndpointStamp: ServiceEndpointCategory, environmentId: string, telemetry: ITelemetry): Promise<{ value: IWebsiteDetails[] } | null> { try { - const accessToken = await powerPlatformAPIAuthentication(telemetry, true); + const accessToken = await powerPlatformAPIAuthentication(telemetry, serviceEndpointStamp, true); const response = await fetch(await PPAPIService.getPPAPIServiceEndpoint(serviceEndpointStamp, telemetry, environmentId), { method: 'GET', headers: getCommonHeaders(accessToken) }); if (response.ok) { - const websiteDetailsArray = await response.json() as unknown as IWebsiteDetails[]; + const websiteDetailsArray = await response.json() as { value: IWebsiteDetails[] }; return websiteDetailsArray; } } From 7c88ce6f17b62ff8c9737d0cdee540dd3b27a588 Mon Sep 17 00:00:00 2001 From: Ashwani Kumar Date: Wed, 27 Nov 2024 13:31:16 +0530 Subject: [PATCH 05/10] merged main branch to runtimePreviewECS --- .../vscode-powerplatform.xlf | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/loc/translations-export/vscode-powerplatform.xlf b/loc/translations-export/vscode-powerplatform.xlf index 8ce799cf2..7fe5d1f2e 100644 --- a/loc/translations-export/vscode-powerplatform.xlf +++ b/loc/translations-export/vscode-powerplatform.xlf @@ -143,6 +143,9 @@ The {3} represents Solution's Type (Managed or Unmanaged), but that test is loca Explain the following code {% include 'Page Copy'%} + + Failed to create a new Power Pages site. Please try again. + Failed to create: {0}. {0} will be replaced by the error message. @@ -159,8 +162,8 @@ The {3} represents Solution's Type (Managed or Unmanaged), but that test is loca Failed to get file ready for edit: {0} - - Failed to get site content from NL2Site service + + Failed to update launch.json: ${0} Feature is not enabled for this geo. @@ -499,8 +502,8 @@ The {3} represents Dataverse Environment's Organization ID (GUID) Auth Profiles - Avoid accidental overwrites when you try to save outdated code in VS Code for the Web. You can compare the changes side-by-side and decide to accept or revert the changes. - + Avoid accidental overwrites when you try to save outdated code in VS Code for the Web. You can compare the changes side-by-side and decide to accept or revert the changes. + To learn more, visit [Prevent accidental overwrites](command:powerplatform-walkthrough.saveConflict-learn-more). This is a Markdown formatted string, and the formatting must persist across translations. The fifth line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.saveConflict-learn-more).', keeping brackets and the text in the parentheses unmodified @@ -563,12 +566,12 @@ The fifth line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.s File explorer - Find your page files organized in folders under your site name. All your pages are arranged into HTML, CSS, and JS files within the respective site component folders. - - You can find site components like forms, content snippets, lists, and more to edit. - - To learn more, visit [Power Pages file structure in VS Code](command:powerplatform-walkthrough.fileSystem-documentation). - + Find your page files organized in folders under your site name. All your pages are arranged into HTML, CSS, and JS files within the respective site component folders. + + You can find site components like forms, content snippets, lists, and more to edit. + + To learn more, visit [Power Pages file structure in VS Code](command:powerplatform-walkthrough.fileSystem-documentation). + [Visit site folder](command:powerplatform-walkthrough.fileSystem-open-folder) This is a Markdown formatted string, and the formatting must persist across translations. The seventh line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.fileSystem-documentation).', keeping brackets and the text in the parentheses unmodified @@ -602,7 +605,7 @@ The second line should be '[TRANSLATION HERE](command:pacCLI.authPanel.newAuthPr Now easily edit code of your Power Pages site. Access code from supported site components like forms, content snippets, lists, and more from within Visual Studio Code. - + To learn more, visit [Edit Power Pages code in VS Code](command:powerplatform-walkthrough.overview-learn-more). This is a Markdown formatted string, and the formatting must persist across translations. The second line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.overview-learn-more)', keeping brackets and the text in the parentheses unmodified @@ -658,11 +661,11 @@ The second line should be '[TRANSLATION HERE](command:powerplatform-walkthrough. Visual Studio Code for Web enables editing and publishing of web pages on your website. - - For a command line interface and more advanced capabilities, install the Power Platform Extension for VS Code, available in the VS Code Marketplace for desktop. - + + For a command line interface and more advanced capabilities, install the Power Platform Extension for VS Code, available in the VS Code Marketplace for desktop. + [Learn More](command:powerplatform-walkthrough.advancedCapabilities-learn-more) about the difference between Visual Studio Code for desktop and web. - + [Start coding](command:powerplatform-walkthrough.advancedCapabilities-start-coding) This is a Markdown formatted string, and the formatting must persist across translations. The fifth line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.advancedCapabilities-learn-more) TRANSLATION', keeping brackets and the text in the parentheses unmodified @@ -672,4 +675,4 @@ The seventh line should be '[TRANSLATION HERE](command:powerplatform-walkthrough Which Azure Cloud Power Platform Tools should use for authentication. - + \ No newline at end of file From 086105de7d7bc6d315242670db96a66c66a5104b Mon Sep 17 00:00:00 2001 From: Ashwani Kumar Date: Wed, 27 Nov 2024 14:57:30 +0530 Subject: [PATCH 06/10] correcting function call for runtime preview --- src/client/extension.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/client/extension.ts b/src/client/extension.ts index e46ddeb58..200fc0ebd 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -239,6 +239,7 @@ export async function activate( if(artemisResponse!==null && PreviewSite.isSiteRuntimePreviewEnabled()) { websiteURL = await PreviewSite.getWebSiteURL(workspaceFolders, artemisResponse?.stamp, orgDetails.EnvironmentId, _telemetry); } + await registerPreviewShowCommand(); }) ); @@ -298,9 +299,6 @@ export async function activate( } }; - await registerPreviewShowCommand(); - - const workspaceFolderWatcher = vscode.workspace.onDidChangeWorkspaceFolders(handleWorkspaceFolderChange); _context.subscriptions.push(workspaceFolderWatcher); From 52048a9fa653db380144e3d8be72e9d97aa65e5d Mon Sep 17 00:00:00 2001 From: Ashwani Kumar Date: Mon, 9 Dec 2024 15:43:35 +0530 Subject: [PATCH 07/10] corrected localised error --- l10n/bundle.l10n.json | 4 ++-- src/client/extension.ts | 4 ++-- .../LaunchJsonHelper.ts | 0 .../PreviewSite.ts | 8 +++++--- 4 files changed, 9 insertions(+), 7 deletions(-) rename src/client/{runtimeSitePreview => runtime-site-preview}/LaunchJsonHelper.ts (100%) rename src/client/{runtimeSitePreview => runtime-site-preview}/PreviewSite.ts (95%) diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index 9c8ad9da3..fdbee6be1 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -83,9 +83,9 @@ "Hi! Power Pages lets you build secure, professional websites that you can quickly configure and publish across web browsers and devices.\n\nTo create your website, visit the [Power Pages](https://powerpages.microsoft.com/).\nReturn to this chat and @powerpages can help you write and edit your website code.": "Hi! Power Pages lets you build secure, professional websites that you can quickly configure and publish across web browsers and devices.\n\nTo create your website, visit the [Power Pages](https://powerpages.microsoft.com/).\nReturn to this chat and @powerpages can help you write and edit your website code.", "Checking for active auth profile...": "Checking for active auth profile...", "@PowerPages is not yet available in your region.": "@PowerPages is not yet available in your region.", - "Failed to get site content from NL2Site service": "Failed to get site content from NL2Site service", "Generating webpages...": "Generating webpages...", "Generating a new Power Pages site...": "Generating a new Power Pages site...", + "Failed to create a new Power Pages site. Please try again.": "Failed to create a new Power Pages site. Please try again.", "Select Folder for new PCF Control/Do not translate 'PCF' as it is a product name.": { "message": "Select Folder for new PCF Control", "comment": [ @@ -119,6 +119,7 @@ }, "The extension Microsoft Edge Tools is required to run this command. Do you want to install it now?": "The extension Microsoft Edge Tools is required to run this command. Do you want to install it now?", "Install": "Install", + "Cancel": "Cancel", "No workspace folder is open.": "No workspace folder is open.", "Failed to update launch.json: ${0}": "Failed to update launch.json: ${0}", "File might be referenced by name {0} here./{0} represents the name of the file": { @@ -223,7 +224,6 @@ }, "Confirm": "Confirm", "Are you sure you want to clear all the Auth Profiles?": "Are you sure you want to clear all the Auth Profiles?", - "Cancel": "Cancel", "Are you sure you want to delete the Auth Profile {0}-{1}?/{0} is the user name, {1} is the URL of environment of the auth profile": { "message": "Are you sure you want to delete the Auth Profile {0}-{1}?", "comment": [ diff --git a/src/client/extension.ts b/src/client/extension.ts index 200fc0ebd..1467b97b0 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -46,7 +46,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 { PreviewSite } from "./runtimeSitePreview/PreviewSite"; +import { PreviewSite } from "./runtime-site-preview/PreviewSite"; let client: LanguageClient; let _context: vscode.ExtensionContext; @@ -236,7 +236,7 @@ export async function activate( copilotNotificationShown = true; } - if(artemisResponse!==null && PreviewSite.isSiteRuntimePreviewEnabled()) { + if(artemisResponse !== null && PreviewSite.isSiteRuntimePreviewEnabled()) { websiteURL = await PreviewSite.getWebSiteURL(workspaceFolders, artemisResponse?.stamp, orgDetails.EnvironmentId, _telemetry); } await registerPreviewShowCommand(); diff --git a/src/client/runtimeSitePreview/LaunchJsonHelper.ts b/src/client/runtime-site-preview/LaunchJsonHelper.ts similarity index 100% rename from src/client/runtimeSitePreview/LaunchJsonHelper.ts rename to src/client/runtime-site-preview/LaunchJsonHelper.ts diff --git a/src/client/runtimeSitePreview/PreviewSite.ts b/src/client/runtime-site-preview/PreviewSite.ts similarity index 95% rename from src/client/runtimeSitePreview/PreviewSite.ts rename to src/client/runtime-site-preview/PreviewSite.ts index f55f08d2f..2fd34e540 100644 --- a/src/client/runtimeSitePreview/PreviewSite.ts +++ b/src/client/runtime-site-preview/PreviewSite.ts @@ -91,9 +91,11 @@ export class PreviewSite { } else { const install = await vscode.window.showWarningMessage( vscode.l10n.t( - `The extension Microsoft Edge Tools is required to run this command. Do you want to install it now?`, - 'Install', 'Cancel' - )); + `The extension Microsoft Edge Tools is required to run this command. Do you want to install it now?` + ), + vscode.l10n.t('Install'), + vscode.l10n.t('Cancel') + ); if (install === vscode.l10n.t('Install')) { // Open the Extensions view with the specific extension From 5b8eefdb836cdb9ded40e9c9d9f180078acce635 Mon Sep 17 00:00:00 2001 From: Ashwani Kumar Date: Mon, 9 Dec 2024 16:46:50 +0530 Subject: [PATCH 08/10] removed bug of no show command found --- l10n/bundle.l10n.json | 1 + .../vscode-powerplatform.xlf | 3 + src/client/extension.ts | 63 ++++++++----------- src/common/services/PPAPIService.ts | 8 +++ 4 files changed, 39 insertions(+), 36 deletions(-) diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index fdbee6be1..4215ccd56 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -33,6 +33,7 @@ }, "Enter the environment URL": "Enter the environment URL", "Active auth profile is not found or has expired. To create a new auth profile, enter the environment URL.": "Active auth profile is not found or has expired. To create a new auth profile, enter the environment URL.", + "Website not found in the environment. Please check the credentials and root folder path.": "Website not found in the environment. Please check the credentials and root folder path.", "Selection is empty.": "Selection is empty.", "PREVIEW": "PREVIEW", "Explain the following code snippet:": "Explain the following code snippet:", diff --git a/loc/translations-export/vscode-powerplatform.xlf b/loc/translations-export/vscode-powerplatform.xlf index 7fe5d1f2e..9f9c6f0fe 100644 --- a/loc/translations-export/vscode-powerplatform.xlf +++ b/loc/translations-export/vscode-powerplatform.xlf @@ -461,6 +461,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID) Webpage names should contain only letters, numbers, hyphens, or underscores. + + Website not found in the environment. Please check the credentials and root folder path. + What do you need help with? diff --git a/src/client/extension.ts b/src/client/extension.ts index 1467b97b0..249f1ef7a 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -239,7 +239,6 @@ export async function activate( if(artemisResponse !== null && PreviewSite.isSiteRuntimePreviewEnabled()) { websiteURL = await PreviewSite.getWebSiteURL(workspaceFolders, artemisResponse?.stamp, orgDetails.EnvironmentId, _telemetry); } - await registerPreviewShowCommand(); }) ); @@ -261,43 +260,35 @@ export async function activate( vscode.commands.executeCommand('setContext', 'powerpages.websiteYmlExists', false); } - const registerPreviewShowCommand = async () => { - const isEnabled = PreviewSite.isSiteRuntimePreviewEnabled(); + const isEnabled = PreviewSite.isSiteRuntimePreviewEnabled(); - _telemetry.sendTelemetryEvent("EnableSiteRuntimePreview", { - isEnabled: isEnabled.toString(), - websiteURL: websiteURL - }); - oneDSLoggerWrapper.getLogger().traceInfo("EnableSiteRuntimePreview", { - isEnabled: isEnabled.toString(), - websiteURL: websiteURL - }); + _telemetry.sendTelemetryEvent("EnableSiteRuntimePreview", { + isEnabled: isEnabled.toString(), + websiteURL: websiteURL + }); + oneDSLoggerWrapper.getLogger().traceInfo("EnableSiteRuntimePreview", { + isEnabled: isEnabled.toString(), + websiteURL: websiteURL + }); - if (!isEnabled || websiteURL === "") { - // portal web view panel - _context.subscriptions.push( - vscode.commands.registerCommand( - "microsoft-powerapps-portals.preview-show", - () => { - _telemetry.sendTelemetryEvent("StartCommand", { - commandId: "microsoft-powerapps-portals.preview-show", - }); - oneDSLoggerWrapper.getLogger().traceInfo("StartCommand", { - commandId: "microsoft-powerapps-portals.preview-show" - }); - PortalWebView.createOrShow(); - } - ) - ); - } else { - _context.subscriptions.push( - vscode.commands.registerCommand( - "microsoft-powerapps-portals.preview-show", - () => PreviewSite.launchBrowserAndDevToolsWithinVsCode(websiteURL) - ) - ); - } - }; + _context.subscriptions.push( + vscode.commands.registerCommand( + "microsoft-powerapps-portals.preview-show", + () => { + if (!isEnabled) { + _telemetry.sendTelemetryEvent("StartCommand", { + commandId: "microsoft-powerapps-portals.preview-show", + }); + oneDSLoggerWrapper.getLogger().traceInfo("StartCommand", { + commandId: "microsoft-powerapps-portals.preview-show" + }); + PortalWebView.createOrShow(); + } else { + PreviewSite.launchBrowserAndDevToolsWithinVsCode(websiteURL); + } + } + ) + ); const workspaceFolderWatcher = vscode.workspace.onDidChangeWorkspaceFolders(handleWorkspaceFolderChange); _context.subscriptions.push(workspaceFolderWatcher); diff --git a/src/common/services/PPAPIService.ts b/src/common/services/PPAPIService.ts index 7862cdf5e..438d3e271 100644 --- a/src/common/services/PPAPIService.ts +++ b/src/common/services/PPAPIService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. */ +import * as vscode from 'vscode'; import { ITelemetry } from "../OneDSLoggerTelemetry/telemetry/ITelemetry"; import { getCommonHeaders, powerPlatformAPIAuthentication } from "./AuthenticationProvider"; import { VSCODE_EXTENSION_SERVICE_STAMP_NOT_FOUND, VSCODE_EXTENSION_GET_CROSS_GEO_DATA_MOVEMENT_ENABLED_FLAG_FAILED, VSCODE_EXTENSION_GET_PPAPI_WEBSITES_ENDPOINT_UNSUPPORTED_REGION, @@ -48,6 +49,13 @@ export class PPAPIService { if (websiteDetails) { sendTelemetryEvent(telemetry, { eventName: VSCODE_EXTENSION_PPAPI_GET_WEBSITE_BY_RECORD_ID_COMPLETED, orgUrl: websiteDetails.dataverseInstanceUrl }); return websiteDetails; + } else { + // Showing a warning message when the website is not found in the environment + vscode.window.showWarningMessage( + vscode.l10n.t( + `Website not found in the environment. Please check the credentials and root folder path.` + ) + ); } return null; } From ac61d88b831965cfa549451b2f5cd284663d4664 Mon Sep 17 00:00:00 2001 From: Priyanshu Agrawal Date: Mon, 16 Dec 2024 20:36:15 +0530 Subject: [PATCH 09/10] Launch Edge new tab instead of launching project --- l10n/bundle.l10n.json | 56 ++++----- .../vscode-powerplatform.xlf | 19 ++- package.json | 4 + package.nls.json | 3 +- src/client/extension.ts | 46 ++++---- .../runtime-site-preview/LaunchJsonHelper.ts | 90 --------------- .../runtime-site-preview/PreviewSite.ts | 108 +++++++++--------- .../telemetry/telemetry.ts | 3 +- src/common/constants.ts | 1 + .../controls/ShowProgressNotification.ts | 19 +++ .../utilities/WorkspaceInfoFinderUtil.ts | 4 +- 11 files changed, 148 insertions(+), 205 deletions(-) delete mode 100644 src/client/runtime-site-preview/LaunchJsonHelper.ts create mode 100644 src/common/controls/ShowProgressNotification.ts diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index 4215ccd56..02f57d61b 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -93,36 +93,13 @@ "Do not translate 'PCF' as it is a product name." ] }, - "Preparing pac CLI (v{0}).../{0} represents the version number": { - "message": "Preparing pac CLI (v{0})...", - "comment": [ - "{0} represents the version number" - ] - }, - "The pac CLI is ready for use in your VS Code terminal!": "The pac CLI is ready for use in your VS Code terminal!", - "Cannot install pac CLI: {0}/{0} represents the error message returned from the exception": { - "message": "Cannot install pac CLI: {0}", - "comment": [ - "{0} represents the error message returned from the exception" - ] - }, - "Installing Power Pages generator(v{0}).../{0} represents the version number": { - "message": "Installing Power Pages generator(v{0})...", - "comment": [ - "{0} represents the version number" - ] - }, - "dotnet sdk 6.0 or greater must be installed/Do not translate 'dotnet' or 'sdk'": { - "message": "dotnet sdk 6.0 or greater must be installed", - "comment": [ - "Do not translate 'dotnet' or 'sdk'" - ] - }, "The extension Microsoft Edge Tools is required to run this command. Do you want to install it now?": "The extension Microsoft Edge Tools is required to run this command. Do you want to install it now?", "Install": "Install", "Cancel": "Cancel", - "No workspace folder is open.": "No workspace folder is open.", - "Failed to update launch.json: ${0}": "Failed to update launch.json: ${0}", + "Site runtime preview feature is not enabled.": "Site runtime preview feature is not enabled.", + "No workspace folder opened. Please open a site folder to preview.": "No workspace folder opened. Please open a site folder to preview.", + "Website URL not found.": "Website URL not found.", + "Opening site preview...": "Opening site preview...", "File might be referenced by name {0} here./{0} represents the name of the file": { "message": "File might be referenced by name {0} here.", "comment": [ @@ -194,6 +171,12 @@ "Do not translate 'npm'" ] }, + "Installing Power Pages generator(v{0}).../{0} represents the version number": { + "message": "Installing Power Pages generator(v{0})...", + "comment": [ + "{0} represents the version number" + ] + }, "Cannot install Power Pages generator: {0}/{0} represents the error message returned from the exception": { "message": "Cannot install Power Pages generator: {0}", "comment": [ @@ -223,6 +206,25 @@ "The {3} represents Dataverse Environment's Organization ID (GUID)" ] }, + "Preparing pac CLI (v{0}).../{0} represents the version number": { + "message": "Preparing pac CLI (v{0})...", + "comment": [ + "{0} represents the version number" + ] + }, + "The pac CLI is ready for use in your VS Code terminal!": "The pac CLI is ready for use in your VS Code terminal!", + "Cannot install pac CLI: {0}/{0} represents the error message returned from the exception": { + "message": "Cannot install pac CLI: {0}", + "comment": [ + "{0} represents the error message returned from the exception" + ] + }, + "dotnet sdk 6.0 or greater must be installed/Do not translate 'dotnet' or 'sdk'": { + "message": "dotnet sdk 6.0 or greater must be installed", + "comment": [ + "Do not translate 'dotnet' or 'sdk'" + ] + }, "Confirm": "Confirm", "Are you sure you want to clear all the Auth Profiles?": "Are you sure you want to clear all the Auth Profiles?", "Are you sure you want to delete the Auth Profile {0}-{1}?/{0} is the user name, {1} is the URL of environment of the auth profile": { diff --git a/loc/translations-export/vscode-powerplatform.xlf b/loc/translations-export/vscode-powerplatform.xlf index 9f9c6f0fe..ca709b390 100644 --- a/loc/translations-export/vscode-powerplatform.xlf +++ b/loc/translations-export/vscode-powerplatform.xlf @@ -162,9 +162,6 @@ The {3} represents Solution's Type (Managed or Unmanaged), but that test is loca Failed to get file ready for edit: {0} - - Failed to update launch.json: ${0} - Feature is not enabled for this geo. @@ -300,8 +297,8 @@ The {3} represents Dataverse Environment's Organization ID (GUID) No workspace folder found - - No workspace folder is open. + + No workspace folder opened. Please open a site folder to preview. One or more attribute names have been changed or removed. Contact your admin. @@ -312,6 +309,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID) Opening preview site... + + Opening site preview... + Operation failed. See output panel for details. @@ -385,6 +385,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID) Show Output Panel + + Site runtime preview feature is not enabled. + Some references might be broken. Please check diagnostics for details. @@ -461,6 +464,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID) Webpage names should contain only letters, numbers, hyphens, or underscores. + + Website URL not found. + Website not found in the environment. Please check the credentials and root folder path. @@ -632,6 +638,9 @@ The second line should be '[TRANSLATION HERE](command:powerplatform-walkthrough. PowerApps Portal -> Show preview + + Preview Site + Refresh diff --git a/package.json b/package.json index ecadf0b2c..40939fadc 100644 --- a/package.json +++ b/package.json @@ -370,6 +370,10 @@ "command": "powerPlatform.previewCurrentActiveUsers", "title": "Current Active Users", "icon": "$(person)" + }, + { + "command": "microsoft.powerplatform.pages.preview-site", + "title": "%powerplatform.pages.previewSite.title%" } ], "configuration": { diff --git a/package.nls.json b/package.nls.json index 70304abaf..e4affd923 100644 --- a/package.nls.json +++ b/package.nls.json @@ -97,5 +97,6 @@ "The fifth line should be '[TRANSLATION HERE](command:powerplatform-walkthrough.saveConflict-learn-more).', keeping brackets and the text in the parentheses unmodified" ] }, - "microsoft-powerplatform-portals.navigation-loop.powerPagesFileExplorer.title": "POWER PAGES ACTIONS" + "microsoft-powerplatform-portals.navigation-loop.powerPagesFileExplorer.title": "POWER PAGES ACTIONS", + "powerplatform.pages.previewSite.title": "Preview Site" } diff --git a/src/client/extension.ts b/src/client/extension.ts index 0a49712a3..1feac8c71 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -46,7 +46,7 @@ import { PowerPagesAppName, PowerPagesClientName } from "../common/ecs-features/ 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"; +import { PreviewSite, SITE_PREVIEW_COMMAND_ID } from "./runtime-site-preview/PreviewSite"; let client: LanguageClient; let _context: vscode.ExtensionContext; @@ -102,6 +102,22 @@ export async function activate( ); } + // portal web view panel + _context.subscriptions.push( + vscode.commands.registerCommand( + "microsoft-powerapps-portals.preview-show", + () => { + _telemetry.sendTelemetryEvent("StartCommand", { + commandId: "microsoft-powerapps-portals.preview-show", + }); + oneDSLoggerWrapper.getLogger().traceInfo("StartCommand", { + commandId: "microsoft-powerapps-portals.preview-show" + }); + PortalWebView.createOrShow(); + } + ) + ); + // registering bootstrapdiff command _context.subscriptions.push( vscode.commands.registerCommand('microsoft-powerapps-portals.bootstrap-diff', async () => { @@ -180,7 +196,8 @@ export async function activate( ) || []; - let websiteURL = ""; + let websiteURL: string | undefined = ""; + const isSiteRuntimePreviewEnabled = PreviewSite.isSiteRuntimePreviewEnabled(); _context.subscriptions.push( orgChangeEvent(async (orgDetails: ActiveOrgOutput) => { @@ -236,7 +253,8 @@ export async function activate( copilotNotificationShown = true; } - if(artemisResponse !== null && PreviewSite.isSiteRuntimePreviewEnabled()) { + + if (artemisResponse !== null && isSiteRuntimePreviewEnabled) { websiteURL = await PreviewSite.getWebSiteURL(workspaceFolders, artemisResponse?.stamp, orgDetails.EnvironmentId, _telemetry); } @@ -260,33 +278,19 @@ export async function activate( vscode.commands.executeCommand('setContext', 'powerpages.websiteYmlExists', false); } - const isEnabled = PreviewSite.isSiteRuntimePreviewEnabled(); - _telemetry.sendTelemetryEvent("EnableSiteRuntimePreview", { - isEnabled: isEnabled.toString(), + isEnabled: isSiteRuntimePreviewEnabled.toString(), websiteURL: websiteURL }); oneDSLoggerWrapper.getLogger().traceInfo("EnableSiteRuntimePreview", { - isEnabled: isEnabled.toString(), + isEnabled: isSiteRuntimePreviewEnabled.toString(), websiteURL: websiteURL }); _context.subscriptions.push( vscode.commands.registerCommand( - "microsoft-powerapps-portals.preview-show", - () => { - if (!isEnabled) { - _telemetry.sendTelemetryEvent("StartCommand", { - commandId: "microsoft-powerapps-portals.preview-show", - }); - oneDSLoggerWrapper.getLogger().traceInfo("StartCommand", { - commandId: "microsoft-powerapps-portals.preview-show" - }); - PortalWebView.createOrShow(); - } else { - PreviewSite.launchBrowserAndDevToolsWithinVsCode(websiteURL); - } - } + SITE_PREVIEW_COMMAND_ID, + async () => await PreviewSite.handlePreviewRequest(isSiteRuntimePreviewEnabled, websiteURL, _telemetry) ) ); diff --git a/src/client/runtime-site-preview/LaunchJsonHelper.ts b/src/client/runtime-site-preview/LaunchJsonHelper.ts deleted file mode 100644 index 9fc29b20f..000000000 --- a/src/client/runtime-site-preview/LaunchJsonHelper.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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'; - -export async function updateLaunchJsonConfig(url: string): Promise { - - const workspaceFolders = vscode.workspace.workspaceFolders; - if (!workspaceFolders) { - vscode.window.showErrorMessage( - vscode.l10n.t('No workspace folder is open.')); - return; - } - - const launchJsonPath = vscode.Uri.joinPath(workspaceFolders[0].uri, '.vscode', 'launch.json'); - try { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let launchJson: any; - let launchJsonDoc: vscode.TextDocument | undefined; - - try { - launchJsonDoc = await vscode.workspace.openTextDocument(launchJsonPath); - const launchJsonText = launchJsonDoc.getText(); - launchJson = launchJsonText ? JSON.parse(launchJsonText) : { configurations: [], compounds: [] }; - } catch (error) { - // If the file does not exist or is empty, initialize it - launchJson = { configurations: [], compounds: [] }; - } - - // Update or add the configurations for Microsoft Edge - const edgeConfigurations = [ - { - type: 'pwa-msedge', - name: 'Launch Microsoft Edge', - request: 'launch', - runtimeArgs: ['--remote-debugging-port=9222'], - url: url, - presentation: { - hidden: true - } - }, - { - type: 'pwa-msedge', - name: 'Launch Microsoft Edge in headless mode', - request: 'launch', - runtimeArgs: ['--headless', '--remote-debugging-port=9222'], - url: url, - presentation: { - hidden: true - } - }, - { - type: 'vscode-edge-devtools.debug', - name: 'Open Edge DevTools', - request: 'attach', - url: url, - presentation: { - hidden: true - } - } - ]; - - // Update or add the compounds for Microsoft Edge - const edgeCompounds = [ - { - name: 'Launch Edge Headless and attach DevTools', - configurations: ['Launch Microsoft Edge in headless mode', 'Open Edge DevTools'] - }, - { - name: 'Launch Edge and attach DevTools', - configurations: ['Launch Microsoft Edge', 'Open Edge DevTools'] - } - ]; - - // Merge the new configurations and compounds with the existing ones - launchJson.configurations = [...launchJson.configurations, ...edgeConfigurations]; - launchJson.compounds = [...launchJson.compounds, ...edgeCompounds]; - - // Write the updated launch.json file - const launchJsonContent = JSON.stringify(launchJson, null, 4); - await vscode.workspace.fs.writeFile(launchJsonPath, Buffer.from(launchJsonContent, 'utf8')); - } catch (e) { - if(e instanceof Error) { - vscode.window.showErrorMessage( - vscode.l10n.t("Failed to update launch.json: ${0}", e.message)); - } - } -} diff --git a/src/client/runtime-site-preview/PreviewSite.ts b/src/client/runtime-site-preview/PreviewSite.ts index 2fd34e540..dc5cbe13c 100644 --- a/src/client/runtime-site-preview/PreviewSite.ts +++ b/src/client/runtime-site-preview/PreviewSite.ts @@ -4,33 +4,33 @@ */ import * as vscode from 'vscode'; -import * as path from 'path'; -import * as fs from 'fs'; -import { updateLaunchJsonConfig } from './LaunchJsonHelper'; import { ECSFeaturesClient } from '../../common/ecs-features/ecsFeatureClient'; import { EnableSiteRuntimePreview } from '../../common/ecs-features/ecsFeatureGates'; import { ITelemetry } from '../../common/OneDSLoggerTelemetry/telemetry/ITelemetry'; import { WorkspaceFolder } from 'vscode-languageclient/node'; -import { getWebsiteRecordID } from '../../common/utilities/WorkspaceInfoFinderUtil'; +import { getWebsiteRecordId } from '../../common/utilities/WorkspaceInfoFinderUtil'; import { ServiceEndpointCategory } from '../../common/services/Constants'; 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 { showProgressNotification } from '../../common/controls/ShowProgressNotification'; -export class PreviewSite { +export const SITE_PREVIEW_COMMAND_ID = "microsoft.powerplatform.pages.preview-site"; +export class PreviewSite { static isSiteRuntimePreviewEnabled(): boolean { const enableSiteRuntimePreview = ECSFeaturesClient.getConfig(EnableSiteRuntimePreview).enableSiteRuntimePreview - if(enableSiteRuntimePreview === undefined) { + if (enableSiteRuntimePreview === undefined) { return false; } - return enableSiteRuntimePreview; + return true; } static async getWebSiteURL(workspaceFolders: WorkspaceFolder[], stamp: ServiceEndpointCategory, envId: string, telemetry: ITelemetry): Promise { - - const websiteRecordId = getWebsiteRecordID(workspaceFolders, telemetry); + const websiteRecordId = getWebsiteRecordId(workspaceFolders, telemetry); if (!websiteRecordId) { telemetry.sendTelemetryEvent(VSCODE_EXTENSION_GET_WEBSITE_RECORD_ID_EMPTY, { websiteRecordId: websiteRecordId @@ -41,54 +41,14 @@ export class PreviewSite { return websiteDetails?.websiteUrl || ""; } - static async launchBrowserAndDevToolsWithinVsCode(webSitePreviewURL: string): Promise { - - const edgeToolsExtensionId = 'ms-edgedevtools.vscode-edge-devtools'; - const edgeToolsExtension = vscode.extensions.getExtension(edgeToolsExtensionId); - - if (edgeToolsExtension) { - // Preserve the original state of the launch.json file and .vscode folder - const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; - const vscodeFolderPath = workspaceFolder ? path.join(workspaceFolder.uri.fsPath, '.vscode') : null; - const launchJsonPath = vscodeFolderPath ? path.join(vscodeFolderPath, 'launch.json') : null; - let originalLaunchJsonContent: string | null = null; - let vscodeFolderExisted = false; + static async launchBrowserAndDevToolsWithinVsCode(webSitePreviewURL: string | undefined): Promise { + if (!webSitePreviewURL || webSitePreviewURL === "") { + return; + } - if (vscodeFolderPath && fs.existsSync(vscodeFolderPath)) { - vscodeFolderExisted = true; - if (launchJsonPath && fs.existsSync(launchJsonPath)) { - originalLaunchJsonContent = fs.readFileSync(launchJsonPath, 'utf8'); - } - } + const edgeToolsExtension = vscode.extensions.getExtension(EDGE_TOOLS_EXTENSION_ID); - await updateLaunchJsonConfig(webSitePreviewURL); - - try { - // Added a 2-second delay before executing the launchProject command to handle the case where the launch.json file is not saved yet - await new Promise(resolve => setTimeout(resolve, 2000)); - await vscode.commands.executeCommand('vscode-edge-devtools-view.launchProject'); - - } finally { - // Revert the changes made to the launch.json file and remove the .vscode folder if it was created - - // Added a 2-second delay to ensure that debug session is closed and then launch.json file is removed - await new Promise(resolve => setTimeout(resolve, 2000)); - if (launchJsonPath) { - if (originalLaunchJsonContent !== null) { - fs.writeFileSync(launchJsonPath, originalLaunchJsonContent, 'utf8'); - } else if (fs.existsSync(launchJsonPath)) { - fs.unlinkSync(launchJsonPath); - } - } - - if (vscodeFolderPath && !vscodeFolderExisted && fs.existsSync(vscodeFolderPath)) { - const files = fs.readdirSync(vscodeFolderPath); - if (files.length === 0) { - fs.rmdirSync(vscodeFolderPath); - } - } - } - } else { + if (!edgeToolsExtension) { const install = await vscode.window.showWarningMessage( vscode.l10n.t( `The extension Microsoft Edge Tools is required to run this command. Do you want to install it now?` @@ -98,11 +58,45 @@ export class PreviewSite { ); if (install === vscode.l10n.t('Install')) { - // Open the Extensions view with the specific extension - vscode.commands.executeCommand('workbench.extensions.search', edgeToolsExtensionId); + await vscode.commands.executeCommand('workbench.extensions.search', EDGE_TOOLS_EXTENSION_ID); } return; } + + const settings = vscode.workspace.getConfiguration('vscode-edge-devtools'); + const currentDefaultUrl = await settings.get('defaultUrl'); + await settings.update('defaultUrl', webSitePreviewURL); + await vscode.commands.executeCommand('vscode-edge-devtools-view.launch'); + await settings.update('defaultUrl', currentDefaultUrl); + } + + static async handlePreviewRequest(isSiteRuntimePreviewEnabled: boolean, websiteURL: string | undefined, telemetry: ITelemetry) { + telemetry.sendTelemetryEvent("StartCommand", { + commandId: SITE_PREVIEW_COMMAND_ID, + }); + oneDSLoggerWrapper.getLogger().traceInfo("StartCommand", { + commandId: SITE_PREVIEW_COMMAND_ID + }); + + if (!isSiteRuntimePreviewEnabled) { + await vscode.window.showInformationMessage(vscode.l10n.t("Site runtime preview feature is not enabled.")); + return; + } + + if (!vscode.workspace.workspaceFolders) { + await vscode.window.showErrorMessage(vscode.l10n.t("No workspace folder opened. Please open a site folder to preview.")); + return; + } + + if (!websiteURL || websiteURL === "") { + await vscode.window.showErrorMessage(vscode.l10n.t("Website URL not found.")); + return; + } + + await showProgressNotification( + vscode.l10n.t('Opening site preview...'), + async () => await PreviewSite.launchBrowserAndDevToolsWithinVsCode(websiteURL) + ); } } diff --git a/src/common/OneDSLoggerTelemetry/telemetry/telemetry.ts b/src/common/OneDSLoggerTelemetry/telemetry/telemetry.ts index 1975fb82f..0eeaaa859 100644 --- a/src/common/OneDSLoggerTelemetry/telemetry/telemetry.ts +++ b/src/common/OneDSLoggerTelemetry/telemetry/telemetry.ts @@ -18,7 +18,7 @@ export const CleanupRelatedFilesEvent = 'CleanupRelatedFilesEvent'; export const UpdateEntityNameInYmlEvent = 'UpdateEntityNameInYmlEvent'; export const UserFileCreateEvent = 'UserFileCreateEvent'; export const FileCreateEvent = 'FileCreateEvent'; -export const GetWebsiteRecordID = 'getWebsiteRecordID'; +export const GetWebsiteRecordID = 'getWebsiteRecordId'; interface ITelemetryData { eventName: string, @@ -69,4 +69,3 @@ export function sendTelemetryEvent(telemetry: ITelemetry, telemetryData: ITeleme oneDSLoggerWrapper.getLogger().traceInfo(telemetryData.eventName, telemetryDataProperties, telemetryDataMeasurements); } } - diff --git a/src/common/constants.ts b/src/common/constants.ts index b7c365381..410a0aac7 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -94,3 +94,4 @@ export const VSCODE_EXTENSION_COPILOT_CONTEXT_RELATED_FILES_FETCH_FAILED = "VSCo export const ADX_WEBPAGE = 'adx_webpage' export const HTML_FILE_EXTENSION = '.html'; export const UTF8_ENCODING = 'utf8'; +export const EDGE_TOOLS_EXTENSION_ID = 'ms-edgedevtools.vscode-edge-devtools'; diff --git a/src/common/controls/ShowProgressNotification.ts b/src/common/controls/ShowProgressNotification.ts new file mode 100644 index 000000000..5a7c606fe --- /dev/null +++ b/src/common/controls/ShowProgressNotification.ts @@ -0,0 +1,19 @@ +/* + * 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 { Progress } from 'vscode'; +import { CancellationToken } from 'vscode-languageserver'; + +export async function showProgressNotification(title: string, task: (progress: Progress<{ + message: string; + increment?: number; +}>, token: CancellationToken) => Thenable) { + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title, + cancellable: false + }, async (progress, token) => await task(progress, token)); +} diff --git a/src/common/utilities/WorkspaceInfoFinderUtil.ts b/src/common/utilities/WorkspaceInfoFinderUtil.ts index 4d60e91ab..e0ed99446 100644 --- a/src/common/utilities/WorkspaceInfoFinderUtil.ts +++ b/src/common/utilities/WorkspaceInfoFinderUtil.ts @@ -40,7 +40,7 @@ export function getPortalsOrgURLs(workspaceRootFolders: WorkspaceFolder[] | null return output; } -export function getWebsiteRecordID(workspaceFolders: { uri: string }[], telemetry: ITelemetry): string { +export function getWebsiteRecordId(workspaceFolders: { uri: string }[], telemetry: ITelemetry): string { try { if (!workspaceFolders || workspaceFolders.length === 0) { return ""; @@ -56,7 +56,7 @@ export function getWebsiteRecordID(workspaceFolders: { uri: string }[], telemetr } } } catch (exception) { - sendTelemetryEvent(telemetry, { methodName: getWebsiteRecordID.name, eventName: GetWebsiteRecordID, exception: exception as Error }); + sendTelemetryEvent(telemetry, { methodName: getWebsiteRecordId.name, eventName: GetWebsiteRecordID, exception: exception as Error }); } return ""; } From 291f5b9df5b2a9caa09ca576af7f9f5b1398d957 Mon Sep 17 00:00:00 2001 From: Priyanshu Agrawal Date: Mon, 16 Dec 2024 20:40:52 +0530 Subject: [PATCH 10/10] Fix runtime preview feature flag --- src/client/runtime-site-preview/PreviewSite.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/runtime-site-preview/PreviewSite.ts b/src/client/runtime-site-preview/PreviewSite.ts index dc5cbe13c..66dc3d1b1 100644 --- a/src/client/runtime-site-preview/PreviewSite.ts +++ b/src/client/runtime-site-preview/PreviewSite.ts @@ -26,7 +26,7 @@ export class PreviewSite { return false; } - return true; + return enableSiteRuntimePreview; } static async getWebSiteURL(workspaceFolders: WorkspaceFolder[], stamp: ServiceEndpointCategory, envId: string, telemetry: ITelemetry): Promise {