Maddy/prompt password when different (#4537)

* Prompt for password once when the sql instance password doesn't work for hdfs. If the user provides the correct password, connect and continue, else show Unauthorized  node.

* Removed the hardcoded bad password

* Added check for empty folder scenarios

* Added ErrorStatusCode as property of TreeNode. Checking for the error code instead of the error string to avoid localization issues

* type fixed

* implemented hasExpansionError
This commit is contained in:
Maddy
2019-03-19 13:46:55 -07:00
committed by GitHub
parent 330fb6dff5
commit 5e72cd12d1
6 changed files with 64 additions and 13 deletions

View File

@@ -107,7 +107,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<MssqlE
credentialsStore.start(); credentialsStore.start();
resourceProvider.start(); resourceProvider.start();
let nodeProvider = new MssqlObjectExplorerNodeProvider(appContext); let nodeProvider = new MssqlObjectExplorerNodeProvider(prompter, appContext);
azdata.dataprotocol.registerObjectExplorerNodeProvider(nodeProvider); azdata.dataprotocol.registerObjectExplorerNodeProvider(nodeProvider);
activateSparkFeatures(appContext); activateSparkFeatures(appContext);
activateNotebookTask(appContext); activateNotebookTask(appContext);

View File

@@ -69,6 +69,12 @@ export class SqlClusterConnection {
return FileSourceFactory.instance.createHdfsFileSource(options); return FileSourceFactory.instance.createHdfsFileSource(options);
} }
public updatePassword(password : string): void{
if(password){
this._password = password;
}
}
private validate(connectionInfo: azdata.ConnectionInfo): void { private validate(connectionInfo: azdata.ConnectionInfo): void {
if (!connectionInfo) { if (!connectionInfo) {
throw new Error(localize('connectionInfoUndefined', 'ConnectionInfo is undefined.')); throw new Error(localize('connectionInfoUndefined', 'ConnectionInfo is undefined.'));

View File

@@ -150,7 +150,7 @@ export class HdfsFileSource implements IFileSource {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.client.readdir(path, (error, files) => { this.client.readdir(path, (error, files) => {
if (error) { if (error) {
reject(error.message); reject(error);
} else { } else {
let hdfsFiles: IFile[] = files.map(file => { let hdfsFiles: IFile[] = files.map(file => {
let hdfsFile = <IHdfsFileStatus> file; let hdfsFile = <IHdfsFileStatus> file;

View File

@@ -136,7 +136,7 @@ export class FolderNode extends HdfsFileSourceNode {
}); });
} }
} catch (error) { } catch (error) {
this.children = [ErrorNode.create(localize('errorExpanding', 'Error: {0}', utils.getErrorMessage(error)), this)]; this.children = [ErrorNode.create(localize('errorExpanding', 'Error: {0}', utils.getErrorMessage(error)), this, error.statusCode)];
} }
} }
return this.children; return this.children;
@@ -342,9 +342,12 @@ export class ErrorNode extends TreeNode {
super(); super();
} }
public static create(message: string, parent: TreeNode): ErrorNode { public static create(message: string, parent: TreeNode, errorCode?: number): ErrorNode {
let node = new ErrorNode(message); let node = new ErrorNode(message);
node.parent = parent; node.parent = parent;
if(errorCode){
node.errorStatusCode = errorCode;
}
return node; return node;
} }

View File

@@ -20,6 +20,7 @@ import { AppContext } from '../appContext';
import * as constants from '../constants'; import * as constants from '../constants';
import * as SqlClusterLookUp from '../sqlClusterLookUp'; import * as SqlClusterLookUp from '../sqlClusterLookUp';
import { ICommandObjectExplorerContext } from './command'; import { ICommandObjectExplorerContext } from './command';
import { IPrompter, IQuestion, QuestionTypes } from '../prompts/question';
export const mssqlOutputChannel = vscode.window.createOutputChannel(constants.providerId); export const mssqlOutputChannel = vscode.window.createOutputChannel(constants.providerId);
@@ -28,7 +29,7 @@ export class MssqlObjectExplorerNodeProvider extends ProviderBase implements azd
private sessionMap: Map<string, SqlClusterSession>; private sessionMap: Map<string, SqlClusterSession>;
private expandCompleteEmitter = new vscode.EventEmitter<azdata.ObjectExplorerExpandInfo>(); private expandCompleteEmitter = new vscode.EventEmitter<azdata.ObjectExplorerExpandInfo>();
constructor(private appContext: AppContext) { constructor(private prompter: IPrompter, private appContext: AppContext) {
super(); super();
this.sessionMap = new Map<string, SqlClusterSession>(); this.sessionMap = new Map<string, SqlClusterSession>();
this.appContext.registerService<MssqlObjectExplorerNodeProvider>(constants.ObjectExplorerService, this); this.appContext.registerService<MssqlObjectExplorerNodeProvider>(constants.ObjectExplorerService, this);
@@ -95,6 +96,13 @@ export class MssqlObjectExplorerNodeProvider extends ProviderBase implements azd
return true; return true;
} }
private hasExpansionError(children: TreeNode[]): boolean {
if(children.find(c => c.errorStatusCode > 0)){
return true;
}
return false;
}
private async startExpansion(session: SqlClusterSession, nodeInfo: azdata.ExpandNodeInfo, isRefresh: boolean = false): Promise<void> { private async startExpansion(session: SqlClusterSession, nodeInfo: azdata.ExpandNodeInfo, isRefresh: boolean = false): Promise<void> {
let expandResult: azdata.ObjectExplorerExpandInfo = { let expandResult: azdata.ObjectExplorerExpandInfo = {
sessionId: session.sessionId, sessionId: session.sessionId,
@@ -107,16 +115,26 @@ export class MssqlObjectExplorerNodeProvider extends ProviderBase implements azd
if (node) { if (node) {
expandResult.errorMessage = node.getNodeInfo().errorMessage; expandResult.errorMessage = node.getNodeInfo().errorMessage;
let children = await node.getChildren(true); let children = await node.getChildren(true);
if (children) { if (children && children.length > 0) {
expandResult.nodes = children.map(c => c.getNodeInfo()); // Only child returned when failure happens : When failed with 'Unauthorized' error, prompt for password.
// There is only child returned when failure happens if (children.length === 1 && this.hasExpansionError(children)) {
if (children.length === 1) { if (children[0].errorStatusCode === 401) {
let child = children[0].getNodeInfo(); //Prompt for password
if (child && child.nodeType === constants.MssqlClusterItems.Error) { let password: string = await this.promptPassword(localize('prmptPwd', 'Please provide the password to connect to HDFS:'));
expandResult.errorMessage = child.label; if (password && password.length > 0) {
expandResult.nodes = []; session.sqlClusterConnection.updatePassword(password);
node.updateFileSource(session.sqlClusterConnection);
children = await node.getChildren(true);
}
} }
} }
expandResult.nodes = children.map(c => c.getNodeInfo());
if (children.length === 1 && this.hasExpansionError(children)) {
let child = children[0].getNodeInfo();
expandResult.errorMessage = child ? child.label : 'Unknown Error';
expandResult.nodes = [];
}
} }
} }
} catch (error) { } catch (error) {
@@ -125,6 +143,15 @@ export class MssqlObjectExplorerNodeProvider extends ProviderBase implements azd
this.expandCompleteEmitter.fire(expandResult); this.expandCompleteEmitter.fire(expandResult);
} }
private async promptPassword(promptMsg: string): Promise<string> {
return await this.prompter.promptSingle(<IQuestion>{
type: QuestionTypes.password,
name: 'passwordPrompt',
message: promptMsg,
default: ''
}).then(confirmed => <string>confirmed);
}
refreshNode(nodeInfo: azdata.ExpandNodeInfo): Thenable<boolean> { refreshNode(nodeInfo: azdata.ExpandNodeInfo): Thenable<boolean> {
// TODO #3815 implement properly // TODO #3815 implement properly
return this.expandNode(nodeInfo, true); return this.expandNode(nodeInfo, true);

View File

@@ -8,11 +8,15 @@
import * as azdata from 'azdata'; import * as azdata from 'azdata';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { ITreeNode } from './types'; import { ITreeNode } from './types';
import { IFileSource } from './fileSources';
import { SqlClusterConnection } from './connection';
type TreeNodePredicate = (node: TreeNode) => boolean; type TreeNodePredicate = (node: TreeNode) => boolean;
export abstract class TreeNode implements ITreeNode { export abstract class TreeNode implements ITreeNode {
private _parent: TreeNode = undefined; private _parent: TreeNode = undefined;
protected fileSource: IFileSource;
private _errorStatusCode: number;
public get parent(): TreeNode { public get parent(): TreeNode {
return this._parent; return this._parent;
@@ -22,6 +26,14 @@ export abstract class TreeNode implements ITreeNode {
this._parent = node; this._parent = node;
} }
public get errorStatusCode(): number {
return this._errorStatusCode;
}
public set errorStatusCode(error: number) {
this._errorStatusCode = error;
}
public generateNodePath(): string { public generateNodePath(): string {
let path = undefined; let path = undefined;
if (this.parent) { if (this.parent) {
@@ -66,6 +78,9 @@ export abstract class TreeNode implements ITreeNode {
return undefined; return undefined;
} }
public updateFileSource(connection: SqlClusterConnection): void{
this.fileSource = connection.createHdfsFileSource();
}
/** /**
* The value to use for this node in the node path * The value to use for this node in the node path
*/ */