mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Add event to track node expansion errors (#19248)
This commit is contained in:
@@ -49,7 +49,8 @@ export const enum TelemetryView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const enum TelemetryError {
|
export const enum TelemetryError {
|
||||||
DatabaseConnectionError = 'DatabaseConnectionError'
|
DatabaseConnectionError = 'DatabaseConnectionError',
|
||||||
|
ObjectExplorerExpandError = 'ObjectExplorerExpandError'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum TelemetryAction {
|
export const enum TelemetryAction {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
|||||||
import { ServerTreeActionProvider } from 'sql/workbench/services/objectExplorer/browser/serverTreeActionProvider';
|
import { ServerTreeActionProvider } from 'sql/workbench/services/objectExplorer/browser/serverTreeActionProvider';
|
||||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||||
import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||||
|
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||||
|
|
||||||
export const SERVICE_ID = 'ObjectExplorerService';
|
export const SERVICE_ID = 'ObjectExplorerService';
|
||||||
|
|
||||||
@@ -63,9 +64,9 @@ export interface IObjectExplorerService {
|
|||||||
|
|
||||||
closeSession(providerId: string, session: azdata.ObjectExplorerSession): Promise<azdata.ObjectExplorerCloseSessionResponse | undefined>;
|
closeSession(providerId: string, session: azdata.ObjectExplorerSession): Promise<azdata.ObjectExplorerCloseSessionResponse | undefined>;
|
||||||
|
|
||||||
expandNode(providerId: string, session: azdata.ObjectExplorerSession, nodePath: string): Promise<azdata.ObjectExplorerExpandInfo>;
|
expandNode(providerId: string, session: azdata.ObjectExplorerSession, node: TreeNode): Promise<azdata.ObjectExplorerExpandInfo>;
|
||||||
|
|
||||||
refreshNode(providerId: string, session: azdata.ObjectExplorerSession, nodePath: string): Promise<azdata.ObjectExplorerExpandInfo | undefined>;
|
refreshNode(providerId: string, session: azdata.ObjectExplorerSession, node: TreeNode): Promise<azdata.ObjectExplorerExpandInfo | undefined>;
|
||||||
|
|
||||||
resolveTreeNodeChildren(session: azdata.ObjectExplorerSession, parentTree: TreeNode): Promise<TreeNode[]>;
|
resolveTreeNodeChildren(session: azdata.ObjectExplorerSession, parentTree: TreeNode): Promise<TreeNode[]>;
|
||||||
|
|
||||||
@@ -231,7 +232,6 @@ export class ObjectExplorerService implements IObjectExplorerService {
|
|||||||
* Gets called when expanded node response is ready
|
* Gets called when expanded node response is ready
|
||||||
*/
|
*/
|
||||||
public onNodeExpanded(expandResponse: NodeExpandInfoWithProviderId) {
|
public onNodeExpanded(expandResponse: NodeExpandInfoWithProviderId) {
|
||||||
|
|
||||||
if (expandResponse.errorMessage) {
|
if (expandResponse.errorMessage) {
|
||||||
this.logService.error(expandResponse.errorMessage);
|
this.logService.error(expandResponse.errorMessage);
|
||||||
}
|
}
|
||||||
@@ -358,7 +358,7 @@ export class ObjectExplorerService implements IObjectExplorerService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async expandNode(providerId: string, session: azdata.ObjectExplorerSession, nodePath: string): Promise<azdata.ObjectExplorerExpandInfo> {
|
public async expandNode(providerId: string, session: azdata.ObjectExplorerSession, node: TreeNode): Promise<azdata.ObjectExplorerExpandInfo> {
|
||||||
const provider = this._providers[providerId];
|
const provider = this._providers[providerId];
|
||||||
if (provider) {
|
if (provider) {
|
||||||
this._telemetryService.createActionEvent(TelemetryKeys.TelemetryView.Shell, TelemetryKeys.TelemetryAction.ObjectExplorerExpand)
|
this._telemetryService.createActionEvent(TelemetryKeys.TelemetryView.Shell, TelemetryKeys.TelemetryAction.ObjectExplorerExpand)
|
||||||
@@ -366,7 +366,7 @@ export class ObjectExplorerService implements IObjectExplorerService {
|
|||||||
refresh: false,
|
refresh: false,
|
||||||
provider: providerId
|
provider: providerId
|
||||||
}).send();
|
}).send();
|
||||||
return await this.expandOrRefreshNode(providerId, session, nodePath);
|
return await this.expandOrRefreshNode(providerId, session, node);
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Provider doesn't exist. id: ${providerId}`);
|
throw new Error(`Provider doesn't exist. id: ${providerId}`);
|
||||||
}
|
}
|
||||||
@@ -383,14 +383,14 @@ export class ObjectExplorerService implements IObjectExplorerService {
|
|||||||
private expandOrRefreshNode(
|
private expandOrRefreshNode(
|
||||||
providerId: string,
|
providerId: string,
|
||||||
session: azdata.ObjectExplorerSession,
|
session: azdata.ObjectExplorerSession,
|
||||||
nodePath: string,
|
node: TreeNode,
|
||||||
refresh: boolean = false): Promise<azdata.ObjectExplorerExpandInfo> {
|
refresh: boolean = false): Promise<azdata.ObjectExplorerExpandInfo> {
|
||||||
let self = this;
|
let self = this;
|
||||||
return new Promise<azdata.ObjectExplorerExpandInfo>((resolve, reject) => {
|
return new Promise<azdata.ObjectExplorerExpandInfo>((resolve, reject) => {
|
||||||
if (session.sessionId! in self._sessions && self._sessions[session.sessionId!]) {
|
if (session.sessionId! in self._sessions && self._sessions[session.sessionId!]) {
|
||||||
let newRequest = false;
|
let newRequest = false;
|
||||||
if (!self._sessions[session.sessionId!].nodes[nodePath]) {
|
if (!self._sessions[session.sessionId!].nodes[node.nodePath]) {
|
||||||
self._sessions[session.sessionId!].nodes[nodePath] = {
|
self._sessions[session.sessionId!].nodes[node.nodePath] = {
|
||||||
expandEmitter: new Emitter<NodeExpandInfoWithProviderId>()
|
expandEmitter: new Emitter<NodeExpandInfoWithProviderId>()
|
||||||
};
|
};
|
||||||
newRequest = true;
|
newRequest = true;
|
||||||
@@ -406,20 +406,33 @@ export class ObjectExplorerService implements IObjectExplorerService {
|
|||||||
allProviders.push(...nodeProviders);
|
allProviders.push(...nodeProviders);
|
||||||
}
|
}
|
||||||
|
|
||||||
self._sessions[session.sessionId!].nodes[nodePath].expandEmitter.event((expandResult: NodeExpandInfoWithProviderId) => {
|
self._sessions[session.sessionId!].nodes[node.nodePath].expandEmitter.event((expandResult: NodeExpandInfoWithProviderId) => {
|
||||||
if (expandResult && expandResult.providerId) {
|
if (expandResult && expandResult.providerId) {
|
||||||
resultMap.set(expandResult.providerId, expandResult);
|
resultMap.set(expandResult.providerId, expandResult);
|
||||||
|
// If we got an error result back then send error our error event
|
||||||
|
// We only do this for the MSSQL provider
|
||||||
|
if (expandResult.errorMessage && expandResult.providerId === mssqlProviderName) {
|
||||||
|
const errorType = expandResult.errorMessage.indexOf('Object Explorer task didn\'t complete') !== -1 ? 'Timeout' : 'Other';
|
||||||
|
// For folders send the actual name of the folder (since the nodeTypeId isn't useful in this case and the names are controlled by us)
|
||||||
|
const nodeType = node.nodeTypeId === NodeType.Folder ? node.label : node.nodeTypeId;
|
||||||
|
this._telemetryService.createErrorEvent(TelemetryKeys.TelemetryView.Shell, TelemetryKeys.TelemetryError.ObjectExplorerExpandError, undefined, errorType)
|
||||||
|
.withAdditionalProperties({
|
||||||
|
nodeType,
|
||||||
|
providerId: expandResult.providerId
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.logService.error('OE provider returns empty result or providerId');
|
this.logService.error('OE provider returns empty result or providerId');
|
||||||
}
|
}
|
||||||
|
|
||||||
// When get all responses from all providers, merge results
|
// When get all responses from all providers, merge results
|
||||||
if (resultMap.size === allProviders.length) {
|
if (resultMap.size === allProviders.length) {
|
||||||
resolve(self.mergeResults(allProviders, resultMap, nodePath));
|
resolve(self.mergeResults(allProviders, resultMap, node.nodePath));
|
||||||
|
|
||||||
// Have to delete it after get all reponses otherwise couldn't find session for not the first response
|
// Have to delete it after get all reponses otherwise couldn't find session for not the first response
|
||||||
if (newRequest) {
|
if (newRequest) {
|
||||||
delete self._sessions[session.sessionId!].nodes[nodePath];
|
delete self._sessions[session.sessionId!].nodes[node.nodePath];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -427,13 +440,13 @@ export class ObjectExplorerService implements IObjectExplorerService {
|
|||||||
allProviders.forEach(provider => {
|
allProviders.forEach(provider => {
|
||||||
self.callExpandOrRefreshFromProvider(provider, {
|
self.callExpandOrRefreshFromProvider(provider, {
|
||||||
sessionId: session.sessionId!,
|
sessionId: session.sessionId!,
|
||||||
nodePath: nodePath
|
nodePath: node.nodePath
|
||||||
}, refresh).then(isExpanding => {
|
}, refresh).then(isExpanding => {
|
||||||
if (!isExpanding) {
|
if (!isExpanding) {
|
||||||
// The provider stated it's not going to expand the node, therefore do not need to track when merging results
|
// The provider stated it's not going to expand the node, therefore do not need to track when merging results
|
||||||
let emptyResult: azdata.ObjectExplorerExpandInfo = {
|
let emptyResult: azdata.ObjectExplorerExpandInfo = {
|
||||||
errorMessage: undefined,
|
errorMessage: undefined,
|
||||||
nodePath: nodePath,
|
nodePath: node.nodePath,
|
||||||
nodes: [],
|
nodes: [],
|
||||||
sessionId: session.sessionId
|
sessionId: session.sessionId
|
||||||
};
|
};
|
||||||
@@ -446,7 +459,7 @@ export class ObjectExplorerService implements IObjectExplorerService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
reject(`session cannot find to expand node. id: ${session.sessionId} nodePath: ${nodePath}`);
|
reject(`session cannot find to expand node. id: ${session.sessionId} nodePath: ${node.nodePath}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -495,7 +508,7 @@ export class ObjectExplorerService implements IObjectExplorerService {
|
|||||||
return finalResult;
|
return finalResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
public refreshNode(providerId: string, session: azdata.ObjectExplorerSession, nodePath: string): Promise<azdata.ObjectExplorerExpandInfo | undefined> {
|
public refreshNode(providerId: string, session: azdata.ObjectExplorerSession, node: TreeNode): Promise<azdata.ObjectExplorerExpandInfo | undefined> {
|
||||||
let provider = this._providers[providerId];
|
let provider = this._providers[providerId];
|
||||||
if (provider) {
|
if (provider) {
|
||||||
this._telemetryService.createActionEvent(TelemetryKeys.TelemetryView.Shell, TelemetryKeys.TelemetryAction.ObjectExplorerExpand)
|
this._telemetryService.createActionEvent(TelemetryKeys.TelemetryView.Shell, TelemetryKeys.TelemetryAction.ObjectExplorerExpand)
|
||||||
@@ -503,7 +516,7 @@ export class ObjectExplorerService implements IObjectExplorerService {
|
|||||||
refresh: true,
|
refresh: true,
|
||||||
provider: providerId
|
provider: providerId
|
||||||
}).send();
|
}).send();
|
||||||
return this.expandOrRefreshNode(providerId, session, nodePath, true);
|
return this.expandOrRefreshNode(providerId, session, node, true);
|
||||||
}
|
}
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
}
|
}
|
||||||
@@ -569,11 +582,11 @@ export class ObjectExplorerService implements IObjectExplorerService {
|
|||||||
return this.expandOrRefreshTreeNode(session, parentTree, true);
|
return this.expandOrRefreshTreeNode(session, parentTree, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private callExpandOrRefreshFromService(providerId: string, session: azdata.ObjectExplorerSession, nodePath: string, refresh: boolean = false): Promise<azdata.ObjectExplorerExpandInfo | undefined> {
|
private callExpandOrRefreshFromService(providerId: string, session: azdata.ObjectExplorerSession, node: TreeNode, refresh: boolean = false): Promise<azdata.ObjectExplorerExpandInfo | undefined> {
|
||||||
if (refresh) {
|
if (refresh) {
|
||||||
return this.refreshNode(providerId, session, nodePath);
|
return this.refreshNode(providerId, session, node);
|
||||||
} else {
|
} else {
|
||||||
return this.expandNode(providerId, session, nodePath);
|
return this.expandNode(providerId, session, node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,7 +598,7 @@ export class ObjectExplorerService implements IObjectExplorerService {
|
|||||||
if (!providerName) {
|
if (!providerName) {
|
||||||
throw new Error('Failed to expand node - no provider name');
|
throw new Error('Failed to expand node - no provider name');
|
||||||
}
|
}
|
||||||
const expandResult = await this.callExpandOrRefreshFromService(providerName, session, parentTree.nodePath, refresh);
|
const expandResult = await this.callExpandOrRefreshFromService(providerName, session, parentTree, refresh);
|
||||||
if (expandResult && expandResult.nodes) {
|
if (expandResult && expandResult.nodes) {
|
||||||
const children = expandResult.nodes.map(node => {
|
const children = expandResult.nodes.map(node => {
|
||||||
return this.toTreeNode(node, parentTree);
|
return this.toTreeNode(node, parentTree);
|
||||||
|
|||||||
@@ -325,9 +325,10 @@ suite('SQL Object Explorer Service tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('expand node should expand node correctly', async () => {
|
test('expand node should expand node correctly', async () => {
|
||||||
|
const tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName/tables', '', '', null, null, undefined, undefined);
|
||||||
await objectExplorerService.createNewSession(mssqlProviderName, connection);
|
await objectExplorerService.createNewSession(mssqlProviderName, connection);
|
||||||
objectExplorerService.onSessionCreated(1, objectExplorerSession);
|
objectExplorerService.onSessionCreated(1, objectExplorerSession);
|
||||||
const expandInfo = await objectExplorerService.expandNode(mssqlProviderName, objectExplorerSession, 'testServerName/tables');
|
const expandInfo = await objectExplorerService.expandNode(mssqlProviderName, objectExplorerSession, tablesNode);
|
||||||
assert.strictEqual(expandInfo !== null || expandInfo !== undefined, true);
|
assert.strictEqual(expandInfo !== null || expandInfo !== undefined, true);
|
||||||
assert.strictEqual(expandInfo.sessionId, '1234');
|
assert.strictEqual(expandInfo.sessionId, '1234');
|
||||||
assert.strictEqual(expandInfo.nodes.length, 2);
|
assert.strictEqual(expandInfo.nodes.length, 2);
|
||||||
@@ -337,9 +338,10 @@ suite('SQL Object Explorer Service tests', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('refresh node should refresh node correctly', async () => {
|
test('refresh node should refresh node correctly', async () => {
|
||||||
|
const tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName/tables', '', '', null, null, undefined, undefined);
|
||||||
await objectExplorerService.createNewSession(mssqlProviderName, connection);
|
await objectExplorerService.createNewSession(mssqlProviderName, connection);
|
||||||
objectExplorerService.onSessionCreated(1, objectExplorerSession);
|
objectExplorerService.onSessionCreated(1, objectExplorerSession);
|
||||||
const expandInfo = await objectExplorerService.refreshNode(mssqlProviderName, objectExplorerSession, 'testServerName/tables');
|
const expandInfo = await objectExplorerService.refreshNode(mssqlProviderName, objectExplorerSession, tablesNode);
|
||||||
assert.strictEqual(expandInfo !== null || expandInfo !== undefined, true);
|
assert.strictEqual(expandInfo !== null || expandInfo !== undefined, true);
|
||||||
assert.strictEqual(expandInfo.sessionId, '1234');
|
assert.strictEqual(expandInfo.sessionId, '1234');
|
||||||
assert.strictEqual(expandInfo.nodes.length, 2);
|
assert.strictEqual(expandInfo.nodes.length, 2);
|
||||||
@@ -651,8 +653,9 @@ suite('SQL Object Explorer Service tests', () => {
|
|||||||
sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.is(x => x.nodePath === nodePath))).callback(() => { }).returns(() => Promise.resolve(true));
|
sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.is(x => x.nodePath === nodePath))).callback(() => { }).returns(() => Promise.resolve(true));
|
||||||
|
|
||||||
// If I queue a second expand request (the first compconstes normally because of the original mock) and then close the session
|
// If I queue a second expand request (the first compconstes normally because of the original mock) and then close the session
|
||||||
await objectExplorerService.expandNode(mssqlProviderName, objectExplorerSession, objectExplorerSession.rootNode.nodePath);
|
const rootNode = new TreeNode(NodeType.Root, '', false, objectExplorerSession.rootNode.nodePath, '', '', null, null, undefined, undefined);
|
||||||
const expandPromise = objectExplorerService.expandNode(mssqlProviderName, objectExplorerSession, objectExplorerSession.rootNode.nodePath);
|
await objectExplorerService.expandNode(mssqlProviderName, objectExplorerSession, rootNode);
|
||||||
|
const expandPromise = objectExplorerService.expandNode(mssqlProviderName, objectExplorerSession, rootNode);
|
||||||
const closeSessionResult = await objectExplorerService.closeSession(mssqlProviderName, objectExplorerSession);
|
const closeSessionResult = await objectExplorerService.closeSession(mssqlProviderName, objectExplorerSession);
|
||||||
|
|
||||||
// Then the expand request has compconsted and the session is closed
|
// Then the expand request has compconsted and the session is closed
|
||||||
|
|||||||
@@ -71,9 +71,9 @@ export class TestObjectExplorerService implements IObjectExplorerService {
|
|||||||
|
|
||||||
public async createNewSession(providerId: string, connection: ConnectionProfile): Promise<azdata.ObjectExplorerSessionResponse> { throw new Error('Method not implemented'); }
|
public async createNewSession(providerId: string, connection: ConnectionProfile): Promise<azdata.ObjectExplorerSessionResponse> { throw new Error('Method not implemented'); }
|
||||||
|
|
||||||
public async expandNode(providerId: string, session: azdata.ObjectExplorerSession, nodePath: string): Promise<azdata.ObjectExplorerExpandInfo> { throw new Error('Method not implemented'); }
|
public async expandNode(providerId: string, session: azdata.ObjectExplorerSession, node: TreeNode): Promise<azdata.ObjectExplorerExpandInfo> { throw new Error('Method not implemented'); }
|
||||||
|
|
||||||
public async refreshNode(providerId: string, session: azdata.ObjectExplorerSession, nodePath: string): Promise<azdata.ObjectExplorerExpandInfo> { throw new Error('Method not implemented'); }
|
public async refreshNode(providerId: string, session: azdata.ObjectExplorerSession, node: TreeNode): Promise<azdata.ObjectExplorerExpandInfo> { throw new Error('Method not implemented'); }
|
||||||
|
|
||||||
public async closeSession(providerId: string, session: azdata.ObjectExplorerSession): Promise<azdata.ObjectExplorerCloseSessionResponse> { throw new Error('Method not implemented'); }
|
public async closeSession(providerId: string, session: azdata.ObjectExplorerSession): Promise<azdata.ObjectExplorerCloseSessionResponse> { throw new Error('Method not implemented'); }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user