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 app dropdown menu for ECP registry link #514

Merged
merged 10 commits into from
Nov 16, 2023
19 changes: 19 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
"@types/semver": "^7.1.0",
"@types/sinon": "7.0.6",
"@types/vscode": "^1.53.0",
"@types/xml2js": "^0.4.14",
"@types/yargs": "^17.0.10",
"@typescript-eslint/eslint-plugin": "^5.14.0",
"@typescript-eslint/parser": "^5.14.0",
Expand Down Expand Up @@ -2725,6 +2726,12 @@
"command": "extension.brightscript.rokuRegistry.exportRegistry",
"title": "Export Registry",
"category": "BrighterScript",
"icon": "$(browser)"
},
{
"command": "extension.brightscript.openRegistryInBrowser",
"title": "Open device registry in a browser",
"category": "BrighterScript",
"icon": "$(arrow-up)"
},
{
Expand Down
2 changes: 1 addition & 1 deletion src/BrightScriptCommands.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describe('BrightScriptFileUtils ', () => {
let languagesMock;

beforeEach(() => {
commands = new BrightScriptCommands({} as any, {} as any, {} as any, {} as any);
commands = new BrightScriptCommands({} as any, {} as any, {} as any, {} as any, {} as any);
commandsMock = sinon.mock(commands);
languagesMock = sinon.mock(vscode.languages);
});
Expand Down
52 changes: 51 additions & 1 deletion src/BrightScriptCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@ import { util as rokuDebugUtil } from 'roku-debug/dist/util';
import type { RemoteControlManager, RemoteControlModeInitiator } from './managers/RemoteControlManager';
import type { WhatsNewManager } from './managers/WhatsNewManager';
import type { ActiveDeviceManager } from './ActiveDeviceManager';
import * as xml2js from 'xml2js';
import { firstBy } from 'thenby';
import type { UserInputManager } from './managers/UserInputManager';

export class BrightScriptCommands {

constructor(
private remoteControlManager: RemoteControlManager,
private whatsNewManager: WhatsNewManager,
private context: vscode.ExtensionContext,
private activeDeviceManager: ActiveDeviceManager
private activeDeviceManager: ActiveDeviceManager,
private userInputManager: UserInputManager
) {
this.fileUtils = new BrightScriptFileUtils();
}
Expand Down Expand Up @@ -263,6 +267,52 @@ export class BrightScriptCommands {
}
});

this.registerCommand('openRegistryInBrowser', async (host: string) => {
if (!host) {
host = await this.userInputManager.promptForHost();
}

let responseText = await util.spinAsync('Fetching app list', async () => {
return (await util.httpGet(`http://${host}:8060/query/apps`, { timeout: 4_000 })).body as string;
});

const parsed = await xml2js.parseStringPromise(responseText);

//convert the items to QuickPick items
const items: Array<vscode.QuickPickItem & { appId?: string }> = parsed.apps.app.map((appData: any) => {
return {
label: appData._,
detail: `ID: ${appData.$.id}`,
description: `${appData.$.version}`,
appId: `${appData.$.id}`
} as vscode.QuickPickItem;
//sort the items alphabetically
}).sort(firstBy('label'));

//move the dev app to the top (and add a label/section to differentiate it)
const devApp = items.find(x => x.appId === 'dev');
if (devApp) {
items.splice(items.indexOf(devApp), 1);
items.unshift(
{ kind: vscode.QuickPickItemKind.Separator, label: 'dev' },
devApp,
{ kind: vscode.QuickPickItemKind.Separator, label: ' ' }
);
}

const selectedApp: typeof items[0] = await vscode.window.showQuickPick(items, { placeHolder: 'Which app would you like to see the registry for?' });

if (selectedApp) {
const appId = selectedApp.appId;
let url = `http://${host}:8060/query/registry/${appId}`;
try {
await vscode.env.openExternal(vscode.Uri.parse(url));
} catch (error) {
await vscode.window.showErrorMessage(`Tried to open url but failed: ${url}`);
}
}
});

this.registerCommand('showReleaseNotes', () => {
this.whatsNewManager.showReleaseNotes();
});
Expand Down
3 changes: 2 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ export class Extension {
this.remoteControlManager,
this.whatsNewManager,
context,
activeDeviceManager
activeDeviceManager,
userInputManager
);

this.rtaManager = new RtaManager();
Expand Down
2 changes: 1 addition & 1 deletion src/managers/WebviewViewProviderManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe('WebviewViewProviderManager', () => {
const config = {} as BrightScriptLaunchConfiguration;
let webviewViewProviderManager: WebviewViewProviderManager;
let rtaManager: RtaManager;
const brightScriptCommands = new BrightScriptCommands({} as any, {} as any, {} as any, {} as any);
const brightScriptCommands = new BrightScriptCommands({} as any, {} as any, {} as any, {} as any, {} as any);

before(() => {
context = {
Expand Down
38 changes: 38 additions & 0 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import { Cache } from 'brighterscript/dist/Cache';
import undent from 'undent';
import { EXTENSION_ID, ROKU_DEBUG_VERSION } from './constants';
import type { DeviceInfo } from 'roku-deploy';
import * as r from 'postman-request';
import type { Response } from 'request';
import type * as requestType from 'request';
const request = r as typeof requestType;

class Util {
public async readDir(dirPath: string) {
Expand Down Expand Up @@ -385,6 +389,17 @@ class Util {
return text?.toString().replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}

/**
* Do an http GET request
*/
public httpGet(url: string, options?: requestType.CoreOptions) {
return new Promise<Response>((resolve, reject) => {
request.get(url, options, (err, response) => {
return err ? reject(err) : resolve(response);
});
});
}

public async openIssueReporter(options: { title?: string; body?: string; deviceInfo?: DeviceInfo }) {
if (!options.body) {
options.body = undent`
Expand All @@ -409,6 +424,29 @@ class Util {
issueBody: options.body
});
}

public createStatusbarSpinner(message: string) {
const statusbarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 9_999_999);
statusbarItem.text = `$(sync~spin) ${message}`;
statusbarItem.show();
return statusbarItem;
}

/**
* Show a statusbar spinner that is hidden once the callback resolves
* @param message the message that should be shown in the statusbar spinner
* @param callback the function to run, that when completed will hide the spinner
* @returns
*/
public async spinAsync<T>(message: string, callback: () => Promise<T>) {
const spinner = this.createStatusbarSpinner(message);
try {
const result = await callback();
return result;
} finally {
spinner.dispose();
}
}
}

const util = new Util();
Expand Down
11 changes: 3 additions & 8 deletions src/viewProviders/OnlineDevicesViewProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,15 +141,15 @@ export class OnlineDevicesViewProvider implements vscode.TreeDataProvider<vscode
// TODO: add ECP system hooks here in the future (like registry call, etc...)
result.unshift(
this.createDeviceInfoTreeItem({
label: '🔗 View Registry',
label: '📋 View Registry',
parent: element,
collapsibleState: vscode.TreeItemCollapsibleState.None,
tooltip: 'View the ECP Registry',
description: device.ip,
command: {
command: 'extension.brightscript.openUrl',
command: 'extension.brightscript.openRegistryInBrowser',
title: 'Open',
arguments: [`http://${device.ip}:8060/query/registry/dev`]
arguments: [device.ip]
}
})
);
Expand All @@ -170,11 +170,6 @@ export class OnlineDevicesViewProvider implements vscode.TreeDataProvider<vscode
})
);


if (semver.satisfies(element.details['software-version'], '>=11')) {
// TODO: add ECP system hooks here in the future (like registry call, etc...)
}

// Return the device details
return result;
}
Expand Down