Add Cosmos DB PostgreSQL Cluster + display only available resources (#23469)

This commit is contained in:
Cheena Malhotra 2023-06-27 15:55:10 -07:00 коммит произвёл GitHub
Родитель 01e66ab3e6
Коммит 0970442a5c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
33 изменённых файлов: 323 добавлений и 170 удалений

Просмотреть файл

@ -24,7 +24,9 @@ export enum AzureResourceItemType {
azureMonitor = 'azure.resource.itemType.azureMonitor',
azureMonitorContainer = 'azure.resource.itemType.azureMonitorContainer',
cosmosDBMongoAccount = 'azure.resource.itemType.cosmosDBMongoAccount',
cosmosDBMongoCluster = 'azure.resource.itemType.cosmosDBMongoCluster'
cosmosDBMongoCluster = 'azure.resource.itemType.cosmosDBMongoCluster',
cosmosDBPostgresAccount = 'azure.resource.itemType.cosmosDBPostgresAccount',
cosmosDBPostgresCluster = 'azure.resource.itemType.cosmosDBPostgresCluster'
}
export enum AzureResourceServiceNames {

Просмотреть файл

@ -157,12 +157,6 @@ export interface IAzureResourceCacheService {
update<T>(key: string, value: T): Promise<void>;
}
export interface IAzureResourceNodeWithProviderId {
resourceProviderId: string;
resourceNode: azureResource.IAzureResourceNode;
}
export interface IAzureResourceDbService<S extends GraphData, T extends GraphData> extends azureResource.IAzureResourceService {
convertDatabaseResource(resource: T, server?: S): azureResource.AzureResource | undefined;
}

Просмотреть файл

@ -56,13 +56,13 @@ export class AzureMonitorTreeDataProvider extends ResourceTreeDataProviderBase<G
};
}
public async getRootChildren(): Promise<TreeItem[]> {
return [{
public async getRootChild(): Promise<TreeItem> {
return {
id: AzureMonitorTreeDataProvider.containerId,
label: AzureMonitorTreeDataProvider.containerLabel,
iconPath: this._extensionContext.asAbsolutePath('resources/logAnalyticsWorkspaces.svg'),
collapsibleState: TreeItemCollapsibleState.Collapsed,
contextValue: AzureResourceItemType.databaseServerContainer
}];
};
}
}

Просмотреть файл

@ -19,7 +19,7 @@ export class CosmosDbMongoService extends ResourceServiceBase<DbServerGraphData>
public convertServerResource(resource: DbServerGraphData): AzureResourceMongoDatabaseServer | undefined {
let host = resource.name;
const isServer = resource.type === azureResource.AzureResourceType.cosmosDbCluster;
const isServer = resource.type === azureResource.AzureResourceType.cosmosDbMongoCluster;
if (isServer) {
const url = new URL(resource.properties.connectionString);
host = url.hostname;

Просмотреть файл

@ -29,7 +29,7 @@ export class CosmosDbMongoTreeDataProvider extends ResourceTreeDataProviderBase<
public getTreeItemForResource(databaseServer: AzureResourceMongoDatabaseServer, account: azdata.Account): azdata.TreeItem {
return {
id: `${AzureResourcePrefixes.cosmosdb}${account.key.accountId}${databaseServer.tenant}${databaseServer.id ?? databaseServer.name}`,
label: `${databaseServer.name} (CosmosDB Mongo API)`,
label: this.browseConnectionMode ? `${databaseServer.name} (${CosmosDbMongoTreeDataProvider.CONTAINER_LABEL}, ${databaseServer.subscription.name})` : `${databaseServer.name}`,
iconPath: this._extensionContext.asAbsolutePath('resources/cosmosDb.svg'),
collapsibleState: TreeItemCollapsibleState.None,
contextValue: AzureResourceItemType.cosmosDBMongoAccount,
@ -58,13 +58,13 @@ export class CosmosDbMongoTreeDataProvider extends ResourceTreeDataProviderBase<
};
}
public async getRootChildren(): Promise<azdata.TreeItem[]> {
return [{
public async getRootChild(): Promise<azdata.TreeItem> {
return {
id: CosmosDbMongoTreeDataProvider.CONTAINER_ID,
label: CosmosDbMongoTreeDataProvider.CONTAINER_LABEL,
iconPath: this._extensionContext.asAbsolutePath('resources/cosmosDb.svg'),
collapsibleState: TreeItemCollapsibleState.Collapsed,
contextValue: AzureResourceItemType.databaseServerContainer
}];
};
}
}

Просмотреть файл

@ -0,0 +1,42 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ResourceServiceBase } from '../../resourceTreeDataProviderBase';
import { azureResource } from 'azurecore';
import { cosmosPostgresDbQuery } from '../../queryStringConstants';
import { DbServerGraphData } from '../../../interfaces';
import { COSMOSDB_POSTGRES_PROVIDER_ID } from '../../../../constants';
export interface AzureResourcePostgresDatabaseServer extends azureResource.AzureResourceDatabaseServer {
isServer: boolean;
}
export class CosmosDbPostgresService extends ResourceServiceBase<DbServerGraphData> {
public override queryFilter: string = cosmosPostgresDbQuery;
public convertServerResource(resource: DbServerGraphData): AzureResourcePostgresDatabaseServer | undefined {
let host = resource.name;
const isServer = resource.type === azureResource.AzureResourceType.cosmosDbPostgresCluster;
if (isServer) {
const url = new URL(resource.properties.connectionString);
host = url.hostname;
}
return {
id: resource.id,
name: resource.name,
provider: COSMOSDB_POSTGRES_PROVIDER_ID,
isServer: isServer,
fullName: host,
loginName: resource.properties.administratorLogin,
defaultDatabaseName: '',
tenant: resource.tenantId,
subscription: {
id: resource.subscriptionId,
name: resource.subscriptionName || ''
}
};
}
}

Просмотреть файл

@ -0,0 +1,70 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TreeItemCollapsibleState, ExtensionContext } from 'vscode';
import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
import { AzureResourceItemType, AzureResourcePrefixes, pgsqlProvider } from '../../../constants';
import { AzureResourcePostgresDatabaseServer } from './cosmosDbPostgresService';
import { generateGuid } from '../../../utils';
import { DbServerGraphData, GraphData } from '../../../interfaces';
import { ResourceTreeDataProviderBase } from '../../resourceTreeDataProviderBase';
import { AzureAccountProperties, azureResource } from 'azurecore';
import * as azdata from 'azdata';
export class CosmosDbPostgresTreeDataProvider extends ResourceTreeDataProviderBase<GraphData, DbServerGraphData> {
private static readonly CONTAINER_ID = 'azure.resource.providers.databaseServer.treeDataProvider.cosmosDbPostgresContainer';
private static readonly CONTAINER_LABEL = localize('azure.resource.providers.databaseServer.treeDataProvider.cosmosDbPostgresContainerLabel', "Azure CosmosDB for PostgreSQL Cluster");
public constructor(
databaseServerService: azureResource.IAzureResourceService,
private _extensionContext: ExtensionContext
) {
super(databaseServerService);
}
public getTreeItemForResource(databaseServer: AzureResourcePostgresDatabaseServer, account: azdata.Account): azdata.TreeItem {
return {
id: `${AzureResourcePrefixes.cosmosdb}${account.key.accountId}${databaseServer.tenant}${databaseServer.id ?? databaseServer.name}`,
label: this.browseConnectionMode ? `${databaseServer.name} ${CosmosDbPostgresTreeDataProvider.CONTAINER_LABEL}, ${databaseServer.subscription.name})` : databaseServer.name,
iconPath: this._extensionContext.asAbsolutePath('resources/cosmosDb.svg'),
collapsibleState: TreeItemCollapsibleState.None,
contextValue: AzureResourceItemType.cosmosDBPostgresAccount,
payload: {
id: generateGuid(),
connectionName: databaseServer.name,
serverName: databaseServer.fullName,
userName: databaseServer.loginName,
password: '',
authenticationType: databaseServer.isServer ? azdata.connection.AuthenticationType.SqlLogin : azdata.connection.AuthenticationType.AzureMFA,
savePassword: true,
groupFullName: '',
groupId: '',
providerName: pgsqlProvider,
saveProfile: false,
options: {
isServer: databaseServer.isServer,
},
azureAccount: account.key.accountId,
azureTenantId: databaseServer.tenant,
azureResourceId: databaseServer.id,
azurePortalEndpoint: (account.properties as AzureAccountProperties).providerSettings.settings.portalEndpoint
},
childProvider: pgsqlProvider,
type: azdata.ExtensionNodeType.Server
};
}
public async getRootChild(): Promise<azdata.TreeItem> {
return {
id: CosmosDbPostgresTreeDataProvider.CONTAINER_ID,
label: CosmosDbPostgresTreeDataProvider.CONTAINER_LABEL,
iconPath: this._extensionContext.asAbsolutePath('resources/cosmosDb.svg'),
collapsibleState: TreeItemCollapsibleState.Collapsed,
contextValue: AzureResourceItemType.databaseServerContainer
};
}
}

