Fix arc node info updating (#12007)

* Fix arc node info updating

* Missed await
This commit is contained in:
Charles Gagnon
2020-08-28 15:53:17 -07:00
committed by GitHub
parent ceb4df5b8b
commit 47c1204e89
12 changed files with 179 additions and 50 deletions

View File

@@ -56,6 +56,10 @@
{
"command": "arc.refresh",
"title": "%command.refresh.title%"
},
{
"command": "arc.editConnection",
"title": "%command.editConnection.title%"
}
],
"menus": {
@@ -71,6 +75,10 @@
{
"command": "arc.refresh",
"when": "false"
},
{
"command": "arc.editConnection",
"when": "false"
}
],
"view/title": [
@@ -92,14 +100,19 @@
"group": "navigation@1"
},
{
"command": "arc.refresh",
"command": "arc.editConnection",
"when": "view == azureArc && viewItem == dataControllers",
"group": "navigation@2"
},
{
"command": "arc.removeController",
"command": "arc.refresh",
"when": "view == azureArc && viewItem == dataControllers",
"group": "navigation@3"
},
{
"command": "arc.removeController",
"when": "view == azureArc && viewItem == dataControllers",
"group": "navigation@4"
}
]
},
@@ -794,6 +807,7 @@
},
"dependencies": {
"request": "^2.88.0",
"uuid": "^8.3.0",
"vscode-nls": "^4.1.2"
},
"devDependencies": {
@@ -801,6 +815,7 @@
"@types/node": "^12.11.7",
"@types/request": "^2.48.3",
"@types/sinon": "^9.0.4",
"@types/uuid": "^8.3.0",
"mocha": "^5.2.0",
"mocha-junit-reporter": "^1.17.0",
"mocha-multi-reporters": "^1.1.7",

View File

@@ -9,6 +9,7 @@
"command.connectToController.title": "Connect to Existing Azure Arc Controller",
"command.removeController.title": "Remove Controller",
"command.refresh.title": "Refresh",
"command.editConnection.title": "Edit Connection",
"arc.openDashboard": "Manage",
"resource.type.azure.arc.display.name": "Azure Arc data controller",

View File

@@ -44,6 +44,15 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
await treeNode.openDashboard().catch(err => vscode.window.showErrorMessage(loc.openDashboardFailed(err)));
});
vscode.commands.registerCommand('arc.editConnection', async (treeNode: ControllerTreeNode) => {
const dialog = new ConnectToControllerDialog(treeDataProvider);
dialog.showDialog(treeNode.model.info, await treeDataProvider.getPassword(treeNode.model.info));
const model = await dialog.waitForClose();
if (model) {
await treeDataProvider.addOrUpdateController(model.controllerModel, model.password, true);
}
});
await checkArcDeploymentExtension();
}

View File

