Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add test client for extensin #784

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release_extension.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
node-version: 18

- name: Build and Pack
run: npm install && npm run build && npm run package
run: npm run install:all && npm run build:all && npm run package

- name: Get Metadata
id: metadata_step
Expand Down
2 changes: 2 additions & 0 deletions tools/vscode-azurewebpubsub/.vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,5 @@ tsconfig.json
tslint.json
typings/**
webpack.config*
webview-ui/**
!webview-ui/build
1,616 changes: 1,613 additions & 3 deletions tools/vscode-azurewebpubsub/package-lock.json

Large diffs are not rendered by default.

38 changes: 29 additions & 9 deletions tools/vscode-azurewebpubsub/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@
"preview": true,
"activationEvents": [
"onView:azureResourceGroups",
"onView:azureResourceGroupsV2"
"onView:azureResourceGroupsV2",
"onWebviewPanel:testClientView"
],
"main": "./main.js",
"contributes": {
Expand Down Expand Up @@ -124,6 +125,11 @@
"title": "%azureWebPubSub.service.openLiveTraceTool%",
"category": "Azure Web PubSub"
},
{
"command": "azureWebPubSub.service.testClient",
"title": "%azureWebPubSub.service.testClient%",
"category": "Azure Web PubSub"
},
{
"command": "azureWebPubSub.service.scaleUp",
"title": "%azureWebPubSub.service.scaleUp%",
Expand Down Expand Up @@ -258,29 +264,34 @@
],
"azureWebPubSub.service.advancedMenu": [
{
"command": "azureWebPubSub.service.scaleUp",
"command": "azureWebPubSub.service.testClient",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /webPubSubServiceItem/i",
"group": "1@1"
},
{
"command": "azureWebPubSub.service.scaleOut",
"command": "azureWebPubSub.service.scaleUp",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /webPubSubServiceItem/i",
"group": "1@2"
},
{
"command": "azureWebPubSub.service.viewMetrics",
"command": "azureWebPubSub.service.scaleOut",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /webPubSubServiceItem/i",
"group": "1@3"
},
{
"command": "azureWebPubSub.service.checkHealth",
"command": "azureWebPubSub.service.viewMetrics",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /webPubSubServiceItem/i",
"group": "1@4"
},
{
"command": "azureWebPubSub.service.regenerateKey",
"command": "azureWebPubSub.service.checkHealth",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /webPubSubServiceItem/i",
"group": "1@5"
},
{
"command": "azureWebPubSub.service.regenerateKey",
"when": "view =~ /(azureResourceGroups|azureFocusView)/ && viewItem =~ /webPubSubServiceItem/i",
"group": "1@6"
}
]
},
Expand All @@ -300,17 +311,23 @@
"scripts": {
"vscode:prepublish": "npm run cleanReadme",
"build": "tsc && gulp webpack-prod",
"build:all": "npm run build && npm run build:webview",
"cleanReadme": "gulp cleanReadme",
"compile": "tsc -watch",
"package": "vsce package --no-dependencies",
"package:pre-release": "vsce package --pre-release",
"lint": "eslint --ext .ts .",
"lint-fix": "eslint --ext .ts . --fix",
"lint": "eslint --ext .ts ./src",
"lint-fix": "eslint --ext .ts ./src --fix",
"lint:all": "npm run lint && cd webview-ui && npm run lint",
"lint-fix:all": "npm run lint-fix && cd webview-ui && npm run lint-fix",
"pretest": "npm run lint && npm run webpack-prod && gulp preTest",
"test": "node ./out/test/runTest.js",
"webpack": "tsc && gulp webpack-dev",
"webpack-prod": "tsc && gulp webpack-prod",
"webpack-profile": "webpack --profile --json --mode production > webpack-stats.json && echo Use http://webpack.github.io/analyse to analyze the stats"
"webpack-profile": "webpack --profile --json --mode production > webpack-stats.json && echo Use http://webpack.github.io/analyse to analyze the stats",
"install:all": "npm install && cd webview-ui && npm install --legacy-peer-deps",
"start:webview": "cd webview-ui && npm run start",
"build:webview": "cd webview-ui && npm run build"
},
"devDependencies": {
"@azure/ms-rest-azure-env": "^2.0.0",
Expand Down Expand Up @@ -345,6 +362,9 @@
"@azure/arm-webpubsub": "^2.0.0-beta.2",
"@azure/core-rest-pipeline": "1.10.3",
"@azure/storage-blob": "^12.4.1",
"@azure/web-pubsub": "^1.1.1",
"@fluentui/react": "^8.106.3",
"@fluentui/react-components": "^9.34.1",
"@microsoft/vscode-azext-azureutils": "^2.0.0",
"@microsoft/vscode-azext-utils": "^2.1.3",
"@microsoft/vscode-azureresources-api": "^2.2.1",
Expand Down
1 change: 1 addition & 0 deletions tools/vscode-azurewebpubsub/package.nls.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"azureWebPubSub.description": "An Azure Web PubSub extension for Visual Studio Code.",
"azureWebPubSub.service.testClient": "Test Client",
"azureWebPubSub.service.advancedMenu": "Advanced",
"azureWebPubSub.service.createInPortal": "Create Web PubSub Service in Portal",
"azureWebPubSub.service.createInPortal.detail": "A fully managed service that supports WebSockets for real-time application development.",
Expand Down
8 changes: 7 additions & 1 deletion tools/vscode-azurewebpubsub/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import { viewMetrics } from "./workflows/service/viewMetric/viewMetrics";
import { checkServiceHealth } from "./workflows/service/checkHealth/checkHealth";
import { attachLocalTunnel } from "./workflows/hubSetting/localTunnel/attachLocalTunnel";
import { switchAnonymousPolicy } from "./workflows/hubSetting/switchAnonymousPolicy/switchAnonymousPolicy";
import { testClient } from "./workflows/service/testClient/testClient";
import { type ExtensionContext} from "vscode";
import { type ServiceItem } from "./tree/service/ServiceItem";

function registerCommandWithTelemetryWrapper(commandId: string, callback: CommandCallback): void {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
Expand All @@ -43,8 +46,11 @@ function registerCommandWithTelemetryWrapper(commandId: string, callback: Comman
registerCommandWithTreeNodeUnwrapping(commandId, callbackWithTroubleshooting);
}

export function registerCommands(): void {
export function registerCommands(extensionContext: ExtensionContext): void {
// Service
registerCommandWithTelemetryWrapper('azureWebPubSub.service.testClient', async (actionContext: IActionContext, node?: ServiceItem) => {
await testClient(extensionContext.extensionUri, actionContext, node);
});
registerCommandWithTelemetryWrapper('azureWebPubSub.service.createInPortal', createServiceInPortal);
registerCommandWithTelemetryWrapper('azureWebPubSub.service.createClassical', createServiceForClassical);
registerCommandWithTelemetryWrapper('azureWebPubSub.service.createSocketIO', createServiceForSocketIO);
Expand Down
2 changes: 1 addition & 1 deletion tools/vscode-azurewebpubsub/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export async function activate(context: vscode.ExtensionContext, perfStats: { lo
ext.branchDataProvider = new ServicesDataProvider();
ext.rgApiV2 = await getAzureResourcesExtensionApi(context, '2.0.0');
ext.rgApiV2.resources.registerAzureResourceBranchDataProvider(AzExtResourceType.WebPubSub, ext.branchDataProvider);
registerCommands();
registerCommands(context);
})();
}

Expand Down
115 changes: 115 additions & 0 deletions tools/vscode-azurewebpubsub/src/panels/BasePanel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { type Disposable, type Webview, type WebviewPanel} from "vscode";
import { window, Uri, ViewColumn } from "vscode";
import { ext } from "../extensionVariables";
import { getNonce, getWebviewUri } from "../utils";

export class BasePanel {
protected readonly _panel: WebviewPanel;
protected _disposables: Disposable[] = [];

/**
* The BasePanel class private constructor (called only from the render method).
*
* @param panel A reference to the webview panel
* @param extensionUri The URI of the directory containing the extension
*/
public constructor(panel: WebviewPanel, extensionUri: Uri) {
this._panel = panel;

// Set an event listener to listen for when the panel is disposed (i.e. when the user closes
// the panel or when the panel is closed programmatically)
this._panel.onDidDispose(() => this.dispose(), null, this._disposables);

// Set the HTML content for the webview panel
this._panel.webview.html = this.getWebviewContent(this._panel.webview, extensionUri);

// Set an event listener to listen for messages passed from the webview context
this.setWebviewMessageListener(this._panel.webview);
}