Просмотреть файл

@ -57,13 +57,13 @@ export class AzureResourceDatabaseTreeDataProvider extends ResourceTreeDataProvi
};
}
public async getRootChildren(): Promise<TreeItem[]> {
return [{
public async getRootChild(): Promise<TreeItem> {
return {
id: AzureResourceDatabaseTreeDataProvider.containerId,
label: AzureResourceDatabaseTreeDataProvider.containerLabel,
iconPath: this._extensionContext.asAbsolutePath('resources/sqlDatabase.svg'),
collapsibleState: vscode.TreeItemCollapsibleState.Collapsed,
contextValue: AzureResourceItemType.databaseContainer
}];
};
}
}

Просмотреть файл

@ -56,13 +56,13 @@ export class AzureResourceDatabaseServerTreeDataProvider extends ResourceTreeDat
};
}
public async getRootChildren(): Promise<TreeItem[]> {
return [{
public async getRootChild(): Promise<TreeItem> {
return {
id: AzureResourceDatabaseServerTreeDataProvider.containerId,
label: AzureResourceDatabaseServerTreeDataProvider.containerLabel,
iconPath: this._extensionContext.asAbsolutePath('resources/sqlServer.svg'),
collapsibleState: vscode.TreeItemCollapsibleState.Collapsed,
contextValue: AzureResourceItemType.databaseServerContainer
}];
};
}
}

Просмотреть файл

@ -56,13 +56,13 @@ export class KustoTreeDataProvider extends ResourceTreeDataProviderBase<GraphDat
};
}
public async getRootChildren(): Promise<TreeItem[]> {
return [{
public async getRootChild(): Promise<TreeItem> {
return {
id: KustoTreeDataProvider.containerId,
label: KustoTreeDataProvider.containerLabel,
iconPath: this._extensionContext.asAbsolutePath('resources/dataExplorerClusterDb.svg'),
collapsibleState: TreeItemCollapsibleState.Collapsed,
contextValue: AzureResourceItemType.databaseServerContainer
}];
};
}
}

Просмотреть файл

@ -57,13 +57,13 @@ export class MysqlFlexibleServerTreeDataProvider extends ResourceTreeDataProvide
};
}
public async getRootChildren(): Promise<TreeItem[]> {
return [{
public async getRootChild(): Promise<TreeItem> {
return {
id: MysqlFlexibleServerTreeDataProvider.CONTAINER_ID,
label: MysqlFlexibleServerTreeDataProvider.CONTAINER_LABEL,
iconPath: this._extensionContext.asAbsolutePath('resources/mysqlDatabase.svg'),
collapsibleState: TreeItemCollapsibleState.Collapsed,
contextValue: AzureResourceItemType.databaseServerContainer
}];
};
}
}

Просмотреть файл

@ -60,13 +60,13 @@ export class PostgresServerArcTreeDataProvider extends ResourceTreeDataProviderB
};
}
public async getRootChildren(): Promise<TreeItem[]> {
return [{
public async getRootChild(): Promise<TreeItem> {
return {
id: PostgresServerArcTreeDataProvider.containerId,
label: PostgresServerArcTreeDataProvider.containerLabel,
iconPath: this._extensionContext.asAbsolutePath('resources/azureArcPostgresServer.svg'),
collapsibleState: TreeItemCollapsibleState.Collapsed,
contextValue: AzureResourceItemType.databaseServerContainer
}];
};
}
}

Просмотреть файл

@ -59,13 +59,13 @@ export class PostgresFlexibleServerTreeDataProvider extends ResourceTreeDataProv
};
}
public async getRootChildren(): Promise<TreeItem[]> {
return [{
public async getRootChild(): Promise<TreeItem> {
return {
id: PostgresFlexibleServerTreeDataProvider.containerId,
label: PostgresFlexibleServerTreeDataProvider.containerLabel,
iconPath: this._extensionContext.asAbsolutePath('resources/postgresServer.svg'),
collapsibleState: TreeItemCollapsibleState.Collapsed,
contextValue: AzureResourceItemType.databaseServerContainer
}];
};
}
}

Просмотреть файл

