mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
show the resources as soon as they become available (#13530)
* results streaming * remove variable
This commit is contained in:
@@ -18,7 +18,7 @@ import { AzureResourceContainerTreeNodeBase } from './baseTreeNodes';
|
|||||||
import { AzureResourceItemType, AzureResourceServiceNames } from '../constants';
|
import { AzureResourceItemType, AzureResourceServiceNames } from '../constants';
|
||||||
import { AzureResourceMessageTreeNode } from '../messageTreeNode';
|
import { AzureResourceMessageTreeNode } from '../messageTreeNode';
|
||||||
import { IAzureResourceTreeChangeHandler } from './treeChangeHandler';
|
import { IAzureResourceTreeChangeHandler } from './treeChangeHandler';
|
||||||
import { IAzureResourceSubscriptionService, IAzureResourceSubscriptionFilterService, IAzureResourceNodeWithProviderId } from '../../azureResource/interfaces';
|
import { IAzureResourceSubscriptionService, IAzureResourceSubscriptionFilterService } from '../../azureResource/interfaces';
|
||||||
import { AzureAccount } from '../../account-provider/interfaces';
|
import { AzureAccount } from '../../account-provider/interfaces';
|
||||||
import { AzureResourceService } from '../resourceService';
|
import { AzureResourceService } from '../resourceService';
|
||||||
import { AzureResourceResourceTreeNode } from '../resourceTreeNode';
|
import { AzureResourceResourceTreeNode } from '../resourceTreeNode';
|
||||||
@@ -39,11 +39,22 @@ export class FlatAccountTreeNode extends AzureResourceContainerTreeNodeBase {
|
|||||||
this._id = `account_${this.account.key.accountId}`;
|
this._id = `account_${this.account.key.accountId}`;
|
||||||
this.setCacheKey(`${this._id}.dataresources`);
|
this.setCacheKey(`${this._id}.dataresources`);
|
||||||
this._label = account.displayInfo.displayName;
|
this._label = account.displayInfo.displayName;
|
||||||
|
this._loader = new FlatAccountTreeNodeLoader(appContext, this._resourceService, this._subscriptionService, this._subscriptionFilterService, this.account, this);
|
||||||
|
this._loader.onNewResourcesAvailable(() => {
|
||||||
|
this.treeChangeHandler.notifyNodeChanged(this);
|
||||||
|
});
|
||||||
|
|
||||||
|
this._loader.onLoadingStatusChanged(async () => {
|
||||||
|
await this.updateLabel();
|
||||||
|
this.treeChangeHandler.notifyNodeChanged(this);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateLabel(): Promise<void> {
|
public async updateLabel(): Promise<void> {
|
||||||
const subscriptionInfo = await this.getSubscriptionInfo();
|
const subscriptionInfo = await getSubscriptionInfo(this.account, this._subscriptionService, this._subscriptionFilterService);
|
||||||
if (subscriptionInfo.total !== 0) {
|
if (this._loader.isLoading) {
|
||||||
|
this._label = localize('azure.resource.tree.accountTreeNode.titleLoading', "{0} - Loading...", this.account.displayInfo.displayName);
|
||||||
|
} else if (subscriptionInfo.total !== 0) {
|
||||||
this._label = localize({
|
this._label = localize({
|
||||||
key: 'azure.resource.tree.accountTreeNode.title',
|
key: 'azure.resource.tree.accountTreeNode.title',
|
||||||
comment: [
|
comment: [
|
||||||
@@ -57,79 +68,13 @@ export class FlatAccountTreeNode extends AzureResourceContainerTreeNodeBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getSubscriptionInfo(): Promise<{
|
|
||||||
subscriptions: azureResource.AzureResourceSubscription[],
|
|
||||||
total: number,
|
|
||||||
selected: number
|
|
||||||
}> {
|
|
||||||
let subscriptions: azureResource.AzureResourceSubscription[] = [];
|
|
||||||
try {
|
|
||||||
for (const tenant of this.account.properties.tenants) {
|
|
||||||
const token = await azdata.accounts.getAccountSecurityToken(this.account, tenant.id, azdata.AzureResource.ResourceManagement);
|
|
||||||
|
|
||||||
subscriptions.push(...(await this._subscriptionService.getSubscriptions(this.account, new TokenCredentials(token.token, token.tokenType), tenant.id) || <azureResource.AzureResourceSubscription[]>[]));
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
throw new AzureResourceCredentialError(localize('azure.resource.tree.accountTreeNode.credentialError', "Failed to get credential for account {0}. Please refresh the account.", this.account.key.accountId), error);
|
|
||||||
}
|
|
||||||
const total = subscriptions.length;
|
|
||||||
let selected = total;
|
|
||||||
|
|
||||||
const selectedSubscriptions = await this._subscriptionFilterService.getSelectedSubscriptions(this.account);
|
|
||||||
const selectedSubscriptionIds = (selectedSubscriptions || <azureResource.AzureResourceSubscription[]>[]).map((subscription) => subscription.id);
|
|
||||||
if (selectedSubscriptionIds.length > 0) {
|
|
||||||
subscriptions = subscriptions.filter((subscription) => selectedSubscriptionIds.indexOf(subscription.id) !== -1);
|
|
||||||
selected = selectedSubscriptionIds.length;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
subscriptions,
|
|
||||||
total,
|
|
||||||
selected
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getChildren(): Promise<TreeNode[]> {
|
public async getChildren(): Promise<TreeNode[]> {
|
||||||
try {
|
|
||||||
let dataResources: IAzureResourceNodeWithProviderId[] = [];
|
|
||||||
if (this._isClearingCache) {
|
if (this._isClearingCache) {
|
||||||
let subscriptions: azureResource.AzureResourceSubscription[] = (await this.getSubscriptionInfo()).subscriptions;
|
this._loader.start();
|
||||||
|
|
||||||
if (subscriptions.length === 0) {
|
|
||||||
return [AzureResourceMessageTreeNode.create(FlatAccountTreeNode.noSubscriptionsLabel, this)];
|
|
||||||
} else {
|
|
||||||
// Filter out everything that we can't authenticate to.
|
|
||||||
subscriptions = subscriptions.filter(async s => {
|
|
||||||
const token = await azdata.accounts.getAccountSecurityToken(this.account, s.tenant, azdata.AzureResource.ResourceManagement);
|
|
||||||
if (!token) {
|
|
||||||
console.info(`Account does not have permissions to view subscription ${JSON.stringify(s)}.`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const resourceProviderIds = await this._resourceService.listResourceProviderIds();
|
|
||||||
for (const subscription of subscriptions) {
|
|
||||||
for (const providerId of resourceProviderIds) {
|
|
||||||
const resourceTypes = await this._resourceService.getRootChildren(providerId, this.account, subscription, subscription.tenant);
|
|
||||||
for (const resourceType of resourceTypes) {
|
|
||||||
dataResources.push(...await this._resourceService.getChildren(providerId, resourceType.resourceNode, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dataResources = dataResources.sort((a, b) => { return a.resourceNode.treeItem.label.localeCompare(b.resourceNode.treeItem.label); });
|
|
||||||
this.updateCache(dataResources);
|
|
||||||
this._isClearingCache = false;
|
this._isClearingCache = false;
|
||||||
|
return [];
|
||||||
} else {
|
} else {
|
||||||
dataResources = this.getCache<IAzureResourceNodeWithProviderId[]>();
|
return this._loader.nodes;
|
||||||
}
|
|
||||||
|
|
||||||
return dataResources.map(dr => new AzureResourceResourceTreeNode(dr, this, this.appContext));
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof AzureResourceCredentialError) {
|
|
||||||
vscode.commands.executeCommand('azure.resource.signin');
|
|
||||||
}
|
|
||||||
return [AzureResourceMessageTreeNode.create(AzureResourceErrorMessageUtil.getErrorMessage(error), this)];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,12 +107,126 @@ export class FlatAccountTreeNode extends AzureResourceContainerTreeNodeBase {
|
|||||||
return this._id;
|
return this._id;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _subscriptionService: IAzureResourceSubscriptionService = undefined;
|
private _subscriptionService: IAzureResourceSubscriptionService;
|
||||||
private _subscriptionFilterService: IAzureResourceSubscriptionFilterService = undefined;
|
private _subscriptionFilterService: IAzureResourceSubscriptionFilterService;
|
||||||
private _resourceService: AzureResourceService = undefined;
|
private _resourceService: AzureResourceService;
|
||||||
|
private _loader: FlatAccountTreeNodeLoader;
|
||||||
private _id: string = undefined;
|
private _id: string;
|
||||||
private _label: string = undefined;
|
private _label: string;
|
||||||
|
}
|
||||||
private static readonly noSubscriptionsLabel = localize('azure.resource.tree.accountTreeNode.noSubscriptionsLabel', "No Subscriptions found.");
|
|
||||||
|
async function getSubscriptionInfo(account: AzureAccount, subscriptionService: IAzureResourceSubscriptionService, subscriptionFilterService: IAzureResourceSubscriptionFilterService): Promise<{
|
||||||
|
subscriptions: azureResource.AzureResourceSubscription[],
|
||||||
|
total: number,
|
||||||
|
selected: number
|
||||||
|
}> {
|
||||||
|
let subscriptions: azureResource.AzureResourceSubscription[] = [];
|
||||||
|
try {
|
||||||
|
for (const tenant of account.properties.tenants) {
|
||||||
|
const token = await azdata.accounts.getAccountSecurityToken(account, tenant.id, azdata.AzureResource.ResourceManagement);
|
||||||
|
subscriptions.push(...(await subscriptionService.getSubscriptions(account, new TokenCredentials(token.token, token.tokenType), tenant.id) || <azureResource.AzureResourceSubscription[]>[]));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new AzureResourceCredentialError(localize('azure.resource.tree.accountTreeNode.credentialError', "Failed to get credential for account {0}. Please refresh the account.", account.key.accountId), error);
|
||||||
|
}
|
||||||
|
const total = subscriptions.length;
|
||||||
|
let selected = total;
|
||||||
|
|
||||||
|
const selectedSubscriptions = await subscriptionFilterService.getSelectedSubscriptions(account);
|
||||||
|
const selectedSubscriptionIds = (selectedSubscriptions || <azureResource.AzureResourceSubscription[]>[]).map((subscription) => subscription.id);
|
||||||
|
if (selectedSubscriptionIds.length > 0) {
|
||||||
|
subscriptions = subscriptions.filter((subscription) => selectedSubscriptionIds.indexOf(subscription.id) !== -1);
|
||||||
|
selected = selectedSubscriptionIds.length;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
subscriptions,
|
||||||
|
total,
|
||||||
|
selected
|
||||||
|
};
|
||||||
|
}
|
||||||
|
class FlatAccountTreeNodeLoader {
|
||||||
|
|
||||||
|
private _isLoading: boolean = false;
|
||||||
|
private _nodes: TreeNode[];
|
||||||
|
private readonly _onNewResourcesAvailable = new vscode.EventEmitter<void>();
|
||||||
|
public readonly onNewResourcesAvailable = this._onNewResourcesAvailable.event;
|
||||||
|
private readonly _onLoadingStatusChanged = new vscode.EventEmitter<void>();
|
||||||
|
public readonly onLoadingStatusChanged = this._onLoadingStatusChanged.event;
|
||||||
|
|
||||||
|
constructor(private readonly appContext: AppContext,
|
||||||
|
private readonly _resourceService: AzureResourceService,
|
||||||
|
private readonly _subscriptionService: IAzureResourceSubscriptionService,
|
||||||
|
private readonly _subscriptionFilterService: IAzureResourceSubscriptionFilterService,
|
||||||
|
private readonly _account: AzureAccount,
|
||||||
|
private readonly _accountNode: TreeNode) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public get isLoading(): boolean {
|
||||||
|
return this._isLoading;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get nodes(): TreeNode[] {
|
||||||
|
return this._nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async start(): Promise<void> {
|
||||||
|
if (this._isLoading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._isLoading = true;
|
||||||
|
this._nodes = [];
|
||||||
|
this._onLoadingStatusChanged.fire();
|
||||||
|
let newNodesAvailable = false;
|
||||||
|
|
||||||
|
// Throttle the refresh events to at most once per 500ms
|
||||||
|
const refreshHandle = setInterval(() => {
|
||||||
|
if (newNodesAvailable) {
|
||||||
|
this._onNewResourcesAvailable.fire();
|
||||||
|
newNodesAvailable = false;
|
||||||
|
}
|
||||||
|
if (!this.isLoading) {
|
||||||
|
clearInterval(refreshHandle);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
try {
|
||||||
|
let subscriptions: azureResource.AzureResourceSubscription[] = (await getSubscriptionInfo(this._account, this._subscriptionService, this._subscriptionFilterService)).subscriptions;
|
||||||
|
|
||||||
|
if (subscriptions.length !== 0) {
|
||||||
|
// Filter out everything that we can't authenticate to.
|
||||||
|
subscriptions = subscriptions.filter(async s => {
|
||||||
|
const token = await azdata.accounts.getAccountSecurityToken(this._account, s.tenant, azdata.AzureResource.ResourceManagement);
|
||||||
|
if (!token) {
|
||||||
|
console.info(`Account does not have permissions to view subscription ${JSON.stringify(s)}.`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const resourceProviderIds = await this._resourceService.listResourceProviderIds();
|
||||||
|
for (const subscription of subscriptions) {
|
||||||
|
for (const providerId of resourceProviderIds) {
|
||||||
|
const resourceTypes = await this._resourceService.getRootChildren(providerId, this._account, subscription, subscription.tenant);
|
||||||
|
for (const resourceType of resourceTypes) {
|
||||||
|
const resources = await this._resourceService.getChildren(providerId, resourceType.resourceNode, true);
|
||||||
|
if (resources.length > 0) {
|
||||||
|
this._nodes.push(...resources.map(dr => new AzureResourceResourceTreeNode(dr, this._accountNode, this.appContext)));
|
||||||
|
this._nodes = this.nodes.sort((a, b) => {
|
||||||
|
return a.getNodeInfo().label.localeCompare(b.getNodeInfo().label);
|
||||||
|
});
|
||||||
|
newNodesAvailable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AzureResourceCredentialError) {
|
||||||
|
vscode.commands.executeCommand('azure.resource.signin');
|
||||||
|
}
|
||||||
|
this._nodes = [AzureResourceMessageTreeNode.create(AzureResourceErrorMessageUtil.getErrorMessage(error), this._accountNode)];
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isLoading = false;
|
||||||
|
this._onLoadingStatusChanged.fire();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user