mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -05:00
Fix arc node info updating (#12007)
* Fix arc node info updating * Missed await
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
17
extensions/arc/src/test/mocks/fakeControllerModel.ts
Normal file
17
extensions/arc/src/test/mocks/fakeControllerModel.ts
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}`;
|
||||
}
|
||||
|
||||
@@ -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[]> {
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user