@@ -12,6 +12,7 @@ import * as loc from '../localizedConstants';
import { ConnectToControllerDialog } from '../ui/dialogs/connectControllerDialog';
export type ControllerInfo = {
id: string,
url: string,
name: string,
username: string,
@@ -41,19 +42,30 @@ export class ControllerModel {
private readonly _onConfigUpdated = new vscode.EventEmitter<azdataExt.DcConfigShowResult | undefined>();
private readonly _onEndpointsUpdated = new vscode.EventEmitter<azdataExt.DcEndpointListResult[]>();
private readonly _onRegistrationsUpdated = new vscode.EventEmitter<Registration[]>();
private readonly _onInfoUpdated = new vscode.EventEmitter<ControllerInfo>();
public onConfigUpdated = this._onConfigUpdated.event;
public onEndpointsUpdated = this._onEndpointsUpdated.event;
public onRegistrationsUpdated = this._onRegistrationsUpdated.event;
public onInfoUpdated = this._onInfoUpdated.event;
public configLastUpdated?: Date;
public endpointsLastUpdated?: Date;
public registrationsLastUpdated?: Date;
constructor(public treeDataProvider: AzureArcTreeDataProvider, public info: ControllerInfo, private _password?: string) {
constructor(public treeDataProvider: AzureArcTreeDataProvider, private _info: ControllerInfo, private _password?: string) {
this._azdataApi = <azdataExt.IExtension>vscode.extensions.getExtension(azdataExt.extension.name)?.exports;
}
public get info(): ControllerInfo {
return this._info;
}
public set info(value: ControllerInfo) {
this._info = value;
this._onInfoUpdated.fire(this._info);
}
/**
* Calls azdata login to set the context to this controller
* @param promptReconnect
@@ -187,15 +199,6 @@ export class ControllerModel {
*/
}
/**
* 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;
}
/**
* property to for use a display label for this controller
*/

View File

@@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { v4 as uuid } from 'uuid';
import { ControllerModel, ControllerInfo } from '../../models/controllerModel';
import { AzureArcTreeDataProvider } from '../../ui/tree/azureArcTreeDataProvider';
export class FakeControllerModel extends ControllerModel {
constructor(treeDataProvider?: AzureArcTreeDataProvider, info?: Partial<ControllerInfo>, password?: string) {
const _info: ControllerInfo = Object.assign({ id: uuid(), url: '', name: '', username: '', rememberPassword: false, resources: [] }, info);
super(treeDataProvider!, _info, password);
}
}

View File

@@ -9,8 +9,9 @@ import * as sinon from 'sinon';
import * as TypeMoq from 'typemoq';
import * as vscode from 'vscode';
import * as should from 'should';
import { v4 as uuid } from 'uuid';
import { ConnectToControllerDialog } from '../../ui/dialogs/connectControllerDialog';
import { ControllerModel } from '../../models/controllerModel';
import { ControllerModel, ControllerInfo } from '../../models/controllerModel';
import { AzureArcTreeDataProvider } from '../../ui/tree/azureArcTreeDataProvider';
import { UserCancelledError } from '../../common/utils';
@@ -36,7 +37,7 @@ describe('ControllerModel', function (): void {
it('Rejected with expected error when user cancels', async function (): Promise<void> {
// Returning an undefined model here indicates that the dialog closed without clicking "Ok" - usually through the user clicking "Cancel"
sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve(undefined));
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] });
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { id: uuid(), url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] });
await should(model.azdataLogin()).be.rejectedWith(new UserCancelledError());
});
@@ -55,7 +56,7 @@ describe('ControllerModel', function (): void {
azdataMock.setup(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => <any>Promise.resolve(undefined));
azdataExtApiMock.setup(x => x.azdata).returns(() => azdataMock.object);
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExtApiMock.object });
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] });
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { id: uuid(), url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] });
await model.azdataLogin();
azdataMock.verify(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), password), TypeMoq.Times.once());
@@ -78,10 +79,10 @@ describe('ControllerModel', function (): void {
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExtApiMock.object });
// Set up dialog to return new model with our password
const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] }, password);
const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { id: uuid(), url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] }, password);
sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve({ controllerModel: newModel, password: password }));
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] });
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { id: uuid(), url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] });
await model.azdataLogin();
azdataMock.verify(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), password), TypeMoq.Times.once());
@@ -103,10 +104,10 @@ describe('ControllerModel', function (): void {
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExtApiMock.object });
// Set up dialog to return new model with our new password from the reprompt
const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] }, password);
const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { id: uuid(), url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] }, password);
const waitForCloseStub = sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve({ controllerModel: newModel, password: password }));
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] });
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { id: uuid(), url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] });
await model.azdataLogin(true);
should(waitForCloseStub.called).be.true('waitForClose should have been called');
@@ -129,16 +130,72 @@ describe('ControllerModel', function (): void {
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExtApiMock.object });
// Set up dialog to return new model with our new password from the reprompt
const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] }, password);
const newModel = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { id: uuid(), url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] }, password);
const waitForCloseStub = sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve({ controllerModel: newModel, password: password }));
// Set up original model with a password
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] }, 'originalPassword');
const model = new ControllerModel(new AzureArcTreeDataProvider(mockExtensionContext.object), { id: uuid(), url: '127.0.0.1', username: 'admin', name: 'arc', rememberPassword: true, resources: [] }, 'originalPassword');
await model.azdataLogin(true);
should(waitForCloseStub.called).be.true('waitForClose should have been called');
azdataMock.verify(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), password), TypeMoq.Times.once());
});
it('Model values are updated correctly when modified during reconnect', async function (): Promise<void> {
const treeDataProvider = new AzureArcTreeDataProvider(mockExtensionContext.object);
// Set up cred store to return a password to start with
const credProviderMock = TypeMoq.Mock.ofType<azdata.CredentialProvider>();
credProviderMock.setup(x => x.readCredential(TypeMoq.It.isAny())).returns(() => Promise.resolve({ credentialId: 'id', password: 'originalPassword' }));
// Need to setup then when Promise.resolving a mocked object : https://github.com/florinn/typemoq/issues/66
credProviderMock.setup((x: any) => x.then).returns(() => undefined);
sinon.stub(azdata.credentials, 'getProvider').returns(Promise.resolve(credProviderMock.object));
const azdataExtApiMock = TypeMoq.Mock.ofType<azdataExt.IExtension>();
const azdataMock = TypeMoq.Mock.ofType<azdataExt.IAzdataApi>();
azdataMock.setup(x => x.login(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => <any>Promise.resolve(undefined));
azdataExtApiMock.setup(x => x.azdata).returns(() => azdataMock.object);
sinon.stub(vscode.extensions, 'getExtension').returns(<any>{ exports: azdataExtApiMock.object });
// Add existing model to provider
const originalPassword = 'originalPassword';
const model = new ControllerModel(
treeDataProvider,
{
id: uuid(),
url: '127.0.0.1',
username: 'admin',
name: 'arc',
rememberPassword: false,
resources: []
},
originalPassword
);
await treeDataProvider.addOrUpdateController(model, originalPassword);
const newInfo: ControllerInfo = {
id: model.info.id, // The ID stays the same since we're just re-entering information for the same model
url: 'newUrl',
username: 'newUser',
name: 'newName',
rememberPassword: true,
resources: []
};
const newPassword = 'newPassword';
// Set up dialog to return new model with our new password from the reprompt
const newModel = new ControllerModel(
treeDataProvider,
newInfo,
newPassword);
const waitForCloseStub = sinon.stub(ConnectToControllerDialog.prototype, 'waitForClose').returns(Promise.resolve(
{ controllerModel: newModel, password: newPassword }));
await model.azdataLogin(true);
should(waitForCloseStub.called).be.true('waitForClose should have been called');
should((await treeDataProvider.getChildren()).length).equal(1, 'Tree Data provider should still only have 1 node');
should(model.info).deepEqual(newInfo, 'Model info should have been updated');
});
});
});