@ -16,7 +16,7 @@ import { AzureAccount, azureResource } from 'azurecore';
export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase<GraphData, DbServerGraphData> {
private static readonly containerId = 'azure.resource.providers.databaseServer.treeDataProvider.postgresServerContainer';
private static readonly containerLabel = localize('azure.resource.providers.databaseServer.treeDataProvider.postgresServerContainerLabel', "Azure Database for PostgreSQL servers");
private static readonly containerLabel = localize('azure.resource.providers.databaseServer.treeDataProvider.postgresServerContainerLabel', "Azure Database for PostgreSQL Servers");
public constructor(
databaseServerService: azureResource.IAzureResourceService,
@ -29,10 +29,7 @@ export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase
return {
id: `${AzureResourcePrefixes.postgresServer}${account.key.accountId}${databaseServer.tenant}${databaseServer.id ?? databaseServer.name}`,
label: this.browseConnectionMode ? `${databaseServer.name} (${PostgresServerTreeDataProvider.containerLabel}, ${databaseServer.subscription.name})` : databaseServer.name,
iconPath: {
dark: this._extensionContext.asAbsolutePath('resources/postgresServer.svg'),
light: this._extensionContext.asAbsolutePath('resources/postgresServer.svg')
},
iconPath: this._extensionContext.asAbsolutePath('resources/postgresServer.svg'),
collapsibleState: this.browseConnectionMode ? TreeItemCollapsibleState.None : TreeItemCollapsibleState.Collapsed,
contextValue: AzureResourceItemType.databaseServer,
payload: {
@ -62,16 +59,13 @@ export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase
};
}
public async getRootChildren(): Promise<TreeItem[]> {
return [{
public async getRootChild(): Promise<TreeItem> {
return {
id: PostgresServerTreeDataProvider.containerId,
label: PostgresServerTreeDataProvider.containerLabel,
iconPath: {
dark: this._extensionContext.asAbsolutePath('resources/postgresServer.svg'),
light: this._extensionContext.asAbsolutePath('resources/postgresServer.svg')
},
iconPath: this._extensionContext.asAbsolutePath('resources/postgresServer.svg'),
collapsibleState: TreeItemCollapsibleState.Collapsed,
contextValue: AzureResourceItemType.databaseServerContainer
}];
};
}
}

Просмотреть файл

@ -46,7 +46,7 @@ export const resourceGroupQuery = `ResourceContainers | where type=="${azureReso
/**
* Lists all postgreSQL servers
*/
export const postgresServerQuery = `type == "${azureResource.AzureResourceType.postgresServer}"`;
export const postgresServerQuery = `type == "${azureResource.AzureResourceType.postgresServer}" or type == "${azureResource.AzureResourceType.postgresServerv2}" or type == "${azureResource.AzureResourceType.postgresSingleServer}"`;
/**
* Lists all postgreSQL flexible servers
@ -71,7 +71,12 @@ export const kustoClusterQuery = `type == "${azureResource.AzureResourceType.kus
/**
* Lists all Cosmos DB for MongoDB accounts
*/
export const cosmosMongoDbQuery = `(type == "${azureResource.AzureResourceType.cosmosDbAccount}" and kind == "${Constants.mongoDbKind}") or type == "${azureResource.AzureResourceType.cosmosDbCluster}"`;
export const cosmosMongoDbQuery = `(type == "${azureResource.AzureResourceType.cosmosDbAccount}" and kind == "${Constants.mongoDbKind}") or type == "${azureResource.AzureResourceType.cosmosDbMongoCluster}"`;
/**
* Lists all Cosmos DB for MongoDB accounts
*/
export const cosmosPostgresDbQuery = `type == "${azureResource.AzureResourceType.postgresServerGroup}" or type == "${azureResource.AzureResourceType.postgresServerGroupv2}"`;
/**
* Lists all Log Analytics workspaces

Просмотреть файл

@ -56,7 +56,7 @@ export abstract class ResourceTreeDataProviderBase<S extends GraphData, T extend
public abstract getTreeItemForResource(resource: azureResource.AzureResource, account: AzureAccount): azdata.TreeItem;
public abstract getRootChildren(): Promise<azdata.TreeItem[]>;
public abstract getRootChild(): Promise<azdata.TreeItem>;
}
export async function queryGraphResources<T extends GraphData>(resourceClient: ResourceGraphClient, subscriptions: azureResource.AzureResourceSubscription[], resourceQuery: string): Promise<T[]> {

Просмотреть файл

@ -56,13 +56,13 @@ export class SqlInstanceTreeDataProvider extends ResourceTreeDataProviderBase<Gr
};
}
public async getRootChildren(): Promise<TreeItem[]> {
return [{
public async getRootChild(): Promise<TreeItem> {
return {
id: SqlInstanceTreeDataProvider.containerId,
label: SqlInstanceTreeDataProvider.containerLabel,
iconPath: this._extensionContext.asAbsolutePath('resources/sqlManagedInstance.svg'),
collapsibleState: TreeItemCollapsibleState.Collapsed,
contextValue: AzureResourceItemType.databaseServerContainer
}];
};
}
}

Просмотреть файл

@ -57,13 +57,13 @@ export class SqlInstanceArcTreeDataProvider extends ResourceTreeDataProviderBase
};
}
public async getRootChildren(): Promise<TreeItem[]> {
return [{
public async getRootChild(): Promise<TreeItem> {
return {
id: SqlInstanceArcTreeDataProvider.containerId,
label: SqlInstanceArcTreeDataProvider.containerLabel,
iconPath: this._extensionContext.asAbsolutePath('resources/azureArcSqlManagedInstance.svg'),
collapsibleState: TreeItemCollapsibleState.Collapsed,
contextValue: AzureResourceItemType.databaseServerContainer
}];
};
}
}

Просмотреть файл

@ -57,13 +57,13 @@ export class AzureResourceSynapseSqlPoolTreeDataProvider extends ResourceTreeDat
};
}
public async getRootChildren(): Promise<TreeItem[]> {
return [{
public async getRootChild(): Promise<TreeItem> {
return {
id: AzureResourceSynapseSqlPoolTreeDataProvider.containerId,
label: AzureResourceSynapseSqlPoolTreeDataProvider.containerLabel,
iconPath: this._extensionContext.asAbsolutePath('resources/sqlPools.svg'),
collapsibleState: vscode.TreeItemCollapsibleState.Collapsed,
contextValue: AzureResourceItemType.synapseSqlPoolContainer
}];
};
}
}

Просмотреть файл

@ -56,13 +56,13 @@ export class AzureResourceSynapseWorkspaceTreeDataProvider extends ResourceTreeD
};
}
public async getRootChildren(): Promise<TreeItem[]> {
return [{
public async getRootChild(): Promise<TreeItem> {
return {
id: AzureResourceSynapseWorkspaceTreeDataProvider.containerId,
label: AzureResourceSynapseWorkspaceTreeDataProvider.containerLabel,
iconPath: this._extensionContext.asAbsolutePath('resources/azureSynapseAnalytics.svg'),
collapsibleState: vscode.TreeItemCollapsibleState.Collapsed,
contextValue: AzureResourceItemType.synapseWorkspaceContainer
}];
};
}
}

Просмотреть файл

@ -13,8 +13,8 @@ import * as nls from 'vscode-nls';
import { AzureResourcePrefixes, ResourceCategory, analyticsKind, mongoDbKind } from '../../constants';
import {
COSMOSDB_MONGO_PROVIDER_ID, DATABASE_PROVIDER_ID, DATABASE_SERVER_PROVIDER_ID, KUSTO_PROVIDER_ID, AZURE_MONITOR_PROVIDER_ID,
MYSQL_FLEXIBLE_SERVER_PROVIDER_ID, POSTGRES_FLEXIBLE_SERVER_PROVIDER_ID, POSTGRES_SERVER_PROVIDER_ID, POSTGRES_ARC_SERVER_PROVIDER_ID,
SQLINSTANCE_PROVIDER_ID, SQLINSTANCE_ARC_PROVIDER_ID, SYNAPSE_SQL_POOL_PROVIDER_ID, SYNAPSE_WORKSPACE_PROVIDER_ID
MYSQL_FLEXIBLE_SERVER_PROVIDER_ID, POSTGRES_SERVER_PROVIDER_ID, POSTGRES_ARC_SERVER_PROVIDER_ID,
SQLINSTANCE_PROVIDER_ID, SQLINSTANCE_ARC_PROVIDER_ID, SYNAPSE_SQL_POOL_PROVIDER_ID, SYNAPSE_WORKSPACE_PROVIDER_ID, COSMOSDB_POSTGRES_PROVIDER_ID, POSTGRES_FLEXIBLE_SERVER_PROVIDER_ID
} from '../../../constants';
import { Logger } from '../../../utils/Logger';
@ -79,8 +79,10 @@ export class AzureResourceUniversalService implements azureResource.IAzureResour
public getProviderFromResourceType(type: string, kind?: string):
[provider: azureResource.IAzureResourceTreeDataProvider, category: ResourceCategory] {
if ((type === azureResource.AzureResourceType.cosmosDbAccount && kind === mongoDbKind) || type === azureResource.AzureResourceType.cosmosDbCluster) {
if ((type === azureResource.AzureResourceType.cosmosDbAccount && kind === mongoDbKind) || type === azureResource.AzureResourceType.cosmosDbMongoCluster) {
return [this.getRegisteredTreeDataProviderInstance(COSMOSDB_MONGO_PROVIDER_ID), ResourceCategory.Server];
} else if (type === azureResource.AzureResourceType.postgresServerGroup || type === azureResource.AzureResourceType.postgresServerGroupv2) {
return [this.getRegisteredTreeDataProviderInstance(COSMOSDB_POSTGRES_PROVIDER_ID), ResourceCategory.Server];
} else if (type === azureResource.AzureResourceType.sqlDatabase || type === azureResource.AzureResourceType.sqlSynapseSqlDatabase) {
return [this.getRegisteredTreeDataProviderInstance(DATABASE_PROVIDER_ID), ResourceCategory.Database];
} else if (type === azureResource.AzureResourceType.sqlServer && kind !== analyticsKind) {
@ -91,10 +93,10 @@ export class AzureResourceUniversalService implements azureResource.IAzureResour
return [this.getRegisteredTreeDataProviderInstance(AZURE_MONITOR_PROVIDER_ID), ResourceCategory.Server];
} else if (type === azureResource.AzureResourceType.mysqlFlexibleServer) {
return [this.getRegisteredTreeDataProviderInstance(MYSQL_FLEXIBLE_SERVER_PROVIDER_ID), ResourceCategory.Server];
} else if (type === azureResource.AzureResourceType.postgresServer || type === azureResource.AzureResourceType.postgresServerv2 || type === azureResource.AzureResourceType.postgresSingleServer) {
return [this.getRegisteredTreeDataProviderInstance(POSTGRES_SERVER_PROVIDER_ID), ResourceCategory.Server];
} else if (type === azureResource.AzureResourceType.postgresFlexibleServer) {
return [this.getRegisteredTreeDataProviderInstance(POSTGRES_FLEXIBLE_SERVER_PROVIDER_ID), ResourceCategory.Server];
} else if (type === azureResource.AzureResourceType.postgresServer) {
return [this.getRegisteredTreeDataProviderInstance(POSTGRES_SERVER_PROVIDER_ID), ResourceCategory.Server];
} else if (type === azureResource.AzureResourceType.azureArcPostgresServer) {
return [this.getRegisteredTreeDataProviderInstance(POSTGRES_ARC_SERVER_PROVIDER_ID), ResourceCategory.Server];
} else if (type === azureResource.AzureResourceType.sqlManagedInstance) {
@ -123,10 +125,10 @@ export class AzureResourceUniversalService implements azureResource.IAzureResour
return this.getRegisteredTreeDataProviderInstance(AZURE_MONITOR_PROVIDER_ID);
} else if (id.startsWith(AzureResourcePrefixes.mySqlFlexibleServer)) {
return this.getRegisteredTreeDataProviderInstance(MYSQL_FLEXIBLE_SERVER_PROVIDER_ID);
} else if (id.startsWith(AzureResourcePrefixes.postgresFlexibleServer)) {
return this.getRegisteredTreeDataProviderInstance(POSTGRES_FLEXIBLE_SERVER_PROVIDER_ID);
} else if (id.startsWith(AzureResourcePrefixes.postgresServer)) {
return this.getRegisteredTreeDataProviderInstance(POSTGRES_SERVER_PROVIDER_ID);
} else if (id.startsWith(AzureResourcePrefixes.postgresFlexibleServer)) {
return this.getRegisteredTreeDataProviderInstance(POSTGRES_FLEXIBLE_SERVER_PROVIDER_ID);
} else if (id.startsWith(AzureResourcePrefixes.postgresServerArc)) {
return this.getRegisteredTreeDataProviderInstance(POSTGRES_ARC_SERVER_PROVIDER_ID);
} else if (id.startsWith(AzureResourcePrefixes.sqlInstance)) {

Просмотреть файл

@ -37,7 +37,7 @@ export class AzureResourceUniversalTreeDataProvider<S extends GraphData, D exten
return provider.getTreeItemForResource(resource, account);
}
public async getRootChildren(): Promise<azdata.TreeItem[]> {
public async getRootChild(): Promise<azdata.TreeItem> {
throw new Error('Method not supported');
}
@ -48,6 +48,7 @@ export class AzureResourceUniversalTreeDataProvider<S extends GraphData, D exten
account: account,
subscription: resource.subscription,
tenantId: resource.tenant,
resourceProviderId: resource.provider,
treeItem: this.getTreeItemForResource(resource, account)
}).sort((a, b) => (<any>a.treeItem.label).localeCompare(b.treeItem.label));
} catch (error) {

Просмотреть файл

@ -6,7 +6,6 @@
import { extensions } from 'vscode';
import * as azdata from 'azdata';
import { IAzureResourceNodeWithProviderId } from './interfaces';
import { AzureAccount, azureResource } from 'azurecore';
import { UNIVERSAL_PROVIDER_ID } from '../constants';
@ -33,34 +32,34 @@ export class AzureResourceService {
this.doRegisterResourceProvider(resourceProvider);
}
public registerUniversalResourceProvider(resourceProvider: azureResource.IAzureUniversalResourceProvider): void {
this._universalProvider = resourceProvider;
}
public clearResourceProviders(): void {
this._resourceProviders = {};
this._treeDataProviders = {};
this._areResourceProvidersLoaded = false;
}
public async getRootChildren(resourceProviderId: string, account: AzureAccount, subscription: azureResource.AzureResourceSubscription): Promise<IAzureResourceNodeWithProviderId[]> {
public async getRootChild(resourceProviderId: string, account: AzureAccount, subscription: azureResource.AzureResourceSubscription): Promise<azureResource.IAzureResourceNode> {
await this.ensureResourceProvidersRegistered();
if (!(resourceProviderId in this._resourceProviders) && resourceProviderId !== UNIVERSAL_PROVIDER_ID) {
throw new Error(`Azure resource provider doesn't exist. Id: ${resourceProviderId}`);
}
const rootChildren = <azdata.TreeItem[]>await this._treeDataProviders[resourceProviderId]?.getRootChildren();
return rootChildren.map(rootChild => {
return {
resourceProviderId,
resourceNode: {
account,
subscription,
tenantId: subscription.tenant!,
treeItem: rootChild
}
};
});
const rootChild = <azdata.TreeItem>await this._treeDataProviders[resourceProviderId]?.getRootChild();
return {
account: account,
subscription: subscription,
tenantId: subscription.tenant!,
resourceProviderId: resourceProviderId,
treeItem: rootChild
};
}
public async getChildren(resourceProviderId: string, element: azureResource.IAzureResourceNode, browseConnectionMode: boolean = false): Promise<IAzureResourceNodeWithProviderId[]> {
public async getChildren(resourceProviderId: string, element: azureResource.IAzureResourceNode, browseConnectionMode: boolean = false): Promise<azureResource.IAzureResourceNode[]> {
await this.ensureResourceProvidersRegistered();
if (!(resourceProviderId in this._resourceProviders) && resourceProviderId !== UNIVERSAL_PROVIDER_ID) {
@ -70,23 +69,15 @@ export class AzureResourceService {
const treeDataProvider = <azureResource.IAzureResourceTreeDataProvider>this._treeDataProviders[resourceProviderId];
treeDataProvider.browseConnectionMode = browseConnectionMode;
const children = <azureResource.IAzureResourceNode[]>await treeDataProvider.getChildren(element);
return children.map((child) => <IAzureResourceNodeWithProviderId>{
resourceProviderId: resourceProviderId,
resourceNode: child
});
return children;
}
public async getAllChildren(account: AzureAccount, subscriptions: azureResource.AzureResourceSubscription[], browseConnectionMode: boolean = false): Promise<IAzureResourceNodeWithProviderId[]> {
public async getAllChildren(account: AzureAccount, subscriptions: azureResource.AzureResourceSubscription[], browseConnectionMode: boolean = false): Promise<azureResource.IAzureResourceNode[]> {
await this.ensureResourceProvidersRegistered();
const treeDataProvider = <azureResource.IAzureUniversalTreeDataProvider>this._universalProvider?.getTreeDataProvider();
treeDataProvider.browseConnectionMode = browseConnectionMode;
const children = <azureResource.IAzureResourceNode[]>await treeDataProvider.getAllChildren(account, subscriptions);
return children.map((child) => <IAzureResourceNodeWithProviderId>{
resourceProviderId: UNIVERSAL_PROVIDER_ID,
resourceNode: child
});
return children;
}
public get areResourceProvidersLoaded(): boolean {

Просмотреть файл

@ -10,17 +10,17 @@ const localize = nls.loadMessageBundle();
import { TreeNode } from './treeNode';
import { AzureResourceService } from './resourceService';
import { IAzureResourceNodeWithProviderId } from './interfaces';
import { AzureResourceMessageTreeNode } from './messageTreeNode';
import { AzureResourceErrorMessageUtil } from './utils';
import { AppContext } from '../appContext';
import { AzureResourceServiceNames } from './constants';
import { azureResource } from 'azurecore';
export class AzureResourceResourceTreeNode extends TreeNode {
private _resourceService: AzureResourceService;
public constructor(
public readonly resourceNodeWithProviderId: IAzureResourceNodeWithProviderId,
public readonly resourceNode: azureResource.IAzureResourceNode,
parent: TreeNode,
private appContext: AppContext
) {
@ -31,19 +31,19 @@ export class AzureResourceResourceTreeNode extends TreeNode {
public async getChildren(): Promise<TreeNode[]> {
// It is a leaf node.
if (this.resourceNodeWithProviderId.resourceNode.treeItem.collapsibleState === TreeItemCollapsibleState.None) {
if (this.resourceNode.treeItem.collapsibleState === TreeItemCollapsibleState.None) {
return <TreeNode[]>[];
}
try {
const children = await this._resourceService.getChildren(this.resourceNodeWithProviderId.resourceProviderId, this.resourceNodeWithProviderId.resourceNode);
const children = await this._resourceService.getChildren(this.resourceNode.resourceProviderId, this.resourceNode);
if (children.length === 0) {
return [AzureResourceMessageTreeNode.create(localize('azure.resource.resourceTreeNode.noResourcesLabel', "No Resources found"), this)];
} else {
return children.map((child) => {
// To make tree node's id unique, otherwise, treeModel.js would complain 'item already registered'
child.resourceNode.treeItem.id = `${this.resourceNodeWithProviderId.resourceNode.treeItem.id}.${child.resourceNode.treeItem.id}`;
child.treeItem.id = `${this.resourceNode.treeItem.id}.${child.treeItem.id}`;
return new AzureResourceResourceTreeNode(child, this, this.appContext);
});
}
@ -53,11 +53,11 @@ export class AzureResourceResourceTreeNode extends TreeNode {
}
public getTreeItem(): TreeItem | Promise<TreeItem> {
return this.resourceNodeWithProviderId.resourceNode.treeItem;
return this.resourceNode.treeItem;
}
public getNodeInfo(): NodeInfo {
const treeItem = this.resourceNodeWithProviderId.resourceNode.treeItem;
const treeItem = this.resourceNode.treeItem;
return {
label: typeof treeItem.label === 'object' ? treeItem.label.label : treeItem.label || '',
@ -74,7 +74,7 @@ export class AzureResourceResourceTreeNode extends TreeNode {
}
public get nodePathValue(): string {
return this.resourceNodeWithProviderId.resourceNode.treeItem.id || '';
return this.resourceNode.treeItem.id || '';
}
}

Просмотреть файл

@ -10,7 +10,6 @@ import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle();
import { TreeNode } from '../treeNode';
import { IAzureResourceNodeWithProviderId } from '../interfaces';
import { AzureResourceContainerTreeNodeBase } from './baseTreeNodes';
import { AzureResourceItemType, AzureResourceServiceNames } from '../constants';
import { IAzureResourceTreeChangeHandler } from './treeChangeHandler';
@ -38,19 +37,21 @@ export class AzureResourceSubscriptionTreeNode extends AzureResourceContainerTre
public async getChildren(): Promise<TreeNode[]> {
try {
const resourceService = this.appContext.getService<AzureResourceService>(AzureResourceServiceNames.resourceService);
const children: IAzureResourceNodeWithProviderId[] = [];
for (const resourceProviderId of await resourceService.listResourceProviderIds()) {
children.push(...await resourceService.getRootChildren(resourceProviderId, this.account, this.subscription));
}
const children: azureResource.IAzureResourceNode[] = await resourceService.getAllChildren(this.account, [this.subscription], true);
let resourceTreeNodes: azureResource.IAzureResourceNode[] = [];
if (children.length === 0) {
return [AzureResourceMessageTreeNode.create(AzureResourceSubscriptionTreeNode.noResourcesLabel, this)];
} else {
return children.map((child) => {
for (let resource of children) {
if (resourceTreeNodes.findIndex(r => r.resourceProviderId === resource.resourceProviderId) !== -1) {
continue;
} else {
resourceTreeNodes.push(await resourceService.getRootChild(resource.resourceProviderId, this.account, this.subscription));
}
}
return resourceTreeNodes.map((child) => {
// To make tree node's id unique, otherwise, treeModel.js would complain 'item already registered'
child.resourceNode.treeItem.id = `${this._id}.${child.resourceNode.treeItem.id}`;
child.treeItem.id = `${this._id}.${child.treeItem.id}`;
return new AzureResourceResourceTreeNode(child, this, this.appContext);
}).sort((a, b) => a.nodePathValue.localeCompare(b.nodePathValue));
}

Просмотреть файл

@ -50,6 +50,8 @@ import { AzureResourceSynapseWorkspaceService } from './providers/synapseWorkspa
import { AzureResourceSynapseWorkspaceTreeDataProvider } from './providers/synapseWorkspace/synapseWorkspaceTreeDataProvider';
import { PostgresFlexibleServerTreeDataProvider } from './providers/postgresFlexibleServer/postgresFlexibleServerTreeDataProvider';
import { PostgresFlexibleServerService } from './providers/postgresFlexibleServer/postgresFlexibleServerService';
import { CosmosDbPostgresTreeDataProvider } from './providers/cosmosdb/postgres/cosmosDbPostgresTreeDataProvider';
import { CosmosDbPostgresService } from './providers/cosmosdb/postgres/cosmosDbPostgresService';
const localize = nls.loadMessageBundle();
@ -266,6 +268,7 @@ export function getAllResourceProviders(extensionContext: vscode.ExtensionContex
const providers: azureResource.IAzureResourceProvider[] = [
new ResourceProvider(Constants.AZURE_MONITOR_PROVIDER_ID, new AzureMonitorTreeDataProvider(new AzureMonitorResourceService(), extensionContext)),
new ResourceProvider(Constants.COSMOSDB_MONGO_PROVIDER_ID, new CosmosDbMongoTreeDataProvider(new CosmosDbMongoService(), extensionContext)),
new ResourceProvider(Constants.COSMOSDB_POSTGRES_PROVIDER_ID, new CosmosDbPostgresTreeDataProvider(new CosmosDbPostgresService(), extensionContext)),
new ResourceProvider(Constants.DATABASE_PROVIDER_ID, new AzureResourceDatabaseTreeDataProvider(new AzureResourceDatabaseService(), extensionContext)),
new ResourceProvider(Constants.DATABASE_SERVER_PROVIDER_ID, new AzureResourceDatabaseServerTreeDataProvider(new AzureResourceDatabaseServerService(), extensionContext)),
new ResourceProvider(Constants.KUSTO_PROVIDER_ID, new KustoTreeDataProvider(new KustoResourceService(), extensionContext)),

12
extensions/azurecore/src/azurecore.d.ts поставляемый
Просмотреть файл

@ -378,12 +378,17 @@ declare module 'azurecore' {
kustoClusters = 'microsoft.kusto/clusters',
azureArcPostgresServer = 'microsoft.azuredata/postgresinstances',
postgresServer = 'microsoft.dbforpostgresql/servers',
postgresServerv2 = 'microsoft.dbforpostgresql/serversv2',
postgresSingleServer = 'microsoft.dbforpostgresql/singleservers',
postgresFlexibleServer = 'microsoft.dbforpostgresql/flexibleservers',
postgresServerGroup = 'microsoft.dbforpostgresql/servergroups',
postgresServerGroupv2 = 'microsoft.dbforpostgresql/servergroupsv2',
azureArcService = 'microsoft.azuredata/datacontrollers',
storageAccount = 'microsoft.storage/storageaccounts',
logAnalytics = 'microsoft.operationalinsights/workspaces',
cosmosDbAccount = 'microsoft.documentdb/databaseaccounts',
cosmosDbCluster = 'microsoft.documentdb/mongoclusters',
cosmosDbPostgresCluster = 'microsoft.documentdb/postgresclusters',
cosmosDbMongoCluster = 'microsoft.documentdb/mongoclusters',
mysqlFlexibleServer = 'microsoft.dbformysql/flexibleservers'
}
@ -406,9 +411,9 @@ declare module 'azurecore' {
getService(): azureResource.IAzureResourceService;
/**
* Gets the root tree item nodes for this provider - these will be used as
* direct children of the Account node in the Azure tree view.
* direct children of the Tenant node in the Azure tree view.
*/
getRootChildren(): Promise<azdata.TreeItem[]>;
getRootChild(): Promise<azdata.TreeItem>;
/**
* Gets the children for a given {@link IAzureResourceNode}
* @param element The parent node to get the children for
@ -427,6 +432,7 @@ declare module 'azurecore' {
readonly account: AzureAccount;
readonly subscription: AzureResourceSubscription;
readonly tenantId: string;
readonly resourceProviderId: string;
readonly treeItem: azdata.TreeItem;
}

Просмотреть файл

@ -146,6 +146,7 @@ export enum Platform {
/////////////// Azure Resource provider Ids
export const AZURE_MONITOR_PROVIDER_ID = 'azure.resource.providers.azureMonitor';
export const COSMOSDB_MONGO_PROVIDER_ID = 'azure.resource.providers.cosmosDbMongo';
export const COSMOSDB_POSTGRES_PROVIDER_ID = 'azure.resource.providers.cosmosDbPostgres';
export const DATABASE_PROVIDER_ID = 'azure.resource.providers.database';
export const DATABASE_SERVER_PROVIDER_ID = 'azure.resource.providers.databaseServer';
export const KUSTO_PROVIDER_ID = 'azure.resource.providers.azureDataExplorer';

Просмотреть файл

@ -58,6 +58,7 @@ const mockResourceRootNode: azureResource.IAzureResourceNode = {
account: mockAccount,
subscription: mockSubscription,
tenantId: mockTenantId,
resourceProviderId: 'mock_resource_provider',
treeItem: {
id: 'mock_resource_root_node',
label: 'mock resource root node',
@ -135,12 +136,9 @@ describe('AzureResourceDatabaseTreeDataProvider.getChildren', function (): void
it('Should return container node when element is undefined.', async function (): Promise<void> {
const treeDataProvider = new AzureResourceDatabaseTreeDataProvider(mockDatabaseService.object, mockExtensionContext.object);
const children = await treeDataProvider.getRootChildren();
const child = await treeDataProvider.getRootChild();
should(children).Array();
should(children.length).equal(1);
const child = children[0];
should(child).Object();
should(child.id).equal('azure.resource.providers.database.treeDataProvider.databaseContainer');
should(child.label).equal('SQL databases');
should(child.collapsibleState).equal(vscode.TreeItemCollapsibleState.Collapsed);

Просмотреть файл

@ -57,6 +57,7 @@ const mockResourceRootNode: azureResource.IAzureResourceNode = {
account: mockAccount,
subscription: mockSubscription,
tenantId: mockTenantId,
resourceProviderId: 'mock_resource_provider',
treeItem: {
id: 'mock_resource_root_node',
label: 'mock resource root node',
@ -134,12 +135,9 @@ describe('AzureResourceDatabaseServerTreeDataProvider.getChildren', function ():
it('Should return container node when element is undefined.', async function (): Promise<void> {
const treeDataProvider = new AzureResourceDatabaseServerTreeDataProvider(mockDatabaseServerService.object, mockExtensionContext.object);
const children = await treeDataProvider.getRootChildren();
const child = await treeDataProvider.getRootChild();
should(children).Array();
should(children.length).equal(1);
const child = children[0];
should(child).Object();
should(child.id).equal('azure.resource.providers.databaseServer.treeDataProvider.databaseServerContainer');
should(child.label).equal('SQL servers');
should(child.collapsibleState).equal(vscode.TreeItemCollapsibleState.Collapsed);

Просмотреть файл

@ -8,6 +8,7 @@ import * as TypeMoq from 'typemoq';
import 'mocha';
import { fail } from 'assert';
import * as azdata from 'azdata';
import * as vscode from 'vscode';
import { AzureResourceService } from '../../azureResource/resourceService';
import { AzureAccount, azureResource } from 'azurecore';
@ -52,18 +53,33 @@ let mockResourceProvider1: TypeMoq.IMock<azureResource.IAzureResourceProvider>;
let mockResourceTreeDataProvider2: TypeMoq.IMock<azureResource.IAzureResourceTreeDataProvider>;
let mockResourceProvider2: TypeMoq.IMock<azureResource.IAzureResourceProvider>;
const mockResourceProviderId1: string = 'mock_resource_provider';
const mockResourceNode1: azureResource.IAzureResourceNode = {
account: mockAccount,
subscription: mockSubscription,
tenantId: mockTenantId,
resourceProviderId: mockResourceProviderId1,
treeItem: {
id: 'mock_resource_node_1',
label: 'mock resource node 1',
iconPath: undefined,
collapsibleState: vscode.TreeItemCollapsibleState.None,
contextValue: 'mock_resource_node'
}
};
let resourceService: AzureResourceService;
describe('AzureResourceService.listResourceProviderIds', function (): void {
beforeEach(() => {
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
mockResourceTreeDataProvider1.setup((o) => o.getRootChild()).returns(() => Promise.resolve(TypeMoq.Mock.ofType<azdata.TreeItem>().object));
mockResourceProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1');
mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object);
mockResourceTreeDataProvider2 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider2.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
mockResourceTreeDataProvider2.setup((o) => o.getRootChild()).returns(() => Promise.resolve(TypeMoq.Mock.ofType<azdata.TreeItem>().object));
mockResourceProvider2 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider2.setup((o) => o.providerId).returns(() => 'mockResourceProvider2');
mockResourceProvider2.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider2.object);
@ -92,9 +108,9 @@ describe('AzureResourceService.listResourceProviderIds', function (): void {
describe('AzureResourceService.getRootChildren', function (): void {
beforeEach(() => {
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
mockResourceTreeDataProvider1.setup((o) => o.getRootChild()).returns(() => Promise.resolve(mockResourceNode1.treeItem));
mockResourceProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1');
mockResourceProvider1.setup((o) => o.providerId).returns(() => mockResourceProviderId1);
mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object);
resourceService.clearResourceProviders();
@ -103,15 +119,14 @@ describe('AzureResourceService.getRootChildren', function (): void {
});
it('Should be correct when provider id is correct.', async function (): Promise<void> {
const children = await resourceService.getRootChildren(mockResourceProvider1.object.providerId, mockAccount, mockSubscription);
should(children).Array();
const child = await resourceService.getRootChild(mockResourceProvider1.object.providerId, mockAccount, mockSubscription);
should(child).Object();
});
it('Should throw exceptions when provider id is incorrect.', async function (): Promise<void> {
const providerId = 'non_existent_provider_id';
try {
await resourceService.getRootChildren(providerId, mockAccount, mockSubscription);
await resourceService.getRootChild(providerId, mockAccount, mockSubscription);
} catch (error) {
should(error.message).equal(`Azure resource provider doesn't exist. Id: ${providerId}`);
return;
@ -124,7 +139,7 @@ describe('AzureResourceService.getRootChildren', function (): void {
describe('AzureResourceService.getChildren', function (): void {
beforeEach(() => {
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
mockResourceTreeDataProvider1.setup((o) => o.getRootChild()).returns(() => Promise.resolve(TypeMoq.Mock.ofType<azdata.TreeItem>().object));
mockResourceTreeDataProvider1.setup((o) => o.getChildren(TypeMoq.It.isAny())).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azureResource.IAzureResourceNode>().object]));
mockResourceProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1');
@ -143,7 +158,7 @@ describe('AzureResourceService.getChildren', function (): void {
it('Should throw exceptions when provider id is incorrect.', async function (): Promise<void> {
const providerId = 'non_existent_provider_id';
try {
await resourceService.getRootChildren(providerId, mockAccount, mockSubscription);
await resourceService.getRootChild(providerId, mockAccount, mockSubscription);
} catch (error) {
should(error.message).equal(`Azure resource provider doesn't exist. Id: ${providerId}`);
return;

Просмотреть файл

@ -56,6 +56,7 @@ const mockResourceRootNode: azureResource.IAzureResourceNode = {
account: mockAccount,
subscription: mockSubscription,
tenantId: mockTenantId,
resourceProviderId: mockResourceProviderId,
treeItem: {
id: 'mock_resource_root_node',
label: 'mock resource root node',
@ -69,6 +70,7 @@ const mockResourceNode1: azureResource.IAzureResourceNode = {
account: mockAccount,
subscription: mockSubscription,
tenantId: mockTenantId,
resourceProviderId: mockResourceProviderId,
treeItem: {
id: 'mock_resource_node_1',
label: 'mock resource node 1',
@ -82,6 +84,7 @@ const mockResourceNode2: azureResource.IAzureResourceNode = {
account: mockAccount,
subscription: mockSubscription,
tenantId: mockTenantId,
resourceProviderId: mockResourceProviderId,
treeItem: {
id: 'mock_resource_node_2',
label: 'mock resource node 2',
@ -116,11 +119,7 @@ describe('AzureResourceResourceTreeNode.info', function (): void {
});
it('Should be correct when created.', async function (): Promise<void> {
const resourceTreeNode = new AzureResourceResourceTreeNode({
resourceProviderId: mockResourceProviderId,
resourceNode: mockResourceRootNode
}, TypeMoq.Mock.ofType<TreeNode>().object, appContext);
const resourceTreeNode = new AzureResourceResourceTreeNode(mockResourceRootNode, TypeMoq.Mock.ofType<TreeNode>().object, appContext);
should(resourceTreeNode.nodePathValue).equal(mockResourceRootNode.treeItem.id);
const treeItem = await resourceTreeNode.getTreeItem();
@ -156,12 +155,7 @@ describe('AzureResourceResourceTreeNode.getChildren', function (): void {
});
it('Should return resource nodes when it is container node.', async function (): Promise<void> {
const resourceTreeNode = new AzureResourceResourceTreeNode({
resourceProviderId: mockResourceProviderId,
resourceNode: mockResourceRootNode
},
TypeMoq.Mock.ofType<TreeNode>().object, appContext);
const resourceTreeNode = new AzureResourceResourceTreeNode(mockResourceRootNode, TypeMoq.Mock.ofType<TreeNode>().object, appContext);
const children = await resourceTreeNode.getChildren();
mockResourceTreeDataProvider.verify((o) => o.getChildren(mockResourceRootNode), TypeMoq.Times.once());
@ -174,27 +168,23 @@ describe('AzureResourceResourceTreeNode.getChildren', function (): void {
should(child).instanceOf(AzureResourceResourceTreeNode);
const childNode = (child as AzureResourceResourceTreeNode).resourceNodeWithProviderId;
const childNode = (child as AzureResourceResourceTreeNode).resourceNode;
should(childNode.account).equal(mockAccount);
should(childNode.subscription).equal(mockSubscription);
should(childNode.tenantId).equal(mockTenantId);
should(childNode.resourceProviderId).equal(mockResourceProviderId);
should(childNode.resourceNode.account).equal(mockAccount);
should(childNode.resourceNode.subscription).equal(mockSubscription);
should(childNode.resourceNode.tenantId).equal(mockTenantId);
should(childNode.resourceNode.treeItem.id).equal(mockResourceNodes[ix].treeItem.id);
should(childNode.resourceNode.treeItem.label).equal(mockResourceNodes[ix].treeItem.label);
should(childNode.resourceNode.treeItem.collapsibleState).equal(mockResourceNodes[ix].treeItem.collapsibleState);
should(childNode.resourceNode.treeItem.contextValue).equal(mockResourceNodes[ix].treeItem.contextValue);
should(childNode.treeItem.id).equal(mockResourceNodes[ix].treeItem.id);
should(childNode.treeItem.label).equal(mockResourceNodes[ix].treeItem.label);
should(childNode.treeItem.collapsibleState).equal(mockResourceNodes[ix].treeItem.collapsibleState);
should(childNode.treeItem.contextValue).equal(mockResourceNodes[ix].treeItem.contextValue);
}
});
it('Should return empty when it is leaf node.', async function (): Promise<void> {
const resourceTreeNode = new AzureResourceResourceTreeNode({
resourceProviderId: mockResourceProviderId,
resourceNode: mockResourceNode1
}, TypeMoq.Mock.ofType<TreeNode>().object, appContext);
const resourceTreeNode = new AzureResourceResourceTreeNode(mockResourceNode1, TypeMoq.Mock.ofType<TreeNode>().object, appContext);
const children = await resourceTreeNode.getChildren();
mockResourceTreeDataProvider.verify((o) => o.getRootChildren(), TypeMoq.Times.exactly(0));
mockResourceTreeDataProvider.verify((o) => o.getRootChild(), TypeMoq.Times.exactly(0));
should(children).Array();
should(children.length).equal(0);

Просмотреть файл

@ -46,6 +46,8 @@ const mockAccount: AzureAccount = {
const mockTenantId: string = 'mock_tenant';
const mockSubscriptionId: string = 'mock_subscription';
const mockResourceProviderId1: string = 'mock_resource_provider';
const mockResourceProviderId2: string = 'mock_resource_provider';
const mockTenant: Tenant = {
id: mockTenantId,
@ -60,12 +62,45 @@ const mockSubscription: azureResource.AzureResourceSubscription = {
tenant: mockTenantId
};
const mockResourceNode1: azureResource.IAzureResourceNode = {
account: mockAccount,
subscription: mockSubscription,
tenantId: mockTenantId,
resourceProviderId: mockResourceProviderId1,
treeItem: {
id: 'mock_resource_node_1',
label: 'mock resource node 1',
iconPath: undefined,
collapsibleState: vscode.TreeItemCollapsibleState.None,
contextValue: 'mock_resource_node'
}
};
const mockResourceNode2: azureResource.IAzureResourceNode = {
account: mockAccount,
subscription: mockSubscription,
tenantId: mockTenantId,
resourceProviderId: mockResourceProviderId2,
treeItem: {
id: 'mock_resource_node_2',
label: 'mock resource node 2',
iconPath: undefined,
collapsibleState: vscode.TreeItemCollapsibleState.None,
contextValue: 'mock_resource_node'
}
};
const mockResourceNodes: azureResource.IAzureResourceNode[] = [mockResourceNode1, mockResourceNode2];
let mockResourceTreeDataProvider1: TypeMoq.IMock<azureResource.IAzureResourceTreeDataProvider>;
let mockResourceProvider1: TypeMoq.IMock<azureResource.IAzureResourceProvider>;
let mockResourceTreeDataProvider2: TypeMoq.IMock<azureResource.IAzureResourceTreeDataProvider>;
let mockResourceProvider2: TypeMoq.IMock<azureResource.IAzureResourceProvider>;
let mockUniversalTreeDataProvider: TypeMoq.IMock<azureResource.IAzureUniversalTreeDataProvider>;
let mockUniversalResourceProvider: TypeMoq.IMock<azureResource.IAzureUniversalResourceProvider>;
const resourceService: AzureResourceService = new AzureResourceService();
describe('AzureResourceSubscriptionTreeNode.info', function (): void {
@ -78,14 +113,14 @@ describe('AzureResourceSubscriptionTreeNode.info', function (): void {
mockTreeChangeHandler = TypeMoq.Mock.ofType<IAzureResourceTreeChangeHandler>();
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
mockResourceTreeDataProvider1.setup((o) => o.getRootChild()).returns(() => Promise.resolve(TypeMoq.Mock.ofType<azdata.TreeItem>().object));
mockResourceTreeDataProvider1.setup((x: any) => x.then).returns(() => undefined);
mockResourceProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1');
mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object);
mockResourceTreeDataProvider2 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider2.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
mockResourceTreeDataProvider2.setup((o) => o.getRootChild()).returns(() => Promise.resolve(TypeMoq.Mock.ofType<azdata.TreeItem>().object));
mockResourceTreeDataProvider2.setup((x: any) => x.then).returns(() => undefined);
mockResourceProvider2 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider2.setup((o) => o.providerId).returns(() => 'mockResourceProvider2');
@ -130,21 +165,28 @@ describe('AzureResourceSubscriptionTreeNode.getChildren', function (): void {
mockTreeChangeHandler = TypeMoq.Mock.ofType<IAzureResourceTreeChangeHandler>();
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([{ label: 'Item1' }] as azdata.TreeItem[]));
mockResourceTreeDataProvider1.setup((o) => o.getRootChild()).returns(() => Promise.resolve({ label: 'Item1' } as azdata.TreeItem));
mockResourceProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1');
mockResourceProvider1.setup((o) => o.providerId).returns(() => mockResourceProviderId1);
mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object);
mockResourceTreeDataProvider2 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider2.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([{ label: 'Item2' }] as azdata.TreeItem[]));
mockResourceTreeDataProvider2.setup((o) => o.getRootChild()).returns(() => Promise.resolve({ label: 'Item2' } as azdata.TreeItem));
mockResourceProvider2 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider2.setup((o) => o.providerId).returns(() => 'mockResourceProvider2');
mockResourceProvider2.setup((o) => o.providerId).returns(() => mockResourceProviderId2);
mockResourceProvider2.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider2.object);
mockUniversalTreeDataProvider = TypeMoq.Mock.ofType<azureResource.IAzureUniversalTreeDataProvider>();
mockUniversalTreeDataProvider.setup((o) => o.getAllChildren(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(mockResourceNodes));
mockUniversalResourceProvider = TypeMoq.Mock.ofType<azureResource.IAzureUniversalResourceProvider>();
mockUniversalResourceProvider.setup((o) => o.providerId).returns(() => 'mockUniversalResourceProvider');
mockUniversalResourceProvider.setup((o) => o.getTreeDataProvider()).returns(() => mockUniversalTreeDataProvider.object);
resourceService.clearResourceProviders();
resourceService.registerResourceProvider(mockResourceProvider1.object);
resourceService.registerResourceProvider(mockResourceProvider2.object);
resourceService.registerUniversalResourceProvider(mockUniversalResourceProvider.object);
resourceService.areResourceProvidersLoaded = true;
appContext = new AppContext(mockExtensionContext.object);
@ -157,14 +199,12 @@ describe('AzureResourceSubscriptionTreeNode.getChildren', function (): void {
const subscriptionTreeNode = new AzureResourceSubscriptionTreeNode(mockAccount, mockSubscription, mockTenant, appContext, mockTreeChangeHandler.object, TypeMoq.Mock.ofType<TreeNode>().object);
const children = await subscriptionTreeNode.getChildren();
mockResourceTreeDataProvider1.verify((o) => o.getRootChildren(), TypeMoq.Times.once());
mockResourceTreeDataProvider2.verify((o) => o.getRootChildren(), TypeMoq.Times.once());
mockUniversalTreeDataProvider.verify((o) => o.getAllChildren(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
const expectedResourceProviderIds = await resourceService.listResourceProviderIds();
should(children).Array();
should(children.length).equal(expectedResourceProviderIds.length, 'There should be one child for each resource provider');
should(children.length).equal(expectedResourceProviderIds.length, 'There should be one child for each resource provider that has a resource.');
for (const child of children) {
should(child).instanceOf(AzureResourceResourceTreeNode);
}