mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
BDC Dashboard connection retry (#7784)
* Open cluster dashboard * Remove old translated strings and update var name * Add exported auth type * Add newline * Add connection retry for dashboard * Change getMainSectionComponent to return multiple (no undefined) * Move try/catch to withConnectRetry * Add connection retry for dashboard * Change getMainSectionComponent to return multiple (no undefined) * Move try/catch to withConnectRetry
This commit is contained in:
@@ -10,6 +10,7 @@ import { BdcRouterApi, Authentication, EndpointModel, BdcStatusModel, DefaultApi
|
|||||||
import { TokenRouterApi } from './clusterApiGenerated2';
|
import { TokenRouterApi } from './clusterApiGenerated2';
|
||||||
import { AuthType } from '../constants';
|
import { AuthType } from '../constants';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
|
import { ConnectControllerDialog, ConnectControllerModel } from '../dialog/connectControllerDialog';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
@@ -89,6 +90,8 @@ export class ClusterController {
|
|||||||
|
|
||||||
private authPromise: Promise<Authentication>;
|
private authPromise: Promise<Authentication>;
|
||||||
private _url: string;
|
private _url: string;
|
||||||
|
private readonly dialog: ConnectControllerDialog;
|
||||||
|
private connectionPromise: Promise<ClusterController>;
|
||||||
|
|
||||||
constructor(url: string,
|
constructor(url: string,
|
||||||
private authType: AuthType,
|
private authType: AuthType,
|
||||||
@@ -105,6 +108,13 @@ export class ClusterController {
|
|||||||
} else {
|
} else {
|
||||||
this.authPromise = this.requestTokenUsingKerberos(ignoreSslVerification);
|
this.authPromise = this.requestTokenUsingKerberos(ignoreSslVerification);
|
||||||
}
|
}
|
||||||
|
this.dialog = new ConnectControllerDialog(new ConnectControllerModel(
|
||||||
|
{
|
||||||
|
url: this._url,
|
||||||
|
auth: this.authType,
|
||||||
|
username: this.username,
|
||||||
|
password: this.password
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async requestTokenUsingKerberos(ignoreSslVerification?: boolean): Promise<Authentication> {
|
private async requestTokenUsingKerberos(ignoreSslVerification?: boolean): Promise<Authentication> {
|
||||||
@@ -134,8 +144,6 @@ export class ClusterController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private async verifyKerberosSupported(ignoreSslVerification: boolean): Promise<boolean> {
|
private async verifyKerberosSupported(ignoreSslVerification: boolean): Promise<boolean> {
|
||||||
let tokenApi = new TokenRouterApi(this._url);
|
let tokenApi = new TokenRouterApi(this._url);
|
||||||
tokenApi.setDefaultAuthentication(new SslAuth(!!ignoreSslVerification));
|
tokenApi.setDefaultAuthentication(new SslAuth(!!ignoreSslVerification));
|
||||||
@@ -150,102 +158,161 @@ export class ClusterController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getEndPoints(): Promise<IEndPointsResponse> {
|
public async getEndPoints(promptConnect: boolean = false): Promise<IEndPointsResponse> {
|
||||||
let auth = await this.authPromise;
|
return await this.withConnectRetry<IEndPointsResponse>(
|
||||||
let endPointApi = new BdcApiWrapper(this.username, this.password, this._url, auth);
|
this.getEndpointsImpl,
|
||||||
|
promptConnect,
|
||||||
|
localize('bdc.error.getEndPoints', "Error retrieving endpoints from {0}", this._url));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getEndpointsImpl(self: ClusterController): Promise<IEndPointsResponse> {
|
||||||
|
let auth = await self.authPromise;
|
||||||
|
let endPointApi = new BdcApiWrapper(self.username, self.password, self._url, auth);
|
||||||
let options: any = {};
|
let options: any = {};
|
||||||
try {
|
|
||||||
let result = await endPointApi.endpointsGet(options);
|
let result = await endPointApi.endpointsGet(options);
|
||||||
return {
|
return {
|
||||||
response: result.response as IHttpResponse,
|
response: result.response as IHttpResponse,
|
||||||
endPoints: result.body as EndpointModel[]
|
endPoints: result.body as EndpointModel[]
|
||||||
};
|
};
|
||||||
} catch (error) {
|
|
||||||
// TODO handle 401 by reauthenticating
|
|
||||||
throw new ControllerError(error, localize('bdc.error.getEndPoints', "Error retrieving endpoints from {0}", this._url));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getBdcStatus(): Promise<IBdcStatusResponse> {
|
public async getBdcStatus(promptConnect: boolean = false): Promise<IBdcStatusResponse> {
|
||||||
let auth = await this.authPromise;
|
return await this.withConnectRetry<IBdcStatusResponse>(
|
||||||
const bdcApi = new BdcApiWrapper(this.username, this.password, this._url, auth);
|
this.getBdcStatusImpl,
|
||||||
|
promptConnect,
|
||||||
|
localize('bdc.error.getBdcStatus', "Error retrieving BDC status from {0}", this._url));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getBdcStatusImpl(self: ClusterController): Promise<IBdcStatusResponse> {
|
||||||
|
let auth = await self.authPromise;
|
||||||
|
const bdcApi = new BdcApiWrapper(self.username, self.password, self._url, auth);
|
||||||
|
|
||||||
try {
|
|
||||||
const bdcStatus = await bdcApi.getBdcStatus('', '', /*all*/ true);
|
const bdcStatus = await bdcApi.getBdcStatus('', '', /*all*/ true);
|
||||||
return {
|
return {
|
||||||
response: bdcStatus.response,
|
response: bdcStatus.response,
|
||||||
bdcStatus: bdcStatus.body
|
bdcStatus: bdcStatus.body
|
||||||
};
|
};
|
||||||
} catch (error) {
|
|
||||||
// TODO handle 401 by reauthenticating
|
|
||||||
throw new ControllerError(error, localize('bdc.error.getBdcStatus', "Error retrieving BDC status from {0}", this._url));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async mountHdfs(mountPath: string, remoteUri: string, credentials: {}): Promise<MountResponse> {
|
public async mountHdfs(mountPath: string, remoteUri: string, credentials: {}, promptConnection: boolean = false): Promise<MountResponse> {
|
||||||
let auth = await this.authPromise;
|
return await this.withConnectRetry<MountResponse>(
|
||||||
const api = new DefaultApiWrapper(this.username, this.password, this._url, auth);
|
this.mountHdfsImpl,
|
||||||
|
promptConnection,
|
||||||
|
localize('bdc.error.mountHdfs', "Error creating mount"),
|
||||||
|
mountPath,
|
||||||
|
remoteUri,
|
||||||
|
credentials);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async mountHdfsImpl(self: ClusterController, mountPath: string, remoteUri: string, credentials: {}): Promise<MountResponse> {
|
||||||
|
let auth = await self.authPromise;
|
||||||
|
const api = new DefaultApiWrapper(self.username, self.password, self._url, auth);
|
||||||
|
|
||||||
try {
|
|
||||||
const mountStatus = await api.createMount('', '', remoteUri, mountPath, credentials);
|
const mountStatus = await api.createMount('', '', remoteUri, mountPath, credentials);
|
||||||
return {
|
return {
|
||||||
response: mountStatus.response,
|
response: mountStatus.response,
|
||||||
status: mountStatus.body
|
status: mountStatus.body
|
||||||
};
|
};
|
||||||
} catch (error) {
|
|
||||||
// TODO handle 401 by reauthenticating
|
|
||||||
throw new ControllerError(error, localize('bdc.error.mountHdfs', "Error creating mount"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getMountStatus(mountPath?: string): Promise<MountStatusResponse> {
|
public async getMountStatus(mountPath?: string, promptConnect: boolean = false): Promise<MountStatusResponse> {
|
||||||
let auth = await this.authPromise;
|
return await this.withConnectRetry<MountStatusResponse>(
|
||||||
const api = new DefaultApiWrapper(this.username, this.password, this._url, auth);
|
this.getMountStatusImpl,
|
||||||
|
promptConnect,
|
||||||
|
localize('bdc.error.mountHdfs', "Error creating mount"),
|
||||||
|
mountPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getMountStatusImpl(self: ClusterController, mountPath?: string): Promise<MountStatusResponse> {
|
||||||
|
const auth = await self.authPromise;
|
||||||
|
const api = new DefaultApiWrapper(self.username, self.password, self._url, auth);
|
||||||
|
|
||||||
try {
|
|
||||||
const mountStatus = await api.listMounts('', '', mountPath);
|
const mountStatus = await api.listMounts('', '', mountPath);
|
||||||
return {
|
return {
|
||||||
response: mountStatus.response,
|
response: mountStatus.response,
|
||||||
mount: mountStatus.body ? JSON.parse(mountStatus.body) : undefined
|
mount: mountStatus.body ? JSON.parse(mountStatus.body) : undefined
|
||||||
};
|
};
|
||||||
} catch (error) {
|
|
||||||
// TODO handle 401 by reauthenticating
|
|
||||||
throw new ControllerError(error, localize('bdc.error.mountHdfs', "Error creating mount"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async refreshMount(mountPath: string): Promise<MountResponse> {
|
public async refreshMount(mountPath: string, promptConnect: boolean = false): Promise<MountResponse> {
|
||||||
let auth = await this.authPromise;
|
return await this.withConnectRetry<MountResponse>(
|
||||||
const api = new DefaultApiWrapper(this.username, this.password, this._url, auth);
|
this.refreshMountImpl,
|
||||||
|
promptConnect,
|
||||||
|
localize('bdc.error.refreshHdfs', "Error refreshing mount"),
|
||||||
|
mountPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async refreshMountImpl(self: ClusterController, mountPath: string): Promise<MountResponse> {
|
||||||
|
const auth = await self.authPromise;
|
||||||
|
const api = new DefaultApiWrapper(self.username, self.password, self._url, auth);
|
||||||
|
|
||||||
try {
|
|
||||||
const mountStatus = await api.refreshMount('', '', mountPath);
|
const mountStatus = await api.refreshMount('', '', mountPath);
|
||||||
return {
|
return {
|
||||||
response: mountStatus.response,
|
response: mountStatus.response,
|
||||||
status: mountStatus.body
|
status: mountStatus.body
|
||||||
};
|
};
|
||||||
} catch (error) {
|
|
||||||
// TODO handle 401 by reauthenticating
|
|
||||||
throw new ControllerError(error, localize('bdc.error.refreshHdfs', "Error refreshing mount"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deleteMount(mountPath: string): Promise<MountResponse> {
|
public async deleteMount(mountPath: string, promptConnect: boolean = false): Promise<MountResponse> {
|
||||||
|
return await this.withConnectRetry<MountResponse>(
|
||||||
|
this.deleteMountImpl,
|
||||||
|
promptConnect,
|
||||||
|
localize('bdc.error.deleteHdfs', "Error deleting mount"),
|
||||||
|
mountPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
try {
|
|
||||||
const mountStatus = await api.deleteMount('', '', mountPath);
|
const mountStatus = await api.deleteMount('', '', mountPath);
|
||||||
return {
|
return {
|
||||||
response: mountStatus.response,
|
response: mountStatus.response,
|
||||||
status: mountStatus.body
|
status: mountStatus.body
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function that wraps a function call in a try/catch and if promptConnect is true
|
||||||
|
* will prompt the user to re-enter connection information and if that succeeds updates
|
||||||
|
* this with the new information.
|
||||||
|
* @param f The API function we're wrapping
|
||||||
|
* @param promptConnect Whether to actually prompt for connection on failure
|
||||||
|
* @param errorMessage The message to include in the wrapped error thrown
|
||||||
|
* @param args The args to pass to the function
|
||||||
|
*/
|
||||||
|
private async withConnectRetry<T>(f: (...args: any[]) => Promise<T>, promptConnect: boolean, errorMessage: string, ...args: any[]): Promise<T> {
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
return await f(this, args);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// TODO handle 401 by reauthenticating
|
if (promptConnect) {
|
||||||
throw new ControllerError(error, localize('bdc.error.deleteHdfs', "Error deleting mount"));
|
// 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
|
||||||
|
// on that promise.
|
||||||
|
if (!this.connectionPromise) {
|
||||||
|
this.connectionPromise = this.dialog.showDialog();
|
||||||
|
}
|
||||||
|
const controller = await this.connectionPromise;
|
||||||
|
this.connectionPromise = undefined;
|
||||||
|
if (controller) {
|
||||||
|
this.username = controller.username;
|
||||||
|
this.password = controller.password;
|
||||||
|
this._url = controller._url;
|
||||||
|
this.authType = controller.authType;
|
||||||
|
this.authPromise = controller.authPromise;
|
||||||
|
}
|
||||||
|
return await f(this, args);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new ControllerError(error, errorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fixes missing protocol and wrong character for port entered by user
|
* Fixes missing protocol and wrong character for port entered by user
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -47,12 +47,12 @@ export class BdcDashboardModel {
|
|||||||
|
|
||||||
public async refresh(): Promise<void> {
|
public async refresh(): Promise<void> {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this._clusterController.getBdcStatus().then(response => {
|
this._clusterController.getBdcStatus(true).then(response => {
|
||||||
this._bdcStatus = response.bdcStatus;
|
this._bdcStatus = response.bdcStatus;
|
||||||
this._bdcStatusLastUpdated = new Date();
|
this._bdcStatusLastUpdated = new Date();
|
||||||
this._onDidUpdateBdcStatus.fire(this.bdcStatus);
|
this._onDidUpdateBdcStatus.fire(this.bdcStatus);
|
||||||
}),
|
}),
|
||||||
this._clusterController.getEndPoints().then(response => {
|
this._clusterController.getEndPoints(true).then(response => {
|
||||||
this._endpoints = response.endPoints || [];
|
this._endpoints = response.endPoints || [];
|
||||||
fixEndpoints(this._endpoints);
|
fixEndpoints(this._endpoints);
|
||||||
this._endpointsLastUpdated = new Date();
|
this._endpointsLastUpdated = new Date();
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as azdata from 'azdata';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
import { HdfsDialogBase, HdfsDialogModelBase, HdfsDialogProperties } from './hdfsDialogBase';
|
||||||
|
import { ClusterController } from '../controller/clusterControllerApi';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export class ConnectControllerDialog extends HdfsDialogBase<HdfsDialogProperties, ClusterController> {
|
||||||
|
constructor(model: ConnectControllerModel) {
|
||||||
|
super(localize('connectController.dialog.title', "Connect to Controller"), model);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getMainSectionComponents(): (azdata.FormComponentGroup | azdata.FormComponent)[] {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async validate(): Promise<{ validated: boolean, value?: ClusterController }> {
|
||||||
|
try {
|
||||||
|
const controller = await this.model.onComplete({
|
||||||
|
url: this.urlInputBox && this.urlInputBox.value,
|
||||||
|
auth: this.authValue,
|
||||||
|
username: this.usernameInputBox && this.usernameInputBox.value,
|
||||||
|
password: this.passwordInputBox && this.passwordInputBox.value
|
||||||
|
});
|
||||||
|
return { validated: true, value: controller };
|
||||||
|
} catch (error) {
|
||||||
|
await this.reportError(error);
|
||||||
|
return { validated: false, value: undefined };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ConnectControllerModel extends HdfsDialogModelBase<HdfsDialogProperties, ClusterController> {
|
||||||
|
|
||||||
|
constructor(props: HdfsDialogProperties) {
|
||||||
|
super(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async handleCompleted(): Promise<ClusterController> {
|
||||||
|
this.throwIfMissingUsernamePassword();
|
||||||
|
|
||||||
|
// We pre-fetch the endpoints here to verify that the information entered is correct (the user is able to connect)
|
||||||
|
return await this.createAndVerifyControllerConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,239 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as azdata from 'azdata';
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
import { ClusterController, ControllerError, IEndPointsResponse } from '../controller/clusterControllerApi';
|
||||||
|
import { AuthType } from '../constants';
|
||||||
|
import { Deferred } from '../../common/promise';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
const basicAuthDisplay = localize('basicAuthName', "Basic");
|
||||||
|
const integratedAuthDisplay = localize('integratedAuthName', "Windows Authentication");
|
||||||
|
|
||||||
|
function getAuthCategory(name: AuthType): azdata.CategoryValue {
|
||||||
|
if (name === 'basic') {
|
||||||
|
return { name: name, displayName: basicAuthDisplay };
|
||||||
|
}
|
||||||
|
return { name: name, displayName: integratedAuthDisplay };
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HdfsDialogProperties {
|
||||||
|
url?: string;
|
||||||
|
auth?: AuthType;
|
||||||
|
username?: string;
|
||||||
|
password?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class HdfsDialogModelBase<T extends HdfsDialogProperties, R> {
|
||||||
|
protected _canceled = false;
|
||||||
|
private _authTypes: azdata.CategoryValue[];
|
||||||
|
constructor(
|
||||||
|
public props: T
|
||||||
|
) {
|
||||||
|
if (!props.auth) {
|
||||||
|
this.props.auth = 'basic';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get authCategories(): azdata.CategoryValue[] {
|
||||||
|
if (!this._authTypes) {
|
||||||
|
this._authTypes = [getAuthCategory('basic'), getAuthCategory('integrated')];
|
||||||
|
}
|
||||||
|
return this._authTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get authCategory(): azdata.CategoryValue {
|
||||||
|
return getAuthCategory(this.props.auth);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onComplete(props: T): Promise<R | undefined> {
|
||||||
|
try {
|
||||||
|
this.props = props;
|
||||||
|
return await this.handleCompleted();
|
||||||
|
} catch (error) {
|
||||||
|
// Ignore the error if we cancelled the request since we can't stop the actual request from completing
|
||||||
|
if (!this._canceled) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract handleCompleted(): Promise<R>;
|
||||||
|
|
||||||
|
public async onError(error: ControllerError): Promise<void> {
|
||||||
|
// implement
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onCancel(): Promise<void> {
|
||||||
|
this._canceled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected createController(): ClusterController {
|
||||||
|
return new ClusterController(this.props.url, this.props.auth, this.props.username, this.props.password, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async createAndVerifyControllerConnection(): Promise<ClusterController> {
|
||||||
|
// We pre-fetch the endpoints here to verify that the information entered is correct (the user is able to connect)
|
||||||
|
let controller = this.createController();
|
||||||
|
let response: IEndPointsResponse;
|
||||||
|
try {
|
||||||
|
response = await controller.getEndPoints();
|
||||||
|
if (!response || !response.endPoints) {
|
||||||
|
throw new Error(localize('mount.hdfs.loginerror1', "Login to controller failed"));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
throw new Error(localize('mount.hdfs.loginerror2', "Login to controller failed: {0}", err.message));
|
||||||
|
}
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected throwIfMissingUsernamePassword(): void {
|
||||||
|
if (this.props.auth === 'basic') {
|
||||||
|
// Verify username and password as we can't make them required in the UI
|
||||||
|
if (!this.props.username) {
|
||||||
|
throw new Error(localize('err.controller.username.required', "Username is required"));
|
||||||
|
} else if (!this.props.password) {
|
||||||
|
throw new Error(localize('err.controller.password.required', "Password is required"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class HdfsDialogBase<T extends HdfsDialogProperties, R> {
|
||||||
|
|
||||||
|
protected dialog: azdata.window.Dialog;
|
||||||
|
protected uiModelBuilder!: azdata.ModelBuilder;
|
||||||
|
|
||||||
|
protected urlInputBox!: azdata.InputBoxComponent;
|
||||||
|
protected authDropdown!: azdata.DropDownComponent;
|
||||||
|
protected usernameInputBox!: azdata.InputBoxComponent;
|
||||||
|
protected passwordInputBox!: azdata.InputBoxComponent;
|
||||||
|
|
||||||
|
private returnPromise: Deferred<R>;
|
||||||
|
|
||||||
|
constructor(private title: string, protected model: HdfsDialogModelBase<T, R>) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public async showDialog(): Promise<R> {
|
||||||
|
this.returnPromise = new Deferred<R>();
|
||||||
|
this.createDialog();
|
||||||
|
azdata.window.openDialog(this.dialog);
|
||||||
|
return this.returnPromise.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createDialog(): void {
|
||||||
|
this.dialog = azdata.window.createModelViewDialog(this.title);
|
||||||
|
this.dialog.registerContent(async view => {
|
||||||
|
this.uiModelBuilder = view.modelBuilder;
|
||||||
|
|
||||||
|
this.urlInputBox = this.uiModelBuilder.inputBox()
|
||||||
|
.withProperties<azdata.InputBoxProperties>({
|
||||||
|
placeHolder: localize('textUrlLower', "url"),
|
||||||
|
value: this.model.props.url,
|
||||||
|
enabled: false
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this.authDropdown = this.uiModelBuilder.dropDown().withProperties({
|
||||||
|
values: this.model.authCategories,
|
||||||
|
value: this.model.authCategory,
|
||||||
|
editable: false,
|
||||||
|
}).component();
|
||||||
|
this.authDropdown.onValueChanged(e => this.onAuthChanged());
|
||||||
|
this.usernameInputBox = this.uiModelBuilder.inputBox()
|
||||||
|
.withProperties<azdata.InputBoxProperties>({
|
||||||
|
placeHolder: localize('textUsernameLower', "username"),
|
||||||
|
value: this.model.props.username
|
||||||
|
}).component();
|
||||||
|
this.passwordInputBox = this.uiModelBuilder.inputBox()
|
||||||
|
.withProperties<azdata.InputBoxProperties>({
|
||||||
|
placeHolder: localize('textPasswordLower', "password"),
|
||||||
|
inputType: 'password',
|
||||||
|
value: this.model.props.password
|
||||||
|
})
|
||||||
|
.component();
|
||||||
|
|
||||||
|
let connectionSection: azdata.FormComponentGroup = {
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
component: this.urlInputBox,
|
||||||
|
title: localize('textUrlCapital', "URL"),
|
||||||
|
required: true
|
||||||
|
}, {
|
||||||
|
component: this.authDropdown,
|
||||||
|
title: localize('textAuthCapital', "Authentication type"),
|
||||||
|
required: true
|
||||||
|
}, {
|
||||||
|
component: this.usernameInputBox,
|
||||||
|
title: localize('textUsernameCapital', "Username"),
|
||||||
|
required: false
|
||||||
|
}, {
|
||||||
|
component: this.passwordInputBox,
|
||||||
|
title: localize('textPasswordCapital', "Password"),
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
title: localize('hdsf.dialog.connection.section', "Cluster Connection")
|
||||||
|
};
|
||||||
|
let formModel = this.uiModelBuilder.formContainer()
|
||||||
|
.withFormItems(
|
||||||
|
this.getMainSectionComponents().concat(
|
||||||
|
connectionSection)
|
||||||
|
).withLayout({ width: '100%' }).component();
|
||||||
|
|
||||||
|
await view.initializeModel(formModel);
|
||||||
|
this.onAuthChanged();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dialog.registerCloseValidator(async () => {
|
||||||
|
const result = await this.validate();
|
||||||
|
if (result.validated) {
|
||||||
|
this.returnPromise.resolve(result.value);
|
||||||
|
this.returnPromise = undefined;
|
||||||
|
}
|
||||||
|
return result.validated;
|
||||||
|
});
|
||||||
|
this.dialog.cancelButton.onClick(async () => await this.cancel());
|
||||||
|
this.dialog.okButton.label = localize('hdfs.dialog.ok', "OK");
|
||||||
|
this.dialog.cancelButton.label = localize('hdfs.dialog.cancel', "Cancel");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract getMainSectionComponents(): (azdata.FormComponentGroup | azdata.FormComponent)[];
|
||||||
|
|
||||||
|
protected get authValue(): AuthType {
|
||||||
|
return (<azdata.CategoryValue>this.authDropdown.value).name as AuthType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private onAuthChanged(): void {
|
||||||
|
let isBasic = this.authValue === 'basic';
|
||||||
|
this.usernameInputBox.enabled = isBasic;
|
||||||
|
this.passwordInputBox.enabled = isBasic;
|
||||||
|
if (!isBasic) {
|
||||||
|
this.usernameInputBox.value = '';
|
||||||
|
this.passwordInputBox.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract validate(): Promise<{ validated: boolean, value?: R }>;
|
||||||
|
|
||||||
|
private async cancel(): Promise<void> {
|
||||||
|
if (this.model && this.model.onCancel) {
|
||||||
|
await this.model.onCancel();
|
||||||
|
}
|
||||||
|
this.returnPromise.reject(new Error('Dialog cancelled'));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async reportError(error: any): Promise<void> {
|
||||||
|
this.dialog.message = {
|
||||||
|
text: (typeof error === 'string') ? error : error.message,
|
||||||
|
level: azdata.window.MessageLevel.Error
|
||||||
|
};
|
||||||
|
if (this.model && this.model.onError) {
|
||||||
|
await this.model.onError(error as ControllerError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,23 +6,14 @@
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { ClusterController, ControllerError, MountInfo, MountState, IEndPointsResponse } from '../controller/clusterControllerApi';
|
import { ClusterController, MountInfo, MountState } from '../controller/clusterControllerApi';
|
||||||
import { AuthType } from '../constants';
|
import { HdfsDialogBase, HdfsDialogModelBase, HdfsDialogProperties } from './hdfsDialogBase';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
const basicAuthDisplay = localize('basicAuthName', "Basic");
|
|
||||||
const integratedAuthDisplay = localize('integratedAuthName', "Windows Authentication");
|
|
||||||
const mountConfigutationTitle = localize('mount.main.section', "Mount Configuration");
|
const mountConfigutationTitle = localize('mount.main.section', "Mount Configuration");
|
||||||
const hdfsPathTitle = localize('mount.hdfsPath', "HDFS Path");
|
const hdfsPathTitle = localize('mount.hdfsPath', "HDFS Path");
|
||||||
|
|
||||||
function getAuthCategory(name: AuthType): azdata.CategoryValue {
|
|
||||||
if (name === 'basic') {
|
|
||||||
return { name: name, displayName: basicAuthDisplay };
|
|
||||||
}
|
|
||||||
return { name: name, displayName: integratedAuthDisplay };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a comma-delimited set of key value pair credentials to a JSON object.
|
* Converts a comma-delimited set of key value pair credentials to a JSON object.
|
||||||
* This code is taken from the azdata implementation written in Python
|
* This code is taken from the azdata implementation written in Python
|
||||||
@@ -65,96 +56,13 @@ function convertCredsToJson(creds: string): { credentials: {} } {
|
|||||||
return credObj;
|
return credObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DialogProperties {
|
export interface MountHdfsProperties extends HdfsDialogProperties {
|
||||||
url?: string;
|
|
||||||
auth?: AuthType;
|
|
||||||
username?: string;
|
|
||||||
password?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MountHdfsProperties extends DialogProperties {
|
|
||||||
hdfsPath?: string;
|
hdfsPath?: string;
|
||||||
remoteUri?: string;
|
remoteUri?: string;
|
||||||
credentials?: string;
|
credentials?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class HdfsDialogModelBase<T extends DialogProperties> {
|
export class MountHdfsDialogModel extends HdfsDialogModelBase<MountHdfsProperties, void> {
|
||||||
protected _canceled = false;
|
|
||||||
private _authTypes: azdata.CategoryValue[];
|
|
||||||
constructor(
|
|
||||||
public props: T
|
|
||||||
) {
|
|
||||||
if (!props.auth) {
|
|
||||||
this.props.auth = 'basic';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public get authCategories(): azdata.CategoryValue[] {
|
|
||||||
if (!this._authTypes) {
|
|
||||||
this._authTypes = [getAuthCategory('basic'), getAuthCategory('integrated')];
|
|
||||||
}
|
|
||||||
return this._authTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get authCategory(): azdata.CategoryValue {
|
|
||||||
return getAuthCategory(this.props.auth);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async onComplete(props: T): Promise<void> {
|
|
||||||
try {
|
|
||||||
this.props = props;
|
|
||||||
await this.handleCompleted();
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
// Ignore the error if we cancelled the request since we can't stop the actual request from completing
|
|
||||||
if (!this._canceled) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract handleCompleted(): Promise<void>;
|
|
||||||
|
|
||||||
public async onError(error: ControllerError): Promise<void> {
|
|
||||||
// implement
|
|
||||||
}
|
|
||||||
|
|
||||||
public async onCancel(): Promise<void> {
|
|
||||||
this._canceled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected createController(): ClusterController {
|
|
||||||
return new ClusterController(this.props.url, this.props.auth, this.props.username, this.props.password, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async createAndVerifyControllerConnection(): Promise<ClusterController> {
|
|
||||||
// We pre-fetch the endpoints here to verify that the information entered is correct (the user is able to connect)
|
|
||||||
let controller = this.createController();
|
|
||||||
let response: IEndPointsResponse;
|
|
||||||
try {
|
|
||||||
response = await controller.getEndPoints();
|
|
||||||
if (!response || !response.endPoints) {
|
|
||||||
throw new Error(localize('mount.hdfs.loginerror1', "Login to controller failed"));
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
throw new Error(localize('mount.hdfs.loginerror2', "Login to controller failed: {0}", err.message));
|
|
||||||
}
|
|
||||||
return controller;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected throwIfMissingUsernamePassword(): void {
|
|
||||||
if (this.props.auth === 'basic') {
|
|
||||||
// Verify username and password as we can't make them required in the UI
|
|
||||||
if (!this.props.username) {
|
|
||||||
throw new Error(localize('err.controller.username.required', "Username is required"));
|
|
||||||
} else if (!this.props.password) {
|
|
||||||
throw new Error(localize('err.controller.password.required', "Password is required"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MountHdfsDialogModel extends HdfsDialogModelBase<MountHdfsProperties> {
|
|
||||||
private credentials: {};
|
private credentials: {};
|
||||||
|
|
||||||
constructor(props: MountHdfsProperties) {
|
constructor(props: MountHdfsProperties) {
|
||||||
@@ -235,128 +143,7 @@ export class MountHdfsDialogModel extends HdfsDialogModelBase<MountHdfsPropertie
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class HdfsDialogBase<T extends DialogProperties> {
|
export class MountHdfsDialog extends HdfsDialogBase<MountHdfsProperties, void> {
|
||||||
|
|
||||||
protected dialog: azdata.window.Dialog;
|
|
||||||
protected uiModelBuilder!: azdata.ModelBuilder;
|
|
||||||
|
|
||||||
protected urlInputBox!: azdata.InputBoxComponent;
|
|
||||||
protected authDropdown!: azdata.DropDownComponent;
|
|
||||||
protected usernameInputBox!: azdata.InputBoxComponent;
|
|
||||||
protected passwordInputBox!: azdata.InputBoxComponent;
|
|
||||||
|
|
||||||
constructor(private title: string, protected model: HdfsDialogModelBase<T>) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public showDialog(): void {
|
|
||||||
this.createDialog();
|
|
||||||
azdata.window.openDialog(this.dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
private createDialog(): void {
|
|
||||||
this.dialog = azdata.window.createModelViewDialog(this.title);
|
|
||||||
this.dialog.registerContent(async view => {
|
|
||||||
this.uiModelBuilder = view.modelBuilder;
|
|
||||||
|
|
||||||
this.urlInputBox = this.uiModelBuilder.inputBox()
|
|
||||||
.withProperties<azdata.InputBoxProperties>({
|
|
||||||
placeHolder: localize('textUrlLower', "url"),
|
|
||||||
value: this.model.props.url,
|
|
||||||
}).component();
|
|
||||||
this.urlInputBox.enabled = false;
|
|
||||||
|
|
||||||
this.authDropdown = this.uiModelBuilder.dropDown().withProperties({
|
|
||||||
values: this.model.authCategories,
|
|
||||||
value: this.model.authCategory,
|
|
||||||
editable: false,
|
|
||||||
}).component();
|
|
||||||
this.authDropdown.onValueChanged(e => this.onAuthChanged());
|
|
||||||
this.usernameInputBox = this.uiModelBuilder.inputBox()
|
|
||||||
.withProperties<azdata.InputBoxProperties>({
|
|
||||||
placeHolder: localize('textUsernameLower', "username"),
|
|
||||||
value: this.model.props.username
|
|
||||||
}).component();
|
|
||||||
this.passwordInputBox = this.uiModelBuilder.inputBox()
|
|
||||||
.withProperties<azdata.InputBoxProperties>({
|
|
||||||
placeHolder: localize('textPasswordLower', "password"),
|
|
||||||
inputType: 'password',
|
|
||||||
value: this.model.props.password
|
|
||||||
})
|
|
||||||
.component();
|
|
||||||
|
|
||||||
let connectionSection: azdata.FormComponentGroup = {
|
|
||||||
components: [
|
|
||||||
{
|
|
||||||
component: this.urlInputBox,
|
|
||||||
title: localize('textUrlCapital', "URL"),
|
|
||||||
required: true
|
|
||||||
}, {
|
|
||||||
component: this.authDropdown,
|
|
||||||
title: localize('textAuthCapital', "Authentication type"),
|
|
||||||
required: true
|
|
||||||
}, {
|
|
||||||
component: this.usernameInputBox,
|
|
||||||
title: localize('textUsernameCapital', "Username"),
|
|
||||||
required: false
|
|
||||||
}, {
|
|
||||||
component: this.passwordInputBox,
|
|
||||||
title: localize('textPasswordCapital', "Password"),
|
|
||||||
required: false
|
|
||||||
}
|
|
||||||
],
|
|
||||||
title: localize('hdsf.dialog.connection.section', "Cluster Connection")
|
|
||||||
};
|
|
||||||
let formModel = this.uiModelBuilder.formContainer()
|
|
||||||
.withFormItems([
|
|
||||||
this.getMainSection(),
|
|
||||||
connectionSection
|
|
||||||
]).withLayout({ width: '100%' }).component();
|
|
||||||
|
|
||||||
await view.initializeModel(formModel);
|
|
||||||
this.onAuthChanged();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.dialog.registerCloseValidator(async () => await this.validate());
|
|
||||||
this.dialog.cancelButton.onClick(async () => await this.cancel());
|
|
||||||
this.dialog.okButton.label = localize('hdfs.dialog.ok', "OK");
|
|
||||||
this.dialog.cancelButton.label = localize('hdfs.dialog.cancel', "Cancel");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract getMainSection(): azdata.FormComponentGroup;
|
|
||||||
|
|
||||||
protected get authValue(): AuthType {
|
|
||||||
return (<azdata.CategoryValue>this.authDropdown.value).name as AuthType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private onAuthChanged(): void {
|
|
||||||
let isBasic = this.authValue === 'basic';
|
|
||||||
this.usernameInputBox.enabled = isBasic;
|
|
||||||
this.passwordInputBox.enabled = isBasic;
|
|
||||||
if (!isBasic) {
|
|
||||||
this.usernameInputBox.value = '';
|
|
||||||
this.passwordInputBox.value = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract validate(): Promise<boolean>;
|
|
||||||
|
|
||||||
private async cancel(): Promise<void> {
|
|
||||||
if (this.model && this.model.onCancel) {
|
|
||||||
await this.model.onCancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async reportError(error: any): Promise<void> {
|
|
||||||
this.dialog.message = {
|
|
||||||
text: (typeof error === 'string') ? error : error.message,
|
|
||||||
level: azdata.window.MessageLevel.Error
|
|
||||||
};
|
|
||||||
if (this.model && this.model.onError) {
|
|
||||||
await this.model.onError(error as ControllerError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export class MountHdfsDialog extends HdfsDialogBase<MountHdfsProperties> {
|
|
||||||
private pathInputBox: azdata.InputBoxComponent;
|
private pathInputBox: azdata.InputBoxComponent;
|
||||||
private remoteUriInputBox: azdata.InputBoxComponent;
|
private remoteUriInputBox: azdata.InputBoxComponent;
|
||||||
private credentialsInputBox: azdata.InputBoxComponent;
|
private credentialsInputBox: azdata.InputBoxComponent;
|
||||||
@@ -365,7 +152,7 @@ export class MountHdfsDialog extends HdfsDialogBase<MountHdfsProperties> {
|
|||||||
super(localize('mount.dialog.title', "Mount HDFS Folder"), model);
|
super(localize('mount.dialog.title', "Mount HDFS Folder"), model);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getMainSection(): azdata.FormComponentGroup {
|
protected getMainSectionComponents(): (azdata.FormComponentGroup | azdata.FormComponent)[] {
|
||||||
const newMountName = '/mymount';
|
const newMountName = '/mymount';
|
||||||
let pathVal = this.model.props.hdfsPath;
|
let pathVal = this.model.props.hdfsPath;
|
||||||
pathVal = (!pathVal || pathVal === '/') ? newMountName : (pathVal + newMountName);
|
pathVal = (!pathVal || pathVal === '/') ? newMountName : (pathVal + newMountName);
|
||||||
@@ -385,7 +172,8 @@ export class MountHdfsDialog extends HdfsDialogBase<MountHdfsProperties> {
|
|||||||
})
|
})
|
||||||
.component();
|
.component();
|
||||||
|
|
||||||
return {
|
return [
|
||||||
|
{
|
||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
component: this.pathInputBox,
|
component: this.pathInputBox,
|
||||||
@@ -402,10 +190,10 @@ export class MountHdfsDialog extends HdfsDialogBase<MountHdfsProperties> {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
title: mountConfigutationTitle
|
title: mountConfigutationTitle
|
||||||
};
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async validate(): Promise<boolean> {
|
protected async validate(): Promise<{ validated: boolean }> {
|
||||||
try {
|
try {
|
||||||
await this.model.onComplete({
|
await this.model.onComplete({
|
||||||
url: this.urlInputBox && this.urlInputBox.value,
|
url: this.urlInputBox && this.urlInputBox.value,
|
||||||
@@ -416,27 +204,28 @@ export class MountHdfsDialog extends HdfsDialogBase<MountHdfsProperties> {
|
|||||||
remoteUri: this.remoteUriInputBox && this.remoteUriInputBox.value,
|
remoteUri: this.remoteUriInputBox && this.remoteUriInputBox.value,
|
||||||
credentials: this.credentialsInputBox && this.credentialsInputBox.value
|
credentials: this.credentialsInputBox && this.credentialsInputBox.value
|
||||||
});
|
});
|
||||||
return true;
|
return { validated: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await this.reportError(error);
|
await this.reportError(error);
|
||||||
return false;
|
return { validated: false };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RefreshMountDialog extends HdfsDialogBase<MountHdfsProperties> {
|
export class RefreshMountDialog extends HdfsDialogBase<MountHdfsProperties, void> {
|
||||||
private pathInputBox: azdata.InputBoxComponent;
|
private pathInputBox: azdata.InputBoxComponent;
|
||||||
|
|
||||||
constructor(model: RefreshMountModel) {
|
constructor(model: RefreshMountModel) {
|
||||||
super(localize('refreshmount.dialog.title', "Refresh Mount"), model);
|
super(localize('refreshmount.dialog.title', "Refresh Mount"), model);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getMainSection(): azdata.FormComponentGroup {
|
protected getMainSectionComponents(): (azdata.FormComponentGroup | azdata.FormComponent)[] {
|
||||||
this.pathInputBox = this.uiModelBuilder.inputBox()
|
this.pathInputBox = this.uiModelBuilder.inputBox()
|
||||||
.withProperties<azdata.InputBoxProperties>({
|
.withProperties<azdata.InputBoxProperties>({
|
||||||
value: this.model.props.hdfsPath
|
value: this.model.props.hdfsPath
|
||||||
}).component();
|
}).component();
|
||||||
return {
|
return [
|
||||||
|
{
|
||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
component: this.pathInputBox,
|
component: this.pathInputBox,
|
||||||
@@ -445,10 +234,10 @@ export class RefreshMountDialog extends HdfsDialogBase<MountHdfsProperties> {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
title: mountConfigutationTitle
|
title: mountConfigutationTitle
|
||||||
};
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async validate(): Promise<boolean> {
|
protected async validate(): Promise<{ validated: boolean }> {
|
||||||
try {
|
try {
|
||||||
await this.model.onComplete({
|
await this.model.onComplete({
|
||||||
url: this.urlInputBox && this.urlInputBox.value,
|
url: this.urlInputBox && this.urlInputBox.value,
|
||||||
@@ -457,15 +246,15 @@ export class RefreshMountDialog extends HdfsDialogBase<MountHdfsProperties> {
|
|||||||
password: this.passwordInputBox && this.passwordInputBox.value,
|
password: this.passwordInputBox && this.passwordInputBox.value,
|
||||||
hdfsPath: this.pathInputBox && this.pathInputBox.value
|
hdfsPath: this.pathInputBox && this.pathInputBox.value
|
||||||
});
|
});
|
||||||
return true;
|
return { validated: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await this.reportError(error);
|
await this.reportError(error);
|
||||||
return false;
|
return { validated: false };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RefreshMountModel extends HdfsDialogModelBase<MountHdfsProperties> {
|
export class RefreshMountModel extends HdfsDialogModelBase<MountHdfsProperties, void> {
|
||||||
|
|
||||||
constructor(props: MountHdfsProperties) {
|
constructor(props: MountHdfsProperties) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -504,19 +293,20 @@ export class RefreshMountModel extends HdfsDialogModelBase<MountHdfsProperties>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DeleteMountDialog extends HdfsDialogBase<MountHdfsProperties> {
|
export class DeleteMountDialog extends HdfsDialogBase<MountHdfsProperties, void> {
|
||||||
private pathInputBox: azdata.InputBoxComponent;
|
private pathInputBox: azdata.InputBoxComponent;
|
||||||
|
|
||||||
constructor(model: DeleteMountModel) {
|
constructor(model: DeleteMountModel) {
|
||||||
super(localize('deleteMount.dialog.title', "Delete Mount"), model);
|
super(localize('deleteMount.dialog.title', "Delete Mount"), model);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getMainSection(): azdata.FormComponentGroup {
|
protected getMainSectionComponents(): (azdata.FormComponentGroup | azdata.FormComponent)[] {
|
||||||
this.pathInputBox = this.uiModelBuilder.inputBox()
|
this.pathInputBox = this.uiModelBuilder.inputBox()
|
||||||
.withProperties<azdata.InputBoxProperties>({
|
.withProperties<azdata.InputBoxProperties>({
|
||||||
value: this.model.props.hdfsPath
|
value: this.model.props.hdfsPath
|
||||||
}).component();
|
}).component();
|
||||||
return {
|
return [
|
||||||
|
{
|
||||||
components: [
|
components: [
|
||||||
{
|
{
|
||||||
component: this.pathInputBox,
|
component: this.pathInputBox,
|
||||||
@@ -525,10 +315,10 @@ export class DeleteMountDialog extends HdfsDialogBase<MountHdfsProperties> {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
title: mountConfigutationTitle
|
title: mountConfigutationTitle
|
||||||
};
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async validate(): Promise<boolean> {
|
protected async validate(): Promise<{ validated: boolean }> {
|
||||||
try {
|
try {
|
||||||
await this.model.onComplete({
|
await this.model.onComplete({
|
||||||
url: this.urlInputBox && this.urlInputBox.value,
|
url: this.urlInputBox && this.urlInputBox.value,
|
||||||
@@ -537,15 +327,15 @@ export class DeleteMountDialog extends HdfsDialogBase<MountHdfsProperties> {
|
|||||||
password: this.passwordInputBox && this.passwordInputBox.value,
|
password: this.passwordInputBox && this.passwordInputBox.value,
|
||||||
hdfsPath: this.pathInputBox && this.pathInputBox.value
|
hdfsPath: this.pathInputBox && this.pathInputBox.value
|
||||||
});
|
});
|
||||||
return true;
|
return { validated: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await this.reportError(error);
|
await this.reportError(error);
|
||||||
return false;
|
return { validated: false };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DeleteMountModel extends HdfsDialogModelBase<MountHdfsProperties> {
|
export class DeleteMountModel extends HdfsDialogModelBase<MountHdfsProperties, void> {
|
||||||
|
|
||||||
constructor(props: MountHdfsProperties) {
|
constructor(props: MountHdfsProperties) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|||||||
25
extensions/big-data-cluster/src/common/promise.ts
Normal file
25
extensions/big-data-cluster/src/common/promise.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deferred promise
|
||||||
|
*/
|
||||||
|
export class Deferred<T> {
|
||||||
|
promise: Promise<T>;
|
||||||
|
resolve: (value?: T | PromiseLike<T>) => void;
|
||||||
|
reject: (reason?: any) => void;
|
||||||
|
constructor() {
|
||||||
|
this.promise = new Promise<T>((resolve, reject) => {
|
||||||
|
this.resolve = resolve;
|
||||||
|
this.reject = reject;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
then<TResult>(onfulfilled?: (value: T) => TResult | Thenable<TResult>, onrejected?: (reason: any) => TResult | Thenable<TResult>): Thenable<TResult>;
|
||||||
|
then<TResult>(onfulfilled?: (value: T) => TResult | Thenable<TResult>, onrejected?: (reason: any) => void): Thenable<TResult>;
|
||||||
|
then<TResult>(onfulfilled?: (value: T) => TResult | Thenable<TResult>, onrejected?: (reason: any) => TResult | Thenable<TResult>): Thenable<TResult> {
|
||||||
|
return this.promise.then(onfulfilled, onrejected);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user