mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -05:00
Add persistence and connect dialog to Arc view (#11014)
* Add controller persistence and info prompting * more stuff * clean up * Add arc tests to scripts
This commit is contained in:
20
extensions/arc/coverConfig.json
Normal file
20
extensions/arc/coverConfig.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"enabled": true,
|
||||
"relativeSourcePath": "..",
|
||||
"relativeCoverageDir": "../../coverage",
|
||||
"ignorePatterns": [
|
||||
"**/generated/**",
|
||||
"**/node_modules/**",
|
||||
"**/test/**"
|
||||
],
|
||||
"reports": [
|
||||
"cobertura",
|
||||
"lcov",
|
||||
"json"
|
||||
],
|
||||
"verbose": false,
|
||||
"remapOptions": {
|
||||
"basePath": "..",
|
||||
"useAbsolutePaths": true
|
||||
}
|
||||
}
|
||||
@@ -49,13 +49,22 @@
|
||||
"title": "%arc.openDashboard%"
|
||||
},
|
||||
{
|
||||
"command": "arc.addController",
|
||||
"title": "%command.addController.title%",
|
||||
"command": "arc.createController",
|
||||
"title": "%command.createController.title%",
|
||||
"icon": "$(add)"
|
||||
},
|
||||
{
|
||||
"command": "arc.connectToController",
|
||||
"title": "%command.connectToController.title%",
|
||||
"icon": "$(debug-disconnect)"
|
||||
},
|
||||
{
|
||||
"command": "arc.removeController",
|
||||
"title": "%command.removeController.title%"
|
||||
},
|
||||
{
|
||||
"command": "arc.refresh",
|
||||
"title": "%command.refresh.title%"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
@@ -79,13 +88,22 @@
|
||||
{
|
||||
"command": "arc.removeController",
|
||||
"when": "false"
|
||||
},
|
||||
{
|
||||
"command": "arc.refresh",
|
||||
"when": "false"
|
||||
}
|
||||
],
|
||||
"view/title": [
|
||||
{
|
||||
"command": "arc.addController",
|
||||
"command": "arc.createController",
|
||||
"when": "view == azureArc",
|
||||
"group": "navigation"
|
||||
"group": "navigation@1"
|
||||
},
|
||||
{
|
||||
"command": "arc.connectToController",
|
||||
"when": "view == azureArc",
|
||||
"group": "navigation@2"
|
||||
}
|
||||
],
|
||||
"view/item/context": [
|
||||
@@ -95,9 +113,14 @@
|
||||
"group": "navigation@1"
|
||||
},
|
||||
{
|
||||
"command": "arc.removeController",
|
||||
"command": "arc.refresh",
|
||||
"when": "view == azureArc && viewItem == dataControllers",
|
||||
"group": "navigation@2"
|
||||
},
|
||||
{
|
||||
"command": "arc.removeController",
|
||||
"when": "view == azureArc && viewItem == dataControllers",
|
||||
"group": "navigation@3"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -125,6 +148,7 @@
|
||||
"mocha-junit-reporter": "^1.17.0",
|
||||
"mocha-multi-reporters": "^1.1.7",
|
||||
"should": "^13.2.3",
|
||||
"typemoq": "2.1.0",
|
||||
"vscodetestcover": "^1.0.9"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
"arc.managePostgres": "Manage Postgres",
|
||||
"arc.manageArcController": "Manage Arc Controller",
|
||||
"arc.view.title" : "Azure Arc Controllers",
|
||||
"command.addController.title": "Connect to Controller",
|
||||
"command.createController.title" : "Create New Controller",
|
||||
"command.connectToController.title": "Connect to Existing Controller",
|
||||
"command.removeController.title": "Remove Controller",
|
||||
"command.refresh.title": "Refresh",
|
||||
"arc.openDashboard": "Manage"
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export const refreshActionId = 'arc.refresh';
|
||||
|
||||
export interface IconPath {
|
||||
dark: string;
|
||||
light: string;
|
||||
|
||||
@@ -5,17 +5,11 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as loc from './localizedConstants';
|
||||
import { IconPathHelper } from './constants';
|
||||
import { BasicAuth } from './controller/auth';
|
||||
import { PostgresDashboard } from './ui/dashboards/postgres/postgresDashboard';
|
||||
import { ControllerModel } from './models/controllerModel';
|
||||
import { PostgresModel } from './models/postgresModel';
|
||||
import { ControllerDashboard } from './ui/dashboards/controller/controllerDashboard';
|
||||
import { MiaaDashboard } from './ui/dashboards/miaa/miaaDashboard';
|
||||
import { MiaaModel } from './models/miaaModel';
|
||||
import { AzureArcTreeDataProvider } from './ui/tree/controllerTreeDataProvider';
|
||||
import { IconPathHelper, refreshActionId } from './constants';
|
||||
import { AzureArcTreeDataProvider } from './ui/tree/azureArcTreeDataProvider';
|
||||
import { ControllerTreeNode } from './ui/tree/controllerTreeNode';
|
||||
import { TreeNode } from './ui/tree/treeNode';
|
||||
import { ConnectToControllerDialog } from './ui/dialogs/connectControllerDialog';
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext): Promise<void> {
|
||||
IconPathHelper.setExtensionContext(context);
|
||||
@@ -23,85 +17,30 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
|
||||
const treeDataProvider = new AzureArcTreeDataProvider(context);
|
||||
vscode.window.registerTreeDataProvider('azureArc', treeDataProvider);
|
||||
|
||||
vscode.commands.registerCommand('arc.addController', () => {
|
||||
// Controller information
|
||||
const controllerUrl = '';
|
||||
const auth = new BasicAuth('', '');
|
||||
|
||||
const controllerModel = new ControllerModel(controllerUrl, auth);
|
||||
treeDataProvider.addController(controllerModel);
|
||||
vscode.commands.registerCommand('arc.createController', async () => {
|
||||
await vscode.commands.executeCommand('azdata.resource.deploy');
|
||||
});
|
||||
|
||||
vscode.commands.registerCommand('arc.removeController', (controllerNode: ControllerTreeNode) => {
|
||||
treeDataProvider.removeController(controllerNode);
|
||||
vscode.commands.registerCommand('arc.connectToController', async () => {
|
||||
const dialog = new ConnectToControllerDialog(treeDataProvider);
|
||||
dialog.showDialog();
|
||||
const model = await dialog.waitForClose();
|
||||
if (model) {
|
||||
await treeDataProvider.addOrUpdateController(model.controllerModel, model.password);
|
||||
}
|
||||
});
|
||||
|
||||
vscode.commands.registerCommand('arc.removeController', async (controllerNode: ControllerTreeNode) => {
|
||||
await treeDataProvider.removeController(controllerNode);
|
||||
});
|
||||
|
||||
vscode.commands.registerCommand(refreshActionId, async (treeNode: TreeNode) => {
|
||||
treeDataProvider.refreshNode(treeNode);
|
||||
});
|
||||
|
||||
vscode.commands.registerCommand('arc.openDashboard', async (treeNode: TreeNode) => {
|
||||
await treeNode.openDashboard().catch(err => vscode.window.showErrorMessage(loc.openDashboardFailed(err)));
|
||||
});
|
||||
|
||||
vscode.commands.registerCommand('arc.manageArcController', async () => {
|
||||
// Controller information
|
||||
const controllerUrl = '';
|
||||
const auth = new BasicAuth('', '');
|
||||
|
||||
try {
|
||||
const controllerModel = new ControllerModel(controllerUrl, auth);
|
||||
const controllerDashboard = new ControllerDashboard(controllerModel);
|
||||
|
||||
await Promise.all([
|
||||
controllerDashboard.showDashboard(),
|
||||
controllerModel.refresh()
|
||||
]);
|
||||
} catch (error) {
|
||||
// vscode.window.showErrorMessage(loc.failedToManagePostgres(`${dbNamespace}.${dbName}`, error));
|
||||
}
|
||||
});
|
||||
|
||||
vscode.commands.registerCommand('arc.manageMiaa', async () => {
|
||||
// Controller information
|
||||
const controllerUrl = '';
|
||||
const auth = new BasicAuth('', '');
|
||||
const instanceNamespace = '';
|
||||
const instanceName = '';
|
||||
|
||||
try {
|
||||
const controllerModel = new ControllerModel(controllerUrl, auth);
|
||||
const miaaModel = new MiaaModel(controllerUrl, auth, instanceNamespace, instanceName);
|
||||
const miaaDashboard = new MiaaDashboard(controllerModel, miaaModel);
|
||||
|
||||
await Promise.all([
|
||||
miaaDashboard.showDashboard(),
|
||||
controllerModel.refresh()
|
||||
]);
|
||||
} catch (error) {
|
||||
// vscode.window.showErrorMessage(loc.failedToManagePostgres(`${dbNamespace}.${dbName}`, error));
|
||||
}
|
||||
});
|
||||
|
||||
vscode.commands.registerCommand('arc.managePostgres', async () => {
|
||||
// Controller information
|
||||
const controllerUrl = '';
|
||||
const auth = new BasicAuth('', '');
|
||||
|
||||
// Postgres information
|
||||
const dbNamespace = '';
|
||||
const dbName = '';
|
||||
|
||||
try {
|
||||
const controllerModel = new ControllerModel(controllerUrl, auth);
|
||||
const postgresModel = new PostgresModel(controllerUrl, auth, dbNamespace, dbName);
|
||||
const postgresDashboard = new PostgresDashboard(context, controllerModel, postgresModel);
|
||||
|
||||
await Promise.all([
|
||||
postgresDashboard.showDashboard(),
|
||||
controllerModel.refresh(),
|
||||
postgresModel.refresh()
|
||||
]);
|
||||
} catch (error) {
|
||||
vscode.window.showErrorMessage(loc.failedToManagePostgres(`${dbNamespace}.${dbName}`, error));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function deactivate(): void {
|
||||
|
||||
@@ -66,6 +66,14 @@ export const running = localize('arc.running', "Running");
|
||||
export const connected = localize('arc.connected', "Connected");
|
||||
export const disconnected = localize('arc.disconnected', "Disconnected");
|
||||
export const loading = localize('arc.loading', "Loading...");
|
||||
export const refreshToEnterCredentials = localize('arc.refreshToEnterCredentials', "Refresh node to enter credentials");
|
||||
export const connectToController = localize('arc.connectToController', "Connect to Existing Controller");
|
||||
export const controllerUrl = localize('arc.controllerUrl', "Controller URL");
|
||||
export const username = localize('arc.username', "Username");
|
||||
export const password = localize('arc.password', "Password");
|
||||
export const rememberPassword = localize('arc.rememberPassword', "Remember Password");
|
||||
export const connect = localize('arc.connect', "Connect");
|
||||
export const cancel = localize('arc.cancel', "Cancel");
|
||||
|
||||
// Database States - see https://docs.microsoft.com/sql/relational-databases/databases/database-states
|
||||
export const online = localize('arc.online', "Online");
|
||||
@@ -98,16 +106,10 @@ export const storagePerNode = localize('arc.storagePerNode', "storage per node")
|
||||
export const arcResources = localize('arc.arcResources', "Azure Arc Resources");
|
||||
|
||||
export function databaseCreated(name: string): string { return localize('arc.databaseCreated', "Database {0} created", name); }
|
||||
export function databaseCreationFailed(name: string, error: any): string { return localize('arc.databaseCreationFailed', "Failed to create database {0}. {1}", name, getErrorMessage(error)); }
|
||||
export function passwordReset(name: string): string { return localize('arc.passwordReset', "Password reset for service {0}", name); }
|
||||
export function passwordResetFailed(name: string, error: any): string { return localize('arc.passwordResetFailed', "Failed to reset password for service {0}. {1}", name, getErrorMessage(error)); }
|
||||
export function resourceDeleted(name: string): string { return localize('arc.resourceDeleted', "Resource '{0}' deleted", name); }
|
||||
export function resourceDeletionFailed(name: string, error: any): string { return localize('arc.resourceDeletionFailed', "Failed to delete resource {0}. {1}", name, getErrorMessage(error)); }
|
||||
export function couldNotFindAzureResource(name: string): string { return localize('arc.couldNotFindAzureResource', "Could not find Azure resource for {0}", name); }
|
||||
export function copiedToClipboard(name: string): string { return localize('arc.copiedToClipboard', "{0} copied to clipboard", name); }
|
||||
export function refreshFailed(error: any): string { return localize('arc.refreshFailed', "Refresh failed. {0}", getErrorMessage(error)); }
|
||||
export function failedToManagePostgres(name: string, error: any): string { return localize('arc.failedToManagePostgres', "Failed to manage Postgres {0}. {1}", name, getErrorMessage(error)); }
|
||||
export function openDashboardFailed(error: any): string { return localize('arc.openDashboardFailed', "Error opening dashboard. {0}", getErrorMessage(error)); }
|
||||
export function clickTheTroubleshootButton(resourceType: string): string { return localize('arc.clickTheTroubleshootButton', "Click the troubleshoot button to open the Azure Arc {0} troubleshooting notebook.", resourceType); }
|
||||
export function numVCores(vCores: string): string {
|
||||
const numCores = +vCores;
|
||||
@@ -120,3 +122,11 @@ export function numVCores(vCores: string): string {
|
||||
export function couldNotFindRegistration(namespace: string, name: string) { return localize('arc.couldNotFindRegistration', "Could not find controller registration for {0} ({1})", name, namespace); }
|
||||
export function resourceDeletionWarning(namespace: string, name: string): string { return localize('arc.resourceDeletionWarning', "Warning! Deleting a resource is permanent and cannot be undone. To delete the resource '{0}.{1}' type the name '{1}' below to proceed.", namespace, name); }
|
||||
export function invalidResourceDeletionName(name: string): string { return localize('arc.invalidResourceDeletionName', "The value '{0}' does not match the instance name. Try again or press escape to exit", name); }
|
||||
|
||||
// Errors
|
||||
export function refreshFailed(error: any): string { return localize('arc.refreshFailed', "Refresh failed. {0}", getErrorMessage(error)); }
|
||||
export function openDashboardFailed(error: any): string { return localize('arc.openDashboardFailed', "Error opening dashboard. {0}", getErrorMessage(error)); }
|
||||
export function resourceDeletionFailed(name: string, error: any): string { return localize('arc.resourceDeletionFailed', "Failed to delete resource {0}. {1}", name, getErrorMessage(error)); }
|
||||
export function passwordResetFailed(name: string, error: any): string { return localize('arc.passwordResetFailed', "Failed to reset password for service {0}. {1}", name, getErrorMessage(error)); }
|
||||
export function databaseCreationFailed(name: string, error: any): string { return localize('arc.databaseCreationFailed', "Failed to create database {0}. {1}", name, getErrorMessage(error)); }
|
||||
export function connectToControllerFailed(url: string, error: any): string { return localize('arc.connectToControllerFailed', "Could not connect to controller {0}. {1}", url, getErrorMessage(error)); }
|
||||
|
||||
@@ -4,10 +4,18 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { Authentication } from '../controller/auth';
|
||||
import { Authentication, BasicAuth } from '../controller/auth';
|
||||
import { EndpointsRouterApi, EndpointModel, RegistrationRouterApi, RegistrationResponse, TokenRouterApi, SqlInstanceRouterApi } from '../controller/generated/v1/api';
|
||||
import { parseEndpoint, parseInstanceName } from '../common/utils';
|
||||
import { ResourceType } from '../constants';
|
||||
import { ConnectToControllerDialog } from '../ui/dialogs/connectControllerDialog';
|
||||
import { AzureArcTreeDataProvider } from '../ui/tree/azureArcTreeDataProvider';
|
||||
|
||||
export type ControllerInfo = {
|
||||
url: string,
|
||||
username: string,
|
||||
rememberPassword: boolean
|
||||
};
|
||||
|
||||
export interface Registration extends RegistrationResponse {
|
||||
externalIp?: string;
|
||||
@@ -23,6 +31,7 @@ export class ControllerModel {
|
||||
private _namespace: string = '';
|
||||
private _registrations: Registration[] = [];
|
||||
private _controllerRegistration: Registration | undefined = undefined;
|
||||
private _auth: Authentication | undefined = undefined;
|
||||
|
||||
private readonly _onEndpointsUpdated = new vscode.EventEmitter<EndpointModel[]>();
|
||||
private readonly _onRegistrationsUpdated = new vscode.EventEmitter<Registration[]>();
|
||||
@@ -30,22 +39,42 @@ export class ControllerModel {
|
||||
public onRegistrationsUpdated = this._onRegistrationsUpdated.event;
|
||||
public endpointsLastUpdated?: Date;
|
||||
public registrationsLastUpdated?: Date;
|
||||
public get auth(): Authentication | undefined {
|
||||
return this._auth;
|
||||
}
|
||||
|
||||
constructor(public readonly controllerUrl: string, public readonly auth: Authentication) {
|
||||
this._endpointsRouter = new EndpointsRouterApi(controllerUrl);
|
||||
this._endpointsRouter.setDefaultAuthentication(auth);
|
||||
|
||||
this._tokenRouter = new TokenRouterApi(controllerUrl);
|
||||
this._tokenRouter.setDefaultAuthentication(auth);
|
||||
|
||||
this._registrationRouter = new RegistrationRouterApi(controllerUrl);
|
||||
this._registrationRouter.setDefaultAuthentication(auth);
|
||||
|
||||
this._sqlInstanceRouter = new SqlInstanceRouterApi(controllerUrl);
|
||||
this._sqlInstanceRouter.setDefaultAuthentication(auth);
|
||||
constructor(private _treeDataProvider: AzureArcTreeDataProvider, public info: ControllerInfo, password?: string) {
|
||||
this._endpointsRouter = new EndpointsRouterApi(this.info.url);
|
||||
this._tokenRouter = new TokenRouterApi(this.info.url);
|
||||
this._registrationRouter = new RegistrationRouterApi(this.info.url);
|
||||
this._sqlInstanceRouter = new SqlInstanceRouterApi(this.info.url);
|
||||
if (password) {
|
||||
this.setAuthentication(new BasicAuth(this.info.username, password));
|
||||
}
|
||||
}
|
||||
|
||||
public async refresh(): Promise<void> {
|
||||
// We haven't gotten our password yet, fetch it now
|
||||
if (!this._auth) {
|
||||
let password = '';
|
||||
if (this.info.rememberPassword) {
|
||||
// It should be in the credentials store, get it from there
|
||||
password = await this._treeDataProvider.getPassword(this.info);
|
||||
}
|
||||
if (password) {
|
||||
this.setAuthentication(new BasicAuth(this.info.username, password));
|
||||
} else {
|
||||
// No password yet so prompt for it from the user
|
||||
const dialog = new ConnectToControllerDialog(this._treeDataProvider);
|
||||
dialog.showDialog(this.info);
|
||||
const model = await dialog.waitForClose();
|
||||
if (model) {
|
||||
this._treeDataProvider.addOrUpdateController(model.controllerModel, model.password, false);
|
||||
this.setAuthentication(new BasicAuth(this.info.username, model.password));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
await Promise.all([
|
||||
this._endpointsRouter.apiV1BdcEndpointsGet().then(response => {
|
||||
this._endpoints = response.body;
|
||||
@@ -88,9 +117,31 @@ export class ControllerModel {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the specified MIAA resource from the controller
|
||||
* @param namespace The namespace of the resource
|
||||
* @param name The name of the resource
|
||||
*/
|
||||
public async miaaDelete(namespace: string, name: string): Promise<void> {
|
||||
await this._sqlInstanceRouter.apiV1HybridSqlNsNameDelete(namespace, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether this model is for the same controller as another
|
||||
* @param other The other instance to test
|
||||
*/
|
||||
public equals(other: ControllerModel): boolean {
|
||||
return this.info.url === other.info.url &&
|
||||
this.info.username === other.info.username;
|
||||
}
|
||||
|
||||
private setAuthentication(auth: Authentication): void {
|
||||
this._auth = auth;
|
||||
this._endpointsRouter.setDefaultAuthentication(auth);
|
||||
this._tokenRouter.setDefaultAuthentication(auth);
|
||||
this._registrationRouter.setDefaultAuthentication(auth);
|
||||
this._sqlInstanceRouter.setDefaultAuthentication(auth);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as should from 'should';
|
||||
import 'mocha';
|
||||
import { resourceTypeToDisplayName, parseEndpoint } from '../../common/utils';
|
||||
import { resourceTypeToDisplayName, parseEndpoint, parseInstanceName } from '../../common/utils';
|
||||
|
||||
import * as loc from '../../localizedConstants';
|
||||
import { ResourceType } from '../../constants';
|
||||
@@ -44,3 +44,20 @@ describe('parseEndpoint Method Tests', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseInstanceName Method Tests', () => {
|
||||
it('Should parse valid instanceName with namespace correctly', function (): void {
|
||||
should(parseInstanceName('mynamespace_myinstance')).equal('myinstance');
|
||||
});
|
||||
|
||||
it('Should parse valid instanceName without namespace correctly', function (): void {
|
||||
should(parseInstanceName('myinstance')).equal('myinstance');
|
||||
});
|
||||
|
||||
it('Should return empty string when undefined value passed in', function (): void {
|
||||
should(parseInstanceName(undefined)).equal('');
|
||||
});
|
||||
|
||||
it('Should return empty string when empty string value passed in', function (): void {
|
||||
should(parseInstanceName('')).equal('');
|
||||
});
|
||||
});
|
||||
|
||||
102
extensions/arc/src/test/ui/tree/azureArcTreeDataProvider.test.ts
Normal file
102
extensions/arc/src/test/ui/tree/azureArcTreeDataProvider.test.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as should from 'should';
|
||||
import 'mocha';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { AzureArcTreeDataProvider } from '../../../ui/tree/azureArcTreeDataProvider';
|
||||
import { ControllerModel } from '../../../models/controllerModel';
|
||||
import { ControllerTreeNode } from '../../../ui/tree/controllerTreeNode';
|
||||
import { LoadingControllerNode } from '../../../ui/tree/loadingTreeNode';
|
||||
|
||||
describe('AzureArcTreeDataProvider tests', function (): void {
|
||||
let treeDataProvider: AzureArcTreeDataProvider;
|
||||
beforeEach(function (): void {
|
||||
const mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
|
||||
const mockGlobalState = TypeMoq.Mock.ofType<vscode.Memento>();
|
||||
mockGlobalState.setup(x => x.update(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve());
|
||||
mockExtensionContext.setup(x => x.globalState).returns(() => mockGlobalState.object);
|
||||
//treeDataProviderMock = TypeMoq.Mock.ofType<AzureArcTreeDataProvider>();
|
||||
treeDataProvider = new AzureArcTreeDataProvider(mockExtensionContext.object);
|
||||
});
|
||||
|
||||
describe('addOrUpdateController', function (): void {
|
||||
it('Multiple Controllers are added correctly', async function (): Promise<void> {
|
||||
treeDataProvider['_loading'] = false;
|
||||
let children = await treeDataProvider.getChildren();
|
||||
should(children.length).equal(0, 'There initially shouldn\'t be any children');
|
||||
const controllerModelMock = TypeMoq.Mock.ofType<ControllerModel>();
|
||||
await treeDataProvider.addOrUpdateController(controllerModelMock.object, '');
|
||||
should(children.length).equal(1, 'Controller node should be added correctly');
|
||||
|
||||
// Add a couple more
|
||||
const controllerModelMock2 = TypeMoq.Mock.ofType<ControllerModel>();
|
||||
const controllerModelMock3 = TypeMoq.Mock.ofType<ControllerModel>();
|
||||
await treeDataProvider.addOrUpdateController(controllerModelMock2.object, '');
|
||||
await treeDataProvider.addOrUpdateController(controllerModelMock3.object, '');
|
||||
should(children.length).equal(3, 'Additional Controller nodes should be added correctly');
|
||||
});
|
||||
|
||||
it('Adding a Controller more than once doesn\'t create duplicates', async function (): Promise<void> {
|
||||
treeDataProvider['_loading'] = false;
|
||||
let children = await treeDataProvider.getChildren();
|
||||
should(children.length).equal(0, 'There initially shouldn\'t be any children');
|
||||
const controllerModel = new ControllerModel(treeDataProvider, { url: '127.0.0.1', username: 'sa', rememberPassword: true });
|
||||
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||
should(children.length).equal(1, 'Controller node should be added correctly');
|
||||
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||
should(children.length).equal(1, 'Shouldn\'t add duplicate controller node');
|
||||
});
|
||||
|
||||
it('Updating an existing controller works as expected', async function (): Promise<void> {
|
||||
treeDataProvider['_loading'] = false;
|
||||
let children = await treeDataProvider.getChildren();
|
||||
should(children.length).equal(0, 'There initially shouldn\'t be any children');
|
||||
const controllerModel = new ControllerModel(treeDataProvider, { url: '127.0.0.1', username: 'sa', rememberPassword: true });
|
||||
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||
should(children.length).equal(1, 'Controller node should be added correctly');
|
||||
should((<ControllerTreeNode>children[0]).model.info.rememberPassword).be.true('Info was not set correctly initially');
|
||||
const controllerModel2 = new ControllerModel(treeDataProvider, { url: '127.0.0.1', username: 'sa', rememberPassword: false });
|
||||
await treeDataProvider.addOrUpdateController(controllerModel2, '');
|
||||
should(children.length).equal(1, 'Shouldn\'t add duplicate controller node');
|
||||
should((<ControllerTreeNode>children[0]).model.info.rememberPassword).be.false('Info was not updated correctly');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getChildren', function (): void {
|
||||
it('should return a loading node before loading stored controllers is completed', async function (): Promise<void> {
|
||||
treeDataProvider['_loading'] = true;
|
||||
let children = await treeDataProvider.getChildren();
|
||||
should(children.length).equal(1, 'While loading we should return the loading node');
|
||||
should(children[0] instanceof LoadingControllerNode).be.true('Node returned was not a LoadingControllerNode');
|
||||
});
|
||||
|
||||
it('should return no children after loading', async function (): Promise<void> {
|
||||
treeDataProvider['_loading'] = false;
|
||||
let children = await treeDataProvider.getChildren();
|
||||
should(children.length).equal(0, 'After loading we should have 0 children');
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeController', function (): void {
|
||||
it('removing a controller should work as expected', async function (): Promise<void> {
|
||||
treeDataProvider['_loading'] = false;
|
||||
const controllerModel = new ControllerModel(treeDataProvider, { url: '127.0.0.1', username: 'sa', rememberPassword: true });
|
||||
const controllerModel2 = new ControllerModel(treeDataProvider, { url: '127.0.0.2', username: 'cloudsa', rememberPassword: true });
|
||||
await treeDataProvider.addOrUpdateController(controllerModel, '');
|
||||
await treeDataProvider.addOrUpdateController(controllerModel2, '');
|
||||
const children = <ControllerTreeNode[]>(await treeDataProvider.getChildren());
|
||||
await treeDataProvider.removeController(children[0]);
|
||||
should((await treeDataProvider.getChildren()).length).equal(1, 'Node should have been removed');
|
||||
await treeDataProvider.removeController(children[0]);
|
||||
should((await treeDataProvider.getChildren()).length).equal(1, 'Removing same node again should do nothing');
|
||||
await treeDataProvider.removeController(children[1]);
|
||||
should((await treeDataProvider.getChildren()).length).equal(0, 'Removing other node should work');
|
||||
await treeDataProvider.removeController(children[1]);
|
||||
should((await treeDataProvider.getChildren()).length).equal(0, 'Removing other node again should do nothing');
|
||||
});
|
||||
});
|
||||
});
|
||||
110
extensions/arc/src/ui/dialogs/connectControllerDialog.ts
Normal file
110
extensions/arc/src/ui/dialogs/connectControllerDialog.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import * as loc from '../../localizedConstants';
|
||||
import { AzureArcTreeDataProvider } from '../tree/azureArcTreeDataProvider';
|
||||
import { ControllerModel, ControllerInfo } from '../../models/controllerModel';
|
||||
import { Deferred } from '../../common/promise';
|
||||
|
||||
export type ConnectToControllerDialogModel = { controllerModel: ControllerModel, password: string };
|
||||
|
||||
export class ConnectToControllerDialog {
|
||||
private modelBuilder!: azdata.ModelBuilder;
|
||||
|
||||
private urlInputBox!: azdata.InputBoxComponent;
|
||||
private usernameInputBox!: azdata.InputBoxComponent;
|
||||
private passwordInputBox!: azdata.InputBoxComponent;
|
||||
private rememberPwCheckBox!: azdata.CheckBoxComponent;
|
||||
|
||||
private _completionPromise = new Deferred<ConnectToControllerDialogModel | undefined>();
|
||||
|
||||
constructor(private _treeDataProvider: AzureArcTreeDataProvider) { }
|
||||
|
||||
public showDialog(controllerInfo?: ControllerInfo): void {
|
||||
const dialog = azdata.window.createModelViewDialog(loc.connectToController);
|
||||
dialog.cancelButton.onClick(() => this.handleCancel());
|
||||
dialog.registerContent(async view => {
|
||||
this.modelBuilder = view.modelBuilder;
|
||||
|
||||
this.urlInputBox = this.modelBuilder.inputBox()
|
||||
.withProperties<azdata.InputBoxProperties>({
|
||||
value: controllerInfo?.url,
|
||||
// If we have a model then we're editing an existing connection so don't let them modify the URL
|
||||
readOnly: !!controllerInfo
|
||||
}).component();
|
||||
this.usernameInputBox = this.modelBuilder.inputBox()
|
||||
.withProperties<azdata.InputBoxProperties>({
|
||||
value: controllerInfo?.username
|
||||
}).component();
|
||||
this.passwordInputBox = this.modelBuilder.inputBox()
|
||||
.withProperties<azdata.InputBoxProperties>({
|
||||
inputType: 'password',
|
||||
})
|
||||
.component();
|
||||
this.rememberPwCheckBox = this.modelBuilder.checkBox()
|
||||
.withProperties<azdata.CheckBoxProperties>({
|
||||
label: loc.rememberPassword,
|
||||
checked: controllerInfo?.rememberPassword
|
||||
}).component();
|
||||
|
||||
let formModel = this.modelBuilder.formContainer()
|
||||
.withFormItems([{
|
||||
components: [
|
||||
{
|
||||
component: this.urlInputBox,
|
||||
title: loc.controllerUrl,
|
||||
required: true
|
||||
}, {
|
||||
component: this.usernameInputBox,
|
||||
title: loc.username,
|
||||
required: true
|
||||
}, {
|
||||
component: this.passwordInputBox,
|
||||
title: loc.password,
|
||||
required: true
|
||||
}, {
|
||||
component: this.rememberPwCheckBox,
|
||||
title: ''
|
||||
}
|
||||
],
|
||||
title: ''
|
||||
}]).withLayout({ width: '100%' }).component();
|
||||
await view.initializeModel(formModel);
|
||||
this.urlInputBox.focus();
|
||||
});
|
||||
|
||||
dialog.registerCloseValidator(async () => await this.validate());
|
||||
dialog.okButton.label = loc.connect;
|
||||
dialog.cancelButton.label = loc.cancel;
|
||||
azdata.window.openDialog(dialog);
|
||||
}
|
||||
|
||||
private async validate(): Promise<boolean> {
|
||||
if (!this.urlInputBox.value || !this.usernameInputBox.value || !this.passwordInputBox.value) {
|
||||
return false;
|
||||
}
|
||||
const controllerInfo: ControllerInfo = { url: this.urlInputBox.value, username: this.usernameInputBox.value, rememberPassword: this.rememberPwCheckBox.checked ?? false };
|
||||
const controllerModel = new ControllerModel(this._treeDataProvider, controllerInfo, this.passwordInputBox.value);
|
||||
try {
|
||||
// Validate that we can connect to the controller
|
||||
await controllerModel.refresh();
|
||||
} catch (err) {
|
||||
vscode.window.showErrorMessage(loc.connectToControllerFailed(this.urlInputBox.value, err));
|
||||
return false;
|
||||
}
|
||||
this._completionPromise.resolve({ controllerModel: controllerModel, password: this.passwordInputBox.value });
|
||||
return true;
|
||||
}
|
||||
|
||||
private handleCancel(): void {
|
||||
this._completionPromise.resolve(undefined);
|
||||
}
|
||||
|
||||
public waitForClose(): Promise<ConnectToControllerDialogModel | undefined> {
|
||||
return this._completionPromise.promise;
|
||||
}
|
||||
}
|
||||
113
extensions/arc/src/ui/tree/azureArcTreeDataProvider.ts
Normal file
113
extensions/arc/src/ui/tree/azureArcTreeDataProvider.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import { ControllerTreeNode } from './controllerTreeNode';
|
||||
import { TreeNode } from './treeNode';
|
||||
import { LoadingControllerNode as LoadingTreeNode } from './loadingTreeNode';
|
||||
import { ControllerModel, ControllerInfo } from '../../models/controllerModel';
|
||||
|
||||
const mementoToken = 'arcControllers';
|
||||
|
||||
/**
|
||||
* The TreeDataProvider for the Azure Arc view, which displays a list of registered
|
||||
* controllers and the resources under them.
|
||||
*/
|
||||
export class AzureArcTreeDataProvider implements vscode.TreeDataProvider<TreeNode> {
|
||||
|
||||
private _credentialsProvider = azdata.credentials.getProvider('arcControllerPasswords');
|
||||
private _onDidChangeTreeData: vscode.EventEmitter<TreeNode | undefined> = new vscode.EventEmitter<TreeNode | undefined>();
|
||||
readonly onDidChangeTreeData: vscode.Event<TreeNode | undefined> = this._onDidChangeTreeData.event;
|
||||
|
||||
private _loading: boolean = true;
|
||||
private _loadingNode = new LoadingTreeNode();
|
||||
|
||||
private _controllerNodes: ControllerTreeNode[] = [];
|
||||
|
||||
constructor(private _context: vscode.ExtensionContext) {
|
||||
this.loadSavedControllers().catch(err => console.log(`Error loading saved Arc controllers ${err}`));
|
||||
}
|
||||
|
||||
public async getChildren(element?: TreeNode): Promise<TreeNode[]> {
|
||||
if (this._loading) {
|
||||
return [this._loadingNode];
|
||||
}
|
||||
|
||||
if (element) {
|
||||
return element.getChildren();
|
||||
} else {
|
||||
return this._controllerNodes;
|
||||
}
|
||||
}
|
||||
|
||||
public getTreeItem(element: TreeNode): TreeNode | Thenable<TreeNode> {
|
||||
return element;
|
||||
}
|
||||
|
||||
public async addOrUpdateController(model: ControllerModel, password: string, refreshTree = true): Promise<void> {
|
||||
const controllerNode = this._controllerNodes.find(node => model.equals(node.model));
|
||||
if (controllerNode) {
|
||||
controllerNode.model.info = model.info;
|
||||
} else {
|
||||
this._controllerNodes.push(new ControllerTreeNode(model, this._context));
|
||||
}
|
||||
await this.updatePassword(model, password);
|
||||
if (refreshTree) {
|
||||
this._onDidChangeTreeData.fire(undefined);
|
||||
}
|
||||
await this.saveControllers();
|
||||
}
|
||||
|
||||
public async removeController(controllerNode: ControllerTreeNode): Promise<void> {
|
||||
this._controllerNodes = this._controllerNodes.filter(node => node !== controllerNode);
|
||||
this._onDidChangeTreeData.fire(undefined);
|
||||
await this.saveControllers();
|
||||
}
|
||||
|
||||
public async getPassword(info: ControllerInfo): Promise<string> {
|
||||
const provider = await this._credentialsProvider;
|
||||
const credential = await provider.readCredential(getCredentialId(info));
|
||||
return credential.password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the specified node, or the entire tree if node is undefined
|
||||
* @param node The node to refresh, or undefined for the whole tree
|
||||
*/
|
||||
public refreshNode(node: TreeNode | undefined): void {
|
||||
this._onDidChangeTreeData.fire(node);
|
||||
}
|
||||
|
||||
private async updatePassword(model: ControllerModel, password: string): Promise<void> {
|
||||
const provider = await this._credentialsProvider;
|
||||
if (model.info.rememberPassword) {
|
||||
provider.saveCredential(getCredentialId(model.info), password);
|
||||
} else {
|
||||
provider.deleteCredential(getCredentialId(model.info));
|
||||
}
|
||||
}
|
||||
|
||||
private async loadSavedControllers(): Promise<void> {
|
||||
try {
|
||||
const controllerMementos: ControllerInfo[] = this._context.globalState.get(mementoToken) || [];
|
||||
this._controllerNodes = controllerMementos.map(memento => {
|
||||
const controllerModel = new ControllerModel(this, memento);
|
||||
return new ControllerTreeNode(controllerModel, this._context);
|
||||
});
|
||||
} finally {
|
||||
this._loading = false;
|
||||
this._onDidChangeTreeData.fire(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
private async saveControllers(): Promise<void> {
|
||||
await this._context.globalState.update(mementoToken, this._controllerNodes.map(node => node.model.info));
|
||||
}
|
||||
}
|
||||
|
||||
function getCredentialId(info: ControllerInfo): string {
|
||||
return `${info.url}::${info.username}`;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { ControllerTreeNode } from './controllerTreeNode';
|
||||
import { TreeNode } from './treeNode';
|
||||
import { LoadingControllerNode as LoadingTreeNode } from './loadingTreeNode';
|
||||
import { ControllerModel } from '../../models/controllerModel';
|
||||
|
||||
/**
|
||||
* The TreeDataProvider for the Azure Arc view, which displays a list of registered
|
||||
* controllers and the resources under them.
|
||||
*/
|
||||
export class AzureArcTreeDataProvider implements vscode.TreeDataProvider<TreeNode> {
|
||||
|
||||
private _onDidChangeTreeData: vscode.EventEmitter<TreeNode | undefined> = new vscode.EventEmitter<TreeNode | undefined>();
|
||||
readonly onDidChangeTreeData: vscode.Event<TreeNode | undefined> = this._onDidChangeTreeData.event;
|
||||
|
||||
private _loading: boolean = true;
|
||||
private _loadingNode = new LoadingTreeNode();
|
||||
|
||||
private _controllerNodes: ControllerTreeNode[] = [];
|
||||
|
||||
constructor(private _context: vscode.ExtensionContext) {
|
||||
// TODO:
|
||||
setTimeout(() => {
|
||||
this._loading = false;
|
||||
this._onDidChangeTreeData.fire(undefined);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
public async getChildren(element?: TreeNode): Promise<TreeNode[]> {
|
||||
if (this._loading) {
|
||||
return [this._loadingNode];
|
||||
}
|
||||
|
||||
if (element) {
|
||||
return element.getChildren();
|
||||
} else {
|
||||
return this._controllerNodes;
|
||||
}
|
||||
}
|
||||
|
||||
public getTreeItem(element: TreeNode): TreeNode | Thenable<TreeNode> {
|
||||
return element;
|
||||
}
|
||||
|
||||
public addController(model: ControllerModel): void {
|
||||
this._controllerNodes.push(new ControllerTreeNode(model, this._context));
|
||||
this._onDidChangeTreeData.fire(undefined);
|
||||
}
|
||||
|
||||
public removeController(controllerNode: ControllerTreeNode): void {
|
||||
this._controllerNodes = this._controllerNodes.filter(node => node !== controllerNode);
|
||||
this._onDidChangeTreeData.fire(undefined);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import { ControllerDashboard } from '../dashboards/controller/controllerDashboar
|
||||
import { PostgresModel } from '../../models/postgresModel';
|
||||
import { parseInstanceName } from '../../common/utils';
|
||||
import { MiaaModel } from '../../models/miaaModel';
|
||||
import { Deferred } from '../../common/promise';
|
||||
import { RefreshTreeNode } from './refreshTreeNode';
|
||||
|
||||
/**
|
||||
* The TreeNode for displaying an Azure Arc Controller
|
||||
@@ -20,19 +22,31 @@ import { MiaaModel } from '../../models/miaaModel';
|
||||
export class ControllerTreeNode extends TreeNode {
|
||||
|
||||
private _children: TreeNode[] = [];
|
||||
private _childrenRefreshPromise = new Deferred();
|
||||
|
||||
constructor(private _model: ControllerModel, private _context: vscode.ExtensionContext) {
|
||||
super(_model.controllerUrl, vscode.TreeItemCollapsibleState.Collapsed, ResourceType.dataControllers);
|
||||
_model.onRegistrationsUpdated(registrations => this.refreshChildren(registrations));
|
||||
_model.refresh().catch(err => console.log(`Error refreshing Arc Controller model for tree node : ${err}`));
|
||||
constructor(public model: ControllerModel, private _context: vscode.ExtensionContext) {
|
||||
super(model.info.url, vscode.TreeItemCollapsibleState.Collapsed, ResourceType.dataControllers);
|
||||
model.onRegistrationsUpdated(registrations => this.refreshChildren(registrations));
|
||||
}
|
||||
|
||||
public async getChildren(): Promise<TreeNode[]> {
|
||||
// First reset our deferred promise so we're sure we'll get the refreshed children
|
||||
this._childrenRefreshPromise = new Deferred();
|
||||
try {
|
||||
await this.model.refresh();
|
||||
await this._childrenRefreshPromise.promise;
|
||||
} catch (err) {
|
||||
// Couldn't get the children and TreeView doesn't have a way to collapse a node
|
||||
// in a way that will refetch its children when expanded again so instead we
|
||||
// display a tempory node that will prompt the user to re-enter credentials
|
||||
return [new RefreshTreeNode(this)];
|
||||
}
|
||||
|
||||
return this._children;
|
||||
}
|
||||
|
||||
public async openDashboard(): Promise<void> {
|
||||
const controllerDashboard = new ControllerDashboard(this._model);
|
||||
const controllerDashboard = new ControllerDashboard(this.model);
|
||||
await controllerDashboard.showDashboard();
|
||||
}
|
||||
|
||||
@@ -44,13 +58,14 @@ export class ControllerTreeNode extends TreeNode {
|
||||
}
|
||||
switch (registration.instanceType) {
|
||||
case ResourceType.postgresInstances:
|
||||
const postgresModel = new PostgresModel(this._model.controllerUrl, this._model.auth, registration.instanceNamespace, parseInstanceName(registration.instanceName));
|
||||
return new PostgresTreeNode(postgresModel, this._model, this._context);
|
||||
const postgresModel = new PostgresModel(this.model.info.url, this.model.auth!, registration.instanceNamespace, parseInstanceName(registration.instanceName));
|
||||
return new PostgresTreeNode(postgresModel, this.model, this._context);
|
||||
case ResourceType.sqlManagedInstances:
|
||||
const miaaModel = new MiaaModel(this._model.controllerUrl, this._model.auth, registration.instanceNamespace, parseInstanceName(registration.instanceName));
|
||||
return new MiaaTreeNode(miaaModel, this._model);
|
||||
const miaaModel = new MiaaModel(this.model.info.url, this.model.auth!, registration.instanceNamespace, parseInstanceName(registration.instanceName));
|
||||
return new MiaaTreeNode(miaaModel, this.model);
|
||||
}
|
||||
return undefined;
|
||||
}).filter(item => item); // filter out invalid nodes (controllers or ones without required properties)
|
||||
this._childrenRefreshPromise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
25
extensions/arc/src/ui/tree/refreshTreeNode.ts
Normal file
25
extensions/arc/src/ui/tree/refreshTreeNode.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as loc from '../../localizedConstants';
|
||||
import { TreeNode } from './treeNode';
|
||||
import { refreshActionId } from '../../constants';
|
||||
|
||||
/**
|
||||
* A placeholder TreeNode to display when credentials weren't entered
|
||||
*/
|
||||
export class RefreshTreeNode extends TreeNode {
|
||||
|
||||
constructor(private _parent: TreeNode) {
|
||||
super(loc.refreshToEnterCredentials, vscode.TreeItemCollapsibleState.None, 'refresh');
|
||||
}
|
||||
|
||||
public command: vscode.Command = {
|
||||
command: refreshActionId,
|
||||
title: loc.refreshToEnterCredentials,
|
||||
arguments: [this._parent]
|
||||
};
|
||||
}
|
||||
@@ -322,6 +322,11 @@ charenc@~0.0.1:
|
||||
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
|
||||
integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
|
||||
|
||||
circular-json@^0.3.1:
|
||||
version "0.3.3"
|
||||
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
|
||||
integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==
|
||||
|
||||
color-convert@^1.9.0:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||
@@ -711,7 +716,7 @@ jsprim@^1.2.2:
|
||||
json-schema "0.2.3"
|
||||
verror "1.10.0"
|
||||
|
||||
lodash@^4.16.4, lodash@^4.17.13:
|
||||
lodash@^4.16.4, lodash@^4.17.13, lodash@^4.17.4:
|
||||
version "4.17.15"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
||||
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
||||
@@ -861,6 +866,11 @@ pify@^4.0.1:
|
||||
resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
|
||||
integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
|
||||
|
||||
postinstall-build@^5.0.1:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/postinstall-build/-/postinstall-build-5.0.3.tgz#238692f712a481d8f5bc8960e94786036241efc7"
|
||||
integrity sha512-vPvPe8TKgp4FLgY3+DfxCE5PIfoXBK2lyLfNCxsRbDsV6vS4oU5RG/IWxrblMn6heagbnMED3MemUQllQ2bQUg==
|
||||
|
||||
psl@^1.1.28:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
|
||||
@@ -1068,6 +1078,15 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
|
||||
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
|
||||
integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
|
||||
|
||||
typemoq@2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/typemoq/-/typemoq-2.1.0.tgz#4452ce360d92cf2a1a180f0c29de2803f87af1e8"
|
||||
integrity sha512-DtRNLb7x8yCTv/KHlwes+NI+aGb4Vl1iPC63Hhtcvk1DpxSAZzKWQv0RQFY0jX2Uqj0SDBNl8Na4e6MV6TNDgw==
|
||||
dependencies:
|
||||
circular-json "^0.3.1"
|
||||
lodash "^4.17.4"
|
||||
postinstall-build "^5.0.1"
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.2.2"
|
||||
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
|
||||
|
||||
@@ -52,6 +52,11 @@ echo *** starting agent tests ***
|
||||
echo ****************************
|
||||
call "%INTEGRATION_TEST_ELECTRON_PATH%" --extensionDevelopmentPath=%~dp0\..\extensions\agent --extensionTestsPath=%~dp0\..\extensions\agent\out\test --user-data-dir=%VSCODEUSERDATADIR% --extensions-dir=%VSCODEEXTENSIONSDIR% --remote-debugging-port=9222 --disable-telemetry --disable-crash-reporter --disable-updates --nogpu
|
||||
|
||||
echo **************************
|
||||
echo *** starting arc tests ***
|
||||
echo **************************
|
||||
call "%INTEGRATION_TEST_ELECTRON_PATH%" --extensionDevelopmentPath=%~dp0\..\extensions\arc --extensionTestsPath=%~dp0\..\extensions\arc\out\test --user-data-dir=%VSCODEUSERDATADIR% --extensions-dir=%VSCODEEXTENSIONSDIR% --remote-debugging-port=9222 --disable-telemetry --disable-crash-reporter --disable-updates --nogpu
|
||||
|
||||
echo ********************************
|
||||
echo *** starting azurecore tests ***
|
||||
echo ********************************
|
||||
|
||||
@@ -49,6 +49,11 @@ echo *** starting agent tests ***
|
||||
echo ****************************
|
||||
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX --extensionDevelopmentPath=$ROOT/extensions/agent --extensionTestsPath=$ROOT/extensions/agent/out/test --user-data-dir=$VSCODEUSERDATADIR --extensions-dir=$VSCODEEXTDIR --disable-telemetry --disable-crash-reporter --disable-updates --nogpu
|
||||
|
||||
echo **************************
|
||||
echo *** starting arc tests ***
|
||||
echo **************************
|
||||
"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX --extensionDevelopmentPath=$ROOT/extensions/arc --extensionTestsPath=$ROOT/extensions/arc/out/test --user-data-dir=$VSCODEUSERDATADIR --extensions-dir=$VSCODEEXTDIR --disable-telemetry --disable-crash-reporter --disable-updates --nogpu
|
||||
|
||||
echo ********************************
|
||||
echo *** starting azurecore tests ***
|
||||
echo ********************************
|
||||
|
||||
Reference in New Issue
Block a user