/**
* Renders a new webview panel
* will be created and displayed.
*
* @param extensionUri The URI of the directory containing the extension.
*/
protected static renderNew(extensionUri: Uri, viewType: string, title: string) {
return window.createWebviewPanel(
viewType,
title,
ViewColumn.One,
{
enableScripts: true,
retainContextWhenHidden: true,
localResourceRoots: [Uri.joinPath(extensionUri, "out"), Uri.joinPath(extensionUri, "webview-ui/build")],
}
);
}

/**
* Cleans up and disposes of webview resources when the webview panel is closed.
*/
public dispose() {
// Dispose of the current webview panel
this._panel.dispose();

// Dispose of all disposables (i.e. commands) for the current webview panel
while (this._disposables.length) {
const disposable = this._disposables.pop();
if (disposable) {
disposable.dispose();
}
}
}

/**
* Defines and returns the HTML that should be rendered within the webview panel.
*
* @remarks This is also the place where references to the React webview build files
* are created and inserted into the webview HTML.
*
* @param webview A reference to the extension webview
* @param extensionUri The URI of the directory containing the extension
* @returns A template string literal containing the HTML that should be
* rendered within the webview panel
*/
protected getWebviewContent(webview: Webview, extensionUri: Uri) {
const assetsPath = ["webview-ui", "build", "assets"];
const stylesUri = getWebviewUri(webview, extensionUri, [...assetsPath, "index.css"]);
const scriptUri = getWebviewUri(webview, extensionUri, [...assetsPath, "index.js"]);

const nonce = getNonce();

// Tip: Install the es6-string-html VS Code extension to enable code highlighting below
return /*html*/ `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Security-Policy" content="default-src *; style-src ${webview.cspSource} 'unsafe-inline'; script-src 'nonce-${nonce}';">
<link rel="stylesheet" type="text/css" href="${stylesUri}">
<title>AWPS Test Client Title</title>
</head>
<body>
<div id="root"></div>
<script type="module" nonce="${nonce}" src="${scriptUri}"></script>
</body>
</html>
`;
}

