diff --git a/extensions/azurecore/src/azureResource/constants.ts b/extensions/azurecore/src/azureResource/constants.ts index c1b6f29560..37837514b9 100644 --- a/extensions/azurecore/src/azureResource/constants.ts +++ b/extensions/azurecore/src/azureResource/constants.ts @@ -34,4 +34,33 @@ export enum AzureResourceServiceNames { terminalService = 'AzureTerminalService', } +export enum AzureResourcePrefixes { + logAnalytics = 'LogAnalytics_', + cosmosdb = 'Cosmosdb_', + database = 'database_', + databaseServer = 'databaseServer_', + kusto = 'Kusto_', + mySqlFlexibleServer = 'mySqlFlexibleServer_', + postgresServerArc = 'postgresServerArc_', + postgresServer = 'postgresServer_', + sqlInstance = 'sqlInstance_', + sqlInstanceArc = 'sqlInstanceArc_', + synapseSqlPool = 'synapseSqlPool_', + synapseWorkspace = 'synapseWorkspace_' +} + export const mssqlProvider = 'MSSQL'; +export const logAnalyticsProvider = 'LOGANALYTICS'; +export const cosmosDBProvider = 'COSMOSDB_MONGO'; +export const kustoProvider = 'KUSTO'; +export const mySqlProvider = 'MySQL'; +export const pgsqlProvider = 'PGSQL'; + +// Kinds +export const analyticsKind = 'v12.0,analytics'; +export const mongoDbKind = 'MongoDB'; + +export enum ResourceCategory { + Server = 0, + Database = 1 +} diff --git a/extensions/azurecore/src/azureResource/interfaces.ts b/extensions/azurecore/src/azureResource/interfaces.ts index a7304d9c16..b12c05b5fb 100644 --- a/extensions/azurecore/src/azureResource/interfaces.ts +++ b/extensions/azurecore/src/azureResource/interfaces.ts @@ -3,10 +3,126 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as msRest from '@azure/ms-rest-js'; - import { AzureAccount, Tenant, azureResource } from 'azurecore'; +export interface GraphData { + subscriptionId: string, + subscriptionName?: string, + tenantId: string; + id: string; + name: string; + location: string; + type: string; + resourceGroup: string; +} + +export interface AzureMonitorGraphData extends GraphData { + properties: { + fullyQualifiedDomainName: string; + administratorLogin: string; + uri: string; + customerId: string + }; +} + +export interface SqlInstanceArcGraphData extends GraphData { + properties: { + admin: string; + hybridDataManager: string; + }; +} + +export interface PostgresArcServerGraphData extends GraphData { + properties: { + admin: string; + }; +} + +export interface DatabaseGraphData extends GraphData { + kind: string; +} + +export interface SynapseGraphData extends GraphData { + kind: string; +} + +export interface DbServerGraphData extends GraphData { + properties: { + fullyQualifiedDomainName: string; + administratorLogin: string; + }; +} + +export interface KustoGraphData extends GraphData { + properties: { + fullyQualifiedDomainName: string; + administratorLogin: string; + uri: string; + }; +} + +export interface SqlInstanceGraphData extends GraphData { + properties: { + fullyQualifiedDomainName: string; + administratorLogin: string; + }; +} + +/** + * Properties combined from all providers to create a universal interface that can be translated to any resource provider. + */ +export interface UniversalGraphData extends GraphData { + kind?: string, + properties?: { + /** + * SQL connectivity endpoint and other endpoints are found here, instead of fullyQualifiedDomainName. + */ + connectivityEndpoints?: { sql: string }; + /** + * managedResourceGroupName is the resource group used by any SQL pools inside the workspace + * which is different from the resource group of the workspace itself. + */ + managedResourceGroupName?: string; + /** + * administratorLogin is called sqlAdministratorLogin for Synapse. + */ + sqlAdministratorLogin?: string; + + admin?: string; + + hybridDataManager?: string; + + fullyQualifiedDomainName?: string; + + administratorLogin?: string; + + uri?: string; + + customerId?: string + } +} + +/** + * Properties returned by the Synapse query are different from the server ones and have to be treated differently. + */ +export interface SynapseWorkspaceGraphData extends GraphData { + properties: { + /** + * SQL connectivity endpoint and other endpoints are found here, instead of fullyQualifiedDomainName. + */ + connectivityEndpoints: { sql: string }; + /** + * managedResourceGroupName is the resource group used by any SQL pools inside the workspace + * which is different from the resource group of the workspace itself. + */ + managedResourceGroupName: string; + /** + * administratorLogin is called sqlAdministratorLogin here. + */ + sqlAdministratorLogin: string; + }; +} + export interface IAzureResourceSubscriptionService { /** * Gets subscriptions for the given account. Any errors that occur while fetching the subscriptions for each tenant @@ -41,6 +157,10 @@ export interface IAzureResourceNodeWithProviderId { resourceNode: azureResource.IAzureResourceNode; } -export interface IAzureResourceService { - getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: msRest.ServiceClientCredentials, account: AzureAccount): Promise; +export interface IAzureResourceDbService extends azureResource.IAzureResourceService { + convertDatabaseResource(resource: T, server?: S): azureResource.AzureResource | undefined; +} + +export interface IAzureResourceServerService extends azureResource.IAzureResourceService { + convertServerResource(resource: T): azureResource.AzureResource | undefined; } diff --git a/extensions/azurecore/src/azureResource/providers/azuremonitor/azuremonitorProvider.ts b/extensions/azurecore/src/azureResource/providers/azuremonitor/azuremonitorProvider.ts deleted file mode 100644 index 5becd2fec5..0000000000 --- a/extensions/azurecore/src/azureResource/providers/azuremonitor/azuremonitorProvider.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ExtensionContext } from 'vscode'; - -import { azureResource } from 'azurecore'; -import { IAzureResourceService } from '../../interfaces'; -import { AzureMonitorTreeDataProvider as AzureMonitorTreeDataProvider } from './azuremonitorTreeDataProvider'; - -export class AzureMonitorProvider implements azureResource.IAzureResourceProvider { - public constructor( - private _service: IAzureResourceService, - private _extensionContext: ExtensionContext - ) { - } - - public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { - return new AzureMonitorTreeDataProvider(this._service, this._extensionContext); - } - - public get providerId(): string { - return 'azure.resource.providers.azureMonitor'; - } -} diff --git a/extensions/azurecore/src/azureResource/providers/azuremonitor/azuremonitorService.ts b/extensions/azurecore/src/azureResource/providers/azuremonitor/azuremonitorService.ts index dc121b35e7..a43c1a48ce 100644 --- a/extensions/azurecore/src/azureResource/providers/azuremonitor/azuremonitorService.ts +++ b/extensions/azurecore/src/azureResource/providers/azuremonitor/azuremonitorService.ts @@ -5,27 +5,18 @@ import { azureResource } from 'azurecore'; import { logAnalyticsQuery } from '../queryStringConstants'; -import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase'; +import { ResourceServiceBase } from '../resourceTreeDataProviderBase'; +import { AzureMonitorGraphData } from '../../interfaces'; +import { AZURE_MONITOR_PROVIDER_ID } from '../../../constants'; -export interface AzureMonitorGraphData extends GraphData { - properties: { - fullyQualifiedDomainName: string; - administratorLogin: string; - uri: string; - customerId: string - }; -} +export class AzureMonitorResourceService extends ResourceServiceBase { + public override queryFilter: string = logAnalyticsQuery; -export class AzureMonitorResourceService extends ResourceServiceBase { - - protected get query(): string { - return logAnalyticsQuery; - } - - protected convertResource(resource: AzureMonitorGraphData): azureResource.AzureResourceDatabaseServer { + public convertServerResource(resource: AzureMonitorGraphData): azureResource.AzureResourceDatabaseServer { return { id: resource.id, name: resource.name, + provider: AZURE_MONITOR_PROVIDER_ID, fullName: resource.properties.customerId, loginName: '', defaultDatabaseName: '', diff --git a/extensions/azurecore/src/azureResource/providers/azuremonitor/azuremonitorTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/azuremonitor/azuremonitorTreeDataProvider.ts index 39f91362d1..357a50679b 100644 --- a/extensions/azurecore/src/azureResource/providers/azuremonitor/azuremonitorTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/azuremonitor/azuremonitorTreeDataProvider.ts @@ -8,27 +8,26 @@ import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { AzureResourceItemType } from '../../constants'; +import { AzureResourceItemType, AzureResourcePrefixes, logAnalyticsProvider } from '../../constants'; import { generateGuid } from '../../utils'; -import { IAzureResourceService } from '../../interfaces'; +import { AzureMonitorGraphData, GraphData } from '../../interfaces'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { AzureAccount, azureResource } from 'azurecore'; -export class AzureMonitorTreeDataProvider extends ResourceTreeDataProviderBase { +export class AzureMonitorTreeDataProvider extends ResourceTreeDataProviderBase { private static readonly containerId = 'azure.resource.providers.AzureMonitorContainer'; private static readonly containerLabel = localize('azure.resource.providers.AzureMonitorContainerLabel', "Log Analytics workspace"); public constructor( - databaseServerService: IAzureResourceService, + databaseServerService: azureResource.IAzureResourceService, private _extensionContext: ExtensionContext ) { super(databaseServerService); } - - protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { + public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { return { - id: `LogAnalytics_${databaseServer.id ? databaseServer.id : databaseServer.name}`, + id: `${AzureResourcePrefixes.logAnalytics}${account.key.accountId}${databaseServer.id ? databaseServer.id : databaseServer.name}`, label: this.browseConnectionMode ? `${databaseServer.name} (${AzureMonitorTreeDataProvider.containerLabel}, ${databaseServer.subscription.name})` : databaseServer.name, iconPath: { dark: this._extensionContext.asAbsolutePath('resources/dark/azure_monitor_dark.svg'), @@ -47,7 +46,7 @@ export class AzureMonitorTreeDataProvider extends ResourceTreeDataProviderBase, - private _extensionContext: ExtensionContext - ) { - } - - public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { - return new CosmosDbMongoTreeDataProvider(this._databaseServerService, this._extensionContext); - } - - public get providerId(): string { - return 'azure.resource.providers.cosmosDbMongo'; - } -} diff --git a/extensions/azurecore/src/azureResource/providers/cosmosdb/mongo/cosmosDbMongoService.ts b/extensions/azurecore/src/azureResource/providers/cosmosdb/mongo/cosmosDbMongoService.ts index 7cd1d1587a..658594ae73 100644 --- a/extensions/azurecore/src/azureResource/providers/cosmosdb/mongo/cosmosDbMongoService.ts +++ b/extensions/azurecore/src/azureResource/providers/cosmosdb/mongo/cosmosDbMongoService.ts @@ -4,28 +4,20 @@ *--------------------------------------------------------------------------------------------*/ -import { ResourceServiceBase, GraphData } from '../../resourceTreeDataProviderBase'; +import { ResourceServiceBase } from '../../resourceTreeDataProviderBase'; import { azureResource } from 'azurecore'; import { cosmosMongoDbQuery } from '../../queryStringConstants'; +import { DbServerGraphData } from '../../../interfaces'; +import { COSMOSDB_MONGO_PROVIDER_ID } from '../../../../constants'; +export class CosmosDbMongoService extends ResourceServiceBase { + public override queryFilter: string = cosmosMongoDbQuery; -interface DbServerGraphData extends GraphData { - properties: { - fullyQualifiedDomainName: string; - administratorLogin: string; - }; -} - -export class CosmosDbMongoService extends ResourceServiceBase { - - protected get query(): string { - return cosmosMongoDbQuery; - } - - protected convertResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer { + public convertServerResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer | undefined { return { id: resource.id, name: resource.name, + provider: COSMOSDB_MONGO_PROVIDER_ID, fullName: resource.properties.fullyQualifiedDomainName, loginName: resource.properties.administratorLogin, defaultDatabaseName: '', diff --git a/extensions/azurecore/src/azureResource/providers/cosmosdb/mongo/cosmosDbMongoTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/cosmosdb/mongo/cosmosDbMongoTreeDataProvider.ts index 74536511ef..1847cc6b8d 100644 --- a/extensions/azurecore/src/azureResource/providers/cosmosdb/mongo/cosmosDbMongoTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/cosmosdb/mongo/cosmosDbMongoTreeDataProvider.ts @@ -7,28 +7,27 @@ import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { AzureResourceItemType } from '../../../constants'; +import { AzureResourceItemType, AzureResourcePrefixes, cosmosDBProvider } from '../../../constants'; import { generateGuid } from '../../../utils'; -import { IAzureResourceService } from '../../../interfaces'; +import { DbServerGraphData, GraphData } from '../../../interfaces'; import { ResourceTreeDataProviderBase } from '../../resourceTreeDataProviderBase'; import { AzureAccountProperties, azureResource } from 'azurecore'; import * as azdata from 'azdata'; -export class CosmosDbMongoTreeDataProvider extends ResourceTreeDataProviderBase { - private static readonly COSMOSDG_MONGO_PROVIDER_ID = 'COSMOSDB_MONGO'; +export class CosmosDbMongoTreeDataProvider extends ResourceTreeDataProviderBase { private static readonly CONTAINER_ID = 'azure.resource.providers.databaseServer.treeDataProvider.cosmosDbMongoContainer'; private static readonly CONTAINER_LABEL = localize('azure.resource.providers.databaseServer.treeDataProvider.cosmosDbMongoContainerLabel', "CosmosDB for Mongo"); public constructor( - databaseServerService: IAzureResourceService, + databaseServerService: azureResource.IAzureResourceService, private _extensionContext: ExtensionContext ) { super(databaseServerService); } - protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: azdata.Account): azdata.TreeItem { + public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: azdata.Account): azdata.TreeItem { return { - id: `Cosmosdb_${databaseServer.id ? databaseServer.id : databaseServer.name}`, + id: `${AzureResourcePrefixes.cosmosdb}${account.key.accountId}${databaseServer.id ?? databaseServer.name}`, label: `${databaseServer.name} (CosmosDB Mongo API)`, iconPath: { dark: this._extensionContext.asAbsolutePath('resources/dark/cosmosdb_inverse.svg'), @@ -46,7 +45,7 @@ export class CosmosDbMongoTreeDataProvider extends ResourceTreeDataProviderBase< savePassword: true, groupFullName: '', groupId: '', - providerName: CosmosDbMongoTreeDataProvider.COSMOSDG_MONGO_PROVIDER_ID, + providerName: cosmosDBProvider, saveProfile: false, options: {}, azureAccount: account.key.accountId, @@ -54,7 +53,7 @@ export class CosmosDbMongoTreeDataProvider extends ResourceTreeDataProviderBase< azureResourceId: databaseServer.id, azurePortalEndpoint: (account.properties as AzureAccountProperties).providerSettings.settings.portalEndpoint }, - childProvider: CosmosDbMongoTreeDataProvider.COSMOSDG_MONGO_PROVIDER_ID, + childProvider: cosmosDBProvider, type: azdata.ExtensionNodeType.Server }; } diff --git a/extensions/azurecore/src/azureResource/providers/database/databaseProvider.ts b/extensions/azurecore/src/azureResource/providers/database/databaseProvider.ts deleted file mode 100644 index aead7045cb..0000000000 --- a/extensions/azurecore/src/azureResource/providers/database/databaseProvider.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ExtensionContext } from 'vscode'; - -import { azureResource } from 'azurecore'; -import { AzureResourceDatabaseTreeDataProvider } from './databaseTreeDataProvider'; -import { IAzureResourceService } from '../../interfaces'; - -export class AzureResourceDatabaseProvider implements azureResource.IAzureResourceProvider { - public constructor( - private _databaseService: IAzureResourceService, - private _extensionContext: ExtensionContext - ) { - } - - public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { - return new AzureResourceDatabaseTreeDataProvider(this._databaseService, this._extensionContext); - } - - public get providerId(): string { - return 'azure.resource.providers.database'; - } -} diff --git a/extensions/azurecore/src/azureResource/providers/database/databaseService.ts b/extensions/azurecore/src/azureResource/providers/database/databaseService.ts index 173ca59439..366d6020cb 100644 --- a/extensions/azurecore/src/azureResource/providers/database/databaseService.ts +++ b/extensions/azurecore/src/azureResource/providers/database/databaseService.ts @@ -4,24 +4,24 @@ *--------------------------------------------------------------------------------------------*/ import { ServiceClientCredentials } from '@azure/ms-rest-js'; -import { IAzureResourceService } from '../../interfaces'; -import { DbServerGraphData } from '../databaseServer/databaseServerService'; -import { sqlServerQuery, sqlDatabaseQuery } from '../queryStringConstants'; +import { DatabaseGraphData, DbServerGraphData, GraphData, IAzureResourceDbService } from '../../interfaces'; +import { sqlServerQuery, sqlDatabaseQuery, where } from '../queryStringConstants'; import { ResourceGraphClient } from '@azure/arm-resourcegraph'; -import { queryGraphResources, GraphData } from '../resourceTreeDataProviderBase'; +import { queryGraphResources } from '../resourceTreeDataProviderBase'; import { AzureAccount, azureResource } from 'azurecore'; +import { DATABASE_PROVIDER_ID } from '../../../constants'; + +export class AzureResourceDatabaseService implements IAzureResourceDbService { + + public queryFilter: string = sqlDatabaseQuery; -interface DatabaseGraphData extends GraphData { - kind: string; -} -export class AzureResourceDatabaseService implements IAzureResourceService { public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise { const databases: azureResource.AzureResourceDatabase[] = []; const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint }); // Query servers and databases in parallel (start all promises before waiting on the 1st) - let serverQueryPromise = queryGraphResources(resourceClient, subscriptions, sqlServerQuery); - let dbQueryPromise = queryGraphResources(resourceClient, subscriptions, sqlDatabaseQuery); + let serverQueryPromise = queryGraphResources(resourceClient, subscriptions, where + sqlServerQuery); + let dbQueryPromise = queryGraphResources(resourceClient, subscriptions, where + this.queryFilter); let servers: DbServerGraphData[] = await serverQueryPromise as DbServerGraphData[]; let dbByGraph: DatabaseGraphData[] = await dbQueryPromise as DatabaseGraphData[]; @@ -39,7 +39,7 @@ export class AzureResourceDatabaseService implements IAzureResourceService { - // Filter master DBs, and for all others find their server to get login info + // Filter master DBs, and for all others find their server to get login information let serversForRg = rgMap.get(db.resourceGroup); if (serversForRg && !db.kind.endsWith('system') && (svrIdRegExp.test(db.id) || synapseDBRegExp.test(db.id))) { const founds = svrIdRegExp.exec(db.id) ?? synapseDBRegExp.exec(db.id); @@ -50,23 +50,32 @@ export class AzureResourceDatabaseService implements IAzureResourceService s.name === serverName); if (server) { - databases.push({ - name: db.name, - id: db.id, - serverName: server.name, - serverFullName: server.properties.fullyQualifiedDomainName, - loginName: server.properties.administratorLogin, - subscription: { - id: db.subscriptionId, - name: (subscriptions.find(sub => sub.id === db.subscriptionId))?.name || '' - }, - tenant: db.tenantId, - resourceGroup: db.resourceGroup - }); + databases.push(this.convertDatabaseResource(db, server)!); } } }); return databases; } + + public convertDatabaseResource(resource: DatabaseGraphData, server?: DbServerGraphData | undefined): azureResource.AzureResourceDatabase | undefined { + if (server) { + return { + name: resource.name, + id: resource.id, + provider: DATABASE_PROVIDER_ID, + serverName: server.name, + serverFullName: server.properties.fullyQualifiedDomainName, + loginName: server.properties.administratorLogin, + subscription: { + id: resource.subscriptionId, + name: resource.subscriptionName! + }, + tenant: resource.tenantId, + resourceGroup: resource.resourceGroup + }; + } else { + return undefined; + } + } } diff --git a/extensions/azurecore/src/azureResource/providers/database/databaseTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/database/databaseTreeDataProvider.ts index d8b5bf04a8..36d2e6ad4d 100644 --- a/extensions/azurecore/src/azureResource/providers/database/databaseTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/database/databaseTreeDataProvider.ts @@ -8,27 +8,27 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { AzureResourceItemType, mssqlProvider } from '../../../azureResource/constants'; +import { AzureResourceItemType, AzureResourcePrefixes, mssqlProvider } from '../../../azureResource/constants'; import { generateGuid } from '../../utils'; -import { IAzureResourceService } from '../../interfaces'; +import { DatabaseGraphData, DbServerGraphData } from '../../interfaces'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { AzureAccount, azureResource } from 'azurecore'; -export class AzureResourceDatabaseTreeDataProvider extends ResourceTreeDataProviderBase { +export class AzureResourceDatabaseTreeDataProvider extends ResourceTreeDataProviderBase { private static readonly containerId = 'azure.resource.providers.database.treeDataProvider.databaseContainer'; private static readonly containerLabel = localize('azure.resource.providers.database.treeDataProvider.databaseContainerLabel', "SQL database"); public constructor( - databaseService: IAzureResourceService, + databaseService: azureResource.IAzureResourceService, private _extensionContext: vscode.ExtensionContext ) { super(databaseService); } - protected getTreeItemForResource(database: azureResource.AzureResourceDatabase, account: AzureAccount): TreeItem { + public getTreeItemForResource(database: azureResource.AzureResourceDatabase, account: AzureAccount): TreeItem { return { - id: `databaseServer_${database.serverFullName}.database_${database.name}`, + id: `${AzureResourcePrefixes.databaseServer}${account.key.accountId}${database.serverFullName}.${AzureResourcePrefixes.database}${database.id ?? database.name}`, label: this.browseConnectionMode ? `${database.serverName}/${database.name} (${AzureResourceDatabaseTreeDataProvider.containerLabel}, ${database.subscription.name})` : `${database.name} (${database.serverName})`, iconPath: { dark: this._extensionContext.asAbsolutePath('resources/dark/sql_database_inverse.svg'), diff --git a/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerProvider.ts b/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerProvider.ts deleted file mode 100644 index 77ec4e8afe..0000000000 --- a/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerProvider.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ExtensionContext } from 'vscode'; - -import { azureResource } from 'azurecore'; -import { IAzureResourceService } from '../../interfaces'; -import { AzureResourceDatabaseServerTreeDataProvider } from './databaseServerTreeDataProvider'; - -export class AzureResourceDatabaseServerProvider implements azureResource.IAzureResourceProvider { - public constructor( - private _databaseServerService: IAzureResourceService, - private _extensionContext: ExtensionContext - ) { - } - - public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { - return new AzureResourceDatabaseServerTreeDataProvider(this._databaseServerService, this._extensionContext); - } - - public get providerId(): string { - return 'azure.resource.providers.databaseServer'; - } -} diff --git a/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerService.ts b/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerService.ts index c76248cc5e..ea7f126b62 100644 --- a/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerService.ts +++ b/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerService.ts @@ -5,42 +5,39 @@ import { ServiceClientCredentials } from '@azure/ms-rest-js'; import { ResourceGraphClient } from '@azure/arm-resourcegraph'; -import { GraphData, queryGraphResources } from '../resourceTreeDataProviderBase'; -import { sqlServerQuery } from '../queryStringConstants'; +import { queryGraphResources } from '../resourceTreeDataProviderBase'; +import { sqlServerQuery, where } from '../queryStringConstants'; import { azureResource, AzureAccount } from 'azurecore'; -import { IAzureResourceService } from '../../interfaces'; +import { DbServerGraphData, IAzureResourceServerService } from '../../interfaces'; +import { DATABASE_SERVER_PROVIDER_ID } from '../../../constants'; -export interface DbServerGraphData extends GraphData { - properties: { - fullyQualifiedDomainName: string; - administratorLogin: string; - }; -} +export class AzureResourceDatabaseServerService implements IAzureResourceServerService { -export class AzureResourceDatabaseServerService implements IAzureResourceService { + public queryFilter: string = sqlServerQuery; public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise { const convertedResources: azureResource.AzureResourceDatabaseServer[] = []; const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint }); - let serverGraphResources: DbServerGraphData[] = await queryGraphResources(resourceClient, subscriptions, sqlServerQuery); + let serverGraphResources: DbServerGraphData[] = await queryGraphResources(resourceClient, subscriptions, where + this.queryFilter); const ids = new Set(); serverGraphResources.forEach((res) => { if (!ids.has(res.id)) { ids.add(res.id); res.subscriptionName = subscriptions.find(sub => sub.id === res.subscriptionId)?.name; - const converted = this.convertResource(res); - convertedResources.push(converted); + const converted = this.convertServerResource(res); + convertedResources.push(converted!); } }); return convertedResources; } - protected convertResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer { + public convertServerResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer | undefined { return { id: resource.id, name: resource.name, + provider: DATABASE_SERVER_PROVIDER_ID, // Determine if resource object is for Synapse Workspace or not and get the needed property from the correct place. fullName: resource.properties.fullyQualifiedDomainName, loginName: resource.properties.administratorLogin, diff --git a/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerTreeDataProvider.ts index db7f49105a..9247b716aa 100644 --- a/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerTreeDataProvider.ts @@ -8,26 +8,26 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { AzureResourceItemType, mssqlProvider } from '../../../azureResource/constants'; +import { AzureResourceItemType, AzureResourcePrefixes, mssqlProvider } from '../../../azureResource/constants'; import { generateGuid } from '../../utils'; -import { IAzureResourceService } from '../../interfaces'; +import { DbServerGraphData, GraphData } from '../../interfaces'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { AzureAccount, azureResource } from 'azurecore'; -export class AzureResourceDatabaseServerTreeDataProvider extends ResourceTreeDataProviderBase { +export class AzureResourceDatabaseServerTreeDataProvider extends ResourceTreeDataProviderBase { private static readonly containerId = 'azure.resource.providers.databaseServer.treeDataProvider.databaseServerContainer'; private static readonly containerLabel = localize('azure.resource.providers.databaseServer.treeDataProvider.databaseServerContainerLabel', "SQL server"); public constructor( - databaseServerService: IAzureResourceService, + databaseServerService: azureResource.IAzureResourceService, private _extensionContext: vscode.ExtensionContext ) { super(databaseServerService); } - protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { + public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { return { - id: `databaseServer_${databaseServer.id ? databaseServer.id : databaseServer.name}`, + id: `${AzureResourcePrefixes.databaseServer}${account.key.accountId}${databaseServer.id ?? databaseServer.name}`, label: this.browseConnectionMode ? `${databaseServer.name} (${AzureResourceDatabaseServerTreeDataProvider.containerLabel}, ${databaseServer.subscription.name})` : databaseServer.name, iconPath: { dark: this._extensionContext.asAbsolutePath('resources/dark/sql_server_inverse.svg'), diff --git a/extensions/azurecore/src/azureResource/providers/kusto/kustoService.ts b/extensions/azurecore/src/azureResource/providers/kusto/kustoService.ts index ad49e1077e..2ef0ebaaa4 100644 --- a/extensions/azurecore/src/azureResource/providers/kusto/kustoService.ts +++ b/extensions/azurecore/src/azureResource/providers/kusto/kustoService.ts @@ -5,26 +5,18 @@ import { azureResource } from 'azurecore'; import { kustoClusterQuery } from '../queryStringConstants'; -import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase'; +import { ResourceServiceBase } from '../resourceTreeDataProviderBase'; +import { KustoGraphData } from '../../interfaces'; +import { KUSTO_PROVIDER_ID } from '../../../constants'; -export interface KustoGraphData extends GraphData { - properties: { - fullyQualifiedDomainName: string; - administratorLogin: string; - uri: string; - }; -} +export class KustoResourceService extends ResourceServiceBase { + public override queryFilter: string = kustoClusterQuery; -export class KustoResourceService extends ResourceServiceBase { - - protected get query(): string { - return kustoClusterQuery; - } - - protected convertResource(resource: KustoGraphData): azureResource.AzureResourceDatabaseServer { + public convertServerResource(resource: KustoGraphData): azureResource.AzureResourceDatabaseServer | undefined { return { id: resource.id, name: resource.name, + provider: KUSTO_PROVIDER_ID, fullName: resource.properties.uri.replace('https://', ''), loginName: '', defaultDatabaseName: '', diff --git a/extensions/azurecore/src/azureResource/providers/kusto/kustoTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/kusto/kustoTreeDataProvider.ts index b74d609fc0..3dacda4036 100644 --- a/extensions/azurecore/src/azureResource/providers/kusto/kustoTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/kusto/kustoTreeDataProvider.ts @@ -8,27 +8,26 @@ import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { AzureResourceItemType } from '../../constants'; +import { AzureResourceItemType, AzureResourcePrefixes, kustoProvider } from '../../constants'; import { generateGuid } from '../../utils'; -import { IAzureResourceService } from '../../interfaces'; +import { GraphData, KustoGraphData } from '../../interfaces'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { AzureAccount, azureResource } from 'azurecore'; -export class KustoTreeDataProvider extends ResourceTreeDataProviderBase { +export class KustoTreeDataProvider extends ResourceTreeDataProviderBase { private static readonly containerId = 'azure.resource.providers.KustoContainer'; private static readonly containerLabel = localize('azure.resource.providers.KustoContainerLabel', "Azure Data Explorer Cluster"); public constructor( - databaseServerService: IAzureResourceService, + databaseServerService: azureResource.IAzureResourceService, private _extensionContext: ExtensionContext ) { super(databaseServerService); } - - protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { + public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { return { - id: `Kusto_${databaseServer.id ? databaseServer.id : databaseServer.name}`, + id: `${AzureResourcePrefixes.kusto}${account.key.accountId}${databaseServer.id ?? databaseServer.name}`, label: this.browseConnectionMode ? `${databaseServer.name} (${KustoTreeDataProvider.containerLabel}, ${databaseServer.subscription.name})` : databaseServer.name, iconPath: { dark: this._extensionContext.asAbsolutePath('resources/dark/azureDE_inverse.svg'), @@ -47,7 +46,7 @@ export class KustoTreeDataProvider extends ResourceTreeDataProviderBase, - private _extensionContext: ExtensionContext - ) { - } - - public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { - return new MysqlFlexibleServerTreeDataProvider(this._databaseServerService, this._extensionContext); - } - - public get providerId(): string { - return 'azure.resource.providers.mysqlFlexibleServer'; - } -} diff --git a/extensions/azurecore/src/azureResource/providers/mysqlFlexibleServer/mysqlFlexibleServerService.ts b/extensions/azurecore/src/azureResource/providers/mysqlFlexibleServer/mysqlFlexibleServerService.ts index 3078432015..0af728a528 100644 --- a/extensions/azurecore/src/azureResource/providers/mysqlFlexibleServer/mysqlFlexibleServerService.ts +++ b/extensions/azurecore/src/azureResource/providers/mysqlFlexibleServer/mysqlFlexibleServerService.ts @@ -4,28 +4,20 @@ *--------------------------------------------------------------------------------------------*/ -import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase'; +import { ResourceServiceBase } from '../resourceTreeDataProviderBase'; import { azureResource } from 'azurecore'; import { mysqlFlexibleServerQuery } from '../queryStringConstants'; +import { DbServerGraphData } from '../../interfaces'; +import { MYSQL_FLEXIBLE_SERVER_PROVIDER_ID } from '../../../constants'; +export class MysqlFlexibleServerService extends ResourceServiceBase { + public override queryFilter: string = mysqlFlexibleServerQuery; -interface DbServerGraphData extends GraphData { - properties: { - fullyQualifiedDomainName: string; - administratorLogin: string; - }; -} - -export class MysqlFlexibleServerService extends ResourceServiceBase { - - protected get query(): string { - return mysqlFlexibleServerQuery; - } - - protected convertResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer { + public convertServerResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer | undefined { return { id: resource.id, name: resource.name, + provider: MYSQL_FLEXIBLE_SERVER_PROVIDER_ID, fullName: resource.properties.fullyQualifiedDomainName, loginName: resource.properties.administratorLogin, defaultDatabaseName: '', diff --git a/extensions/azurecore/src/azureResource/providers/mysqlFlexibleServer/mysqlFlexibleServerTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/mysqlFlexibleServer/mysqlFlexibleServerTreeDataProvider.ts index ec02feae15..f37d6793fa 100644 --- a/extensions/azurecore/src/azureResource/providers/mysqlFlexibleServer/mysqlFlexibleServerTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/mysqlFlexibleServer/mysqlFlexibleServerTreeDataProvider.ts @@ -7,28 +7,27 @@ import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { AzureResourceItemType } from '../../constants'; +import { AzureResourceItemType, AzureResourcePrefixes, mySqlProvider } from '../../constants'; import { generateGuid } from '../../utils'; -import { IAzureResourceService } from '../../interfaces'; +import { DbServerGraphData, GraphData } from '../../interfaces'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { AzureAccountProperties, azureResource } from 'azurecore'; import { Account, ExtensionNodeType, TreeItem, connection } from 'azdata'; -export class MysqlFlexibleServerTreeDataProvider extends ResourceTreeDataProviderBase { - private static readonly MYSQL_FLEXIBLE_SERVER_PROVIDER_ID = 'MySQL'; +export class MysqlFlexibleServerTreeDataProvider extends ResourceTreeDataProviderBase { private static readonly CONTAINER_ID = 'azure.resource.providers.databaseServer.treeDataProvider.mysqlFlexibleServerContainer'; private static readonly CONTAINER_LABEL = localize('azure.resource.providers.databaseServer.treeDataProvider.mysqlFlexibleServerContainerLabel', "Azure Database for MySQL Flexible server"); public constructor( - databaseServerService: IAzureResourceService, + databaseServerService: azureResource.IAzureResourceService, private _extensionContext: ExtensionContext ) { super(databaseServerService); } - protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: Account): TreeItem { + public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: Account): TreeItem { return { - id: `databaseServer_${databaseServer.id ? databaseServer.id : databaseServer.name}`, + id: `${AzureResourcePrefixes.mySqlFlexibleServer}${account.key.accountId}${databaseServer.id ?? databaseServer.name}`, label: this.browseConnectionMode ? `${databaseServer.name} (${MysqlFlexibleServerTreeDataProvider.CONTAINER_LABEL}, ${databaseServer.subscription.name})` : databaseServer.name, iconPath: { dark: this._extensionContext.asAbsolutePath('resources/dark/mysql_server_inverse.svg'), @@ -47,7 +46,7 @@ export class MysqlFlexibleServerTreeDataProvider extends ResourceTreeDataProvide savePassword: true, groupFullName: '', groupId: '', - providerName: MysqlFlexibleServerTreeDataProvider.MYSQL_FLEXIBLE_SERVER_PROVIDER_ID, + providerName: mySqlProvider, saveProfile: false, options: { }, @@ -56,7 +55,7 @@ export class MysqlFlexibleServerTreeDataProvider extends ResourceTreeDataProvide azureResourceId: databaseServer.id, azurePortalEndpoint: (account.properties as AzureAccountProperties).providerSettings.settings.portalEndpoint }, - childProvider: MysqlFlexibleServerTreeDataProvider.MYSQL_FLEXIBLE_SERVER_PROVIDER_ID, + childProvider: mySqlProvider, type: ExtensionNodeType.Server }; } diff --git a/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerService.ts b/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresArcServerService.ts similarity index 64% rename from extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerService.ts rename to extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresArcServerService.ts index 5ae36dc8f8..912763078c 100644 --- a/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerService.ts +++ b/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresArcServerService.ts @@ -3,26 +3,21 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase'; +import { ResourceServiceBase } from '../resourceTreeDataProviderBase'; import { azureResource } from 'azurecore'; import { postgresArcServerQuery } from '../queryStringConstants'; +import { PostgresArcServerGraphData } from '../../interfaces'; +import { POSTGRES_ARC_SERVER_PROVIDER_ID } from '../../../constants'; -export interface PostgresArcServerGraphData extends GraphData { - properties: { - admin: string; - }; -} +export class PostgresServerArcService extends ResourceServiceBase { -export class PostgresServerArcService extends ResourceServiceBase { + public override queryFilter: string = postgresArcServerQuery; - protected get query(): string { - return postgresArcServerQuery; - } - - protected convertResource(resource: PostgresArcServerGraphData): azureResource.AzureResourceDatabaseServer { + public convertServerResource(resource: PostgresArcServerGraphData): azureResource.AzureResourceDatabaseServer | undefined { return { id: resource.id, name: resource.name, + provider: POSTGRES_ARC_SERVER_PROVIDER_ID, fullName: resource.name, loginName: resource.properties.admin, defaultDatabaseName: 'postgres', diff --git a/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresArcServerTreeDataProvider.ts similarity index 83% rename from extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerTreeDataProvider.ts rename to extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresArcServerTreeDataProvider.ts index 81adcb9701..c8911997c2 100644 --- a/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresArcServerTreeDataProvider.ts @@ -8,28 +8,27 @@ import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { AzureResourceItemType } from '../../constants'; +import { AzureResourceItemType, AzureResourcePrefixes, pgsqlProvider } from '../../constants'; import { generateGuid } from '../../utils'; -import { IAzureResourceService } from '../../interfaces'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { AzureAccount, azureResource } from 'azurecore'; +import { GraphData, PostgresArcServerGraphData } from '../../interfaces'; -export class PostgresServerArcTreeDataProvider extends ResourceTreeDataProviderBase { +export class PostgresServerArcTreeDataProvider extends ResourceTreeDataProviderBase { private static readonly containerId = 'azure.resource.providers.postgresArcServer.treeDataProvider.postgresServerContainer'; // allow-any-unicode-next-line private static readonly containerLabel = localize('azure.resource.providers.postgresArcServer.treeDataProvider.postgresServerContainerLabel', "PostgreSQL Hyperscale – Azure Arc"); public constructor( - databaseServerService: IAzureResourceService, + databaseServerService: azureResource.IAzureResourceService, private _extensionContext: ExtensionContext ) { super(databaseServerService); } - - protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { + public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { return { - id: `databaseServer_${databaseServer.id ? databaseServer.id : databaseServer.name}`, + id: `${AzureResourcePrefixes.postgresServerArc}${account.key.accountId}${databaseServer.id ?? databaseServer.name}`, label: this.browseConnectionMode ? `${databaseServer.name} (${PostgresServerArcTreeDataProvider.containerLabel}, ${databaseServer.subscription.name})` : databaseServer.name, iconPath: { dark: this._extensionContext.asAbsolutePath('resources/dark/sql_server_inverse.svg'), @@ -48,7 +47,7 @@ export class PostgresServerArcTreeDataProvider extends ResourceTreeDataProviderB savePassword: true, groupFullName: '', groupId: '', - providerName: 'PGSQL', + providerName: pgsqlProvider, saveProfile: false, options: { // Set default for SSL or will get error complaining about it not being set correctly @@ -59,7 +58,7 @@ export class PostgresServerArcTreeDataProvider extends ResourceTreeDataProviderB azureResourceId: databaseServer.id, azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint }, - childProvider: 'PGSQL', + childProvider: pgsqlProvider, type: ExtensionNodeType.Server }; } diff --git a/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerProvider.ts b/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerProvider.ts deleted file mode 100644 index b25d73eaf6..0000000000 --- a/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerProvider.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ExtensionContext } from 'vscode'; - -import { azureResource } from 'azurecore'; -import { IAzureResourceService } from '../../interfaces'; -import { PostgresServerArcTreeDataProvider as PostgresServerArcTreeDataProvider } from './postgresServerTreeDataProvider'; - -export class PostgresServerArcProvider implements azureResource.IAzureResourceProvider { - public constructor( - private _databaseServerService: IAzureResourceService, - private _extensionContext: ExtensionContext - ) { - } - - public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { - return new PostgresServerArcTreeDataProvider(this._databaseServerService, this._extensionContext); - } - - public get providerId(): string { - return 'azure.resource.providers.postgresArcServer'; - } -} diff --git a/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerProvider.ts b/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerProvider.ts deleted file mode 100644 index ea5363d1a1..0000000000 --- a/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerProvider.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ExtensionContext } from 'vscode'; - -import { azureResource } from 'azurecore'; -import { IAzureResourceService } from '../../interfaces'; -import { PostgresServerTreeDataProvider as PostgresServerTreeDataProvider } from './postgresServerTreeDataProvider'; - -export class PostgresServerProvider implements azureResource.IAzureResourceProvider { - public constructor( - private _databaseServerService: IAzureResourceService, - private _extensionContext: ExtensionContext - ) { - } - - public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { - return new PostgresServerTreeDataProvider(this._databaseServerService, this._extensionContext); - } - - public get providerId(): string { - return 'azure.resource.providers.postgresServer'; - } -} diff --git a/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerService.ts b/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerService.ts index 31537c3ee1..11a61c7951 100644 --- a/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerService.ts +++ b/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerService.ts @@ -4,27 +4,21 @@ *--------------------------------------------------------------------------------------------*/ -import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase'; +import { ResourceServiceBase } from '../resourceTreeDataProviderBase'; import { azureResource } from 'azurecore'; import { postgresServerQuery } from '../queryStringConstants'; +import { DbServerGraphData } from '../../interfaces'; +import { POSTGRES_SERVER_PROVIDER_ID } from '../../../constants'; -interface DbServerGraphData extends GraphData { - properties: { - fullyQualifiedDomainName: string; - administratorLogin: string; - }; -} +export class PostgresServerService extends ResourceServiceBase { -export class PostgresServerService extends ResourceServiceBase { + public override queryFilter: string = postgresServerQuery; - protected get query(): string { - return postgresServerQuery; - } - - protected convertResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer { + public override convertServerResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer | undefined { return { id: resource.id, name: resource.name, + provider: POSTGRES_SERVER_PROVIDER_ID, fullName: resource.properties.fullyQualifiedDomainName, loginName: resource.properties.administratorLogin, defaultDatabaseName: 'postgres', diff --git a/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerTreeDataProvider.ts index ffa660f70c..5ec37b1653 100644 --- a/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerTreeDataProvider.ts @@ -8,27 +8,26 @@ import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { AzureResourceItemType } from '../../constants'; +import { AzureResourceItemType, AzureResourcePrefixes, pgsqlProvider } from '../../constants'; import { generateGuid } from '../../utils'; -import { IAzureResourceService } from '../../interfaces'; +import { GraphData, DbServerGraphData } from '../../interfaces'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { AzureAccount, azureResource } from 'azurecore'; -export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase { +export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase { 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 server"); public constructor( - databaseServerService: IAzureResourceService, + databaseServerService: azureResource.IAzureResourceService, private _extensionContext: ExtensionContext ) { super(databaseServerService); } - - protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { + public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { return { - id: `databaseServer_${databaseServer.id ? databaseServer.id : databaseServer.name}`, + id: `${AzureResourcePrefixes.postgresServer}${account.key.accountId}${databaseServer.id ?? databaseServer.name}`, label: this.browseConnectionMode ? `${databaseServer.name} (${PostgresServerTreeDataProvider.containerLabel}, ${databaseServer.subscription.name})` : databaseServer.name, iconPath: { dark: this._extensionContext.asAbsolutePath('resources/dark/sql_server_inverse.svg'), @@ -47,7 +46,7 @@ export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase savePassword: true, groupFullName: '', groupId: '', - providerName: 'PGSQL', + providerName: pgsqlProvider, saveProfile: false, options: { // Set default for SSL or will get error complaining about it not being set correctly @@ -58,7 +57,7 @@ export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase azureResourceId: databaseServer.id, azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint }, - childProvider: 'PGSQL', + childProvider: pgsqlProvider, type: ExtensionNodeType.Server }; } diff --git a/extensions/azurecore/src/azureResource/providers/queryStringConstants.ts b/extensions/azurecore/src/azureResource/providers/queryStringConstants.ts index ccfb083d29..7f4790e014 100644 --- a/extensions/azurecore/src/azureResource/providers/queryStringConstants.ts +++ b/extensions/azurecore/src/azureResource/providers/queryStringConstants.ts @@ -4,37 +4,39 @@ *--------------------------------------------------------------------------------------------*/ import { azureResource } from 'azurecore'; +import * as Constants from '../constants'; +export const where = `where `; /** * Lists all SQL Databases and Synapse SQL Databases */ -export const sqlDatabaseQuery = `where type == "${azureResource.AzureResourceType.sqlDatabase}" or type == "${azureResource.AzureResourceType.sqlSynapseSqlDatabase}"`; +export const sqlDatabaseQuery = `type == "${azureResource.AzureResourceType.sqlDatabase}" or type == "${azureResource.AzureResourceType.sqlSynapseSqlDatabase}"`; /** * Lists all Synapse Workspaces with information such as SQL connection endpoints. */ -export const synapseWorkspacesQuery = `where type == "${azureResource.AzureResourceType.sqlSynapseWorkspace}"`; +export const synapseWorkspacesQuery = `type == "${azureResource.AzureResourceType.sqlSynapseWorkspace}"`; /** * Lists all Synapse Dedicated SQL Pools */ -export const synapseSqlPoolsQuery = `where type == "${azureResource.AzureResourceType.sqlSynapseSqlPool}"`; +export const synapseSqlPoolsQuery = `type == "${azureResource.AzureResourceType.sqlSynapseSqlPool}"`; /** * Lists all Sql Servers excluding Synapse Pool Servers * (they have different properties and need to be handled separately) */ -export const sqlServerQuery = `where type == "${azureResource.AzureResourceType.sqlServer}" and kind != "v12.0,analytics"`; +export const sqlServerQuery = `type == "${azureResource.AzureResourceType.sqlServer}" and kind != "${Constants.analyticsKind}"`; /** * Lists all Azure Arc SQL Managed Instances */ -export const sqlInstanceArcQuery = `where type == "${azureResource.AzureResourceType.azureArcSqlManagedInstance}"`; +export const sqlInstanceArcQuery = `type == "${azureResource.AzureResourceType.azureArcSqlManagedInstance}"`; /** * Lists all Azure SQL Managed Instances */ -export const sqlInstanceQuery = `where type == "${azureResource.AzureResourceType.sqlManagedInstance}"`; +export const sqlInstanceQuery = `type == "${azureResource.AzureResourceType.sqlManagedInstance}"`; /** * Lists all resource containers and resource groups @@ -44,29 +46,29 @@ export const resourceGroupQuery = `ResourceContainers | where type=="${azureReso /** * Lists all postgreSQL servers */ -export const postgresServerQuery = `where type == "${azureResource.AzureResourceType.postgresServer}"`; +export const postgresServerQuery = `type == "${azureResource.AzureResourceType.postgresServer}"`; /** * Lists all Azure Arc PostgreSQL servers */ -export const postgresArcServerQuery = `where type == "${azureResource.AzureResourceType.azureArcPostgresServer}"`; +export const postgresArcServerQuery = `type == "${azureResource.AzureResourceType.azureArcPostgresServer}"`; /** * Lists all MySQL Flexible servers */ -export const mysqlFlexibleServerQuery = `where type == "${azureResource.AzureResourceType.mysqlFlexibleServer}"`; +export const mysqlFlexibleServerQuery = `type == "${azureResource.AzureResourceType.mysqlFlexibleServer}"`; /** * Lists all Kusto Clusters */ -export const kustoClusterQuery = `where type == "${azureResource.AzureResourceType.kustoClusters}"`; +export const kustoClusterQuery = `type == "${azureResource.AzureResourceType.kustoClusters}"`; /** * Lists all Cosmos DB for MongoDB accounts */ -export const cosmosMongoDbQuery = `where type == "${azureResource.AzureResourceType.cosmosDbAccount}" and kind == "MongoDB"`; +export const cosmosMongoDbQuery = `type == "${azureResource.AzureResourceType.cosmosDbAccount}" and kind == "${Constants.mongoDbKind}"`; /** * Lists all Log Analytics workspaces */ -export const logAnalyticsQuery = `where type == "${azureResource.AzureResourceType.logAnalytics}"`; +export const logAnalyticsQuery = `type == "${azureResource.AzureResourceType.logAnalytics}"`; diff --git a/extensions/azurecore/src/azureResource/providers/resourceGroup/resourceGroupService.ts b/extensions/azurecore/src/azureResource/providers/resourceGroup/resourceGroupService.ts index 34cadd72c8..11ee46b608 100644 --- a/extensions/azurecore/src/azureResource/providers/resourceGroup/resourceGroupService.ts +++ b/extensions/azurecore/src/azureResource/providers/resourceGroup/resourceGroupService.ts @@ -3,18 +3,16 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DbServerGraphData } from '../databaseServer/databaseServerService'; import { azureResource } from 'azurecore'; import { ResourceServiceBase } from '../resourceTreeDataProviderBase'; import { resourceGroupQuery } from '../queryStringConstants'; +import { DbServerGraphData } from '../../interfaces'; -export class AzureResourceGroupService extends ResourceServiceBase { +export class AzureResourceGroupService extends ResourceServiceBase { - protected get query(): string { - return resourceGroupQuery; - } + public override queryFilter: string = resourceGroupQuery; - protected convertResource(resource: DbServerGraphData): azureResource.AzureResourceResourceGroup { + public override convertServerResource(resource: DbServerGraphData): azureResource.AzureResourceResourceGroup | undefined { return { id: resource.id, name: resource.name, diff --git a/extensions/azurecore/src/azureResource/providers/kusto/kustoProvider.ts b/extensions/azurecore/src/azureResource/providers/resourceProvider.ts similarity index 50% rename from extensions/azurecore/src/azureResource/providers/kusto/kustoProvider.ts rename to extensions/azurecore/src/azureResource/providers/resourceProvider.ts index 8fe27a383a..9c6d266a7c 100644 --- a/extensions/azurecore/src/azureResource/providers/kusto/kustoProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/resourceProvider.ts @@ -3,24 +3,19 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ExtensionContext } from 'vscode'; - import { azureResource } from 'azurecore'; -import { IAzureResourceService } from '../../interfaces'; -import { KustoTreeDataProvider as KustoTreeDataProvider } from './kustoTreeDataProvider'; -export class KustoProvider implements azureResource.IAzureResourceProvider { +export class ResourceProvider implements azureResource.IAzureResourceProvider { public constructor( - private _service: IAzureResourceService, - private _extensionContext: ExtensionContext - ) { - } + private _providerId: string, + private _treeProvider: azureResource.IAzureResourceTreeDataProvider + ) { } public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { - return new KustoTreeDataProvider(this._service, this._extensionContext); + return this._treeProvider; } public get providerId(): string { - return 'azure.resource.providers.azureDataExplorer'; + return this._providerId; } } diff --git a/extensions/azurecore/src/azureResource/providers/resourceTreeDataProviderBase.ts b/extensions/azurecore/src/azureResource/providers/resourceTreeDataProviderBase.ts index 5f5d7c5620..6f8a1039f2 100644 --- a/extensions/azurecore/src/azureResource/providers/resourceTreeDataProviderBase.ts +++ b/extensions/azurecore/src/azureResource/providers/resourceTreeDataProviderBase.ts @@ -6,67 +6,59 @@ import * as azdata from 'azdata'; import * as msRest from '@azure/ms-rest-js'; -import { IAzureResourceService } from '../interfaces'; +import { GraphData, IAzureResourceDbService, IAzureResourceServerService } from '../interfaces'; import { AzureResourceErrorMessageUtil } from '../utils'; import { ResourceGraphClient } from '@azure/arm-resourcegraph'; import { AzureAccount, azureResource } from 'azurecore'; import { Logger } from '../../utils/Logger'; import { ErrorResponse } from '@azure/arm-resourcegraph/esm/models'; +import { where } from './queryStringConstants'; -export abstract class ResourceTreeDataProviderBase implements azureResource.IAzureResourceTreeDataProvider { +export abstract class ResourceTreeDataProviderBase implements azureResource.IAzureResourceTreeDataProvider { public browseConnectionMode: boolean = false; - public constructor(protected _resourceService: IAzureResourceService) { + public constructor( + protected _resourceService: azureResource.IAzureResourceService | IAzureResourceServerService | IAzureResourceDbService) { } - public async getResourceTreeItem(element: azureResource.IAzureResourceNode): Promise { - return element.treeItem; + public getService(): azureResource.IAzureResourceService { + return this._resourceService; } - public async getChildren(element: azureResource.IAzureResourceNode): Promise { + public async getChildren(parent: azureResource.IAzureResourceNode): Promise { try { - let resources: T[] = await this.getResources(element); - - return resources.map((resource) => { - account: element.account, - subscription: element.subscription, - tenantId: element.subscription.tenant, - treeItem: this.getTreeItemForResource(resource, element.account) - }).sort((a, b) => (a.treeItem.label).localeCompare(b.treeItem.label)); + let resources: azureResource.AzureResource[] = await this.getResources(parent); + return resources.map((resource) => this.convertDataToResource(resource, parent)) + .sort((a, b) => (a.treeItem.label).localeCompare(b.treeItem.label)); } catch (error) { Logger.error(AzureResourceErrorMessageUtil.getErrorMessage(error)); throw error; } } - private async getResources(element: azureResource.IAzureResourceNode): Promise { + public convertDataToResource(resource: azureResource.AzureResource, parent: azureResource.IAzureResourceNode): any { + return { + account: parent.account, + subscription: parent.subscription, + tenantId: parent.subscription.tenant, + treeItem: this.getTreeItemForResource(resource, parent.account) + } + } + + private async getResources(element: azureResource.IAzureResourceNode): Promise { const response = await azdata.accounts.getAccountSecurityToken(element.account, element.subscription.tenant!, azdata.AzureResource.ResourceManagement); if (!response) { throw new Error(`Did not receive security token when getting resources for account ${element.account.displayInfo.displayName}`); } const credential = new msRest.TokenCredentials(response.token, response.tokenType); - - const resources: T[] = await this._resourceService.getResources([element.subscription], credential, element.account) || []; - return resources; + return await this._resourceService.getResources([element.subscription], credential, element.account) || []; } - protected abstract getTreeItemForResource(resource: T, account: AzureAccount): azdata.TreeItem; + public abstract getTreeItemForResource(resource: azureResource.AzureResource, account: AzureAccount): azdata.TreeItem; public abstract getRootChildren(): Promise; } -export interface GraphData { - subscriptionId: string, - subscriptionName?: string, - tenantId: string; - id: string; - name: string; - location: string; - type: string; - resourceGroup: string; -} - - export async function queryGraphResources(resourceClient: ResourceGraphClient, subscriptions: azureResource.AzureResourceSubscription[], resourceQuery: string): Promise { const allResources: T[] = []; let totalProcessed = 0; @@ -112,34 +104,31 @@ export async function queryGraphResources(resourceClient: R return allResources; } -export abstract class ResourceServiceBase implements IAzureResourceService { - constructor() { - } - +export abstract class ResourceServiceBase implements IAzureResourceServerService { /** * The query to use - see https://docs.microsoft.com/azure/governance/resource-graph/concepts/query-language * for more information on the supported syntax and tables/properties */ - protected abstract get query(): string; + public abstract queryFilter: string; - public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: msRest.ServiceClientCredentials, account: AzureAccount): Promise { - const convertedResources: U[] = []; + public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: msRest.ServiceClientCredentials, account: AzureAccount): Promise { + const convertedResources: azureResource.AzureResource[] = []; const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint }); - const graphResources = await queryGraphResources(resourceClient, subscriptions, this.query); + const graphResources = await queryGraphResources(resourceClient, subscriptions, where + this.queryFilter); const ids = new Set(); graphResources.forEach((res) => { if (!ids.has(res.id)) { ids.add(res.id); res.subscriptionName = subscriptions.find(sub => sub.id === res.subscriptionId)?.name; - const converted = this.convertResource(res); - convertedResources.push(converted); + const converted = this.convertServerResource(res); + convertedResources.push(converted!); } }); return convertedResources; } - protected abstract convertResource(resource: T): U; + public abstract convertServerResource(resource: T): azureResource.AzureResource | undefined; } diff --git a/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceProvider.ts b/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceProvider.ts deleted file mode 100644 index 986ce8a0fd..0000000000 --- a/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceProvider.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ExtensionContext } from 'vscode'; - -import { azureResource } from 'azurecore'; -import { IAzureResourceService } from '../../interfaces'; -import { SqlInstanceTreeDataProvider as SqlInstanceTreeDataProvider } from './sqlInstanceTreeDataProvider'; - -export class SqlInstanceProvider implements azureResource.IAzureResourceProvider { - public constructor( - private _service: IAzureResourceService, - private _extensionContext: ExtensionContext - ) { - } - - public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { - return new SqlInstanceTreeDataProvider(this._service, this._extensionContext); - } - - public get providerId(): string { - return 'azure.resource.providers.sqlInstance'; - } -} diff --git a/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceService.ts b/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceService.ts index a2f523f4e3..16ee302736 100644 --- a/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceService.ts +++ b/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceService.ts @@ -5,25 +5,19 @@ import { azureResource } from 'azurecore'; import { sqlInstanceQuery } from '../queryStringConstants'; -import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase'; +import { ResourceServiceBase } from '../resourceTreeDataProviderBase'; +import { SqlInstanceGraphData } from '../../interfaces'; +import { SQLINSTANCE_PROVIDER_ID } from '../../../constants'; -interface SqlInstanceGraphData extends GraphData { - properties: { - fullyQualifiedDomainName: string; - administratorLogin: string; - }; -} +export class SqlInstanceResourceService extends ResourceServiceBase { -export class SqlInstanceResourceService extends ResourceServiceBase { + public override queryFilter: string = sqlInstanceQuery; - protected get query(): string { - return sqlInstanceQuery; - } - - protected convertResource(resource: SqlInstanceGraphData): azureResource.AzureResourceDatabaseServer { + public override convertServerResource(resource: SqlInstanceGraphData): azureResource.AzureResourceDatabaseServer | undefined { return { id: resource.id, name: resource.name, + provider: SQLINSTANCE_PROVIDER_ID, fullName: resource.properties.fullyQualifiedDomainName, loginName: resource.properties.administratorLogin, defaultDatabaseName: 'master', diff --git a/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceTreeDataProvider.ts index 2f8c98f8ad..fdf94d12d1 100644 --- a/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceTreeDataProvider.ts @@ -8,27 +8,26 @@ import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { AzureResourceItemType, mssqlProvider } from '../../constants'; +import { AzureResourceItemType, AzureResourcePrefixes, mssqlProvider } from '../../constants'; import { generateGuid } from '../../utils'; -import { IAzureResourceService } from '../../interfaces'; +import { GraphData, SqlInstanceGraphData } from '../../interfaces'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { AzureAccount, azureResource } from 'azurecore'; -export class SqlInstanceTreeDataProvider extends ResourceTreeDataProviderBase { +export class SqlInstanceTreeDataProvider extends ResourceTreeDataProviderBase { private static readonly containerId = 'azure.resource.providers.sqlInstanceContainer'; private static readonly containerLabel = localize('azure.resource.providers.sqlInstanceContainerLabel', "Azure SQL DB managed instance"); public constructor( - databaseServerService: IAzureResourceService, + databaseServerService: azureResource.IAzureResourceService, private _extensionContext: ExtensionContext ) { super(databaseServerService); } - - protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { + public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { return { - id: `sqlInstance_${databaseServer.id ? databaseServer.id : databaseServer.name}`, + id: `${AzureResourcePrefixes.sqlInstance}${account.key.accountId}${account.key.accountId}${databaseServer.id ?? databaseServer.name}`, label: this.browseConnectionMode ? `${databaseServer.name} (${SqlInstanceTreeDataProvider.containerLabel}, ${databaseServer.subscription.name})` : databaseServer.name, iconPath: { dark: this._extensionContext.asAbsolutePath('resources/dark/sql_instance_inverse.svg'), diff --git a/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcProvider.ts b/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcProvider.ts deleted file mode 100644 index 6cb6caad55..0000000000 --- a/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcProvider.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ExtensionContext } from 'vscode'; - -import { azureResource } from 'azurecore'; -import { IAzureResourceService } from '../../interfaces'; -import { SqlInstanceArcTreeDataProvider as SqlInstanceArcTreeDataProvider } from './sqlInstanceArcTreeDataProvider'; - -export class SqlInstanceArcProvider implements azureResource.IAzureResourceProvider { - public constructor( - private _service: IAzureResourceService, - private _extensionContext: ExtensionContext - ) { - } - - public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { - return new SqlInstanceArcTreeDataProvider(this._service, this._extensionContext); - } - - public get providerId(): string { - return 'azure.resource.providers.sqlInstanceArc'; - } -} diff --git a/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcService.ts b/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcService.ts index e57c587877..57cc552a7e 100644 --- a/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcService.ts +++ b/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcService.ts @@ -3,27 +3,21 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase'; +import { ResourceServiceBase } from '../resourceTreeDataProviderBase'; import { azureResource } from 'azurecore'; import { sqlInstanceArcQuery } from '../queryStringConstants'; +import { SqlInstanceArcGraphData } from '../../interfaces'; +import { SQLINSTANCE_ARC_PROVIDER_ID } from '../../../constants'; -export interface SqlInstanceArcGraphData extends GraphData { - properties: { - admin: string; - hybridDataManager: string; - }; -} +export class SqlInstanceArcResourceService extends ResourceServiceBase { -export class SqlInstanceArcResourceService extends ResourceServiceBase { + public override queryFilter: string = sqlInstanceArcQuery; - protected get query(): string { - return sqlInstanceArcQuery; - } - - protected convertResource(resource: SqlInstanceArcGraphData): azureResource.AzureResourceDatabaseServer { + public override convertServerResource(resource: SqlInstanceArcGraphData): azureResource.AzureResourceDatabaseServer | undefined { return { id: resource.id, name: resource.name, + provider: SQLINSTANCE_ARC_PROVIDER_ID, fullName: resource.name, loginName: resource.properties.admin, defaultDatabaseName: 'master', diff --git a/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcTreeDataProvider.ts index 5be54771a4..b7f030e3e8 100644 --- a/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcTreeDataProvider.ts @@ -8,28 +8,27 @@ import { TreeItemCollapsibleState, ExtensionContext } from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { AzureResourceItemType, mssqlProvider } from '../../constants'; +import { AzureResourceItemType, AzureResourcePrefixes, mssqlProvider } from '../../constants'; import { generateGuid } from '../../utils'; -import { IAzureResourceService } from '../../interfaces'; +import { GraphData, SqlInstanceArcGraphData } from '../../interfaces'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { AzureAccount, azureResource } from 'azurecore'; -export class SqlInstanceArcTreeDataProvider extends ResourceTreeDataProviderBase { +export class SqlInstanceArcTreeDataProvider extends ResourceTreeDataProviderBase { private static readonly containerId = 'azure.resource.providers.sqlInstanceArcContainer'; // allow-any-unicode-next-line private static readonly containerLabel = localize('azure.resource.providers.sqlInstanceArcContainerLabel', "SQL managed instance – Azure Arc"); public constructor( - databaseServerService: IAzureResourceService, + databaseServerService: azureResource.IAzureResourceService, private _extensionContext: ExtensionContext ) { super(databaseServerService); } - - protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { + public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { return { - id: `sqlInstance_${databaseServer.id ? databaseServer.id : databaseServer.name}`, + id: `${AzureResourcePrefixes.sqlInstanceArc}${account.key.accountId}${databaseServer.id ?? databaseServer.name}`, label: this.browseConnectionMode ? `${databaseServer.name} (${SqlInstanceArcTreeDataProvider.containerLabel}, ${databaseServer.subscription.name})` : databaseServer.name, iconPath: { dark: this._extensionContext.asAbsolutePath('resources/dark/sql_instance_inverse.svg'), diff --git a/extensions/azurecore/src/azureResource/providers/synapseSqlPool/synapseSqlPoolProvider.ts b/extensions/azurecore/src/azureResource/providers/synapseSqlPool/synapseSqlPoolProvider.ts deleted file mode 100644 index d7665565be..0000000000 --- a/extensions/azurecore/src/azureResource/providers/synapseSqlPool/synapseSqlPoolProvider.ts +++ /dev/null @@ -1,27 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - - -import { ExtensionContext } from 'vscode'; - -import { azureResource } from 'azurecore'; -import { AzureResourceSynapseSqlPoolTreeDataProvider as AzureResourceSynapseSqlPoolTreeDataProvider } from './synapseSqlPoolTreeDataProvider'; -import { IAzureResourceService } from '../../interfaces'; - -export class AzureResourceSynapseSqlPoolProvider implements azureResource.IAzureResourceProvider { - public constructor( - private _synapseSqlPoolService: IAzureResourceService, - private _extensionContext: ExtensionContext - ) { - } - - public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { - return new AzureResourceSynapseSqlPoolTreeDataProvider(this._synapseSqlPoolService, this._extensionContext); - } - - public get providerId(): string { - return 'azure.resource.providers.synapseSqlPool'; - } -} diff --git a/extensions/azurecore/src/azureResource/providers/synapseSqlPool/synapseSqlPoolService.ts b/extensions/azurecore/src/azureResource/providers/synapseSqlPool/synapseSqlPoolService.ts index bb87812400..22dc3473dd 100644 --- a/extensions/azurecore/src/azureResource/providers/synapseSqlPool/synapseSqlPoolService.ts +++ b/extensions/azurecore/src/azureResource/providers/synapseSqlPool/synapseSqlPoolService.ts @@ -4,24 +4,45 @@ *--------------------------------------------------------------------------------------------*/ import { ServiceClientCredentials } from '@azure/ms-rest-js'; -import { IAzureResourceService } from '../../interfaces'; -import { SynapseWorkspaceGraphData } from '../synapseWorkspace/synapseWorkspaceService'; -import { synapseWorkspacesQuery, synapseSqlPoolsQuery } from '../queryStringConstants'; +import { GraphData, IAzureResourceDbService, SynapseGraphData, SynapseWorkspaceGraphData } from '../../interfaces'; +import { synapseWorkspacesQuery, synapseSqlPoolsQuery, where } from '../queryStringConstants'; import { ResourceGraphClient } from '@azure/arm-resourcegraph'; -import { queryGraphResources, GraphData } from '../resourceTreeDataProviderBase'; +import { queryGraphResources } from '../resourceTreeDataProviderBase'; import { AzureAccount, azureResource } from 'azurecore'; +import { SYNAPSE_SQL_POOL_PROVIDER_ID } from '../../../constants'; -interface SynapseGraphData extends GraphData { - kind: string; -} -export class AzureResourceSynapseService implements IAzureResourceService { +export class AzureResourceSynapseService implements IAzureResourceDbService { + + // TODO: @Cheena + public convertDatabaseResource(resource: SynapseGraphData, server?: SynapseWorkspaceGraphData | undefined): azureResource.AzureResourceDatabase | undefined { + if (server) { + return { + name: resource.name, + id: resource.id, + provider: SYNAPSE_SQL_POOL_PROVIDER_ID, + serverName: server.name, + serverFullName: server.properties.connectivityEndpoints?.sql, + loginName: server.properties.sqlAdministratorLogin, + subscription: { + id: resource.subscriptionId, + name: resource.subscriptionName! + }, + tenant: resource.tenantId, + resourceGroup: resource.resourceGroup + }; + } else { + return undefined; + } + } + + public queryFilter: string = synapseSqlPoolsQuery; public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise { const databases: azureResource.AzureResourceDatabase[] = []; const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint }); // Query synapse servers, and databases in parallel (start all promises before waiting on the 1st) - let synapseQueryPromise = queryGraphResources(resourceClient, subscriptions, synapseSqlPoolsQuery); - let synapseWorkspaceQueryPromise = queryGraphResources(resourceClient, subscriptions, synapseWorkspacesQuery); + let synapseQueryPromise = queryGraphResources(resourceClient, subscriptions, where + this.queryFilter); + let synapseWorkspaceQueryPromise = queryGraphResources(resourceClient, subscriptions, where + synapseWorkspacesQuery); let synapse = await synapseQueryPromise as SynapseGraphData[]; let synapseWorkspaceByGraph: SynapseWorkspaceGraphData[] = await synapseWorkspaceQueryPromise as SynapseWorkspaceGraphData[]; @@ -50,19 +71,8 @@ export class AzureResourceSynapseService implements IAzureResourceService s.name === serverName); if (server) { - databases.push({ - name: db.name, - id: db.id, - serverName: server.name, - serverFullName: server.properties.connectivityEndpoints?.sql, - loginName: server.properties.sqlAdministratorLogin, - subscription: { - id: db.subscriptionId, - name: (subscriptions.find(sub => sub.id === db.subscriptionId))?.name || '' - }, - tenant: db.tenantId, - resourceGroup: db.resourceGroup - }); + let res = this.convertDatabaseResource(db, server); + databases.push(res!); } } }); diff --git a/extensions/azurecore/src/azureResource/providers/synapseSqlPool/synapseSqlPoolTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/synapseSqlPool/synapseSqlPoolTreeDataProvider.ts index bebef79597..e3cc90330e 100644 --- a/extensions/azurecore/src/azureResource/providers/synapseSqlPool/synapseSqlPoolTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/synapseSqlPool/synapseSqlPoolTreeDataProvider.ts @@ -8,27 +8,27 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { AzureResourceItemType, mssqlProvider } from '../../constants'; +import { AzureResourceItemType, AzureResourcePrefixes, mssqlProvider } from '../../constants'; import { generateGuid } from '../../utils'; -import { IAzureResourceService } from '../../interfaces'; +import { SynapseGraphData, SynapseWorkspaceGraphData } from '../../interfaces'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { AzureAccount, azureResource } from 'azurecore'; -export class AzureResourceSynapseSqlPoolTreeDataProvider extends ResourceTreeDataProviderBase { +export class AzureResourceSynapseSqlPoolTreeDataProvider extends ResourceTreeDataProviderBase { private static readonly containerId = 'azure.resource.providers.synapseSqlPool.treeDataProvider.synapseSqlPoolContainer'; private static readonly containerLabel = localize('azure.resource.providers.synapseSqlPool.treeDataProvider.synapseSqlPoolContainerLabel', "Dedicated SQL Pools"); public constructor( - synapseSqlPoolService: IAzureResourceService, + synapseSqlPoolService: azureResource.IAzureResourceService, private _extensionContext: vscode.ExtensionContext ) { super(synapseSqlPoolService); } - protected getTreeItemForResource(synapse: azureResource.AzureResourceDatabase, account: AzureAccount): TreeItem { + public getTreeItemForResource(synapse: azureResource.AzureResourceDatabase, account: AzureAccount): TreeItem { return { - id: `synapseWorkspace_${synapse.serverFullName}.synapseSqlPool_${synapse.name}`, + id: `${AzureResourcePrefixes.synapseWorkspace}${account.key.accountId}${synapse.serverFullName}.${AzureResourcePrefixes.synapseSqlPool}${synapse.id ?? synapse.name}`, label: this.browseConnectionMode ? `${synapse.serverName}/${synapse.name} (${AzureResourceSynapseSqlPoolTreeDataProvider.containerLabel}, ${synapse.subscription.name})` : `${synapse.name} (${synapse.serverName})`, iconPath: { dark: this._extensionContext.asAbsolutePath('resources/dark/sql_database_inverse.svg'), diff --git a/extensions/azurecore/src/azureResource/providers/synapseWorkspace/synapseWorkspaceProvider.ts b/extensions/azurecore/src/azureResource/providers/synapseWorkspace/synapseWorkspaceProvider.ts deleted file mode 100644 index d44c8da4c2..0000000000 --- a/extensions/azurecore/src/azureResource/providers/synapseWorkspace/synapseWorkspaceProvider.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { ExtensionContext } from 'vscode'; - -import { azureResource } from 'azurecore'; -import { AzureResourceSynapseWorkspaceTreeDataProvider } from './synapseWorkspaceTreeDataProvider'; -import { IAzureResourceService } from '../../interfaces'; - -export class AzureResourceSynapseWorkspaceProvider implements azureResource.IAzureResourceProvider { - public constructor( - private _synapseWorkspaceService: IAzureResourceService, - private _extensionContext: ExtensionContext - ) { - } - - public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { - return new AzureResourceSynapseWorkspaceTreeDataProvider(this._synapseWorkspaceService, this._extensionContext); - } - - public get providerId(): string { - return 'azure.resource.providers.synapseWorkspace'; - } -} diff --git a/extensions/azurecore/src/azureResource/providers/synapseWorkspace/synapseWorkspaceService.ts b/extensions/azurecore/src/azureResource/providers/synapseWorkspace/synapseWorkspaceService.ts index 6ee11f0336..41fc176a24 100644 --- a/extensions/azurecore/src/azureResource/providers/synapseWorkspace/synapseWorkspaceService.ts +++ b/extensions/azurecore/src/azureResource/providers/synapseWorkspace/synapseWorkspaceService.ts @@ -5,60 +5,39 @@ import { ServiceClientCredentials } from '@azure/ms-rest-js'; import { ResourceGraphClient } from '@azure/arm-resourcegraph'; -import { GraphData, queryGraphResources } from '../resourceTreeDataProviderBase'; +import { queryGraphResources } from '../resourceTreeDataProviderBase'; import { azureResource, AzureAccount } from 'azurecore'; -import { IAzureResourceService } from '../../interfaces'; -import { synapseWorkspacesQuery } from '../queryStringConstants'; +import { IAzureResourceServerService, SynapseWorkspaceGraphData } from '../../interfaces'; +import { synapseWorkspacesQuery, where } from '../queryStringConstants'; +import { SYNAPSE_WORKSPACE_PROVIDER_ID } from '../../../constants'; -/** - * Properties returned by the Synapse query are different from the server ones and have to be treated differently. - */ -export interface SynapseWorkspaceGraphData extends GraphData { - properties: { - /** - * SQL connectivity endpoint and other endpoints are found here, instead of fullyQualifiedDomainName. - */ - connectivityEndpoints: { sql: string }; - /** - * managedResourceGroupName is the resource group used by any SQL pools inside the workspace - * which is different from the resource group of the workspace itself. - */ - managedResourceGroupName: string; - /** - * administratorLogin is called sqlAdministratorLogin here. - */ - sqlAdministratorLogin: string; - }; -} +export class AzureResourceSynapseWorkspaceService implements IAzureResourceServerService { -export class AzureResourceSynapseWorkspaceService implements IAzureResourceService { - - protected get query(): string { - return synapseWorkspacesQuery; - } + public queryFilter: string = synapseWorkspacesQuery; public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise { const convertedResources: azureResource.AzureResourceDatabaseServer[] = []; const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint }); - let serverGraphResources: SynapseWorkspaceGraphData[] = await queryGraphResources(resourceClient, subscriptions, this.query); + let serverGraphResources: SynapseWorkspaceGraphData[] = await queryGraphResources(resourceClient, subscriptions, where + this.queryFilter); const ids = new Set(); serverGraphResources.forEach((res) => { if (!ids.has(res.id)) { ids.add(res.id); res.subscriptionName = subscriptions.find(sub => sub.id === res.subscriptionId)?.name; - const converted = this.convertResource(res); - convertedResources.push(converted); + const converted = this.convertServerResource(res); + convertedResources.push(converted!); } }); return convertedResources; } - protected convertResource(resource: SynapseWorkspaceGraphData): azureResource.AzureResourceDatabaseServer { + public convertServerResource(resource: SynapseWorkspaceGraphData): azureResource.AzureResourceDatabaseServer | undefined { return { id: resource.id, name: resource.name, + provider: SYNAPSE_WORKSPACE_PROVIDER_ID, fullName: resource.properties.connectivityEndpoints?.sql, loginName: resource.properties.sqlAdministratorLogin, defaultDatabaseName: 'master', diff --git a/extensions/azurecore/src/azureResource/providers/synapseWorkspace/synapseWorkspaceTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/synapseWorkspace/synapseWorkspaceTreeDataProvider.ts index 0556194ca7..1b4671c007 100644 --- a/extensions/azurecore/src/azureResource/providers/synapseWorkspace/synapseWorkspaceTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/synapseWorkspace/synapseWorkspaceTreeDataProvider.ts @@ -8,26 +8,26 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { AzureResourceItemType, mssqlProvider } from '../../constants'; +import { AzureResourceItemType, AzureResourcePrefixes, mssqlProvider } from '../../constants'; import { generateGuid } from '../../utils'; -import { IAzureResourceService } from '../../interfaces'; +import { GraphData, SynapseWorkspaceGraphData } from '../../interfaces'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { AzureAccount, azureResource } from 'azurecore'; -export class AzureResourceSynapseWorkspaceTreeDataProvider extends ResourceTreeDataProviderBase { +export class AzureResourceSynapseWorkspaceTreeDataProvider extends ResourceTreeDataProviderBase { private static readonly containerId = 'azure.resource.providers.synapseWorkspace.treeDataProvider.synapseWorkspaceContainer'; private static readonly containerLabel = localize('azure.resource.providers.synapseWorkspace.treeDataProvider.synapseWorkspaceContainerLabel', "Azure Synapse Analytics"); public constructor( - synapseWorkspaceService: IAzureResourceService, + synapseWorkspaceService: azureResource.IAzureResourceService, private _extensionContext: vscode.ExtensionContext ) { super(synapseWorkspaceService); } - protected getTreeItemForResource(synapseWorkspace: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { + public getTreeItemForResource(synapseWorkspace: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { return { - id: `synapseWorkspace_${synapseWorkspace.id ?? synapseWorkspace.name}`, + id: `${AzureResourcePrefixes.synapseWorkspace}${account.key.accountId}${synapseWorkspace.id ?? synapseWorkspace.name}`, label: this.browseConnectionMode ? `${synapseWorkspace.name} (${AzureResourceSynapseWorkspaceTreeDataProvider.containerLabel}, ${synapseWorkspace.subscription.name})` : synapseWorkspace.name, iconPath: { dark: this._extensionContext.asAbsolutePath('resources/dark/sql_server_inverse.svg'), diff --git a/extensions/azurecore/src/azureResource/providers/universal/universalProvider.ts b/extensions/azurecore/src/azureResource/providers/universal/universalProvider.ts new file mode 100644 index 0000000000..fd18e47e28 --- /dev/null +++ b/extensions/azurecore/src/azureResource/providers/universal/universalProvider.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { azureResource } from 'azurecore'; + +export class AzureResourceUniversalResourceProvider implements azureResource.IAzureUniversalResourceProvider { + public constructor( + private _providerId: string, + private _treeProvider: azureResource.IAzureUniversalTreeDataProvider + ) { } + + public getTreeDataProvider(): azureResource.IAzureUniversalTreeDataProvider { + return this._treeProvider; + } + + public get providerId(): string { + return this._providerId; + } +} diff --git a/extensions/azurecore/src/azureResource/providers/universal/universalService.ts b/extensions/azurecore/src/azureResource/providers/universal/universalService.ts new file mode 100644 index 0000000000..974fb582df --- /dev/null +++ b/extensions/azurecore/src/azureResource/providers/universal/universalService.ts @@ -0,0 +1,153 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ServiceClientCredentials } from '@azure/ms-rest-js'; +import { ResourceGraphClient } from '@azure/arm-resourcegraph'; +import { queryGraphResources } from '../resourceTreeDataProviderBase'; +import { azureResource, AzureAccount } from 'azurecore'; +import { UniversalGraphData, IAzureResourceServerService, IAzureResourceDbService } from '../../interfaces'; +import { where } from '../queryStringConstants'; +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_SERVER_PROVIDER_ID, POSTGRES_ARC_SERVER_PROVIDER_ID, SQLINSTANCE_PROVIDER_ID, + SQLINSTANCE_ARC_PROVIDER_ID, SYNAPSE_SQL_POOL_PROVIDER_ID, SYNAPSE_WORKSPACE_PROVIDER_ID +} from '../../../constants'; +import { Logger } from '../../../utils/Logger'; + +const localize = nls.loadMessageBundle(); + +export class AzureResourceUniversalService implements azureResource.IAzureResourceService { + + constructor( + private registeredTreeDataProviders: Map + ) { + this.queryFilter = this.generateUniversalQueryFilter(); + } + + public queryFilter: string; + + private generateUniversalQueryFilter(): string { + let queryFilter = where; + this.registeredTreeDataProviders.forEach((v, k) => { + queryFilter += ' (' + v.getService().queryFilter + ') ' + 'or'; + }) + return queryFilter.substring(0, queryFilter.length - 3); // remove last || clause. + } + + public getRegisteredTreeDataProviderInstance(id: string): azureResource.IAzureResourceTreeDataProvider { + if (this.registeredTreeDataProviders.has(id)) { + return this.registeredTreeDataProviders.get(id)!; + } + throw new Error(localize('azurecore.unregisteredProvider', 'Unrecognized Provider resource: {0}', id)); + } + + public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise { + const convertedResources: azureResource.AzureResource[] = []; + const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint }); + let graphResources: UniversalGraphData[] = await queryGraphResources(resourceClient, subscriptions, this.queryFilter); + const ids = new Set(); + graphResources.forEach((res) => { + if (!ids.has(res.id)) { + ids.add(res.id); + res.subscriptionName = subscriptions.find(sub => sub.id === res.subscriptionId)?.name; + let converted: azureResource.AzureResource | undefined; + let providerInfo = this.getProviderFromResourceType(res.type, res.kind); + // Convert based on server/database type. + if (providerInfo[1] === ResourceCategory.Server) { + let serverProvider = providerInfo[0].getService() as IAzureResourceServerService; + converted = serverProvider.convertServerResource(res); + } else { // database + // Don't select 'master' Azure databases as they are internal databases and repititive with server names. + if (!res.kind?.endsWith('system')) { + let dbProvider = providerInfo[0].getService() as IAzureResourceDbService; + let serverResource = this.getServerResource(res, graphResources); + converted = dbProvider.convertDatabaseResource(res, serverResource); + } + } + if (converted) { + convertedResources.push(converted); + } + } + }); + + return convertedResources; + } + + public getProviderFromResourceType(type: string, kind?: string): + [provider: azureResource.IAzureResourceTreeDataProvider, category: ResourceCategory] { + if (type === azureResource.AzureResourceType.cosmosDbAccount && kind === mongoDbKind) { + return [this.getRegisteredTreeDataProviderInstance(COSMOSDB_MONGO_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) { + return [this.getRegisteredTreeDataProviderInstance(DATABASE_SERVER_PROVIDER_ID), ResourceCategory.Server]; + } else if (type === azureResource.AzureResourceType.kustoClusters) { + return [this.getRegisteredTreeDataProviderInstance(KUSTO_PROVIDER_ID), ResourceCategory.Server]; + } else if (type === azureResource.AzureResourceType.logAnalytics) { + 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) { + 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) { + return [this.getRegisteredTreeDataProviderInstance(SQLINSTANCE_PROVIDER_ID), ResourceCategory.Server]; + } else if (type === azureResource.AzureResourceType.azureArcSqlManagedInstance) { + return [this.getRegisteredTreeDataProviderInstance(SQLINSTANCE_ARC_PROVIDER_ID), ResourceCategory.Server]; + } else if (type === azureResource.AzureResourceType.sqlSynapseSqlPool) { + return [this.getRegisteredTreeDataProviderInstance(SYNAPSE_SQL_POOL_PROVIDER_ID), ResourceCategory.Database]; + } else if (type === azureResource.AzureResourceType.sqlSynapseWorkspace) { + return [this.getRegisteredTreeDataProviderInstance(SYNAPSE_WORKSPACE_PROVIDER_ID), ResourceCategory.Server]; + } + Logger.error(`Type provider not registered: ${type}`); + throw new Error(localize('azurecore.unregisteredProviderType', 'Unrecognized Provider resource type: {0}', type)); + } + + public getProviderFromResourceId(id: string): azureResource.IAzureResourceTreeDataProvider { + if (id.startsWith(AzureResourcePrefixes.cosmosdb)) { + return this.getRegisteredTreeDataProviderInstance(COSMOSDB_MONGO_PROVIDER_ID); + } else if (id.startsWith(AzureResourcePrefixes.database)) { + return this.getRegisteredTreeDataProviderInstance(DATABASE_PROVIDER_ID); + } else if (id.startsWith(AzureResourcePrefixes.databaseServer)) { + return this.getRegisteredTreeDataProviderInstance(DATABASE_SERVER_PROVIDER_ID); + } else if (id.startsWith(AzureResourcePrefixes.kusto)) { + return this.getRegisteredTreeDataProviderInstance(KUSTO_PROVIDER_ID); + } else if (id.startsWith(AzureResourcePrefixes.logAnalytics)) { + 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.postgresServer)) { + return this.getRegisteredTreeDataProviderInstance(POSTGRES_SERVER_PROVIDER_ID); + } else if (id.startsWith(AzureResourcePrefixes.postgresServerArc)) { + return this.getRegisteredTreeDataProviderInstance(POSTGRES_ARC_SERVER_PROVIDER_ID); + } else if (id.startsWith(AzureResourcePrefixes.sqlInstance)) { + return this.getRegisteredTreeDataProviderInstance(SQLINSTANCE_PROVIDER_ID); + } else if (id.startsWith(AzureResourcePrefixes.sqlInstanceArc)) { + return this.getRegisteredTreeDataProviderInstance(SQLINSTANCE_ARC_PROVIDER_ID); + } else if (id.startsWith(AzureResourcePrefixes.synapseSqlPool)) { + return this.getRegisteredTreeDataProviderInstance(SYNAPSE_SQL_POOL_PROVIDER_ID); + } else if (id.startsWith(AzureResourcePrefixes.synapseWorkspace)) { + return this.getRegisteredTreeDataProviderInstance(SYNAPSE_WORKSPACE_PROVIDER_ID); + } + Logger.error(`Unrecognized provider prefix for id: ${id}`); + throw new Error(localize('azurecore.unregisteredProvider', 'Unrecognized Provider resource: {0}', id)); + } + + /** + * Resource Id format: + * '/subscriptions//resourceGroups//providers/Microsoft.Sql/servers//databases/' + * We find server with it's name in the same subscription, as resource groups can still be different. + */ + private getServerResource(resource: UniversalGraphData, allResources: UniversalGraphData[]): UniversalGraphData | undefined { + const resourceParts = resource.id.split('/'); + const serverNameIndex = resourceParts.length - 3; + const subscriptionId = resourceParts[2]; + return allResources.find(res => res.name === resourceParts[serverNameIndex] + && res.subscriptionId === subscriptionId) ?? undefined; + } +} diff --git a/extensions/azurecore/src/azureResource/providers/universal/universalTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/universal/universalTreeDataProvider.ts new file mode 100644 index 0000000000..7b22d47001 --- /dev/null +++ b/extensions/azurecore/src/azureResource/providers/universal/universalTreeDataProvider.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as azdata from 'azdata'; +import * as msRest from '@azure/ms-rest-js'; + +import { AzureResourceErrorMessageUtil } from '../../utils'; +import { GraphData } from '../../interfaces'; +import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; +import { AzureAccount, azureResource } from 'azurecore'; +import { Logger } from '../../../utils/Logger'; +import { AzureResourceUniversalService } from './universalService'; + +export class AzureResourceUniversalTreeDataProvider + extends ResourceTreeDataProviderBase implements azureResource.IAzureUniversalTreeDataProvider { + + public constructor( + private universalService: azureResource.IAzureResourceService, + ) { + super(universalService); + } + + public override getService(): azureResource.IAzureResourceService { + return this.universalService; + } + + public override getChildren(element: azureResource.IAzureResourceNode): Promise { + throw new Error('Method not supported for universal provider.'); + } + + public getTreeItemForResource(resource: azureResource.AzureResource, account: AzureAccount): azdata.TreeItem { + let service: AzureResourceUniversalService = this.universalService as AzureResourceUniversalService; + let provider = service.getRegisteredTreeDataProviderInstance(resource.provider!); + provider.browseConnectionMode = this.browseConnectionMode; + return provider.getTreeItemForResource(resource, account); + } + + public async getRootChildren(): Promise { + throw new Error('Method not supported'); + } + + public async getAllChildren(account: AzureAccount, subscriptions: azureResource.AzureResourceSubscription[]): Promise { + try { + let resources: azureResource.AzureResource[] = await this.getAllResources(account, subscriptions); + return resources.map((resource) => { + account: account, + subscription: resource.subscription, + tenantId: resource.tenant, + treeItem: this.getTreeItemForResource(resource, account) + }).sort((a, b) => (a.treeItem.label).localeCompare(b.treeItem.label)); + } catch (error) { + Logger.error(AzureResourceErrorMessageUtil.getErrorMessage(error)); + throw error; + } + } + + private async getAllResources(account: AzureAccount, subscriptions: azureResource.AzureResourceSubscription[]): Promise { + let resources: azureResource.AzureResource[] = []; + for (const tenant of account.properties.tenants) { + const subs = subscriptions.filter(sub => sub.tenant === tenant.id); + if (subs && subs.length > 0) { + const response = await azdata.accounts.getAccountSecurityToken(account, tenant.id, azdata.AzureResource.ResourceManagement); + if (!response) { + throw new Error(`Did not receive security token when getting resources for account ${account.displayInfo.displayName}`); + } + const credential = new msRest.TokenCredentials(response.token, response.tokenType); + resources = resources.concat(await this.universalService.getResources(subs, credential, account)); + } + } + return resources; + } +} diff --git a/extensions/azurecore/src/azureResource/resourceService.ts b/extensions/azurecore/src/azureResource/resourceService.ts index 7ebc4f9e67..e5584439bf 100644 --- a/extensions/azurecore/src/azureResource/resourceService.ts +++ b/extensions/azurecore/src/azureResource/resourceService.ts @@ -3,18 +3,20 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { extensions, TreeItem } from 'vscode'; +import { extensions } from 'vscode'; +import * as azdata from 'azdata'; import { IAzureResourceNodeWithProviderId } from './interfaces'; import { AzureAccount, azureResource } from 'azurecore'; +import { UNIVERSAL_PROVIDER_ID } from '../constants'; export class AzureResourceService { private _areResourceProvidersLoaded: boolean = false; private _resourceProviders: { [resourceProviderId: string]: azureResource.IAzureResourceProvider } = {}; private _treeDataProviders: { [resourceProviderId: string]: azureResource.IAzureResourceTreeDataProvider } = {}; + private _universalProvider: azureResource.IAzureUniversalResourceProvider | undefined = undefined; - public constructor() { - } + public constructor() { } public async listResourceProviderIds(): Promise { await this.ensureResourceProvidersRegistered(); @@ -22,6 +24,11 @@ export class AzureResourceService { return Object.keys(this._resourceProviders); } + public async getResourceProviders(): Promise<{ [resourceProviderId: string]: azureResource.IAzureResourceProvider }> { + await this.ensureResourceProvidersRegistered(); + return this._resourceProviders; + } + public registerResourceProvider(resourceProvider: azureResource.IAzureResourceProvider): void { this.doRegisterResourceProvider(resourceProvider); } @@ -35,13 +42,11 @@ export class AzureResourceService { public async getRootChildren(resourceProviderId: string, account: AzureAccount, subscription: azureResource.AzureResourceSubscription): Promise { await this.ensureResourceProvidersRegistered(); - if (!(resourceProviderId in this._resourceProviders)) { + if (!(resourceProviderId in this._resourceProviders) && resourceProviderId !== UNIVERSAL_PROVIDER_ID) { throw new Error(`Azure resource provider doesn't exist. Id: ${resourceProviderId}`); } - const treeDataProvider = this._treeDataProviders[resourceProviderId]; - const rootChildren = await treeDataProvider.getRootChildren(); - + const rootChildren = await this._treeDataProviders[resourceProviderId]?.getRootChildren(); return rootChildren.map(rootChild => { return { resourceProviderId, @@ -58,13 +63,13 @@ export class AzureResourceService { public async getChildren(resourceProviderId: string, element: azureResource.IAzureResourceNode, browseConnectionMode: boolean = false): Promise { await this.ensureResourceProvidersRegistered(); - if (!(resourceProviderId in this._resourceProviders)) { + if (!(resourceProviderId in this._resourceProviders) && resourceProviderId !== UNIVERSAL_PROVIDER_ID) { throw new Error(`Azure resource provider doesn't exist. Id: ${resourceProviderId}`); } - const treeDataProvider = this._treeDataProviders[resourceProviderId]; + const treeDataProvider = this._treeDataProviders[resourceProviderId]; treeDataProvider.browseConnectionMode = browseConnectionMode; - const children = await treeDataProvider.getChildren(element); + const children = await treeDataProvider.getChildren(element); return children.map((child) => { resourceProviderId: resourceProviderId, @@ -72,15 +77,16 @@ export class AzureResourceService { }); } - public async getTreeItem(resourceProviderId: string, element: azureResource.IAzureResourceNode): Promise { + public async getAllChildren(account: AzureAccount, subscriptions: azureResource.AzureResourceSubscription[], browseConnectionMode: boolean = false): Promise { await this.ensureResourceProvidersRegistered(); + const treeDataProvider = this._universalProvider?.getTreeDataProvider(); + treeDataProvider.browseConnectionMode = browseConnectionMode; + const children = await treeDataProvider.getAllChildren(account, subscriptions); - if (!(resourceProviderId in this._resourceProviders)) { - throw new Error(`Azure resource provider doesn't exist. Id: ${resourceProviderId}`); - } - - const treeDataProvider = this._treeDataProviders[resourceProviderId]; - return treeDataProvider.getResourceTreeItem(element); + return children.map((child) => { + resourceProviderId: UNIVERSAL_PROVIDER_ID, + resourceNode: child + }); } public get areResourceProvidersLoaded(): boolean { @@ -107,11 +113,14 @@ export class AzureResourceService { if (extension.exports && extension.exports.provideResources) { for (const resourceProvider of extension.exports.provideResources()) { - if (resourceProvider) { + if (resourceProvider && resourceProvider.providerId !== UNIVERSAL_PROVIDER_ID) { this.doRegisterResourceProvider(resourceProvider); } } } + if (extension.exports && extension.exports.getUniversalProvider) { + this._universalProvider = extension.exports.getUniversalProvider(); + } } } @@ -122,5 +131,4 @@ export class AzureResourceService { this._resourceProviders[resourceProvider.providerId] = resourceProvider; this._treeDataProviders[resourceProvider.providerId] = resourceProvider.getTreeDataProvider(); } - } diff --git a/extensions/azurecore/src/azureResource/resourceTreeNode.ts b/extensions/azurecore/src/azureResource/resourceTreeNode.ts index f6adc5c400..85ccfca8d8 100644 --- a/extensions/azurecore/src/azureResource/resourceTreeNode.ts +++ b/extensions/azurecore/src/azureResource/resourceTreeNode.ts @@ -53,7 +53,7 @@ export class AzureResourceResourceTreeNode extends TreeNode { } public getTreeItem(): TreeItem | Promise { - return this._resourceService.getTreeItem(this.resourceNodeWithProviderId.resourceProviderId, this.resourceNodeWithProviderId.resourceNode); + return this.resourceNodeWithProviderId.resourceNode.treeItem; } public getNodeInfo(): NodeInfo { diff --git a/extensions/azurecore/src/azureResource/tree/connectionDialogTreeProvider.ts b/extensions/azurecore/src/azureResource/tree/connectionDialogTreeProvider.ts index bf69812a6b..50fd13f7e4 100644 --- a/extensions/azurecore/src/azureResource/tree/connectionDialogTreeProvider.ts +++ b/extensions/azurecore/src/azureResource/tree/connectionDialogTreeProvider.ts @@ -63,7 +63,6 @@ export class ConnectionDialogTreeProvider implements vscode.TreeDataProvider { - await this.updateLabel(); + this._loader.onLoadingStatusChanged(() => { this.treeChangeHandler.notifyNodeChanged(this); }); } @@ -69,6 +68,7 @@ export class FlatAccountTreeNode extends AzureResourceContainerTreeNodeBase { public async getChildren(): Promise { if (this._isClearingCache) { + await this.updateLabel(); this._loader.start().catch(err => console.error('Error loading Azure FlatAccountTreeNodes ', err)); this._isClearingCache = false; return []; @@ -183,7 +183,6 @@ class FlatAccountTreeNodeLoader { try { // Authenticate to tenants to filter out subscriptions that are not accessible. - let tenants = this._account.properties.tenants; // Filter out tenants that we can't authenticate to. tenants = tenants.filter(async tenant => { @@ -209,21 +208,13 @@ class FlatAccountTreeNodeLoader { }); } - const resourceProviderIds = await this._resourceService.listResourceProviderIds(); - for (const subscription of subscriptions) { - for (const providerId of resourceProviderIds) { - const resourceTypes = await this._resourceService.getRootChildren(providerId, this._account, subscription); - for (const resourceType of resourceTypes) { - const resources = await this._resourceService.getChildren(providerId, resourceType.resourceNode, true); - if (resources?.length > 0) { - this._nodes.push(...resources.map(dr => new AzureResourceResourceTreeNode(dr, this._accountNode, this.appContext))); - this._nodes = this.nodes.sort((a, b) => { - return a.getNodeInfo().label.localeCompare(b.getNodeInfo().label); - }); - newNodesAvailable = true; - } - } - } + const resources = await this._resourceService.getAllChildren(this._account, subscriptions, true); + if (resources?.length > 0) { + this._nodes.push(...resources.map(dr => new AzureResourceResourceTreeNode(dr, this._accountNode, this.appContext))); + this._nodes = this.nodes.sort((a, b) => { + return a.getNodeInfo().label.localeCompare(b.getNodeInfo().label); + }); + newNodesAvailable = true; } // Create "No Resources Found" message node if no resources found under azure account. if (this._nodes.length === 0) { diff --git a/extensions/azurecore/src/azureResource/utils.ts b/extensions/azurecore/src/azureResource/utils.ts index 61ef3de4b0..0c6ca2d3f9 100644 --- a/extensions/azurecore/src/azureResource/utils.ts +++ b/extensions/azurecore/src/azureResource/utils.ts @@ -3,12 +3,14 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as azdata from 'azdata'; +import * as vscode from 'vscode'; +import * as nls from 'vscode-nls'; +import * as Constants from '../constants'; import { ResourceGraphClient } from '@azure/arm-resourcegraph'; import { TokenCredentials } from '@azure/ms-rest-js'; -import * as azdata from 'azdata'; import { AzureRestResponse, GetResourceGroupsResult, GetSubscriptionsResult, ResourceQueryResult, GetBlobContainersResult, GetFileSharesResult, HttpRequestMethod, GetLocationsResult, GetManagedDatabasesResult, CreateResourceGroupResult, GetBlobsResult, GetStorageAccountAccessKeyResult, AzureAccount, azureResource, AzureAccountProviderMetadata, AzureNetworkResponse } from 'azurecore'; import { EOL } from 'os'; -import * as nls from 'vscode-nls'; import { AppContext } from '../appContext'; import { invalidAzureAccount, invalidTenant, unableToFetchTokenError } from '../localizedConstants'; import { AzureResourceServiceNames } from './constants'; @@ -16,12 +18,36 @@ import { IAzureResourceSubscriptionFilterService, IAzureResourceSubscriptionServ import { AzureResourceGroupService } from './providers/resourceGroup/resourceGroupService'; import { BlobServiceClient, StorageSharedKeyCredential } from '@azure/storage-blob'; import providerSettings from '../account-provider/providerSettings'; -import * as Constants from '../constants'; import { getProxyEnabledHttpClient } from '../utils'; import { HttpClient } from '../account-provider/auths/httpClient'; import { NetworkRequestOptions } from '@azure/msal-common'; import { ErrorResponseBody } from '@azure/arm-subscriptions/esm/models'; import { TenantIgnoredError } from '../utils/TenantIgnoredError'; +import { AzureMonitorResourceService } from './providers/azuremonitor/azuremonitorService'; +import { AzureMonitorTreeDataProvider } from './providers/azuremonitor/azuremonitorTreeDataProvider'; +import { CosmosDbMongoService } from './providers/cosmosdb/mongo/cosmosDbMongoService'; +import { CosmosDbMongoTreeDataProvider } from './providers/cosmosdb/mongo/cosmosDbMongoTreeDataProvider'; +import { AzureResourceDatabaseService } from './providers/database/databaseService'; +import { AzureResourceDatabaseTreeDataProvider } from './providers/database/databaseTreeDataProvider'; +import { AzureResourceDatabaseServerService } from './providers/databaseServer/databaseServerService'; +import { AzureResourceDatabaseServerTreeDataProvider } from './providers/databaseServer/databaseServerTreeDataProvider'; +import { KustoResourceService } from './providers/kusto/kustoService'; +import { KustoTreeDataProvider } from './providers/kusto/kustoTreeDataProvider'; +import { MysqlFlexibleServerService } from './providers/mysqlFlexibleServer/mysqlFlexibleServerService'; +import { MysqlFlexibleServerTreeDataProvider } from './providers/mysqlFlexibleServer/mysqlFlexibleServerTreeDataProvider'; +import { PostgresServerArcService } from './providers/postgresArcServer/postgresArcServerService'; +import { PostgresServerArcTreeDataProvider } from './providers/postgresArcServer/postgresArcServerTreeDataProvider'; +import { PostgresServerService } from './providers/postgresServer/postgresServerService'; +import { PostgresServerTreeDataProvider } from './providers/postgresServer/postgresServerTreeDataProvider'; +import { ResourceProvider } from './providers/resourceProvider'; +import { SqlInstanceResourceService } from './providers/sqlinstance/sqlInstanceService'; +import { SqlInstanceTreeDataProvider } from './providers/sqlinstance/sqlInstanceTreeDataProvider'; +import { SqlInstanceArcResourceService } from './providers/sqlinstanceArc/sqlInstanceArcService'; +import { SqlInstanceArcTreeDataProvider } from './providers/sqlinstanceArc/sqlInstanceArcTreeDataProvider'; +import { AzureResourceSynapseService } from './providers/synapseSqlPool/synapseSqlPoolService'; +import { AzureResourceSynapseSqlPoolTreeDataProvider } from './providers/synapseSqlPool/synapseSqlPoolTreeDataProvider'; +import { AzureResourceSynapseWorkspaceService } from './providers/synapseWorkspace/synapseWorkspaceService'; +import { AzureResourceSynapseWorkspaceTreeDataProvider } from './providers/synapseWorkspace/synapseWorkspaceTreeDataProvider'; const localize = nls.loadMessageBundle(); @@ -233,6 +259,30 @@ export async function getLocations(appContext: AppContext, account?: AzureAccoun return result; } +export function getAllResourceProviders(extensionContext: vscode.ExtensionContext): azureResource.IAzureResourceProvider[] { + const arcFeaturedEnabled = vscode.workspace.getConfiguration(Constants.AzureSection).get(Constants.EnableArcFeaturesSection); + 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.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)), + new ResourceProvider(Constants.MYSQL_FLEXIBLE_SERVER_PROVIDER_ID, new MysqlFlexibleServerTreeDataProvider(new MysqlFlexibleServerService(), extensionContext)), + new ResourceProvider(Constants.POSTGRES_SERVER_PROVIDER_ID, new PostgresServerTreeDataProvider(new PostgresServerService(), extensionContext)), + new ResourceProvider(Constants.SQLINSTANCE_PROVIDER_ID, new SqlInstanceTreeDataProvider(new SqlInstanceResourceService(), extensionContext)), + new ResourceProvider(Constants.SYNAPSE_SQL_POOL_PROVIDER_ID, new AzureResourceSynapseSqlPoolTreeDataProvider(new AzureResourceSynapseService(), extensionContext)), + new ResourceProvider(Constants.SYNAPSE_WORKSPACE_PROVIDER_ID, new AzureResourceSynapseWorkspaceTreeDataProvider(new AzureResourceSynapseWorkspaceService(), extensionContext)), + ]; + + if (arcFeaturedEnabled) { + providers.push( + new ResourceProvider(Constants.SQLINSTANCE_ARC_PROVIDER_ID, new SqlInstanceArcTreeDataProvider(new SqlInstanceArcResourceService(), extensionContext)), + new ResourceProvider(Constants.POSTGRES_ARC_SERVER_PROVIDER_ID, new PostgresServerArcTreeDataProvider(new PostgresServerArcService(), extensionContext)) + ); + } + return providers; +} + export async function runResourceQuery( account: AzureAccount, subscriptions: azureResource.AzureResourceSubscription[], diff --git a/extensions/azurecore/src/azurecore.d.ts b/extensions/azurecore/src/azurecore.d.ts index 10a550b3d1..0c6ecdcbda 100644 --- a/extensions/azurecore/src/azurecore.d.ts +++ b/extensions/azurecore/src/azurecore.d.ts @@ -6,6 +6,7 @@ declare module 'azurecore' { import * as azdata from 'azdata'; import * as vscode from 'vscode'; + import * as msRest from '@azure/ms-rest-js'; import { BlobItem } from '@azure/storage-blob'; /** @@ -327,6 +328,7 @@ declare module 'azurecore' { getRegionDisplayName(region?: string): string; getProviderMetadataForAccount(account: AzureAccount): AzureAccountProviderMetadata; provideResources(): azureResource.IAzureResourceProvider[]; + getUniversalProvider(): azureResource.IAzureUniversalResourceProvider; runGraphQuery(account: AzureAccount, subscriptions: azureResource.AzureResourceSubscription[], ignoreErrors: boolean, query: string): Promise>; /** * Event emitted when MSAL cache encryption keys are updated in credential store. @@ -383,11 +385,23 @@ declare module 'azurecore' { mysqlFlexibleServer = 'microsoft.dbformysql/flexibleservers' } + export interface IAzureUniversalTreeDataProvider extends IAzureResourceTreeDataProvider { + /** + * Gets all the children for user account for provided subscription list. + */ + getAllChildren(account: AzureAccount, subscriptions: azureResource.AzureResourceSubscription[]): Promise; + } + + export interface IAzureUniversalResourceProvider extends IAzureResourceProvider { + getTreeDataProvider(): IAzureUniversalTreeDataProvider; + } + export interface IAzureResourceProvider extends azdata.DataProvider { getTreeDataProvider(): IAzureResourceTreeDataProvider; } export interface IAzureResourceTreeDataProvider { + 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. @@ -399,10 +413,11 @@ declare module 'azurecore' { */ getChildren(element: IAzureResourceNode): Promise; /** - * Gets the tree item to display for a given {@link IAzureResourceNode} - * @param element The resource node to get the TreeItem for + * Converts resource to VS Code treeItem + * @param resource Azure resource to convert. + * @param account User account */ - getResourceTreeItem(element: IAzureResourceNode): Promise; + getTreeItemForResource(resource: azureResource.AzureResource, account: AzureAccount): vscode.TreeItem; browseConnectionMode: boolean; } @@ -422,10 +437,16 @@ declare module 'azurecore' { name: string; id: string; subscription: IAzureSubscriptionInfo; + provider?: string, resourceGroup?: string; tenant?: string; } + export interface IAzureResourceService { + queryFilter: string; + getResources(subscriptions: AzureResourceSubscription[], credential: msRest.ServiceClientCredentials, account: AzureAccount): Promise; + } + export interface AzureResourceSubscription extends Omit { } diff --git a/extensions/azurecore/src/constants.ts b/extensions/azurecore/src/constants.ts index eea151d64f..81bde08895 100644 --- a/extensions/azurecore/src/constants.ts +++ b/extensions/azurecore/src/constants.ts @@ -134,3 +134,18 @@ export enum Platform { Mac = 'darwin', Linux = 'linux' } + +/////////////// 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 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'; +export const MYSQL_FLEXIBLE_SERVER_PROVIDER_ID = 'azure.resource.providers.mysqlFlexibleServer'; +export const POSTGRES_ARC_SERVER_PROVIDER_ID = 'azure.resource.providers.postgresArcServer'; +export const POSTGRES_SERVER_PROVIDER_ID = 'azure.resource.providers.postgresServer'; +export const SQLINSTANCE_PROVIDER_ID = 'azure.resource.providers.sqlInstance'; +export const SQLINSTANCE_ARC_PROVIDER_ID = 'azure.resource.providers.sqlInstanceArc'; +export const SYNAPSE_SQL_POOL_PROVIDER_ID = 'azure.resource.providers.synapseSqlPool'; +export const SYNAPSE_WORKSPACE_PROVIDER_ID = 'azure.resource.providers.synapseWorkspace'; +export const UNIVERSAL_PROVIDER_ID = 'azure.resource.providers.universal'; diff --git a/extensions/azurecore/src/extension.ts b/extensions/azurecore/src/extension.ts index a235c218a7..8a5103bee5 100644 --- a/extensions/azurecore/src/extension.ts +++ b/extensions/azurecore/src/extension.ts @@ -11,11 +11,6 @@ import * as os from 'os'; import { AppContext } from './appContext'; import { AzureAccountProviderService } from './account-provider/azureAccountProviderService'; - -import { AzureResourceDatabaseServerProvider } from './azureResource/providers/databaseServer/databaseServerProvider'; -import { AzureResourceDatabaseServerService } from './azureResource/providers/databaseServer/databaseServerService'; -import { AzureResourceDatabaseProvider } from './azureResource/providers/database/databaseProvider'; -import { AzureResourceDatabaseService } from './azureResource/providers/database/databaseService'; import { AzureResourceService } from './azureResource/resourceService'; import { IAzureResourceCacheService, IAzureResourceSubscriptionService, IAzureResourceSubscriptionFilterService, IAzureTerminalService } from './azureResource/interfaces'; import { AzureResourceServiceNames } from './azureResource/constants'; @@ -24,23 +19,7 @@ import { AzureResourceSubscriptionFilterService } from './azureResource/services import { AzureResourceCacheService } from './azureResource/services/cacheService'; import { registerAzureResourceCommands } from './azureResource/commands'; import { AzureResourceTreeProvider } from './azureResource/tree/treeProvider'; -import { SqlInstanceResourceService } from './azureResource/providers/sqlinstance/sqlInstanceService'; -import { SqlInstanceProvider } from './azureResource/providers/sqlinstance/sqlInstanceProvider'; -import { KustoResourceService } from './azureResource/providers/kusto/kustoService'; -import { KustoProvider } from './azureResource/providers/kusto/kustoProvider'; -import { AzureMonitorResourceService } from './azureResource/providers/azuremonitor/azuremonitorService'; -import { AzureMonitorProvider } from './azureResource/providers/azuremonitor/azuremonitorProvider'; -import { PostgresServerProvider } from './azureResource/providers/postgresServer/postgresServerProvider'; -import { PostgresServerService } from './azureResource/providers/postgresServer/postgresServerService'; import { AzureTerminalService } from './azureResource/services/terminalService'; -import { SqlInstanceArcProvider } from './azureResource/providers/sqlinstanceArc/sqlInstanceArcProvider'; -import { SqlInstanceArcResourceService } from './azureResource/providers/sqlinstanceArc/sqlInstanceArcService'; -import { PostgresServerArcProvider } from './azureResource/providers/postgresArcServer/postgresServerProvider'; -import { PostgresServerArcService } from './azureResource/providers/postgresArcServer/postgresServerService'; -import { CosmosDbMongoProvider } from './azureResource/providers/cosmosdb/mongo/cosmosDbMongoProvider'; -import { CosmosDbMongoService } from './azureResource/providers/cosmosdb/mongo/cosmosDbMongoService'; -import { MysqlFlexibleServerProvider } from './azureResource/providers/mysqlFlexibleServer/mysqlFlexibleServerProvider'; -import { MysqlFlexibleServerService } from './azureResource/providers/mysqlFlexibleServer/mysqlFlexibleServerService'; import * as azurecore from 'azurecore'; import * as azureResourceUtils from './azureResource/utils'; import * as utils from './utils'; @@ -50,10 +29,11 @@ import { AzureResourceGroupService } from './azureResource/providers/resourceGro import { Logger } from './utils/Logger'; import { ConnectionDialogTreeProvider } from './azureResource/tree/connectionDialogTreeProvider'; import { AzureDataGridProvider } from './azureDataGridProvider'; -import { AzureResourceSynapseSqlPoolProvider } from './azureResource/providers/synapseSqlPool/synapseSqlPoolProvider'; -import { AzureResourceSynapseWorkspaceProvider } from './azureResource/providers/synapseWorkspace/synapseWorkspaceProvider'; -import { AzureResourceSynapseWorkspaceService } from './azureResource/providers/synapseWorkspace/synapseWorkspaceService'; -import { AzureResourceSynapseService } from './azureResource/providers/synapseSqlPool/synapseSqlPoolService'; +// import { AzureResourceUniversalService } from './azureResource/providers/universal/universalService'; +import { AzureResourceUniversalService } from './azureResource/providers/universal/universalService'; +import { AzureResourceUniversalTreeDataProvider } from './azureResource/providers/universal/universalTreeDataProvider'; +import { AzureResourceUniversalResourceProvider } from './azureResource/providers/universal/universalProvider'; +// import { AzureResourceUniversalTreeDataProvider } from './azureResource/providers/universal/universalTreeDataProvider'; let extensionContext: vscode.ExtensionContext; @@ -151,26 +131,15 @@ export async function activate(context: vscode.ExtensionContext): Promise(); + providers.forEach(provider => { + treeDataProviders.set(provider.providerId, provider.getTreeDataProvider()); + }) + return new AzureResourceUniversalResourceProvider(Constants.UNIVERSAL_PROVIDER_ID, new AzureResourceUniversalTreeDataProvider(new AzureResourceUniversalService(treeDataProviders))); }, getSqlManagedInstances(account: azurecore.AzureAccount, subscriptions: azurecore.azureResource.AzureResourceSubscription[], diff --git a/extensions/azurecore/src/test/azureResource/providers/database/databaseTreeDataProvider.test.ts b/extensions/azurecore/src/test/azureResource/providers/database/databaseTreeDataProvider.test.ts index 692ec7bf62..a67ce7689e 100644 --- a/extensions/azurecore/src/test/azureResource/providers/database/databaseTreeDataProvider.test.ts +++ b/extensions/azurecore/src/test/azureResource/providers/database/databaseTreeDataProvider.test.ts @@ -12,12 +12,12 @@ import 'mocha'; import { AzureResourceDatabaseTreeDataProvider } from '../../../../azureResource/providers/database/databaseTreeDataProvider'; import { AzureResourceItemType } from '../../../../azureResource/constants'; -import { IAzureResourceService } from '../../../../azureResource/interfaces'; import { AzureAccount, azureResource } from 'azurecore'; import settings from '../../../../account-provider/providerSettings'; +import { DATABASE_PROVIDER_ID } from '../../../../constants'; // Mock services -let mockDatabaseService: TypeMoq.IMock>; +let mockDatabaseService: TypeMoq.IMock; let mockExtensionContext: TypeMoq.IMock; // Mock test data @@ -76,6 +76,7 @@ const mockDatabases: azureResource.AzureResourceDatabase[] = [ { name: 'mock database 1', id: 'mock-id-1', + provider: DATABASE_PROVIDER_ID, serverName: 'mock database server 1', serverFullName: 'mock database server full name 1', loginName: 'mock login', @@ -88,6 +89,7 @@ const mockDatabases: azureResource.AzureResourceDatabase[] = [ { name: 'mock database 2', id: 'mock-id-2', + provider: DATABASE_PROVIDER_ID, serverName: 'mock database server 2', serverFullName: 'mock database server full name 2', loginName: 'mock login', @@ -101,14 +103,12 @@ const mockDatabases: azureResource.AzureResourceDatabase[] = [ describe('AzureResourceDatabaseTreeDataProvider.info', function (): void { beforeEach(() => { - mockDatabaseService = TypeMoq.Mock.ofType>(); + mockDatabaseService = TypeMoq.Mock.ofType(); mockExtensionContext = TypeMoq.Mock.ofType(); }); it('Should be correct when created.', async function (): Promise { - const treeDataProvider = new AzureResourceDatabaseTreeDataProvider(mockDatabaseService.object, mockExtensionContext.object); - - const treeItem = await treeDataProvider.getResourceTreeItem(mockResourceRootNode); + const treeItem = mockResourceRootNode.treeItem; should(treeItem.id).equal(mockResourceRootNode.treeItem.id); should(treeItem.label).equal(mockResourceRootNode.treeItem.label); should(treeItem.collapsibleState).equal(mockResourceRootNode.treeItem.collapsibleState); @@ -118,7 +118,7 @@ describe('AzureResourceDatabaseTreeDataProvider.info', function (): void { describe('AzureResourceDatabaseTreeDataProvider.getChildren', function (): void { beforeEach(() => { - mockDatabaseService = TypeMoq.Mock.ofType>(); + mockDatabaseService = TypeMoq.Mock.ofType(); mockExtensionContext = TypeMoq.Mock.ofType(); sinon.stub(azdata.accounts, 'getAccountSecurityToken').returns(Promise.resolve(mockToken)); @@ -160,7 +160,7 @@ describe('AzureResourceDatabaseTreeDataProvider.getChildren', function (): void should(child.account).equal(mockAccount); should(child.subscription).equal(mockSubscription); should(child.tenantId).equal(mockTenantId); - should(child.treeItem.id).equal(`databaseServer_${database.serverFullName}.database_${database.name}`); + should(child.treeItem.id).equal(`databaseServer_${mockAccount.key.accountId}${database.serverFullName}.database_${database.id}`); should(child.treeItem.label).equal(`${database.name} (${database.serverName})`); should(child.treeItem.collapsibleState).equal(vscode.TreeItemCollapsibleState.Collapsed); should(child.treeItem.contextValue).equal(AzureResourceItemType.database); diff --git a/extensions/azurecore/src/test/azureResource/providers/databaseServer/databaseServerTreeDataProvider.test.ts b/extensions/azurecore/src/test/azureResource/providers/databaseServer/databaseServerTreeDataProvider.test.ts index 0d0f826a90..b87abb565d 100644 --- a/extensions/azurecore/src/test/azureResource/providers/databaseServer/databaseServerTreeDataProvider.test.ts +++ b/extensions/azurecore/src/test/azureResource/providers/databaseServer/databaseServerTreeDataProvider.test.ts @@ -12,13 +12,13 @@ import 'mocha'; import { AzureResourceDatabaseServerTreeDataProvider } from '../../../../azureResource/providers/databaseServer/databaseServerTreeDataProvider'; import { AzureResourceItemType } from '../../../../azureResource/constants'; -import { IAzureResourceService } from '../../../../azureResource/interfaces'; // Mock services -let mockDatabaseServerService: TypeMoq.IMock>; +let mockDatabaseServerService: TypeMoq.IMock; let mockExtensionContext: TypeMoq.IMock; import settings from '../../../../account-provider/providerSettings'; import { AzureAccount, azureResource } from 'azurecore'; +import { DATABASE_SERVER_PROVIDER_ID } from '../../../../constants'; // Mock test data const mockAccount: AzureAccount = { @@ -75,6 +75,7 @@ const mockDatabaseServers: azureResource.AzureResourceDatabaseServer[] = [ { name: 'mock database server 1', id: 'mock-id-1', + provider: DATABASE_SERVER_PROVIDER_ID, fullName: 'mock database server full name 1', loginName: 'mock login', defaultDatabaseName: 'master', @@ -87,6 +88,7 @@ const mockDatabaseServers: azureResource.AzureResourceDatabaseServer[] = [ { name: 'mock database server 2', id: 'mock-id-2', + provider: DATABASE_SERVER_PROVIDER_ID, fullName: 'mock database server full name 2', loginName: 'mock login', defaultDatabaseName: 'master', @@ -100,14 +102,12 @@ const mockDatabaseServers: azureResource.AzureResourceDatabaseServer[] = [ describe('AzureResourceDatabaseServerTreeDataProvider.info', function (): void { beforeEach(() => { - mockDatabaseServerService = TypeMoq.Mock.ofType>(); + mockDatabaseServerService = TypeMoq.Mock.ofType(); mockExtensionContext = TypeMoq.Mock.ofType(); }); it('Should be correct when created.', async function (): Promise { - const treeDataProvider = new AzureResourceDatabaseServerTreeDataProvider(mockDatabaseServerService.object, mockExtensionContext.object); - - const treeItem = await treeDataProvider.getResourceTreeItem(mockResourceRootNode); + const treeItem = mockResourceRootNode.treeItem; should(treeItem.id).equal(mockResourceRootNode.treeItem.id); should(treeItem.label).equal(mockResourceRootNode.treeItem.label); should(treeItem.collapsibleState).equal(mockResourceRootNode.treeItem.collapsibleState); @@ -117,7 +117,7 @@ describe('AzureResourceDatabaseServerTreeDataProvider.info', function (): void { describe('AzureResourceDatabaseServerTreeDataProvider.getChildren', function (): void { beforeEach(() => { - mockDatabaseServerService = TypeMoq.Mock.ofType>(); + mockDatabaseServerService = TypeMoq.Mock.ofType(); mockExtensionContext = TypeMoq.Mock.ofType(); sinon.stub(azdata.accounts, 'getAccountSecurityToken').returns(Promise.resolve(mockToken)); @@ -159,7 +159,7 @@ describe('AzureResourceDatabaseServerTreeDataProvider.getChildren', function (): should(child.account).equal(mockAccount); should(child.subscription).equal(mockSubscription); should(child.tenantId).equal(mockTenantId); - should(child.treeItem.id).equal(`databaseServer_${databaseServer.id}`); + should(child.treeItem.id).equal(`databaseServer_${mockAccount.key.accountId}${databaseServer.id}`); should(child.treeItem.label).equal(databaseServer.name); should(child.treeItem.collapsibleState).equal(vscode.TreeItemCollapsibleState.Collapsed); should(child.treeItem.contextValue).equal(AzureResourceItemType.databaseServer); diff --git a/extensions/azurecore/src/test/azureResource/resourceService.test.ts b/extensions/azurecore/src/test/azureResource/resourceService.test.ts index ca286aed07..c66065f63f 100644 --- a/extensions/azurecore/src/test/azureResource/resourceService.test.ts +++ b/extensions/azurecore/src/test/azureResource/resourceService.test.ts @@ -58,14 +58,12 @@ describe('AzureResourceService.listResourceProviderIds', function (): void { beforeEach(() => { mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType(); mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType().object])); - mockResourceTreeDataProvider1.setup((o) => o.getResourceTreeItem(TypeMoq.It.isAny())).returns(() => Promise.resolve(TypeMoq.It.isAny())); mockResourceProvider1 = TypeMoq.Mock.ofType(); mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1'); mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object); mockResourceTreeDataProvider2 = TypeMoq.Mock.ofType(); mockResourceTreeDataProvider2.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType().object])); - mockResourceTreeDataProvider2.setup((o) => o.getResourceTreeItem(TypeMoq.It.isAny())).returns(() => Promise.resolve(TypeMoq.It.isAny())); mockResourceProvider2 = TypeMoq.Mock.ofType(); mockResourceProvider2.setup((o) => o.providerId).returns(() => 'mockResourceProvider2'); mockResourceProvider2.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider2.object); @@ -95,7 +93,6 @@ describe('AzureResourceService.getRootChildren', function (): void { beforeEach(() => { mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType(); mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType().object])); - mockResourceTreeDataProvider1.setup((o) => o.getResourceTreeItem(TypeMoq.It.isAny())).returns(() => Promise.resolve(TypeMoq.It.isAny())); mockResourceProvider1 = TypeMoq.Mock.ofType(); mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1'); mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object); @@ -129,7 +126,6 @@ describe('AzureResourceService.getChildren', function (): void { mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType(); mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType().object])); mockResourceTreeDataProvider1.setup((o) => o.getChildren(TypeMoq.It.isAny())).returns(() => Promise.resolve([TypeMoq.Mock.ofType().object])); - mockResourceTreeDataProvider1.setup((o) => o.getResourceTreeItem(TypeMoq.It.isAny())).returns(() => Promise.resolve(TypeMoq.It.isAny())); mockResourceProvider1 = TypeMoq.Mock.ofType(); mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1'); mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object); @@ -156,36 +152,3 @@ describe('AzureResourceService.getChildren', function (): void { fail(); }); }); - -describe('AzureResourceService.getTreeItem', function (): void { - beforeEach(() => { - mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType(); - mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType().object])); - mockResourceTreeDataProvider1.setup((o) => o.getChildren(TypeMoq.It.isAny())).returns(() => Promise.resolve([TypeMoq.Mock.ofType().object])); - mockResourceTreeDataProvider1.setup((o) => o.getResourceTreeItem(TypeMoq.It.isAny())).returns(() => Promise.resolve(TypeMoq.It.isAny())); - mockResourceProvider1 = TypeMoq.Mock.ofType(); - mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1'); - mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object); - - resourceService.clearResourceProviders(); - resourceService.registerResourceProvider(mockResourceProvider1.object); - resourceService.areResourceProvidersLoaded = true; - }); - - it('Should be correct when provider id is correct.', async function (): Promise { - const treeItem = await resourceService.getTreeItem(mockResourceProvider1.object.providerId, TypeMoq.It.isAny()); - should(treeItem).Object(); - }); - - it('Should throw exceptions when provider id is incorrect.', async function (): Promise { - const providerId = 'non_existent_provider_id'; - try { - await resourceService.getRootChildren(providerId, mockAccount, mockSubscription); - } catch (error) { - should(error.message).equal(`Azure resource provider doesn't exist. Id: ${providerId}`); - return; - } - - fail(); - }); -}); diff --git a/extensions/azurecore/src/test/azureResource/resourceTreeNode.test.ts b/extensions/azurecore/src/test/azureResource/resourceTreeNode.test.ts index b4629dec3d..2efec695bf 100644 --- a/extensions/azurecore/src/test/azureResource/resourceTreeNode.test.ts +++ b/extensions/azurecore/src/test/azureResource/resourceTreeNode.test.ts @@ -101,7 +101,6 @@ let appContext: AppContext; describe('AzureResourceResourceTreeNode.info', function (): void { beforeEach(() => { mockResourceTreeDataProvider = TypeMoq.Mock.ofType(); - mockResourceTreeDataProvider.setup((o) => o.getResourceTreeItem(mockResourceRootNode)).returns(() => Promise.resolve(mockResourceRootNode.treeItem)); mockResourceTreeDataProvider.setup((o) => o.getChildren(mockResourceRootNode)).returns(() => Promise.resolve(mockResourceNodes)); mockResourceProvider = TypeMoq.Mock.ofType(); diff --git a/extensions/azurecore/src/test/azureResource/tree/subscriptionTreeNode.test.ts b/extensions/azurecore/src/test/azureResource/tree/subscriptionTreeNode.test.ts index d29808ed0a..cec3182f28 100644 --- a/extensions/azurecore/src/test/azureResource/tree/subscriptionTreeNode.test.ts +++ b/extensions/azurecore/src/test/azureResource/tree/subscriptionTreeNode.test.ts @@ -61,7 +61,7 @@ let mockResourceProvider2: TypeMoq.IMock; const resourceService: AzureResourceService = new AzureResourceService(); -describe('AzureResourceSubscriptionTreeNode.info', function(): void { +describe('AzureResourceSubscriptionTreeNode.info', function (): void { beforeEach(() => { mockExtensionContext = TypeMoq.Mock.ofType(); mockCacheService = TypeMoq.Mock.ofType(); @@ -95,7 +95,7 @@ describe('AzureResourceSubscriptionTreeNode.info', function(): void { }); - it('Should be correct when created.', async function(): Promise { + it('Should be correct when created.', async function (): Promise { const subscriptionTreeNode = new AzureResourceSubscriptionTreeNode(mockAccount, mockSubscription, mockTenantId, appContext, mockTreeChangeHandler.object, TypeMoq.Mock.ofType().object); should(subscriptionTreeNode.nodePathValue).equal(`account_${mockAccount.key.accountId}.subscription_${mockSubscription.id}.tenant_${mockTenantId}`); @@ -113,7 +113,7 @@ describe('AzureResourceSubscriptionTreeNode.info', function(): void { }); }); -describe('AzureResourceSubscriptionTreeNode.getChildren', function(): void { +describe('AzureResourceSubscriptionTreeNode.getChildren', function (): void { beforeEach(() => { mockExtensionContext = TypeMoq.Mock.ofType(); mockCacheService = TypeMoq.Mock.ofType(); @@ -123,8 +123,7 @@ describe('AzureResourceSubscriptionTreeNode.getChildren', function(): void { mockTreeChangeHandler = TypeMoq.Mock.ofType(); mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType(); - mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([{ label: 'Item1'}] as azdata.TreeItem[])); - mockResourceTreeDataProvider1.setup((o) => o.getResourceTreeItem(TypeMoq.It.isAny())).returns(() => Promise.resolve(TypeMoq.It.isAny())); + mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([{ label: 'Item1' }] as azdata.TreeItem[])); mockResourceProvider1 = TypeMoq.Mock.ofType(); mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1'); @@ -132,7 +131,6 @@ describe('AzureResourceSubscriptionTreeNode.getChildren', function(): void { mockResourceTreeDataProvider2 = TypeMoq.Mock.ofType(); mockResourceTreeDataProvider2.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([{ label: 'Item2' }] as azdata.TreeItem[])); - mockResourceTreeDataProvider2.setup((o) => o.getResourceTreeItem(TypeMoq.It.isAny())).returns(() => Promise.resolve(TypeMoq.It.isAny())); mockResourceProvider2 = TypeMoq.Mock.ofType(); mockResourceProvider2.setup((o) => o.providerId).returns(() => 'mockResourceProvider2'); mockResourceProvider2.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider2.object); @@ -148,7 +146,7 @@ describe('AzureResourceSubscriptionTreeNode.getChildren', function(): void { }); - it('Should return resource containers.', async function(): Promise { + it('Should return resource containers.', async function (): Promise { const subscriptionTreeNode = new AzureResourceSubscriptionTreeNode(mockAccount, mockSubscription, mockTenantId, appContext, mockTreeChangeHandler.object, TypeMoq.Mock.ofType().object); const children = await subscriptionTreeNode.getChildren(); diff --git a/extensions/machine-learning/src/test/stubs.ts b/extensions/machine-learning/src/test/stubs.ts index aaafa8ed58..c8249dc98d 100644 --- a/extensions/machine-learning/src/test/stubs.ts +++ b/extensions/machine-learning/src/test/stubs.ts @@ -61,6 +61,9 @@ export class AzurecoreApiStub implements azurecore.IExtension { provideResources(): azurecore.azureResource.IAzureResourceProvider[] { throw new Error('Method not implemented.'); } + getUniversalProvider(): azurecore.azureResource.IAzureUniversalResourceProvider { + throw new Error('Method not implemented.'); + } onEncryptionKeysUpdated: any getEncryptionKeys(): Promise { throw new Error('Method not implemented.');