mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Fix BDC remember password and reprompting connection (#7957)
* Fix remember password and reprompting connection * comment * Fix to remember password for session * Fix floating promises
This commit is contained in:
@@ -88,35 +88,51 @@ class DefaultApiWrapper extends DefaultApi {
|
|||||||
|
|
||||||
export class ClusterController {
|
export class ClusterController {
|
||||||
|
|
||||||
private authPromise: Promise<Authentication>;
|
private _authPromise: Promise<Authentication>;
|
||||||
private _url: string;
|
private _url: string;
|
||||||
private readonly dialog: ConnectControllerDialog;
|
private readonly _dialog: ConnectControllerDialog;
|
||||||
private connectionPromise: Promise<ClusterController>;
|
private _connectionPromise: Promise<ClusterController>;
|
||||||
|
|
||||||
constructor(url: string,
|
constructor(url: string,
|
||||||
private authType: AuthType,
|
private _authType: AuthType,
|
||||||
private username?: string,
|
private _username?: string,
|
||||||
private password?: string,
|
private _password?: string,
|
||||||
ignoreSslVerification?: boolean
|
ignoreSslVerification?: boolean
|
||||||
) {
|
) {
|
||||||
if (!url || (authType === 'basic' && (!username || !password))) {
|
if (!url || (_authType === 'basic' && (!_username || !_password))) {
|
||||||
throw new Error('Missing required inputs for Cluster controller API (URL, username, password)');
|
throw new Error('Missing required inputs for Cluster controller API (URL, username, password)');
|
||||||
}
|
}
|
||||||
this._url = adjustUrl(url);
|
this._url = adjustUrl(url);
|
||||||
if (this.authType === 'basic') {
|
if (this._authType === 'basic') {
|
||||||
this.authPromise = Promise.resolve(new BasicAuth(username, password, !!ignoreSslVerification));
|
this._authPromise = Promise.resolve(new BasicAuth(_username, _password, !!ignoreSslVerification));
|
||||||
} else {
|
} else {
|
||||||
this.authPromise = this.requestTokenUsingKerberos(ignoreSslVerification);
|
this._authPromise = this.requestTokenUsingKerberos(ignoreSslVerification);
|
||||||
}
|
}
|
||||||
this.dialog = new ConnectControllerDialog(new ConnectControllerModel(
|
this._dialog = new ConnectControllerDialog(new ConnectControllerModel(
|
||||||
{
|
{
|
||||||
url: this._url,
|
url: this._url,
|
||||||
auth: this.authType,
|
auth: this._authType,
|
||||||
username: this.username,
|
username: this._username,
|
||||||
password: this.password
|
password: this._password
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get url(): string {
|
||||||
|
return this._url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get authType(): AuthType {
|
||||||
|
return this._authType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get username(): string | undefined {
|
||||||
|
return this._username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get password(): string | undefined {
|
||||||
|
return this._password;
|
||||||
|
}
|
||||||
|
|
||||||
private async requestTokenUsingKerberos(ignoreSslVerification?: boolean): Promise<Authentication> {
|
private async requestTokenUsingKerberos(ignoreSslVerification?: boolean): Promise<Authentication> {
|
||||||
let supportsKerberos = await this.verifyKerberosSupported(ignoreSslVerification);
|
let supportsKerberos = await this.verifyKerberosSupported(ignoreSslVerification);
|
||||||
if (!supportsKerberos) {
|
if (!supportsKerberos) {
|
||||||
@@ -166,8 +182,8 @@ export class ClusterController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getEndpointsImpl(self: ClusterController): Promise<IEndPointsResponse> {
|
private async getEndpointsImpl(self: ClusterController): Promise<IEndPointsResponse> {
|
||||||
let auth = await self.authPromise;
|
let auth = await self._authPromise;
|
||||||
let endPointApi = new BdcApiWrapper(self.username, self.password, self._url, auth);
|
let endPointApi = new BdcApiWrapper(self._username, self._password, self._url, auth);
|
||||||
let options: any = {};
|
let options: any = {};
|
||||||
|
|
||||||
let result = await endPointApi.endpointsGet(options);
|
let result = await endPointApi.endpointsGet(options);
|
||||||
@@ -185,8 +201,8 @@ export class ClusterController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getBdcStatusImpl(self: ClusterController): Promise<IBdcStatusResponse> {
|
private async getBdcStatusImpl(self: ClusterController): Promise<IBdcStatusResponse> {
|
||||||
let auth = await self.authPromise;
|
let auth = await self._authPromise;
|
||||||
const bdcApi = new BdcApiWrapper(self.username, self.password, self._url, auth);
|
const bdcApi = new BdcApiWrapper(self._username, self._password, self._url, auth);
|
||||||
|
|
||||||
const bdcStatus = await bdcApi.getBdcStatus('', '', /*all*/ true);
|
const bdcStatus = await bdcApi.getBdcStatus('', '', /*all*/ true);
|
||||||
return {
|
return {
|
||||||
@@ -206,8 +222,8 @@ export class ClusterController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async mountHdfsImpl(self: ClusterController, mountPath: string, remoteUri: string, credentials: {}): Promise<MountResponse> {
|
private async mountHdfsImpl(self: ClusterController, mountPath: string, remoteUri: string, credentials: {}): Promise<MountResponse> {
|
||||||
let auth = await self.authPromise;
|
let auth = await self._authPromise;
|
||||||
const api = new DefaultApiWrapper(self.username, self.password, self._url, auth);
|
const api = new DefaultApiWrapper(self._username, self._password, self._url, auth);
|
||||||
|
|
||||||
const mountStatus = await api.createMount('', '', remoteUri, mountPath, credentials);
|
const mountStatus = await api.createMount('', '', remoteUri, mountPath, credentials);
|
||||||
return {
|
return {
|
||||||
@@ -225,8 +241,8 @@ export class ClusterController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getMountStatusImpl(self: ClusterController, mountPath?: string): Promise<MountStatusResponse> {
|
private async getMountStatusImpl(self: ClusterController, mountPath?: string): Promise<MountStatusResponse> {
|
||||||
const auth = await self.authPromise;
|
const auth = await self._authPromise;
|
||||||
const api = new DefaultApiWrapper(self.username, self.password, self._url, auth);
|
const api = new DefaultApiWrapper(self._username, self._password, self._url, auth);
|
||||||
|
|
||||||
const mountStatus = await api.listMounts('', '', mountPath);
|
const mountStatus = await api.listMounts('', '', mountPath);
|
||||||
return {
|
return {
|
||||||
@@ -244,8 +260,8 @@ export class ClusterController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async refreshMountImpl(self: ClusterController, mountPath: string): Promise<MountResponse> {
|
private async refreshMountImpl(self: ClusterController, mountPath: string): Promise<MountResponse> {
|
||||||
const auth = await self.authPromise;
|
const auth = await self._authPromise;
|
||||||
const api = new DefaultApiWrapper(self.username, self.password, self._url, auth);
|
const api = new DefaultApiWrapper(self._username, self._password, self._url, auth);
|
||||||
|
|
||||||
const mountStatus = await api.refreshMount('', '', mountPath);
|
const mountStatus = await api.refreshMount('', '', mountPath);
|
||||||
return {
|
return {
|
||||||
@@ -263,8 +279,8 @@ export class ClusterController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async deleteMountImpl(mountPath: string): Promise<MountResponse> {
|
private async deleteMountImpl(mountPath: string): Promise<MountResponse> {
|
||||||
let auth = await this.authPromise;
|
let auth = await this._authPromise;
|
||||||
const api = new DefaultApiWrapper(this.username, this.password, this._url, auth);
|
const api = new DefaultApiWrapper(this._username, this._password, this._url, auth);
|
||||||
|
|
||||||
const mountStatus = await api.deleteMount('', '', mountPath);
|
const mountStatus = await api.deleteMount('', '', mountPath);
|
||||||
return {
|
return {
|
||||||
@@ -291,17 +307,17 @@ export class ClusterController {
|
|||||||
// We don't want to open multiple dialogs here if multiple calls come in the same time so check
|
// We don't want to open multiple dialogs here if multiple calls come in the same time so check
|
||||||
// and see if we have are actively waiting on an open dialog to return and if so then just wait
|
// and see if we have are actively waiting on an open dialog to return and if so then just wait
|
||||||
// on that promise.
|
// on that promise.
|
||||||
if (!this.connectionPromise) {
|
if (!this._connectionPromise) {
|
||||||
this.connectionPromise = this.dialog.showDialog();
|
this._connectionPromise = this._dialog.showDialog();
|
||||||
}
|
}
|
||||||
const controller = await this.connectionPromise;
|
const controller = await this._connectionPromise;
|
||||||
this.connectionPromise = undefined;
|
this._connectionPromise = undefined;
|
||||||
if (controller) {
|
if (controller) {
|
||||||
this.username = controller.username;
|
this._username = controller._username;
|
||||||
this.password = controller.password;
|
this._password = controller._password;
|
||||||
this._url = controller._url;
|
this._url = controller._url;
|
||||||
this.authType = controller.authType;
|
this._authType = controller._authType;
|
||||||
this.authPromise = controller.authPromise;
|
this._authPromise = controller._authPromise;
|
||||||
}
|
}
|
||||||
return await f(this, args);
|
return await f(this, args);
|
||||||
}
|
}
|
||||||
@@ -378,7 +394,7 @@ export class ControllerError extends Error {
|
|||||||
public code?: number;
|
public code?: number;
|
||||||
public reason?: string;
|
public reason?: string;
|
||||||
public address?: string;
|
public address?: string;
|
||||||
|
public statusMessage?: string;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param error The original error to wrap
|
* @param error The original error to wrap
|
||||||
@@ -391,6 +407,7 @@ export class ControllerError extends Error {
|
|||||||
this.code = error.response.statusCode;
|
this.code = error.response.statusCode;
|
||||||
this.message += `${error.response.statusMessage ? ` - ${error.response.statusMessage}` : ''}` || '';
|
this.message += `${error.response.statusMessage ? ` - ${error.response.statusMessage}` : ''}` || '';
|
||||||
this.address = error.response.url || '';
|
this.address = error.response.url || '';
|
||||||
|
this.statusMessage = error.response.statusMessage;
|
||||||
}
|
}
|
||||||
else if (error.message) {
|
else if (error.message) {
|
||||||
this.message += ` - ${error.message}`;
|
this.message += ` - ${error.message}`;
|
||||||
|
|||||||
@@ -6,11 +6,14 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { ClusterController, ControllerError } from '../controller/clusterControllerApi';
|
import { ClusterController, ControllerError } from '../controller/clusterControllerApi';
|
||||||
import { ControllerTreeDataProvider } from '../tree/controllerTreeDataProvider';
|
import { ControllerTreeDataProvider } from '../tree/controllerTreeDataProvider';
|
||||||
import { TreeNode } from '../tree/treeNode';
|
import { TreeNode } from '../tree/treeNode';
|
||||||
import { AuthType } from '../constants';
|
import { AuthType } from '../constants';
|
||||||
|
import { ManageControllerCommand } from '../../extension';
|
||||||
|
import { BdcDashboardOptions } from './bdcDashboardModel';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
@@ -73,7 +76,8 @@ export class AddControllerDialogModel {
|
|||||||
if (this._canceled) {
|
if (this._canceled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.treeDataProvider.addController(url, auth, username, password, rememberPassword);
|
this.treeDataProvider.addOrUpdateController(url, auth, username, password, rememberPassword);
|
||||||
|
vscode.commands.executeCommand(ManageControllerCommand, <BdcDashboardOptions>{ url: url, auth: auth, username: username, password: password });
|
||||||
await this.treeDataProvider.saveControllers();
|
await this.treeDataProvider.saveControllers();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import { IconPathHelper, cssStyles } from '../constants';
|
|||||||
import { BdcServiceStatusPage } from './bdcServiceStatusPage';
|
import { BdcServiceStatusPage } from './bdcServiceStatusPage';
|
||||||
import { BdcDashboardOverviewPage } from './bdcDashboardOverviewPage';
|
import { BdcDashboardOverviewPage } from './bdcDashboardOverviewPage';
|
||||||
import { BdcStatusModel, ServiceStatusModel } from '../controller/apiGenerated';
|
import { BdcStatusModel, ServiceStatusModel } from '../controller/apiGenerated';
|
||||||
import { getHealthStatusDot, getServiceNameDisplayText } from '../utils';
|
import { getHealthStatusDot, getServiceNameDisplayText, showErrorMessage } from '../utils';
|
||||||
|
import { HdfsDialogCancelledError } from './hdfsDialogBase';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
@@ -43,6 +44,7 @@ export class BdcDashboard {
|
|||||||
|
|
||||||
constructor(private title: string, private model: BdcDashboardModel) {
|
constructor(private title: string, private model: BdcDashboardModel) {
|
||||||
this.model.onDidUpdateBdcStatus(bdcStatus => this.handleBdcStatusUpdate(bdcStatus));
|
this.model.onDidUpdateBdcStatus(bdcStatus => this.handleBdcStatusUpdate(bdcStatus));
|
||||||
|
this.model.onError(error => this.handleError(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
public showDashboard(): void {
|
public showDashboard(): void {
|
||||||
@@ -165,6 +167,14 @@ export class BdcDashboard {
|
|||||||
this.updateServiceNavTabs(bdcStatus.services);
|
this.updateServiceNavTabs(bdcStatus.services);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handleError(error: Error): void {
|
||||||
|
// We don't want to show an error for the connection dialog being
|
||||||
|
// canceled since that's a normal case.
|
||||||
|
if (!(error instanceof HdfsDialogCancelledError)) {
|
||||||
|
showErrorMessage(error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async doRefresh(): Promise<void> {
|
private async doRefresh(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
this.refreshButton.enabled = false;
|
this.refreshButton.enabled = false;
|
||||||
|
|||||||
@@ -7,8 +7,10 @@ import * as azdata from 'azdata';
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { ClusterController } from '../controller/clusterControllerApi';
|
import { ClusterController } from '../controller/clusterControllerApi';
|
||||||
import { EndpointModel, BdcStatusModel } from '../controller/apiGenerated';
|
import { EndpointModel, BdcStatusModel } from '../controller/apiGenerated';
|
||||||
import { showErrorMessage, Endpoint, Service } from '../utils';
|
import { Endpoint, Service } from '../utils';
|
||||||
import { AuthType } from '../constants';
|
import { AuthType } from '../constants';
|
||||||
|
import { ConnectControllerDialog, ConnectControllerModel } from './connectControllerDialog';
|
||||||
|
import { ControllerTreeDataProvider } from '../tree/controllerTreeDataProvider';
|
||||||
|
|
||||||
export type BdcDashboardOptions = { url: string, auth: AuthType, username: string, password: string };
|
export type BdcDashboardOptions = { url: string, auth: AuthType, username: string, password: string };
|
||||||
|
|
||||||
@@ -21,12 +23,23 @@ export class BdcDashboardModel {
|
|||||||
private _endpointsLastUpdated: Date;
|
private _endpointsLastUpdated: Date;
|
||||||
private readonly _onDidUpdateEndpoints = new vscode.EventEmitter<EndpointModel[]>();
|
private readonly _onDidUpdateEndpoints = new vscode.EventEmitter<EndpointModel[]>();
|
||||||
private readonly _onDidUpdateBdcStatus = new vscode.EventEmitter<BdcStatusModel>();
|
private readonly _onDidUpdateBdcStatus = new vscode.EventEmitter<BdcStatusModel>();
|
||||||
|
private readonly _onError = new vscode.EventEmitter<Error>();
|
||||||
public onDidUpdateEndpoints = this._onDidUpdateEndpoints.event;
|
public onDidUpdateEndpoints = this._onDidUpdateEndpoints.event;
|
||||||
public onDidUpdateBdcStatus = this._onDidUpdateBdcStatus.event;
|
public onDidUpdateBdcStatus = this._onDidUpdateBdcStatus.event;
|
||||||
|
public onError = this._onError.event;
|
||||||
|
|
||||||
constructor(private options: BdcDashboardOptions, ignoreSslVerification = true) {
|
constructor(private _options: BdcDashboardOptions, private _treeDataProvider: ControllerTreeDataProvider, ignoreSslVerification = true) {
|
||||||
this._clusterController = new ClusterController(options.url, options.auth, options.username, options.password, ignoreSslVerification);
|
try {
|
||||||
|
this._clusterController = new ClusterController(_options.url, _options.auth, _options.username, _options.password, ignoreSslVerification);
|
||||||
|
// tslint:disable-next-line:no-floating-promises
|
||||||
this.refresh();
|
this.refresh();
|
||||||
|
} catch {
|
||||||
|
this.promptReconnect().then(async () => {
|
||||||
|
await this.refresh();
|
||||||
|
}).catch(error => {
|
||||||
|
this._onError.fire(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get bdcStatus(): BdcStatusModel | undefined {
|
public get bdcStatus(): BdcStatusModel | undefined {
|
||||||
@@ -46,6 +59,12 @@ export class BdcDashboardModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async refresh(): Promise<void> {
|
public async refresh(): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (!this._clusterController) {
|
||||||
|
// If this succeeds without error we know we have a clusterController at this point
|
||||||
|
await this.promptReconnect();
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this._clusterController.getBdcStatus(true).then(response => {
|
this._clusterController.getBdcStatus(true).then(response => {
|
||||||
this._bdcStatus = response.bdcStatus;
|
this._bdcStatus = response.bdcStatus;
|
||||||
@@ -58,9 +77,10 @@ export class BdcDashboardModel {
|
|||||||
this._endpointsLastUpdated = new Date();
|
this._endpointsLastUpdated = new Date();
|
||||||
this._onDidUpdateEndpoints.fire(this.serviceEndpoints);
|
this._onDidUpdateEndpoints.fire(this.serviceEndpoints);
|
||||||
})
|
})
|
||||||
]).catch(error => {
|
]);
|
||||||
showErrorMessage(error);
|
} catch (error) {
|
||||||
});
|
this._onError.fire(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,7 +101,7 @@ export class BdcDashboardModel {
|
|||||||
serverName: sqlServerMasterEndpoint.endpoint,
|
serverName: sqlServerMasterEndpoint.endpoint,
|
||||||
databaseName: undefined,
|
databaseName: undefined,
|
||||||
userName: 'sa',
|
userName: 'sa',
|
||||||
password: this.options.password,
|
password: this._options.password,
|
||||||
authenticationType: '',
|
authenticationType: '',
|
||||||
savePassword: true,
|
savePassword: true,
|
||||||
groupFullName: undefined,
|
groupFullName: undefined,
|
||||||
@@ -92,6 +112,19 @@ export class BdcDashboardModel {
|
|||||||
options: {}
|
options: {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens up a dialog prompting the user to re-enter credentials for the controller
|
||||||
|
*/
|
||||||
|
private async promptReconnect(): Promise<void> {
|
||||||
|
this._clusterController = await new ConnectControllerDialog(new ConnectControllerModel(this._options)).showDialog();
|
||||||
|
this._treeDataProvider.addOrUpdateController(
|
||||||
|
this._clusterController.url,
|
||||||
|
this._clusterController.authType,
|
||||||
|
this._clusterController.username,
|
||||||
|
this._clusterController.password,
|
||||||
|
/* Remember password */false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -28,6 +28,12 @@ export interface HdfsDialogProperties {
|
|||||||
password?: string;
|
password?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class HdfsDialogCancelledError extends Error {
|
||||||
|
constructor(message: string = 'Dialog cancelled') {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export abstract class HdfsDialogModelBase<T extends HdfsDialogProperties, R> {
|
export abstract class HdfsDialogModelBase<T extends HdfsDialogProperties, R> {
|
||||||
protected _canceled = false;
|
protected _canceled = false;
|
||||||
private _authTypes: azdata.CategoryValue[];
|
private _authTypes: azdata.CategoryValue[];
|
||||||
@@ -87,7 +93,7 @@ export abstract class HdfsDialogModelBase<T extends HdfsDialogProperties, R> {
|
|||||||
throw new Error(localize('mount.hdfs.loginerror1', "Login to controller failed"));
|
throw new Error(localize('mount.hdfs.loginerror1', "Login to controller failed"));
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(localize('mount.hdfs.loginerror2', "Login to controller failed: {0}", err.message));
|
throw new Error(localize('mount.hdfs.loginerror2', "Login to controller failed: {0}", err.statusMessage || err.message));
|
||||||
}
|
}
|
||||||
return controller;
|
return controller;
|
||||||
}
|
}
|
||||||
@@ -224,7 +230,7 @@ export abstract class HdfsDialogBase<T extends HdfsDialogProperties, R> {
|
|||||||
if (this.model && this.model.onCancel) {
|
if (this.model && this.model.onCancel) {
|
||||||
await this.model.onCancel();
|
await this.model.onCancel();
|
||||||
}
|
}
|
||||||
this.returnPromise.reject(new Error('Dialog cancelled'));
|
this.returnPromise.reject(new HdfsDialogCancelledError());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async reportError(error: any): Promise<void> {
|
protected async reportError(error: any): Promise<void> {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export class ControllerTreeDataProvider implements vscode.TreeDataProvider<TreeN
|
|||||||
return this.root.getChildren();
|
return this.root.getChildren();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loadSavedControllers();
|
await this.loadSavedControllers();
|
||||||
return [new LoadingControllerNode()];
|
return [new LoadingControllerNode()];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +57,15 @@ export class ControllerTreeDataProvider implements vscode.TreeDataProvider<TreeN
|
|||||||
this._onDidChangeTreeData.fire(node);
|
this._onDidChangeTreeData.fire(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
public addController(
|
/**
|
||||||
|
* Creates or updates a node in the tree with the specified connection information
|
||||||
|
* @param url The URL for the BDC management endpoint
|
||||||
|
* @param auth The type of auth to use
|
||||||
|
* @param username The username (if basic auth)
|
||||||
|
* @param password The password (if basic auth)
|
||||||
|
* @param rememberPassword Whether to store the password in the password store when saving
|
||||||
|
*/
|
||||||
|
public addOrUpdateController(
|
||||||
url: string,
|
url: string,
|
||||||
auth: AuthType,
|
auth: AuthType,
|
||||||
username: string,
|
username: string,
|
||||||
@@ -65,7 +73,7 @@ export class ControllerTreeDataProvider implements vscode.TreeDataProvider<TreeN
|
|||||||
rememberPassword: boolean
|
rememberPassword: boolean
|
||||||
): void {
|
): void {
|
||||||
this.removeNonControllerNodes();
|
this.removeNonControllerNodes();
|
||||||
this.root.addControllerNode(url, auth, username, password, rememberPassword);
|
this.root.addOrUpdateControllerNode(url, auth, username, password, rememberPassword);
|
||||||
this.notifyNodeChanged();
|
this.notifyNodeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +121,7 @@ export class ControllerTreeDataProvider implements vscode.TreeDataProvider<TreeN
|
|||||||
this.root.clearChildren();
|
this.root.clearChildren();
|
||||||
let controllers: IControllerInfoSlim[] = this.memento.get('controllers');
|
let controllers: IControllerInfoSlim[] = this.memento.get('controllers');
|
||||||
if (controllers) {
|
if (controllers) {
|
||||||
for (let c of controllers) {
|
for (const c of controllers) {
|
||||||
let password = undefined;
|
let password = undefined;
|
||||||
if (c.rememberPassword) {
|
if (c.rememberPassword) {
|
||||||
password = await this.getPassword(c.url, c.username);
|
password = await this.getPassword(c.url, c.username);
|
||||||
@@ -138,18 +146,18 @@ export class ControllerTreeDataProvider implements vscode.TreeDataProvider<TreeN
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async saveControllers(): Promise<void> {
|
public async saveControllers(): Promise<void> {
|
||||||
let controllers = this.root.children.map((e): IControllerInfoSlim => {
|
const controllers = this.root.children.map((e): IControllerInfoSlim => {
|
||||||
let controller = e as ControllerNode;
|
const controller = e as ControllerNode;
|
||||||
return {
|
return {
|
||||||
url: controller.url,
|
url: controller.url,
|
||||||
auth: controller.auth,
|
auth: controller.auth,
|
||||||
username: controller.username,
|
username: controller.username,
|
||||||
password: controller.password,
|
password: controller.password,
|
||||||
rememberPassword: !!controller.rememberPassword
|
rememberPassword: controller.rememberPassword
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
let controllersWithoutPassword = controllers.map((e): IControllerInfoSlim => {
|
const controllersWithoutPassword = controllers.map((e): IControllerInfoSlim => {
|
||||||
return {
|
return {
|
||||||
url: e.url,
|
url: e.url,
|
||||||
auth: e.auth,
|
auth: e.auth,
|
||||||
@@ -164,7 +172,7 @@ export class ControllerTreeDataProvider implements vscode.TreeDataProvider<TreeN
|
|||||||
showErrorMessage(error);
|
showErrorMessage(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let e of controllers) {
|
for (const e of controllers) {
|
||||||
if (e.rememberPassword) {
|
if (e.rememberPassword) {
|
||||||
await this.savePassword(e.url, e.username, e.password);
|
await this.savePassword(e.url, e.username, e.password);
|
||||||
} else {
|
} else {
|
||||||
@@ -187,7 +195,7 @@ export class ControllerTreeDataProvider implements vscode.TreeDataProvider<TreeN
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getPassword(url: string, username: string): Promise<string> {
|
private async getPassword(url: string, username: string): Promise<string | undefined> {
|
||||||
let provider = await this.getCredentialProvider();
|
let provider = await this.getCredentialProvider();
|
||||||
let id = this.createId(url, username);
|
let id = this.createId(url, username);
|
||||||
let credential = await provider.readCredential(id);
|
let credential = await provider.readCredential(id);
|
||||||
|
|||||||
@@ -103,7 +103,15 @@ export class ControllerRootNode extends ControllerTreeNode {
|
|||||||
return this.children as ControllerNode[];
|
return this.children as ControllerNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
public addControllerNode(
|
/**
|
||||||
|
* Creates or updates a node in the tree with the specified connection information
|
||||||
|
* @param url The URL for the BDC management endpoint
|
||||||
|
* @param auth The type of auth to use
|
||||||
|
* @param username The username (if basic auth)
|
||||||
|
* @param password The password (if basic auth)
|
||||||
|
* @param rememberPassword Whether to store the password in the password store when saving
|
||||||
|
*/
|
||||||
|
public addOrUpdateControllerNode(
|
||||||
url: string,
|
url: string,
|
||||||
auth: AuthType,
|
auth: AuthType,
|
||||||
username: string,
|
username: string,
|
||||||
@@ -201,6 +209,10 @@ export class ControllerNode extends ControllerTreeNode {
|
|||||||
this._password = pw;
|
this._password = pw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public set label(label: string) {
|
||||||
|
super.label = label || this.generateLabel();
|
||||||
|
}
|
||||||
|
|
||||||
public get rememberPassword() {
|
public get rememberPassword() {
|
||||||
return this._rememberPassword;
|
return this._rememberPassword;
|
||||||
}
|
}
|
||||||
@@ -209,10 +221,6 @@ export class ControllerNode extends ControllerTreeNode {
|
|||||||
this._rememberPassword = rememberPassword;
|
this._rememberPassword = rememberPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
public set label(label: string) {
|
|
||||||
super.label = label || this.generateLabel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private generateLabel(): string {
|
private generateLabel(): string {
|
||||||
let label = `controller: ${ControllerNode.toIpAndPort(this._url)}`;
|
let label = `controller: ${ControllerNode.toIpAndPort(this._url)}`;
|
||||||
if (this._auth === 'basic') {
|
if (this._auth === 'basic') {
|
||||||
|
|||||||
@@ -20,13 +20,13 @@ import { getControllerEndpoint } from './bigDataCluster/utils';
|
|||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
const AddControllerCommand = 'bigDataClusters.command.addController';
|
export const AddControllerCommand = 'bigDataClusters.command.addController';
|
||||||
const DeleteControllerCommand = 'bigDataClusters.command.deleteController';
|
export const DeleteControllerCommand = 'bigDataClusters.command.deleteController';
|
||||||
const RefreshControllerCommand = 'bigDataClusters.command.refreshController';
|
export const RefreshControllerCommand = 'bigDataClusters.command.refreshController';
|
||||||
const ManageControllerCommand = 'bigDataClusters.command.manageController';
|
export const ManageControllerCommand = 'bigDataClusters.command.manageController';
|
||||||
const MountHdfsCommand = 'bigDataClusters.command.mount';
|
export const MountHdfsCommand = 'bigDataClusters.command.mount';
|
||||||
const RefreshMountCommand = 'bigDataClusters.command.refreshmount';
|
export const RefreshMountCommand = 'bigDataClusters.command.refreshmount';
|
||||||
const DeleteMountCommand = 'bigDataClusters.command.deletemount';
|
export const DeleteMountCommand = 'bigDataClusters.command.deletemount';
|
||||||
|
|
||||||
const endpointNotFoundError = localize('mount.error.endpointNotFound', "Controller endpoint information was not found");
|
const endpointNotFoundError = localize('mount.error.endpointNotFound', "Controller endpoint information was not found");
|
||||||
|
|
||||||
@@ -51,8 +51,8 @@ function registerCommands(context: vscode.ExtensionContext, treeDataProvider: Co
|
|||||||
runThrottledAction(AddControllerCommand, () => addBdcController(treeDataProvider, node));
|
runThrottledAction(AddControllerCommand, () => addBdcController(treeDataProvider, node));
|
||||||
});
|
});
|
||||||
|
|
||||||
vscode.commands.registerCommand(DeleteControllerCommand, (node: TreeNode) => {
|
vscode.commands.registerCommand(DeleteControllerCommand, async (node: TreeNode) => {
|
||||||
deleteBdcController(treeDataProvider, node);
|
await deleteBdcController(treeDataProvider, node);
|
||||||
});
|
});
|
||||||
|
|
||||||
vscode.commands.registerCommand(RefreshControllerCommand, (node: TreeNode) => {
|
vscode.commands.registerCommand(RefreshControllerCommand, (node: TreeNode) => {
|
||||||
@@ -64,7 +64,7 @@ function registerCommands(context: vscode.ExtensionContext, treeDataProvider: Co
|
|||||||
|
|
||||||
vscode.commands.registerCommand(ManageControllerCommand, async (info: ControllerNode | BdcDashboardOptions) => {
|
vscode.commands.registerCommand(ManageControllerCommand, async (info: ControllerNode | BdcDashboardOptions) => {
|
||||||
const title: string = `${localize('bdc.dashboard.title', "Big Data Cluster Dashboard -")} ${ControllerNode.toIpAndPort(info.url)}`;
|
const title: string = `${localize('bdc.dashboard.title', "Big Data Cluster Dashboard -")} ${ControllerNode.toIpAndPort(info.url)}`;
|
||||||
const dashboard: BdcDashboard = new BdcDashboard(title, new BdcDashboardModel(info));
|
const dashboard: BdcDashboard = new BdcDashboard(title, new BdcDashboardModel(info, treeDataProvider));
|
||||||
dashboard.showDashboard();
|
dashboard.showDashboard();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -80,26 +80,26 @@ function registerCommands(context: vscode.ExtensionContext, treeDataProvider: Co
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function mountHdfs(explorerContext?: azdata.ObjectExplorerContext): Promise<void> {
|
async function mountHdfs(explorerContext?: azdata.ObjectExplorerContext): Promise<void> {
|
||||||
let mountProps = await getMountProps(explorerContext);
|
const mountProps = await getMountProps(explorerContext);
|
||||||
if (mountProps) {
|
if (mountProps) {
|
||||||
let dialog = new MountHdfsDialog(new MountHdfsModel(mountProps));
|
const dialog = new MountHdfsDialog(new MountHdfsModel(mountProps));
|
||||||
dialog.showDialog();
|
await dialog.showDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshMount(explorerContext?: azdata.ObjectExplorerContext): Promise<void> {
|
async function refreshMount(explorerContext?: azdata.ObjectExplorerContext): Promise<void> {
|
||||||
let mountProps = await getMountProps(explorerContext);
|
const mountProps = await getMountProps(explorerContext);
|
||||||
if (mountProps) {
|
if (mountProps) {
|
||||||
let dialog = new RefreshMountDialog(new RefreshMountModel(mountProps));
|
const dialog = new RefreshMountDialog(new RefreshMountModel(mountProps));
|
||||||
dialog.showDialog();
|
await dialog.showDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function deleteMount(explorerContext?: azdata.ObjectExplorerContext): Promise<void> {
|
async function deleteMount(explorerContext?: azdata.ObjectExplorerContext): Promise<void> {
|
||||||
let mountProps = await getMountProps(explorerContext);
|
const mountProps = await getMountProps(explorerContext);
|
||||||
if (mountProps) {
|
if (mountProps) {
|
||||||
let dialog = new DeleteMountDialog(new DeleteMountModel(mountProps));
|
const dialog = new DeleteMountDialog(new DeleteMountModel(mountProps));
|
||||||
dialog.showDialog();
|
await dialog.showDialog();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,15 +169,15 @@ async function deleteBdcController(treeDataProvider: ControllerTreeDataProvider,
|
|||||||
let result = await vscode.window.showQuickPick(Object.keys(choices), options);
|
let result = await vscode.window.showQuickPick(Object.keys(choices), options);
|
||||||
let remove: boolean = !!(result && choices[result]);
|
let remove: boolean = !!(result && choices[result]);
|
||||||
if (remove) {
|
if (remove) {
|
||||||
deleteControllerInternal(treeDataProvider, controllerNode);
|
await deleteControllerInternal(treeDataProvider, controllerNode);
|
||||||
}
|
}
|
||||||
return remove;
|
return remove;
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteControllerInternal(treeDataProvider: ControllerTreeDataProvider, controllerNode: ControllerNode): void {
|
async function deleteControllerInternal(treeDataProvider: ControllerTreeDataProvider, controllerNode: ControllerNode): Promise<void> {
|
||||||
let deleted = treeDataProvider.deleteController(controllerNode.url, controllerNode.auth, controllerNode.username);
|
const deleted = treeDataProvider.deleteController(controllerNode.url, controllerNode.auth, controllerNode.username);
|
||||||
if (deleted) {
|
if (deleted) {
|
||||||
treeDataProvider.saveControllers();
|
await treeDataProvider.saveControllers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user