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
committed by GitHub
parent 01e66ab3e6
commit 0970442a5c
33 changed files with 323 additions and 170 deletions

View File

@@ -24,7 +24,9 @@ export enum AzureResourceItemType {
azureMonitor = 'azure.resource.itemType.azureMonitor', azureMonitor = 'azure.resource.itemType.azureMonitor',
azureMonitorContainer = 'azure.resource.itemType.azureMonitorContainer', azureMonitorContainer = 'azure.resource.itemType.azureMonitorContainer',
cosmosDBMongoAccount = 'azure.resource.itemType.cosmosDBMongoAccount', 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 { export enum AzureResourceServiceNames {

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 || ''
}
};
}
}

View File

@@ -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
};
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -46,7 +46,7 @@ export const resourceGroupQuery = `ResourceContainers | where type=="${azureReso
/** /**
* Lists all postgreSQL servers * 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 * Lists all postgreSQL flexible servers
@@ -71,7 +71,12 @@ export const kustoClusterQuery = `type == "${azureResource.AzureResourceType.kus
/** /**
* Lists all Cosmos DB for MongoDB accounts * 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 * Lists all Log Analytics workspaces

View File

@@ -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 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[]> { export async function queryGraphResources<T extends GraphData>(resourceClient: ResourceGraphClient, subscriptions: azureResource.AzureResourceSubscription[], resourceQuery: string): Promise<T[]> {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,7 +10,6 @@ import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
import { TreeNode } from '../treeNode'; import { TreeNode } from '../treeNode';
import { IAzureResourceNodeWithProviderId } from '../interfaces';
import { AzureResourceContainerTreeNodeBase } from './baseTreeNodes'; import { AzureResourceContainerTreeNodeBase } from './baseTreeNodes';
import { AzureResourceItemType, AzureResourceServiceNames } from '../constants'; import { AzureResourceItemType, AzureResourceServiceNames } from '../constants';
import { IAzureResourceTreeChangeHandler } from './treeChangeHandler'; import { IAzureResourceTreeChangeHandler } from './treeChangeHandler';
@@ -38,19 +37,21 @@ export class AzureResourceSubscriptionTreeNode extends AzureResourceContainerTre
public async getChildren(): Promise<TreeNode[]> { public async getChildren(): Promise<TreeNode[]> {
try { try {
const resourceService = this.appContext.getService<AzureResourceService>(AzureResourceServiceNames.resourceService); const resourceService = this.appContext.getService<AzureResourceService>(AzureResourceServiceNames.resourceService);
const children: azureResource.IAzureResourceNode[] = await resourceService.getAllChildren(this.account, [this.subscription], true);
const children: IAzureResourceNodeWithProviderId[] = []; let resourceTreeNodes: azureResource.IAzureResourceNode[] = [];
for (const resourceProviderId of await resourceService.listResourceProviderIds()) {
children.push(...await resourceService.getRootChildren(resourceProviderId, this.account, this.subscription));
}
if (children.length === 0) { if (children.length === 0) {
return [AzureResourceMessageTreeNode.create(AzureResourceSubscriptionTreeNode.noResourcesLabel, this)]; return [AzureResourceMessageTreeNode.create(AzureResourceSubscriptionTreeNode.noResourcesLabel, this)];
} else { } 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' // 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); return new AzureResourceResourceTreeNode(child, this, this.appContext);
}).sort((a, b) => a.nodePathValue.localeCompare(b.nodePathValue)); }).sort((a, b) => a.nodePathValue.localeCompare(b.nodePathValue));
} }

View File

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

View File

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

View File

@@ -146,6 +146,7 @@ export enum Platform {
/////////////// Azure Resource provider Ids /////////////// Azure Resource provider Ids
export const AZURE_MONITOR_PROVIDER_ID = 'azure.resource.providers.azureMonitor'; export const AZURE_MONITOR_PROVIDER_ID = 'azure.resource.providers.azureMonitor';
export const COSMOSDB_MONGO_PROVIDER_ID = 'azure.resource.providers.cosmosDbMongo'; 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_PROVIDER_ID = 'azure.resource.providers.database';
export const DATABASE_SERVER_PROVIDER_ID = 'azure.resource.providers.databaseServer'; export const DATABASE_SERVER_PROVIDER_ID = 'azure.resource.providers.databaseServer';
export const KUSTO_PROVIDER_ID = 'azure.resource.providers.azureDataExplorer'; export const KUSTO_PROVIDER_ID = 'azure.resource.providers.azureDataExplorer';

View File

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

View File

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

View File

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

View File

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

View File

@@ -46,6 +46,8 @@ const mockAccount: AzureAccount = {
const mockTenantId: string = 'mock_tenant'; const mockTenantId: string = 'mock_tenant';
const mockSubscriptionId: string = 'mock_subscription'; const mockSubscriptionId: string = 'mock_subscription';
const mockResourceProviderId1: string = 'mock_resource_provider';
const mockResourceProviderId2: string = 'mock_resource_provider';
const mockTenant: Tenant = { const mockTenant: Tenant = {
id: mockTenantId, id: mockTenantId,
@@ -60,12 +62,45 @@ const mockSubscription: azureResource.AzureResourceSubscription = {
tenant: mockTenantId 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 mockResourceTreeDataProvider1: TypeMoq.IMock<azureResource.IAzureResourceTreeDataProvider>;
let mockResourceProvider1: TypeMoq.IMock<azureResource.IAzureResourceProvider>; let mockResourceProvider1: TypeMoq.IMock<azureResource.IAzureResourceProvider>;
let mockResourceTreeDataProvider2: TypeMoq.IMock<azureResource.IAzureResourceTreeDataProvider>; let mockResourceTreeDataProvider2: TypeMoq.IMock<azureResource.IAzureResourceTreeDataProvider>;
let mockResourceProvider2: TypeMoq.IMock<azureResource.IAzureResourceProvider>; let mockResourceProvider2: TypeMoq.IMock<azureResource.IAzureResourceProvider>;
let mockUniversalTreeDataProvider: TypeMoq.IMock<azureResource.IAzureUniversalTreeDataProvider>;
let mockUniversalResourceProvider: TypeMoq.IMock<azureResource.IAzureUniversalResourceProvider>;
const resourceService: AzureResourceService = new AzureResourceService(); const resourceService: AzureResourceService = new AzureResourceService();
describe('AzureResourceSubscriptionTreeNode.info', function (): void { describe('AzureResourceSubscriptionTreeNode.info', function (): void {
@@ -78,14 +113,14 @@ describe('AzureResourceSubscriptionTreeNode.info', function (): void {
mockTreeChangeHandler = TypeMoq.Mock.ofType<IAzureResourceTreeChangeHandler>(); mockTreeChangeHandler = TypeMoq.Mock.ofType<IAzureResourceTreeChangeHandler>();
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>(); 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); mockResourceTreeDataProvider1.setup((x: any) => x.then).returns(() => undefined);
mockResourceProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>(); mockResourceProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1'); mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1');
mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object); mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object);
mockResourceTreeDataProvider2 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>(); 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); mockResourceTreeDataProvider2.setup((x: any) => x.then).returns(() => undefined);
mockResourceProvider2 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>(); mockResourceProvider2 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider2.setup((o) => o.providerId).returns(() => 'mockResourceProvider2'); mockResourceProvider2.setup((o) => o.providerId).returns(() => 'mockResourceProvider2');
@@ -130,21 +165,28 @@ describe('AzureResourceSubscriptionTreeNode.getChildren', function (): void {
mockTreeChangeHandler = TypeMoq.Mock.ofType<IAzureResourceTreeChangeHandler>(); mockTreeChangeHandler = TypeMoq.Mock.ofType<IAzureResourceTreeChangeHandler>();
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>(); 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 = 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); mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object);
mockResourceTreeDataProvider2 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>(); 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 = 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); 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.clearResourceProviders();
resourceService.registerResourceProvider(mockResourceProvider1.object); resourceService.registerResourceProvider(mockResourceProvider1.object);
resourceService.registerResourceProvider(mockResourceProvider2.object); resourceService.registerResourceProvider(mockResourceProvider2.object);
resourceService.registerUniversalResourceProvider(mockUniversalResourceProvider.object);
resourceService.areResourceProvidersLoaded = true; resourceService.areResourceProvidersLoaded = true;
appContext = new AppContext(mockExtensionContext.object); 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 subscriptionTreeNode = new AzureResourceSubscriptionTreeNode(mockAccount, mockSubscription, mockTenant, appContext, mockTreeChangeHandler.object, TypeMoq.Mock.ofType<TreeNode>().object);
const children = await subscriptionTreeNode.getChildren(); const children = await subscriptionTreeNode.getChildren();
mockResourceTreeDataProvider1.verify((o) => o.getRootChildren(), TypeMoq.Times.once()); mockUniversalTreeDataProvider.verify((o) => o.getAllChildren(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
mockResourceTreeDataProvider2.verify((o) => o.getRootChildren(), TypeMoq.Times.once());
const expectedResourceProviderIds = await resourceService.listResourceProviderIds(); const expectedResourceProviderIds = await resourceService.listResourceProviderIds();
should(children).Array(); 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) { for (const child of children) {
should(child).instanceOf(AzureResourceResourceTreeNode); should(child).instanceOf(AzureResourceResourceTreeNode);
} }