Introduce Universal resource provider to fix ARM throttling issues (#23078)

This commit is contained in:
Cheena Malhotra
2023-05-10 21:06:23 -07:00
committed by GitHub
parent c292c1e7ce
commit c592c74958
58 changed files with 851 additions and 810 deletions

View File

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

View File

@@ -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<T extends azureResource.AzureResource> {
getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: msRest.ServiceClientCredentials, account: AzureAccount): Promise<T[]>;
export interface IAzureResourceDbService<S extends GraphData, T extends GraphData> extends azureResource.IAzureResourceService {
convertDatabaseResource(resource: T, server?: S): azureResource.AzureResource | undefined;
}
export interface IAzureResourceServerService<T extends GraphData> extends azureResource.IAzureResourceService {
convertServerResource(resource: T): azureResource.AzureResource | undefined;
}

View File

@@ -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<azureResource.AzureResourceDatabaseServer>,
private _extensionContext: ExtensionContext
) {
}
public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider {
return new AzureMonitorTreeDataProvider(this._service, this._extensionContext);
}
public get providerId(): string {
return 'azure.resource.providers.azureMonitor';
}
}

View File

@@ -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<AzureMonitorGraphData> {
public override queryFilter: string = logAnalyticsQuery;
export class AzureMonitorResourceService extends ResourceServiceBase<AzureMonitorGraphData, azureResource.AzureResourceDatabaseServer> {
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: '',

View File

@@ -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<azureResource.AzureResourceDatabaseServer> {
export class AzureMonitorTreeDataProvider extends ResourceTreeDataProviderBase<GraphData, AzureMonitorGraphData> {
private static readonly containerId = 'azure.resource.providers.AzureMonitorContainer';
private static readonly containerLabel = localize('azure.resource.providers.AzureMonitorContainerLabel', "Log Analytics workspace");
public constructor(
databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>,
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<a
savePassword: true,
groupFullName: '',
groupId: '',
providerName: 'LOGANALYTICS',
providerName: logAnalyticsProvider,
saveProfile: false,
options: {},
azureAccount: account.key.accountId,
@@ -55,7 +54,7 @@ export class AzureMonitorTreeDataProvider extends ResourceTreeDataProviderBase<a
azureResourceId: databaseServer.id,
azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint
},
childProvider: 'LOGANALYTICS',
childProvider: logAnalyticsProvider,
type: ExtensionNodeType.Server
};
}

View File

@@ -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 { CosmosDbMongoTreeDataProvider } from './cosmosDbMongoTreeDataProvider';
export class CosmosDbMongoProvider implements azureResource.IAzureResourceProvider {
public constructor(
private _databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>,
private _extensionContext: ExtensionContext
) {
}
public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider {
return new CosmosDbMongoTreeDataProvider(this._databaseServerService, this._extensionContext);
}
public get providerId(): string {
return 'azure.resource.providers.cosmosDbMongo';
}
}

View File

@@ -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<DbServerGraphData> {
public override queryFilter: string = cosmosMongoDbQuery;
interface DbServerGraphData extends GraphData {
properties: {
fullyQualifiedDomainName: string;
administratorLogin: string;
};
}
export class CosmosDbMongoService extends ResourceServiceBase<DbServerGraphData, azureResource.AzureResourceDatabaseServer> {
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: '',

View File

@@ -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<azureResource.AzureResourceDatabaseServer> {
private static readonly COSMOSDG_MONGO_PROVIDER_ID = 'COSMOSDB_MONGO';
export class CosmosDbMongoTreeDataProvider extends ResourceTreeDataProviderBase<GraphData, DbServerGraphData> {
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<azureResource.AzureResourceDatabaseServer>,
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
};
}

View File

@@ -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<azureResource.AzureResourceDatabase>,
private _extensionContext: ExtensionContext
) {
}
public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider {
return new AzureResourceDatabaseTreeDataProvider(this._databaseService, this._extensionContext);
}
public get providerId(): string {
return 'azure.resource.providers.database';
}
}

View File

@@ -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<DbServerGraphData, DatabaseGraphData> {
public queryFilter: string = sqlDatabaseQuery;
interface DatabaseGraphData extends GraphData {
kind: string;
}
export class AzureResourceDatabaseService implements IAzureResourceService<azureResource.AzureResourceDatabase> {
public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise<azureResource.AzureResourceDatabase[]> {
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<GraphData>(resourceClient, subscriptions, sqlServerQuery);
let dbQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, sqlDatabaseQuery);
let serverQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, where + sqlServerQuery);
let dbQueryPromise = queryGraphResources<GraphData>(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<azure
const synapseDBRegExp = new RegExp(`\/subscriptions\/.+\/resourceGroups\/(.+)\/providers\/Microsoft\.Synapse\/workspaces\/(.+)\/databases\/.+`);
dbByGraph.forEach(db => {
// 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<azure
const serverName = founds[2];
let server = servers.find(s => 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;
}
}
}

View File

@@ -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<azureResource.AzureResourceDatabase> {
export class AzureResourceDatabaseTreeDataProvider extends ResourceTreeDataProviderBase<DbServerGraphData, DatabaseGraphData> {
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<azureResource.AzureResourceDatabase>,
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'),

View File

@@ -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<azureResource.AzureResourceDatabaseServer>,
private _extensionContext: ExtensionContext
) {
}
public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider {
return new AzureResourceDatabaseServerTreeDataProvider(this._databaseServerService, this._extensionContext);
}
public get providerId(): string {
return 'azure.resource.providers.databaseServer';
}
}

View File

@@ -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<DbServerGraphData> {
export class AzureResourceDatabaseServerService implements IAzureResourceService<azureResource.AzureResourceDatabaseServer> {
public queryFilter: string = sqlServerQuery;
public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise<azureResource.AzureResourceDatabaseServer[]> {
const convertedResources: azureResource.AzureResourceDatabaseServer[] = [];
const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint });
let serverGraphResources: DbServerGraphData[] = await queryGraphResources<DbServerGraphData>(resourceClient, subscriptions, sqlServerQuery);
let serverGraphResources: DbServerGraphData[] = await queryGraphResources<DbServerGraphData>(resourceClient, subscriptions, where + this.queryFilter);
const ids = new Set<string>();
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,

View File

@@ -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<azureResource.AzureResourceDatabaseServer> {
export class AzureResourceDatabaseServerTreeDataProvider extends ResourceTreeDataProviderBase<GraphData, DbServerGraphData> {
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<azureResource.AzureResourceDatabaseServer>,
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'),

View File

@@ -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<KustoGraphData> {
public override queryFilter: string = kustoClusterQuery;
export class KustoResourceService extends ResourceServiceBase<KustoGraphData, azureResource.AzureResourceDatabaseServer> {
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: '',

View File

@@ -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<azureResource.AzureResourceDatabaseServer> {
export class KustoTreeDataProvider extends ResourceTreeDataProviderBase<GraphData, KustoGraphData> {
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<azureResource.AzureResourceDatabaseServer>,
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<azureRes
savePassword: true,
groupFullName: '',
groupId: '',
providerName: 'KUSTO',
providerName: kustoProvider,
saveProfile: false,
options: {},
azureAccount: account.key.accountId,
@@ -55,7 +54,7 @@ export class KustoTreeDataProvider extends ResourceTreeDataProviderBase<azureRes
azureResourceId: databaseServer.id,
azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint
},
childProvider: 'KUSTO',
childProvider: kustoProvider,
type: ExtensionNodeType.Server
};
}

View File

@@ -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 { MysqlFlexibleServerTreeDataProvider } from './mysqlFlexibleServerTreeDataProvider';
export class MysqlFlexibleServerProvider implements azureResource.IAzureResourceProvider {
public constructor(
private _databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>,
private _extensionContext: ExtensionContext
) {
}
public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider {
return new MysqlFlexibleServerTreeDataProvider(this._databaseServerService, this._extensionContext);
}
public get providerId(): string {
return 'azure.resource.providers.mysqlFlexibleServer';
}
}

View File

@@ -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<DbServerGraphData> {
public override queryFilter: string = mysqlFlexibleServerQuery;
interface DbServerGraphData extends GraphData {
properties: {
fullyQualifiedDomainName: string;
administratorLogin: string;
};
}
export class MysqlFlexibleServerService extends ResourceServiceBase<DbServerGraphData, azureResource.AzureResourceDatabaseServer> {
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: '',

View File

@@ -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<azureResource.AzureResourceDatabaseServer> {
private static readonly MYSQL_FLEXIBLE_SERVER_PROVIDER_ID = 'MySQL';
export class MysqlFlexibleServerTreeDataProvider extends ResourceTreeDataProviderBase<GraphData, DbServerGraphData> {
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<azureResource.AzureResourceDatabaseServer>,
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
};
}

View File

@@ -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<PostgresArcServerGraphData> {
export class PostgresServerArcService extends ResourceServiceBase<PostgresArcServerGraphData, azureResource.AzureResourceDatabaseServer> {
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',

View File

@@ -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<azureResource.AzureResourceDatabaseServer> {
export class PostgresServerArcTreeDataProvider extends ResourceTreeDataProviderBase<GraphData, PostgresArcServerGraphData> {
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<azureResource.AzureResourceDatabaseServer>,
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
};
}

View File

@@ -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<azureResource.AzureResourceDatabaseServer>,
private _extensionContext: ExtensionContext
) {
}
public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider {
return new PostgresServerArcTreeDataProvider(this._databaseServerService, this._extensionContext);
}
public get providerId(): string {
return 'azure.resource.providers.postgresArcServer';
}
}

View File

@@ -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<azureResource.AzureResourceDatabaseServer>,
private _extensionContext: ExtensionContext
) {
}
public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider {
return new PostgresServerTreeDataProvider(this._databaseServerService, this._extensionContext);
}
public get providerId(): string {
return 'azure.resource.providers.postgresServer';
}
}

View File

@@ -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<DbServerGraphData> {
export class PostgresServerService extends ResourceServiceBase<DbServerGraphData, azureResource.AzureResourceDatabaseServer> {
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',

View File

@@ -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<azureResource.AzureResourceDatabaseServer> {
export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase<GraphData, DbServerGraphData> {
private static readonly containerId = 'azure.resource.providers.databaseServer.treeDataProvider.postgresServerContainer';
private static readonly containerLabel = localize('azure.resource.providers.databaseServer.treeDataProvider.postgresServerContainerLabel', "Azure Database for PostgreSQL server");
public constructor(
databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>,
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
};
}

View File

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

View File

@@ -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<DbServerGraphData, azureResource.AzureResourceResourceGroup> {
export class AzureResourceGroupService extends ResourceServiceBase<DbServerGraphData> {
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,

View File

@@ -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<azureResource.AzureResourceDatabaseServer>,
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;
}
}

View File

@@ -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<T extends azureResource.AzureResource> implements azureResource.IAzureResourceTreeDataProvider {
export abstract class ResourceTreeDataProviderBase<S extends GraphData, T extends GraphData> implements azureResource.IAzureResourceTreeDataProvider {
public browseConnectionMode: boolean = false;
public constructor(protected _resourceService: IAzureResourceService<T>) {
public constructor(
protected _resourceService: azureResource.IAzureResourceService | IAzureResourceServerService<T> | IAzureResourceDbService<S, T>) {
}
public async getResourceTreeItem(element: azureResource.IAzureResourceNode): Promise<azdata.TreeItem> {
return element.treeItem;
public getService(): azureResource.IAzureResourceService {
return this._resourceService;
}
public async getChildren(element: azureResource.IAzureResourceNode): Promise<azureResource.IAzureResourceNode[]> {
public async getChildren(parent: azureResource.IAzureResourceNode): Promise<azureResource.IAzureResourceNode[]> {
try {
let resources: T[] = await this.getResources(element);
return resources.map((resource) => <azureResource.IAzureResourceNode>{
account: element.account,
subscription: element.subscription,
tenantId: element.subscription.tenant,
treeItem: this.getTreeItemForResource(resource, element.account)
}).sort((a, b) => (<any>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) => (<any>a.treeItem.label).localeCompare(b.treeItem.label));
} catch (error) {
Logger.error(AzureResourceErrorMessageUtil.getErrorMessage(error));
throw error;
}
}
private async getResources(element: azureResource.IAzureResourceNode): Promise<T[]> {
public convertDataToResource(resource: azureResource.AzureResource, parent: azureResource.IAzureResourceNode): any {
return <azureResource.IAzureResourceNode>{
account: parent.account,
subscription: parent.subscription,
tenantId: parent.subscription.tenant,
treeItem: this.getTreeItemForResource(resource, parent.account)
}
}
private async getResources(element: azureResource.IAzureResourceNode): Promise<azureResource.AzureResource[]> {
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) || <T[]>[];
return resources;
return await this._resourceService.getResources([element.subscription], credential, element.account) || <azureResource.AzureResource[]>[];
}
protected abstract getTreeItemForResource(resource: T, account: AzureAccount): azdata.TreeItem;
public abstract getTreeItemForResource(resource: azureResource.AzureResource, account: AzureAccount): azdata.TreeItem;
public abstract getRootChildren(): Promise<azdata.TreeItem[]>;
}
export interface GraphData {
subscriptionId: string,
subscriptionName?: string,
tenantId: string;
id: string;
name: string;
location: string;
type: string;
resourceGroup: string;
}
export async function queryGraphResources<T extends GraphData>(resourceClient: ResourceGraphClient, subscriptions: azureResource.AzureResourceSubscription[], resourceQuery: string): Promise<T[]> {
const allResources: T[] = [];
let totalProcessed = 0;
@@ -112,34 +104,31 @@ export async function queryGraphResources<T extends GraphData>(resourceClient: R
return allResources;
}
export abstract class ResourceServiceBase<T extends GraphData, U extends azureResource.AzureResource> implements IAzureResourceService<U> {
constructor() {
}
export abstract class ResourceServiceBase<T extends GraphData> implements IAzureResourceServerService<T> {
/**
* 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<U[]> {
const convertedResources: U[] = [];
public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: msRest.ServiceClientCredentials, account: AzureAccount): Promise<azureResource.AzureResource[]> {
const convertedResources: azureResource.AzureResource[] = [];
const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint });
const graphResources = await queryGraphResources<T>(resourceClient, subscriptions, this.query);
const graphResources = await queryGraphResources<T>(resourceClient, subscriptions, where + this.queryFilter);
const ids = new Set<string>();
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;
}

View File

@@ -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<azureResource.AzureResourceDatabaseServer>,
private _extensionContext: ExtensionContext
) {
}
public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider {
return new SqlInstanceTreeDataProvider(this._service, this._extensionContext);
}
public get providerId(): string {
return 'azure.resource.providers.sqlInstance';
}
}

View File

@@ -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<SqlInstanceGraphData> {
export class SqlInstanceResourceService extends ResourceServiceBase<SqlInstanceGraphData, azureResource.AzureResourceDatabaseServer> {
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',

View File

@@ -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<azureResource.AzureResourceDatabaseServer> {
export class SqlInstanceTreeDataProvider extends ResourceTreeDataProviderBase<GraphData, SqlInstanceGraphData> {
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<azureResource.AzureResourceDatabaseServer>,
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'),

View File

@@ -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<azureResource.AzureResourceDatabaseServer>,
private _extensionContext: ExtensionContext
) {
}
public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider {
return new SqlInstanceArcTreeDataProvider(this._service, this._extensionContext);
}
public get providerId(): string {
return 'azure.resource.providers.sqlInstanceArc';
}
}

View File

@@ -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<SqlInstanceArcGraphData> {
export class SqlInstanceArcResourceService extends ResourceServiceBase<SqlInstanceArcGraphData, azureResource.AzureResourceDatabaseServer> {
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',

View File

@@ -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<azureResource.AzureResourceDatabaseServer> {
export class SqlInstanceArcTreeDataProvider extends ResourceTreeDataProviderBase<GraphData, SqlInstanceArcGraphData> {
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<azureResource.AzureResourceDatabaseServer>,
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'),

View File

@@ -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<azureResource.AzureResourceDatabase>,
private _extensionContext: ExtensionContext
) {
}
public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider {
return new AzureResourceSynapseSqlPoolTreeDataProvider(this._synapseSqlPoolService, this._extensionContext);
}
public get providerId(): string {
return 'azure.resource.providers.synapseSqlPool';
}
}

View File

@@ -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 IAzureResourceDbService<SynapseWorkspaceGraphData, SynapseGraphData> {
// 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;
}
export class AzureResourceSynapseService implements IAzureResourceService<azureResource.AzureResourceDatabase> {
}
public queryFilter: string = synapseSqlPoolsQuery;
public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise<azureResource.AzureResourceDatabase[]> {
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<GraphData>(resourceClient, subscriptions, synapseSqlPoolsQuery);
let synapseWorkspaceQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, synapseWorkspacesQuery);
let synapseQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, where + this.queryFilter);
let synapseWorkspaceQueryPromise = queryGraphResources<GraphData>(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<azureR
const serverName = founds[2];
let server = synapseWorkspaceByGraph.find(s => 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!);
}
}
});

View File

@@ -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<azureResource.AzureResourceDatabase> {
export class AzureResourceSynapseSqlPoolTreeDataProvider extends ResourceTreeDataProviderBase<SynapseWorkspaceGraphData, SynapseGraphData> {
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<azureResource.AzureResourceDatabase>,
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'),

View File

@@ -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<azureResource.AzureResourceDatabaseServer>,
private _extensionContext: ExtensionContext
) {
}
public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider {
return new AzureResourceSynapseWorkspaceTreeDataProvider(this._synapseWorkspaceService, this._extensionContext);
}
public get providerId(): string {
return 'azure.resource.providers.synapseWorkspace';
}
}

View File

@@ -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<SynapseWorkspaceGraphData> {
export class AzureResourceSynapseWorkspaceService implements IAzureResourceService<azureResource.AzureResourceDatabaseServer> {
protected get query(): string {
return synapseWorkspacesQuery;
}
public queryFilter: string = synapseWorkspacesQuery;
public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise<azureResource.AzureResourceDatabaseServer[]> {
const convertedResources: azureResource.AzureResourceDatabaseServer[] = [];
const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint });
let serverGraphResources: SynapseWorkspaceGraphData[] = await queryGraphResources<SynapseWorkspaceGraphData>(resourceClient, subscriptions, this.query);
let serverGraphResources: SynapseWorkspaceGraphData[] = await queryGraphResources<SynapseWorkspaceGraphData>(resourceClient, subscriptions, where + this.queryFilter);
const ids = new Set<string>();
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',

View File

@@ -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<azureResource.AzureResourceDatabaseServer> {
export class AzureResourceSynapseWorkspaceTreeDataProvider extends ResourceTreeDataProviderBase<GraphData, SynapseWorkspaceGraphData> {
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<azureResource.AzureResourceDatabaseServer>,
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'),

View File

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

View File

@@ -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<string, azureResource.IAzureResourceTreeDataProvider>
) {
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<azureResource.AzureResource[]> {
const convertedResources: azureResource.AzureResource[] = [];
const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint });
let graphResources: UniversalGraphData[] = await queryGraphResources<UniversalGraphData>(resourceClient, subscriptions, this.queryFilter);
const ids = new Set<string>();
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<UniversalGraphData>;
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<UniversalGraphData, UniversalGraphData>;
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/<subscriptionid>/resourceGroups/<resourcegroupname>/providers/Microsoft.Sql/servers/<servername>/databases/<dbname>'
* 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;
}
}

View File

@@ -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<S extends GraphData, D extends GraphData>
extends ResourceTreeDataProviderBase<S, D> 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<azureResource.IAzureResourceNode[]> {
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<azdata.TreeItem[]> {
throw new Error('Method not supported');
}
public async getAllChildren(account: AzureAccount, subscriptions: azureResource.AzureResourceSubscription[]): Promise<azureResource.IAzureResourceNode[]> {
try {
let resources: azureResource.AzureResource[] = await this.getAllResources(account, subscriptions);
return resources.map((resource) => <azureResource.IAzureResourceNode>{
account: account,
subscription: resource.subscription,
tenantId: resource.tenant,
treeItem: this.getTreeItemForResource(resource, account)
}).sort((a, b) => (<any>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<azureResource.AzureResource[]> {
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;
}
}

View File

@@ -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<string[]> {
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<IAzureResourceNodeWithProviderId[]> {
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 = <azdata.TreeItem[]>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<IAzureResourceNodeWithProviderId[]> {
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 = <azureResource.IAzureResourceTreeDataProvider>this._treeDataProviders[resourceProviderId];
treeDataProvider.browseConnectionMode = browseConnectionMode;
const children = await treeDataProvider.getChildren(element);
const children = <azureResource.IAzureResourceNode[]>await treeDataProvider.getChildren(element);
return children.map((child) => <IAzureResourceNodeWithProviderId>{
resourceProviderId: resourceProviderId,
@@ -72,15 +77,16 @@ export class AzureResourceService {
});
}
public async getTreeItem(resourceProviderId: string, element: azureResource.IAzureResourceNode): Promise<TreeItem> {
public async getAllChildren(account: AzureAccount, subscriptions: azureResource.AzureResourceSubscription[], browseConnectionMode: boolean = false): Promise<IAzureResourceNodeWithProviderId[]> {
await this.ensureResourceProvidersRegistered();
const treeDataProvider = <azureResource.IAzureUniversalTreeDataProvider>this._universalProvider?.getTreeDataProvider();
treeDataProvider.browseConnectionMode = browseConnectionMode;
const children = <azureResource.IAzureResourceNode[]>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) => <IAzureResourceNodeWithProviderId>{
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 <azureResource.IAzureResourceProvider[]>extension.exports.provideResources()) {
if (resourceProvider) {
if (resourceProvider && resourceProvider.providerId !== UNIVERSAL_PROVIDER_ID) {
this.doRegisterResourceProvider(resourceProvider);
}
}
}
if (extension.exports && extension.exports.getUniversalProvider) {
this._universalProvider = <azureResource.IAzureUniversalResourceProvider>extension.exports.getUniversalProvider();
}
}
}
@@ -122,5 +131,4 @@ export class AzureResourceService {
this._resourceProviders[resourceProvider.providerId] = resourceProvider;
this._treeDataProviders[resourceProvider.providerId] = resourceProvider.getTreeDataProvider();
}
}

View File

@@ -53,7 +53,7 @@ export class AzureResourceResourceTreeNode extends TreeNode {
}
public getTreeItem(): TreeItem | Promise<TreeItem> {
return this._resourceService.getTreeItem(this.resourceNodeWithProviderId.resourceProviderId, this.resourceNodeWithProviderId.resourceNode);
return this.resourceNodeWithProviderId.resourceNode.treeItem;
}
public getNodeInfo(): NodeInfo {

View File

@@ -63,7 +63,6 @@ export class ConnectionDialogTreeProvider implements vscode.TreeDataProvider<Tre
for (const account of accounts) {
try {
const accountNode = new FlatAccountTreeNode(account, this.appContext, this);
await accountNode.updateLabel();
accountNodes.push(accountNode);
}
catch (error) {

View File

@@ -43,8 +43,7 @@ export class FlatAccountTreeNode extends AzureResourceContainerTreeNodeBase {
this.treeChangeHandler.notifyNodeChanged(this);
});
this._loader.onLoadingStatusChanged(async () => {
await this.updateLabel();
this._loader.onLoadingStatusChanged(() => {
this.treeChangeHandler.notifyNodeChanged(this);
});
}
@@ -69,6 +68,7 @@ export class FlatAccountTreeNode extends AzureResourceContainerTreeNodeBase {
public async getChildren(): Promise<TreeNode[]> {
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,12 +208,7 @@ 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);
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) => {
@@ -222,9 +216,6 @@ class FlatAccountTreeNodeLoader {
});
newNodesAvailable = true;
}
}
}
}
// Create "No Resources Found" message node if no resources found under azure account.
if (this._nodes.length === 0) {
this._nodes.push(AzureResourceMessageTreeNode.create(localize('azure.resource.flatAccountTreeNode.noResourcesLabel', "No Resources found."), this._accountNode))

View File

@@ -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<T extends azureResource.AzureGraphResource>(
account: AzureAccount,
subscriptions: azureResource.AzureResourceSubscription[],

View File

@@ -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<T extends azureResource.AzureGraphResource>(account: AzureAccount, subscriptions: azureResource.AzureResourceSubscription[], ignoreErrors: boolean, query: string): Promise<ResourceQueryResult<T>>;
/**
* 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<IAzureResourceNode[]>;
}
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<IAzureResourceNode[]>;
/**
* 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<azdata.TreeItem>;
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<AzureResource[]>;
}
export interface AzureResourceSubscription extends Omit<AzureResource, 'subscription'> {
}

View File

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

View File

@@ -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<azurec
return azureResourceUtils.getLocations(appContext, account, subscription, ignoreErrors);
},
provideResources(): azurecore.azureResource.IAzureResourceProvider[] {
const arcFeaturedEnabled = vscode.workspace.getConfiguration(Constants.AzureSection).get(Constants.EnableArcFeaturesSection);
const providers: azurecore.azureResource.IAzureResourceProvider[] = [
new KustoProvider(new KustoResourceService(), extensionContext),
new AzureMonitorProvider(new AzureMonitorResourceService(), extensionContext),
new AzureResourceDatabaseServerProvider(new AzureResourceDatabaseServerService(), extensionContext),
new AzureResourceDatabaseProvider(new AzureResourceDatabaseService(), extensionContext),
new AzureResourceSynapseSqlPoolProvider(new AzureResourceSynapseService(), extensionContext),
new AzureResourceSynapseWorkspaceProvider(new AzureResourceSynapseWorkspaceService(), extensionContext),
new SqlInstanceProvider(new SqlInstanceResourceService(), extensionContext),
new PostgresServerProvider(new PostgresServerService(), extensionContext),
new CosmosDbMongoProvider(new CosmosDbMongoService(), extensionContext),
new MysqlFlexibleServerProvider(new MysqlFlexibleServerService(), extensionContext)
];
if (arcFeaturedEnabled) {
providers.push(
new SqlInstanceArcProvider(new SqlInstanceArcResourceService(), extensionContext),
new PostgresServerArcProvider(new PostgresServerArcService(), extensionContext)
);
}
return providers;
return azureResourceUtils.getAllResourceProviders(extensionContext);
},
getUniversalProvider(): azurecore.azureResource.IAzureUniversalResourceProvider {
let providers = azureResourceUtils.getAllResourceProviders(extensionContext);
let treeDataProviders = new Map<string, azurecore.azureResource.IAzureResourceTreeDataProvider>();
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[],

View File

@@ -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<IAzureResourceService<azureResource.AzureResourceDatabase>>;
let mockDatabaseService: TypeMoq.IMock<azureResource.IAzureResourceService>;
let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
// 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<IAzureResourceService<azureResource.AzureResourceDatabase>>();
mockDatabaseService = TypeMoq.Mock.ofType<azureResource.IAzureResourceService>();
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
});
it('Should be correct when created.', async function (): Promise<void> {
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<IAzureResourceService<azureResource.AzureResourceDatabase>>();
mockDatabaseService = TypeMoq.Mock.ofType<azureResource.IAzureResourceService>();
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
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);

View File

@@ -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<IAzureResourceService<azureResource.AzureResourceDatabaseServer>>;
let mockDatabaseServerService: TypeMoq.IMock<azureResource.IAzureResourceService>;
let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
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<IAzureResourceService<azureResource.AzureResourceDatabaseServer>>();
mockDatabaseServerService = TypeMoq.Mock.ofType<azureResource.IAzureResourceService>();
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
});
it('Should be correct when created.', async function (): Promise<void> {
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<IAzureResourceService<azureResource.AzureResourceDatabaseServer>>();
mockDatabaseServerService = TypeMoq.Mock.ofType<azureResource.IAzureResourceService>();
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
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);

View File

@@ -58,14 +58,12 @@ describe('AzureResourceService.listResourceProviderIds', function (): void {
beforeEach(() => {
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
mockResourceTreeDataProvider1.setup((o) => o.getResourceTreeItem(TypeMoq.It.isAny())).returns(() => Promise.resolve(TypeMoq.It.isAny()));
mockResourceProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1');
mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object);
mockResourceTreeDataProvider2 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider2.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
mockResourceTreeDataProvider2.setup((o) => o.getResourceTreeItem(TypeMoq.It.isAny())).returns(() => Promise.resolve(TypeMoq.It.isAny()));
mockResourceProvider2 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
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<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
mockResourceTreeDataProvider1.setup((o) => o.getResourceTreeItem(TypeMoq.It.isAny())).returns(() => Promise.resolve(TypeMoq.It.isAny()));
mockResourceProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
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<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
mockResourceTreeDataProvider1.setup((o) => o.getChildren(TypeMoq.It.isAny())).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azureResource.IAzureResourceNode>().object]));
mockResourceTreeDataProvider1.setup((o) => o.getResourceTreeItem(TypeMoq.It.isAny())).returns(() => Promise.resolve(TypeMoq.It.isAny()));
mockResourceProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
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<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object]));
mockResourceTreeDataProvider1.setup((o) => o.getChildren(TypeMoq.It.isAny())).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azureResource.IAzureResourceNode>().object]));
mockResourceTreeDataProvider1.setup((o) => o.getResourceTreeItem(TypeMoq.It.isAny())).returns(() => Promise.resolve(TypeMoq.It.isAny()));
mockResourceProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
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<void> {
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<void> {
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();
});
});

View File

@@ -101,7 +101,6 @@ let appContext: AppContext;
describe('AzureResourceResourceTreeNode.info', function (): void {
beforeEach(() => {
mockResourceTreeDataProvider = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
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<azureResource.IAzureResourceProvider>();

View File

@@ -124,7 +124,6 @@ describe('AzureResourceSubscriptionTreeNode.getChildren', function(): void {
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
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()));
mockResourceProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1');
@@ -132,7 +131,6 @@ describe('AzureResourceSubscriptionTreeNode.getChildren', function(): void {
mockResourceTreeDataProvider2 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
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<azureResource.IAzureResourceProvider>();
mockResourceProvider2.setup((o) => o.providerId).returns(() => 'mockResourceProvider2');
mockResourceProvider2.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider2.object);

View File

@@ -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<azurecore.CacheEncryptionKeys> {
throw new Error('Method not implemented.');