View File

@@ -8,6 +8,7 @@ import * as sinon from 'sinon';
import { ControllerInfo, ControllerModel } from '../../../models/controllerModel';
import { ConnectToControllerDialog } from '../../../ui/dialogs/connectControllerDialog';
import * as loc from '../../../localizedConstants';
import { v4 as uuid } from 'uuid';
describe('ConnectControllerDialog', function (): void {
afterEach(function (): void {
@@ -30,7 +31,7 @@ describe('ConnectControllerDialog', function (): void {
it('validate returns false if controller refresh fails', async function (): Promise<void> {
sinon.stub(ControllerModel.prototype, 'refresh').returns(Promise.reject('Controller refresh failed'));
const connectControllerDialog = new ConnectToControllerDialog(undefined!);
const info = { url: 'https://127.0.0.1:30080', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] };
const info = { id: uuid(), url: 'https://127.0.0.1:30080', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] };
connectControllerDialog.showDialog(info, 'pwd');
await connectControllerDialog.isInitialized;
const validateResult = await connectControllerDialog.validate();
@@ -39,36 +40,36 @@ describe('ConnectControllerDialog', function (): void {
it('validate replaces http with https', async function (): Promise<void> {
await validateConnectControllerDialog(
{ url: 'http://127.0.0.1:30081', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] },
{ id: uuid(), url: 'http://127.0.0.1:30081', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] },
'https://127.0.0.1:30081');
});
it('validate appends https if missing', async function (): Promise<void> {
await validateConnectControllerDialog({ url: '127.0.0.1:30080', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] },
await validateConnectControllerDialog({ id: uuid(), url: '127.0.0.1:30080', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] },
'https://127.0.0.1:30080');
});
it('validate appends default port if missing', async function (): Promise<void> {
await validateConnectControllerDialog({ url: 'https://127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] },
await validateConnectControllerDialog({ id: uuid(), url: 'https://127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] },
'https://127.0.0.1:30080');
});
it('validate appends both port and https if missing', async function (): Promise<void> {
await validateConnectControllerDialog({ url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] },
await validateConnectControllerDialog({ id: uuid(), url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] },
'https://127.0.0.1:30080');
});
for (const name of ['', undefined]) {
it.skip(`validate display name gets set to arc instance name for user chosen name of:${name}`, async function (): Promise<void> {
await validateConnectControllerDialog(
{ url: 'http://127.0.0.1:30081', name: name!, username: 'sa', rememberPassword: true, resources: [] },
{ id: uuid(), url: 'http://127.0.0.1:30081', name: name!, username: 'sa', rememberPassword: true, resources: [] },
'https://127.0.0.1:30081');
});
}
it.skip(`validate display name gets set to default data controller name for user chosen name of:'' and instanceName in explicably returned as undefined from the controller endpoint`, async function (): Promise<void> {
await validateConnectControllerDialog(
{ url: 'http://127.0.0.1:30081', name: '', username: 'sa', rememberPassword: true, resources: [] },
{ id: uuid(), url: 'http://127.0.0.1:30081', name: '', username: 'sa', rememberPassword: true, resources: [] },
'https://127.0.0.1:30081',
undefined);
});

View File

@@ -6,10 +6,12 @@
import 'mocha';
import * as should from 'should';
import * as TypeMoq from 'typemoq';
import { v4 as uuid } from 'uuid';
import * as vscode from 'vscode';
import { ControllerModel } from '../../../models/controllerModel';
import { ControllerModel, ControllerInfo } from '../../../models/controllerModel';
import { AzureArcTreeDataProvider } from '../../../ui/tree/azureArcTreeDataProvider';
import { ControllerTreeNode } from '../../../ui/tree/controllerTreeNode';
import { FakeControllerModel } from '../../mocks/fakeControllerModel';
describe('AzureArcTreeDataProvider tests', function (): void {
let treeDataProvider: AzureArcTreeDataProvider;
@@ -27,15 +29,17 @@ describe('AzureArcTreeDataProvider tests', function (): 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, '');
const controllerModel = new FakeControllerModel();
await treeDataProvider.addOrUpdateController(controllerModel, '');
children = await treeDataProvider.getChildren();
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, '');
const controllerModel2 = new FakeControllerModel();
const controllerModel3 = new FakeControllerModel();
await treeDataProvider.addOrUpdateController(controllerModel2, '');
await treeDataProvider.addOrUpdateController(controllerModel3, '');
children = await treeDataProvider.getChildren();
should(children.length).equal(3, 'Additional Controller nodes should be added correctly');
});
@@ -43,7 +47,7 @@ describe('AzureArcTreeDataProvider tests', function (): 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', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] });
const controllerModel = new ControllerModel(treeDataProvider, { id: uuid(), url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] });
await treeDataProvider.addOrUpdateController(controllerModel, '');
should(children.length).equal(1, 'Controller node should be added correctly');
await treeDataProvider.addOrUpdateController(controllerModel, '');
@@ -54,14 +58,16 @@ describe('AzureArcTreeDataProvider tests', function (): 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', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] });
const originalInfo: ControllerInfo = { id: uuid(), url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] };
const controllerModel = new ControllerModel(treeDataProvider, originalInfo);
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', name: 'my-arc', username: 'sa', rememberPassword: false, resources: [] });
should((<ControllerTreeNode>children[0]).model.info).deepEqual(originalInfo);
const newInfo = { id: originalInfo.id, url: '1.1.1.1', name: 'new-name', username: 'admin', rememberPassword: false, resources: [] };
const controllerModel2 = new ControllerModel(treeDataProvider, newInfo);
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');
should((<ControllerTreeNode>children[0]).model.info).deepEqual(newInfo);
});
});
@@ -82,8 +88,8 @@ describe('AzureArcTreeDataProvider tests', function (): void {
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', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] });
const controllerModel2 = new ControllerModel(treeDataProvider, { url: '127.0.0.2', name: 'my-arc', username: 'cloudsa', rememberPassword: true, resources: [] });
const controllerModel = new ControllerModel(treeDataProvider, { id: uuid(), url: '127.0.0.1', name: 'my-arc', username: 'sa', rememberPassword: true, resources: [] });
const controllerModel2 = new ControllerModel(treeDataProvider, { id: uuid(), url: '127.0.0.2', name: 'my-arc', username: 'cloudsa', rememberPassword: true, resources: [] });
await treeDataProvider.addOrUpdateController(controllerModel, '');
await treeDataProvider.addOrUpdateController(controllerModel2, '');
const children = <ControllerTreeNode[]>(await treeDataProvider.getChildren());

View File

@@ -5,6 +5,7 @@
import * as azdata from 'azdata';
import * as vscode from 'vscode';
import { v4 as uuid } from 'uuid';
import { Deferred } from '../../common/promise';
import * as loc from '../../localizedConstants';
import { ControllerInfo, ControllerModel } from '../../models/controllerModel';
@@ -23,11 +24,14 @@ export class ConnectToControllerDialog extends InitializingComponent {
private _completionPromise = new Deferred<ConnectToControllerDialogModel | undefined>();
private _id!: string;
constructor(private _treeDataProvider: AzureArcTreeDataProvider) {
super();
}
public showDialog(controllerInfo?: ControllerInfo, password?: string): azdata.window.Dialog {
this._id = controllerInfo?.id ?? uuid();
const dialog = azdata.window.createModelViewDialog(loc.connectToController);
dialog.cancelButton.onClick(() => this.handleCancel());
dialog.registerContent(async view => {
@@ -115,6 +119,7 @@ export class ConnectToControllerDialog extends InitializingComponent {
url = `${url}:30080`;
}
const controllerInfo: ControllerInfo = {
id: this._id,
url: url,
name: this.nameInputBox.value ?? '',
username: this.usernameInputBox.value,

View File

@@ -65,21 +65,27 @@ export class AzureArcTreeDataProvider implements vscode.TreeDataProvider<TreeNod
}
public getControllerNode(model: ControllerModel): ControllerTreeNode | undefined {
return this._controllerNodes.find(node => model.equals(node.model));
return this._controllerNodes.find(node => model.info.id === node.model.info.id);
}
public async removeController(controllerNode: ControllerTreeNode): Promise<void> {
this._controllerNodes = this._controllerNodes.filter(node => node !== controllerNode);
await this.deletePassword(controllerNode.model.info);
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));
const credential = await provider.readCredential(info.id);
return credential.password;
}
private async deletePassword(info: ControllerInfo): Promise<void> {
const provider = await this._credentialsProvider;
await provider.deleteCredential(info.id);
}
/**
* Refreshes the specified node, or the entire tree if node is undefined
* @param node The node to refresh, or undefined for the whole tree
@@ -91,9 +97,9 @@ export class AzureArcTreeDataProvider implements vscode.TreeDataProvider<TreeNod
private async updatePassword(model: ControllerModel, password: string): Promise<void> {
const provider = await this._credentialsProvider;
if (model.info.rememberPassword) {
provider.saveCredential(getCredentialId(model.info), password);
await provider.saveCredential(model.info.id, password);
} else {
provider.deleteCredential(getCredentialId(model.info));
await provider.deleteCredential(model.info.id);
}
}
@@ -136,7 +142,3 @@ export class AzureArcTreeDataProvider implements vscode.TreeDataProvider<TreeNod
}
}
}
function getCredentialId(info: ControllerInfo): string {
return `${info.url}::${info.username}`;
}

View File

@@ -27,6 +27,9 @@ export class ControllerTreeNode extends TreeNode {
constructor(public model: ControllerModel, private _context: vscode.ExtensionContext, private _treeDataProvider: AzureArcTreeDataProvider) {
super(model.label, vscode.TreeItemCollapsibleState.Collapsed, ResourceType.dataControllers);
model.onInfoUpdated(_ => {
this.label = model.label;
});
}
public async getChildren(): Promise<TreeNode[]> {

View File

@@ -265,6 +265,11 @@
resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.0.tgz#fef1904e4668b6e5ecee60c52cc6a078ffa6697d"
integrity sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==
"@types/uuid@^8.3.0":
version "8.3.0"
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f"
integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==
ajv@^6.5.5:
version "6.12.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7"
@@ -1203,6 +1208,11 @@ uuid@^3.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
uuid@^8.3.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea"
integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==
verror@1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"