protected setWebviewMessageListener(webview: Webview) {
webview.onDidReceiveMessage(
/* eslint-disable */
async (message: any) => {
const command = message.command;
const payload = message.payload;
ext.outputChannel.appendLog(`Received a message from webview, command = ${command}, payload = ${JSON.stringify(payload)}`);
},
undefined,
this._disposables
/* eslint-enable */
);
}
}
81 changes: 81 additions & 0 deletions tools/vscode-azurewebpubsub/src/panels/TestClientWebviewPanel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { type Webview, type WebviewPanel, type Uri} from "vscode";
import { ViewColumn } from "vscode";
import { type ServiceModel } from "../tree/service/ServiceModel";
import { type WebPubSubManagementClient } from "@azure/arm-webpubsub";
import { getClientAccessUrl, localize, postMessageToWebviewWithLog } from "../utils";
import { BasePanel } from "./BasePanel";
import { ext } from "../extensionVariables";

export class TestClientWebviewPanel extends BasePanel {
public static currentPanel: TestClientWebviewPanel | undefined;
private readonly _service: ServiceModel;
private readonly _managementClient: WebPubSubManagementClient;

public constructor(panel: WebviewPanel, extensionUri: Uri, service: ServiceModel, managementClient: WebPubSubManagementClient) {
super(panel, extensionUri);
this._service = service;
this._managementClient = managementClient;
}

public static render(extensionUri: Uri, service: ServiceModel, managementClient: WebPubSubManagementClient) {
// If the webview panel already exists reveal it
if (TestClientWebviewPanel.currentPanel) {
return TestClientWebviewPanel.currentPanel._panel.reveal(ViewColumn.One);
}
const panel = super.renderNew(extensionUri, "testClientView", `Test Client for ${service.name}`);
TestClientWebviewPanel.currentPanel = new TestClientWebviewPanel(panel, extensionUri, service, managementClient);
}

public override dispose() {
TestClientWebviewPanel.currentPanel = undefined;
return super.dispose();
}

protected override setWebviewMessageListener(webview: Webview) {
webview.onDidReceiveMessage(
/* eslint-disable */
async (message: any) => {
const command = message.command;
let payload = message.payload;
ext.outputChannel.appendLog(`Receive message from webview, command = ${command}, payload = ${JSON.stringify(payload)}`);

const commandName = command.split("-")[0];
const commandIdx = command.split("-")[1];
switch (commandName) {
case "reportServiceConfiguration":
const hubsIterator = this._managementClient.webPubSubHubs.list(this._service.resourceGroup, this._service.name);
const hubNames: string[] = [];
for await (const hub of hubsIterator) {
hub.name && hubNames.push(hub.name);
}
await postMessageToWebviewWithLog(webview, {
command: `ack-reportServiceConfiguration-${commandIdx}`,
payload: {
resourceGroup: this._service.resourceGroup,
resourceName: this._service.name,
hubNames: hubNames
}
});
return;

case "getClientAccessUrl":
payload = { hub: payload.hub ?? "", userId: payload.userId ?? "", roles: payload.roles ?? [], groups: payload.groups ?? []};
const { hub, userId, roles, groups } = payload;
const connectionString = (await (this._managementClient.webPubSub.listKeys(this._service.resourceGroup, this._service.name))).primaryConnectionString;
if (!connectionString) {
throw new Error(localize('getConnectionStringError', `Failed to get connection string of ${this._service.name}.`));
}
const clientAccessUrl = await getClientAccessUrl(connectionString, hub, userId, roles, groups);
postMessageToWebviewWithLog(webview, {
command: `ack-getClientAccessUrl-${commandIdx}`,
payload: clientAccessUrl
});
return ;
}
},
/* eslint-enable */
undefined,
this._disposables
);
}
}
Loading
Loading