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', 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 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. * 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'; 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 { export interface IAzureResourceSubscriptionService {
/** /**
* Gets subscriptions for the given account. Any errors that occur while fetching the subscriptions for each tenant * 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; resourceNode: azureResource.IAzureResourceNode;
} }
export interface IAzureResourceService<T extends azureResource.AzureResource> { export interface IAzureResourceDbService<S extends GraphData, T extends GraphData> extends azureResource.IAzureResourceService {
getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: msRest.ServiceClientCredentials, account: AzureAccount): Promise<T[]>; 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 { azureResource } from 'azurecore';
import { logAnalyticsQuery } from '../queryStringConstants'; 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 { export class AzureMonitorResourceService extends ResourceServiceBase<AzureMonitorGraphData> {
properties: { public override queryFilter: string = logAnalyticsQuery;
fullyQualifiedDomainName: string;
administratorLogin: string;
uri: string;
customerId: string
};
}
export class AzureMonitorResourceService extends ResourceServiceBase<AzureMonitorGraphData, azureResource.AzureResourceDatabaseServer> { public convertServerResource(resource: AzureMonitorGraphData): azureResource.AzureResourceDatabaseServer {
protected get query(): string {
return logAnalyticsQuery;
}
protected convertResource(resource: AzureMonitorGraphData): azureResource.AzureResourceDatabaseServer {
return { return {
id: resource.id, id: resource.id,
name: resource.name, name: resource.name,
provider: AZURE_MONITOR_PROVIDER_ID,
fullName: resource.properties.customerId, fullName: resource.properties.customerId,
loginName: '', loginName: '',
defaultDatabaseName: '', defaultDatabaseName: '',

View File

@@ -8,27 +8,26 @@ import { TreeItemCollapsibleState, ExtensionContext } from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
import { AzureResourceItemType } from '../../constants'; import { AzureResourceItemType, AzureResourcePrefixes, logAnalyticsProvider } from '../../constants';
import { generateGuid } from '../../utils'; import { generateGuid } from '../../utils';
import { IAzureResourceService } from '../../interfaces'; import { AzureMonitorGraphData, GraphData } from '../../interfaces';
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
import { AzureAccount, azureResource } from 'azurecore'; 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 containerId = 'azure.resource.providers.AzureMonitorContainer';
private static readonly containerLabel = localize('azure.resource.providers.AzureMonitorContainerLabel', "Log Analytics workspace"); private static readonly containerLabel = localize('azure.resource.providers.AzureMonitorContainerLabel', "Log Analytics workspace");
public constructor( public constructor(
databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>, databaseServerService: azureResource.IAzureResourceService,
private _extensionContext: ExtensionContext private _extensionContext: ExtensionContext
) { ) {
super(databaseServerService); super(databaseServerService);
} }
public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem {
protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem {
return { 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, label: this.browseConnectionMode ? `${databaseServer.name} (${AzureMonitorTreeDataProvider.containerLabel}, ${databaseServer.subscription.name})` : databaseServer.name,
iconPath: { iconPath: {
dark: this._extensionContext.asAbsolutePath('resources/dark/azure_monitor_dark.svg'), dark: this._extensionContext.asAbsolutePath('resources/dark/azure_monitor_dark.svg'),
@@ -47,7 +46,7 @@ export class AzureMonitorTreeDataProvider extends ResourceTreeDataProviderBase<a
savePassword: true, savePassword: true,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
providerName: 'LOGANALYTICS', providerName: logAnalyticsProvider,
saveProfile: false, saveProfile: false,
options: {}, options: {},
azureAccount: account.key.accountId, azureAccount: account.key.accountId,
@@ -55,7 +54,7 @@ export class AzureMonitorTreeDataProvider extends ResourceTreeDataProviderBase<a
azureResourceId: databaseServer.id, azureResourceId: databaseServer.id,
azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint
}, },
childProvider: 'LOGANALYTICS', childProvider: logAnalyticsProvider,
type: ExtensionNodeType.Server 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 { azureResource } from 'azurecore';
import { cosmosMongoDbQuery } from '../../queryStringConstants'; 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 { public convertServerResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer | undefined {
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 {
return { return {
id: resource.id, id: resource.id,
name: resource.name, name: resource.name,
provider: COSMOSDB_MONGO_PROVIDER_ID,
fullName: resource.properties.fullyQualifiedDomainName, fullName: resource.properties.fullyQualifiedDomainName,
loginName: resource.properties.administratorLogin, loginName: resource.properties.administratorLogin,
defaultDatabaseName: '', defaultDatabaseName: '',

View File

@@ -7,28 +7,27 @@ import { TreeItemCollapsibleState, ExtensionContext } from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
import { AzureResourceItemType } from '../../../constants'; import { AzureResourceItemType, AzureResourcePrefixes, cosmosDBProvider } from '../../../constants';
import { generateGuid } from '../../../utils'; import { generateGuid } from '../../../utils';
import { IAzureResourceService } from '../../../interfaces'; import { DbServerGraphData, GraphData } from '../../../interfaces';
import { ResourceTreeDataProviderBase } from '../../resourceTreeDataProviderBase'; import { ResourceTreeDataProviderBase } from '../../resourceTreeDataProviderBase';
import { AzureAccountProperties, azureResource } from 'azurecore'; import { AzureAccountProperties, azureResource } from 'azurecore';
import * as azdata from 'azdata'; import * as azdata from 'azdata';
export class CosmosDbMongoTreeDataProvider extends ResourceTreeDataProviderBase<azureResource.AzureResourceDatabaseServer> { export class CosmosDbMongoTreeDataProvider extends ResourceTreeDataProviderBase<GraphData, DbServerGraphData> {
private static readonly COSMOSDG_MONGO_PROVIDER_ID = 'COSMOSDB_MONGO';
private static readonly CONTAINER_ID = 'azure.resource.providers.databaseServer.treeDataProvider.cosmosDbMongoContainer'; 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"); private static readonly CONTAINER_LABEL = localize('azure.resource.providers.databaseServer.treeDataProvider.cosmosDbMongoContainerLabel', "CosmosDB for Mongo");
public constructor( public constructor(
databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>, databaseServerService: azureResource.IAzureResourceService,
private _extensionContext: ExtensionContext private _extensionContext: ExtensionContext
) { ) {
super(databaseServerService); super(databaseServerService);
} }
protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: azdata.Account): azdata.TreeItem { public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: azdata.Account): azdata.TreeItem {
return { 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)`, label: `${databaseServer.name} (CosmosDB Mongo API)`,
iconPath: { iconPath: {
dark: this._extensionContext.asAbsolutePath('resources/dark/cosmosdb_inverse.svg'), dark: this._extensionContext.asAbsolutePath('resources/dark/cosmosdb_inverse.svg'),
@@ -46,7 +45,7 @@ export class CosmosDbMongoTreeDataProvider extends ResourceTreeDataProviderBase<
savePassword: true, savePassword: true,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
providerName: CosmosDbMongoTreeDataProvider.COSMOSDG_MONGO_PROVIDER_ID, providerName: cosmosDBProvider,
saveProfile: false, saveProfile: false,
options: {}, options: {},
azureAccount: account.key.accountId, azureAccount: account.key.accountId,
@@ -54,7 +53,7 @@ export class CosmosDbMongoTreeDataProvider extends ResourceTreeDataProviderBase<
azureResourceId: databaseServer.id, azureResourceId: databaseServer.id,
azurePortalEndpoint: (account.properties as AzureAccountProperties).providerSettings.settings.portalEndpoint azurePortalEndpoint: (account.properties as AzureAccountProperties).providerSettings.settings.portalEndpoint
}, },
childProvider: CosmosDbMongoTreeDataProvider.COSMOSDG_MONGO_PROVIDER_ID, childProvider: cosmosDBProvider,
type: azdata.ExtensionNodeType.Server 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 { ServiceClientCredentials } from '@azure/ms-rest-js';
import { IAzureResourceService } from '../../interfaces'; import { DatabaseGraphData, DbServerGraphData, GraphData, IAzureResourceDbService } from '../../interfaces';
import { DbServerGraphData } from '../databaseServer/databaseServerService'; import { sqlServerQuery, sqlDatabaseQuery, where } from '../queryStringConstants';
import { sqlServerQuery, sqlDatabaseQuery } from '../queryStringConstants';
import { ResourceGraphClient } from '@azure/arm-resourcegraph'; import { ResourceGraphClient } from '@azure/arm-resourcegraph';
import { queryGraphResources, GraphData } from '../resourceTreeDataProviderBase'; import { queryGraphResources } from '../resourceTreeDataProviderBase';
import { AzureAccount, azureResource } from 'azurecore'; 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[]> { public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise<azureResource.AzureResourceDatabase[]> {
const databases: azureResource.AzureResourceDatabase[] = []; const databases: azureResource.AzureResourceDatabase[] = [];
const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint }); 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) // Query servers and databases in parallel (start all promises before waiting on the 1st)
let serverQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, sqlServerQuery); let serverQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, where + sqlServerQuery);
let dbQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, sqlDatabaseQuery); let dbQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, where + this.queryFilter);
let servers: DbServerGraphData[] = await serverQueryPromise as DbServerGraphData[]; let servers: DbServerGraphData[] = await serverQueryPromise as DbServerGraphData[];
let dbByGraph: DatabaseGraphData[] = await dbQueryPromise as DatabaseGraphData[]; 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\/.+`); const synapseDBRegExp = new RegExp(`\/subscriptions\/.+\/resourceGroups\/(.+)\/providers\/Microsoft\.Synapse\/workspaces\/(.+)\/databases\/.+`);
dbByGraph.forEach(db => { 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); let serversForRg = rgMap.get(db.resourceGroup);
if (serversForRg && !db.kind.endsWith('system') && (svrIdRegExp.test(db.id) || synapseDBRegExp.test(db.id))) { if (serversForRg && !db.kind.endsWith('system') && (svrIdRegExp.test(db.id) || synapseDBRegExp.test(db.id))) {
const founds = svrIdRegExp.exec(db.id) ?? synapseDBRegExp.exec(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]; const serverName = founds[2];
let server = servers.find(s => s.name === serverName); let server = servers.find(s => s.name === serverName);
if (server) { if (server) {
databases.push({ databases.push(this.convertDatabaseResource(db, server)!);
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
});
} }
} }
}); });
return databases; 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'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
import { AzureResourceItemType, mssqlProvider } from '../../../azureResource/constants'; import { AzureResourceItemType, AzureResourcePrefixes, mssqlProvider } from '../../../azureResource/constants';
import { generateGuid } from '../../utils'; import { generateGuid } from '../../utils';
import { IAzureResourceService } from '../../interfaces'; import { DatabaseGraphData, DbServerGraphData } from '../../interfaces';
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
import { AzureAccount, azureResource } from 'azurecore'; 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 containerId = 'azure.resource.providers.database.treeDataProvider.databaseContainer';
private static readonly containerLabel = localize('azure.resource.providers.database.treeDataProvider.databaseContainerLabel', "SQL database"); private static readonly containerLabel = localize('azure.resource.providers.database.treeDataProvider.databaseContainerLabel', "SQL database");
public constructor( public constructor(
databaseService: IAzureResourceService<azureResource.AzureResourceDatabase>, databaseService: azureResource.IAzureResourceService,
private _extensionContext: vscode.ExtensionContext private _extensionContext: vscode.ExtensionContext
) { ) {
super(databaseService); super(databaseService);
} }
protected getTreeItemForResource(database: azureResource.AzureResourceDatabase, account: AzureAccount): TreeItem { public getTreeItemForResource(database: azureResource.AzureResourceDatabase, account: AzureAccount): TreeItem {
return { 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})`, label: this.browseConnectionMode ? `${database.serverName}/${database.name} (${AzureResourceDatabaseTreeDataProvider.containerLabel}, ${database.subscription.name})` : `${database.name} (${database.serverName})`,
iconPath: { iconPath: {
dark: this._extensionContext.asAbsolutePath('resources/dark/sql_database_inverse.svg'), 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 { ServiceClientCredentials } from '@azure/ms-rest-js';
import { ResourceGraphClient } from '@azure/arm-resourcegraph'; import { ResourceGraphClient } from '@azure/arm-resourcegraph';
import { GraphData, queryGraphResources } from '../resourceTreeDataProviderBase'; import { queryGraphResources } from '../resourceTreeDataProviderBase';
import { sqlServerQuery } from '../queryStringConstants'; import { sqlServerQuery, where } from '../queryStringConstants';
import { azureResource, AzureAccount } from 'azurecore'; 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 { export class AzureResourceDatabaseServerService implements IAzureResourceServerService<DbServerGraphData> {
properties: {
fullyQualifiedDomainName: string;
administratorLogin: string;
};
}
export class AzureResourceDatabaseServerService implements IAzureResourceService<azureResource.AzureResourceDatabaseServer> { public queryFilter: string = sqlServerQuery;
public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise<azureResource.AzureResourceDatabaseServer[]> { public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise<azureResource.AzureResourceDatabaseServer[]> {
const convertedResources: azureResource.AzureResourceDatabaseServer[] = []; const convertedResources: azureResource.AzureResourceDatabaseServer[] = [];
const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint }); 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>(); const ids = new Set<string>();
serverGraphResources.forEach((res) => { serverGraphResources.forEach((res) => {
if (!ids.has(res.id)) { if (!ids.has(res.id)) {
ids.add(res.id); ids.add(res.id);
res.subscriptionName = subscriptions.find(sub => sub.id === res.subscriptionId)?.name; res.subscriptionName = subscriptions.find(sub => sub.id === res.subscriptionId)?.name;
const converted = this.convertResource(res); const converted = this.convertServerResource(res);
convertedResources.push(converted); convertedResources.push(converted!);
} }
}); });
return convertedResources; return convertedResources;
} }
protected convertResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer { public convertServerResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer | undefined {
return { return {
id: resource.id, id: resource.id,
name: resource.name, 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. // Determine if resource object is for Synapse Workspace or not and get the needed property from the correct place.
fullName: resource.properties.fullyQualifiedDomainName, fullName: resource.properties.fullyQualifiedDomainName,
loginName: resource.properties.administratorLogin, loginName: resource.properties.administratorLogin,

View File

@@ -8,26 +8,26 @@ import * as vscode from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
import { AzureResourceItemType, mssqlProvider } from '../../../azureResource/constants'; import { AzureResourceItemType, AzureResourcePrefixes, mssqlProvider } from '../../../azureResource/constants';
import { generateGuid } from '../../utils'; import { generateGuid } from '../../utils';
import { IAzureResourceService } from '../../interfaces'; import { DbServerGraphData, GraphData } from '../../interfaces';
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
import { AzureAccount, azureResource } from 'azurecore'; 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 containerId = 'azure.resource.providers.databaseServer.treeDataProvider.databaseServerContainer';
private static readonly containerLabel = localize('azure.resource.providers.databaseServer.treeDataProvider.databaseServerContainerLabel', "SQL server"); private static readonly containerLabel = localize('azure.resource.providers.databaseServer.treeDataProvider.databaseServerContainerLabel', "SQL server");
public constructor( public constructor(
databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>, databaseServerService: azureResource.IAzureResourceService,
private _extensionContext: vscode.ExtensionContext private _extensionContext: vscode.ExtensionContext
) { ) {
super(databaseServerService); super(databaseServerService);
} }
protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem {
return { 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, label: this.browseConnectionMode ? `${databaseServer.name} (${AzureResourceDatabaseServerTreeDataProvider.containerLabel}, ${databaseServer.subscription.name})` : databaseServer.name,
iconPath: { iconPath: {
dark: this._extensionContext.asAbsolutePath('resources/dark/sql_server_inverse.svg'), dark: this._extensionContext.asAbsolutePath('resources/dark/sql_server_inverse.svg'),

View File

@@ -5,26 +5,18 @@
import { azureResource } from 'azurecore'; import { azureResource } from 'azurecore';
import { kustoClusterQuery } from '../queryStringConstants'; 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 { export class KustoResourceService extends ResourceServiceBase<KustoGraphData> {
properties: { public override queryFilter: string = kustoClusterQuery;
fullyQualifiedDomainName: string;
administratorLogin: string;
uri: string;
};
}
export class KustoResourceService extends ResourceServiceBase<KustoGraphData, azureResource.AzureResourceDatabaseServer> { public convertServerResource(resource: KustoGraphData): azureResource.AzureResourceDatabaseServer | undefined {
protected get query(): string {
return kustoClusterQuery;
}
protected convertResource(resource: KustoGraphData): azureResource.AzureResourceDatabaseServer {
return { return {
id: resource.id, id: resource.id,
name: resource.name, name: resource.name,
provider: KUSTO_PROVIDER_ID,
fullName: resource.properties.uri.replace('https://', ''), fullName: resource.properties.uri.replace('https://', ''),
loginName: '', loginName: '',
defaultDatabaseName: '', defaultDatabaseName: '',

View File

@@ -8,27 +8,26 @@ import { TreeItemCollapsibleState, ExtensionContext } from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
import { AzureResourceItemType } from '../../constants'; import { AzureResourceItemType, AzureResourcePrefixes, kustoProvider } from '../../constants';
import { generateGuid } from '../../utils'; import { generateGuid } from '../../utils';
import { IAzureResourceService } from '../../interfaces'; import { GraphData, KustoGraphData } from '../../interfaces';
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
import { AzureAccount, azureResource } from 'azurecore'; 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 containerId = 'azure.resource.providers.KustoContainer';
private static readonly containerLabel = localize('azure.resource.providers.KustoContainerLabel', "Azure Data Explorer Cluster"); private static readonly containerLabel = localize('azure.resource.providers.KustoContainerLabel', "Azure Data Explorer Cluster");
public constructor( public constructor(
databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>, databaseServerService: azureResource.IAzureResourceService,
private _extensionContext: ExtensionContext private _extensionContext: ExtensionContext
) { ) {
super(databaseServerService); super(databaseServerService);
} }
public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem {
protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem {
return { 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, label: this.browseConnectionMode ? `${databaseServer.name} (${KustoTreeDataProvider.containerLabel}, ${databaseServer.subscription.name})` : databaseServer.name,
iconPath: { iconPath: {
dark: this._extensionContext.asAbsolutePath('resources/dark/azureDE_inverse.svg'), dark: this._extensionContext.asAbsolutePath('resources/dark/azureDE_inverse.svg'),
@@ -47,7 +46,7 @@ export class KustoTreeDataProvider extends ResourceTreeDataProviderBase<azureRes
savePassword: true, savePassword: true,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
providerName: 'KUSTO', providerName: kustoProvider,
saveProfile: false, saveProfile: false,
options: {}, options: {},
azureAccount: account.key.accountId, azureAccount: account.key.accountId,
@@ -55,7 +54,7 @@ export class KustoTreeDataProvider extends ResourceTreeDataProviderBase<azureRes
azureResourceId: databaseServer.id, azureResourceId: databaseServer.id,
azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint
}, },
childProvider: 'KUSTO', childProvider: kustoProvider,
type: ExtensionNodeType.Server 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 { azureResource } from 'azurecore';
import { mysqlFlexibleServerQuery } from '../queryStringConstants'; 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 { public convertServerResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer | undefined {
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 {
return { return {
id: resource.id, id: resource.id,
name: resource.name, name: resource.name,
provider: MYSQL_FLEXIBLE_SERVER_PROVIDER_ID,
fullName: resource.properties.fullyQualifiedDomainName, fullName: resource.properties.fullyQualifiedDomainName,
loginName: resource.properties.administratorLogin, loginName: resource.properties.administratorLogin,
defaultDatabaseName: '', defaultDatabaseName: '',

View File

@@ -7,28 +7,27 @@ import { TreeItemCollapsibleState, ExtensionContext } from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
import { AzureResourceItemType } from '../../constants'; import { AzureResourceItemType, AzureResourcePrefixes, mySqlProvider } from '../../constants';
import { generateGuid } from '../../utils'; import { generateGuid } from '../../utils';
import { IAzureResourceService } from '../../interfaces'; import { DbServerGraphData, GraphData } from '../../interfaces';
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
import { AzureAccountProperties, azureResource } from 'azurecore'; import { AzureAccountProperties, azureResource } from 'azurecore';
import { Account, ExtensionNodeType, TreeItem, connection } from 'azdata'; import { Account, ExtensionNodeType, TreeItem, connection } from 'azdata';
export class MysqlFlexibleServerTreeDataProvider extends ResourceTreeDataProviderBase<azureResource.AzureResourceDatabaseServer> { export class MysqlFlexibleServerTreeDataProvider extends ResourceTreeDataProviderBase<GraphData, DbServerGraphData> {
private static readonly MYSQL_FLEXIBLE_SERVER_PROVIDER_ID = 'MySQL';
private static readonly CONTAINER_ID = 'azure.resource.providers.databaseServer.treeDataProvider.mysqlFlexibleServerContainer'; 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"); private static readonly CONTAINER_LABEL = localize('azure.resource.providers.databaseServer.treeDataProvider.mysqlFlexibleServerContainerLabel', "Azure Database for MySQL Flexible server");
public constructor( public constructor(
databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>, databaseServerService: azureResource.IAzureResourceService,
private _extensionContext: ExtensionContext private _extensionContext: ExtensionContext
) { ) {
super(databaseServerService); super(databaseServerService);
} }
protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: Account): TreeItem { public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: Account): TreeItem {
return { 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, label: this.browseConnectionMode ? `${databaseServer.name} (${MysqlFlexibleServerTreeDataProvider.CONTAINER_LABEL}, ${databaseServer.subscription.name})` : databaseServer.name,
iconPath: { iconPath: {
dark: this._extensionContext.asAbsolutePath('resources/dark/mysql_server_inverse.svg'), dark: this._extensionContext.asAbsolutePath('resources/dark/mysql_server_inverse.svg'),
@@ -47,7 +46,7 @@ export class MysqlFlexibleServerTreeDataProvider extends ResourceTreeDataProvide
savePassword: true, savePassword: true,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
providerName: MysqlFlexibleServerTreeDataProvider.MYSQL_FLEXIBLE_SERVER_PROVIDER_ID, providerName: mySqlProvider,
saveProfile: false, saveProfile: false,
options: { options: {
}, },
@@ -56,7 +55,7 @@ export class MysqlFlexibleServerTreeDataProvider extends ResourceTreeDataProvide
azureResourceId: databaseServer.id, azureResourceId: databaseServer.id,
azurePortalEndpoint: (account.properties as AzureAccountProperties).providerSettings.settings.portalEndpoint azurePortalEndpoint: (account.properties as AzureAccountProperties).providerSettings.settings.portalEndpoint
}, },
childProvider: MysqlFlexibleServerTreeDataProvider.MYSQL_FLEXIBLE_SERVER_PROVIDER_ID, childProvider: mySqlProvider,
type: ExtensionNodeType.Server type: ExtensionNodeType.Server
}; };
} }

View File

@@ -3,26 +3,21 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * 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 { azureResource } from 'azurecore';
import { postgresArcServerQuery } from '../queryStringConstants'; import { postgresArcServerQuery } from '../queryStringConstants';
import { PostgresArcServerGraphData } from '../../interfaces';
import { POSTGRES_ARC_SERVER_PROVIDER_ID } from '../../../constants';
export interface PostgresArcServerGraphData extends GraphData { export class PostgresServerArcService extends ResourceServiceBase<PostgresArcServerGraphData> {
properties: {
admin: string;
};
}
export class PostgresServerArcService extends ResourceServiceBase<PostgresArcServerGraphData, azureResource.AzureResourceDatabaseServer> { public override queryFilter: string = postgresArcServerQuery;
protected get query(): string { public convertServerResource(resource: PostgresArcServerGraphData): azureResource.AzureResourceDatabaseServer | undefined {
return postgresArcServerQuery;
}
protected convertResource(resource: PostgresArcServerGraphData): azureResource.AzureResourceDatabaseServer {
return { return {
id: resource.id, id: resource.id,
name: resource.name, name: resource.name,
provider: POSTGRES_ARC_SERVER_PROVIDER_ID,
fullName: resource.name, fullName: resource.name,
loginName: resource.properties.admin, loginName: resource.properties.admin,
defaultDatabaseName: 'postgres', defaultDatabaseName: 'postgres',

View File

@@ -8,28 +8,27 @@ import { TreeItemCollapsibleState, ExtensionContext } from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
import { AzureResourceItemType } from '../../constants'; import { AzureResourceItemType, AzureResourcePrefixes, pgsqlProvider } from '../../constants';
import { generateGuid } from '../../utils'; import { generateGuid } from '../../utils';
import { IAzureResourceService } from '../../interfaces';
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
import { AzureAccount, azureResource } from 'azurecore'; 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'; private static readonly containerId = 'azure.resource.providers.postgresArcServer.treeDataProvider.postgresServerContainer';
// allow-any-unicode-next-line // allow-any-unicode-next-line
private static readonly containerLabel = localize('azure.resource.providers.postgresArcServer.treeDataProvider.postgresServerContainerLabel', "PostgreSQL Hyperscale Azure Arc"); private static readonly containerLabel = localize('azure.resource.providers.postgresArcServer.treeDataProvider.postgresServerContainerLabel', "PostgreSQL Hyperscale Azure Arc");
public constructor( public constructor(
databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>, databaseServerService: azureResource.IAzureResourceService,
private _extensionContext: ExtensionContext private _extensionContext: ExtensionContext
) { ) {
super(databaseServerService); super(databaseServerService);
} }
public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem {
protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem {
return { 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, label: this.browseConnectionMode ? `${databaseServer.name} (${PostgresServerArcTreeDataProvider.containerLabel}, ${databaseServer.subscription.name})` : databaseServer.name,
iconPath: { iconPath: {
dark: this._extensionContext.asAbsolutePath('resources/dark/sql_server_inverse.svg'), dark: this._extensionContext.asAbsolutePath('resources/dark/sql_server_inverse.svg'),
@@ -48,7 +47,7 @@ export class PostgresServerArcTreeDataProvider extends ResourceTreeDataProviderB
savePassword: true, savePassword: true,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
providerName: 'PGSQL', providerName: pgsqlProvider,
saveProfile: false, saveProfile: false,
options: { options: {
// Set default for SSL or will get error complaining about it not being set correctly // 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, azureResourceId: databaseServer.id,
azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint
}, },
childProvider: 'PGSQL', childProvider: pgsqlProvider,
type: ExtensionNodeType.Server 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 { azureResource } from 'azurecore';
import { postgresServerQuery } from '../queryStringConstants'; import { postgresServerQuery } from '../queryStringConstants';
import { DbServerGraphData } from '../../interfaces';
import { POSTGRES_SERVER_PROVIDER_ID } from '../../../constants';
interface DbServerGraphData extends GraphData { export class PostgresServerService extends ResourceServiceBase<DbServerGraphData> {
properties: {
fullyQualifiedDomainName: string;
administratorLogin: string;
};
}
export class PostgresServerService extends ResourceServiceBase<DbServerGraphData, azureResource.AzureResourceDatabaseServer> { public override queryFilter: string = postgresServerQuery;
protected get query(): string { public override convertServerResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer | undefined {
return postgresServerQuery;
}
protected convertResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer {
return { return {
id: resource.id, id: resource.id,
name: resource.name, name: resource.name,
provider: POSTGRES_SERVER_PROVIDER_ID,
fullName: resource.properties.fullyQualifiedDomainName, fullName: resource.properties.fullyQualifiedDomainName,
loginName: resource.properties.administratorLogin, loginName: resource.properties.administratorLogin,
defaultDatabaseName: 'postgres', defaultDatabaseName: 'postgres',

View File

@@ -8,27 +8,26 @@ import { TreeItemCollapsibleState, ExtensionContext } from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
import { AzureResourceItemType } from '../../constants'; import { AzureResourceItemType, AzureResourcePrefixes, pgsqlProvider } from '../../constants';
import { generateGuid } from '../../utils'; import { generateGuid } from '../../utils';
import { IAzureResourceService } from '../../interfaces'; import { GraphData, DbServerGraphData } from '../../interfaces';
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
import { AzureAccount, azureResource } from 'azurecore'; 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 containerId = 'azure.resource.providers.databaseServer.treeDataProvider.postgresServerContainer';
private static readonly containerLabel = localize('azure.resource.providers.databaseServer.treeDataProvider.postgresServerContainerLabel', "Azure Database for PostgreSQL server"); private static readonly containerLabel = localize('azure.resource.providers.databaseServer.treeDataProvider.postgresServerContainerLabel', "Azure Database for PostgreSQL server");
public constructor( public constructor(
databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>, databaseServerService: azureResource.IAzureResourceService,
private _extensionContext: ExtensionContext private _extensionContext: ExtensionContext
) { ) {
super(databaseServerService); super(databaseServerService);
} }
public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem {
protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem {
return { 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, label: this.browseConnectionMode ? `${databaseServer.name} (${PostgresServerTreeDataProvider.containerLabel}, ${databaseServer.subscription.name})` : databaseServer.name,
iconPath: { iconPath: {
dark: this._extensionContext.asAbsolutePath('resources/dark/sql_server_inverse.svg'), dark: this._extensionContext.asAbsolutePath('resources/dark/sql_server_inverse.svg'),
@@ -47,7 +46,7 @@ export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase
savePassword: true, savePassword: true,
groupFullName: '', groupFullName: '',
groupId: '', groupId: '',
providerName: 'PGSQL', providerName: pgsqlProvider,
saveProfile: false, saveProfile: false,
options: { options: {
// Set default for SSL or will get error complaining about it not being set correctly // 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, azureResourceId: databaseServer.id,
azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint
}, },
childProvider: 'PGSQL', childProvider: pgsqlProvider,
type: ExtensionNodeType.Server type: ExtensionNodeType.Server
}; };
} }

View File

@@ -4,37 +4,39 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { azureResource } from 'azurecore'; import { azureResource } from 'azurecore';
import * as Constants from '../constants';
export const where = `where `;
/** /**
* Lists all SQL Databases and Synapse SQL Databases * 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. * 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 * 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 * Lists all Sql Servers excluding Synapse Pool Servers
* (they have different properties and need to be handled separately) * (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 * 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 * 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 * Lists all resource containers and resource groups
@@ -44,29 +46,29 @@ export const resourceGroupQuery = `ResourceContainers | where type=="${azureReso
/** /**
* Lists all postgreSQL servers * 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 * 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 * Lists all MySQL Flexible servers
*/ */
export const mysqlFlexibleServerQuery = `where type == "${azureResource.AzureResourceType.mysqlFlexibleServer}"`; export const mysqlFlexibleServerQuery = `type == "${azureResource.AzureResourceType.mysqlFlexibleServer}"`;
/** /**
* Lists all Kusto Clusters * 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 * 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 * 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. * 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 { azureResource } from 'azurecore';
import { ResourceServiceBase } from '../resourceTreeDataProviderBase'; import { ResourceServiceBase } from '../resourceTreeDataProviderBase';
import { resourceGroupQuery } from '../queryStringConstants'; 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 { public override queryFilter: string = resourceGroupQuery;
return resourceGroupQuery;
}
protected convertResource(resource: DbServerGraphData): azureResource.AzureResourceResourceGroup { public override convertServerResource(resource: DbServerGraphData): azureResource.AzureResourceResourceGroup | undefined {
return { return {
id: resource.id, id: resource.id,
name: resource.name, name: resource.name,

View File

@@ -3,24 +3,19 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ExtensionContext } from 'vscode';
import { azureResource } from 'azurecore'; 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( public constructor(
private _service: IAzureResourceService<azureResource.AzureResourceDatabaseServer>, private _providerId: string,
private _extensionContext: ExtensionContext private _treeProvider: azureResource.IAzureResourceTreeDataProvider
) { ) { }
}
public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider {
return new KustoTreeDataProvider(this._service, this._extensionContext); return this._treeProvider;
} }
public get providerId(): string { 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 azdata from 'azdata';
import * as msRest from '@azure/ms-rest-js'; import * as msRest from '@azure/ms-rest-js';
import { IAzureResourceService } from '../interfaces'; import { GraphData, IAzureResourceDbService, IAzureResourceServerService } from '../interfaces';
import { AzureResourceErrorMessageUtil } from '../utils'; import { AzureResourceErrorMessageUtil } from '../utils';
import { ResourceGraphClient } from '@azure/arm-resourcegraph'; import { ResourceGraphClient } from '@azure/arm-resourcegraph';
import { AzureAccount, azureResource } from 'azurecore'; import { AzureAccount, azureResource } from 'azurecore';
import { Logger } from '../../utils/Logger'; import { Logger } from '../../utils/Logger';
import { ErrorResponse } from '@azure/arm-resourcegraph/esm/models'; 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 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> { public getService(): azureResource.IAzureResourceService {
return element.treeItem; return this._resourceService;
} }
public async getChildren(element: azureResource.IAzureResourceNode): Promise<azureResource.IAzureResourceNode[]> { public async getChildren(parent: azureResource.IAzureResourceNode): Promise<azureResource.IAzureResourceNode[]> {
try { try {
let resources: T[] = await this.getResources(element); let resources: azureResource.AzureResource[] = await this.getResources(parent);
return resources.map((resource) => this.convertDataToResource(resource, parent))
return resources.map((resource) => <azureResource.IAzureResourceNode>{ .sort((a, b) => (<any>a.treeItem.label).localeCompare(b.treeItem.label));
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));
} catch (error) { } catch (error) {
Logger.error(AzureResourceErrorMessageUtil.getErrorMessage(error)); Logger.error(AzureResourceErrorMessageUtil.getErrorMessage(error));
throw 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); const response = await azdata.accounts.getAccountSecurityToken(element.account, element.subscription.tenant!, azdata.AzureResource.ResourceManagement);
if (!response) { if (!response) {
throw new Error(`Did not receive security token when getting resources for account ${element.account.displayInfo.displayName}`); 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 credential = new msRest.TokenCredentials(response.token, response.tokenType);
return await this._resourceService.getResources([element.subscription], credential, element.account) || <azureResource.AzureResource[]>[];
const resources: T[] = await this._resourceService.getResources([element.subscription], credential, element.account) || <T[]>[];
return resources;
} }
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[]>; 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[]> { export async function queryGraphResources<T extends GraphData>(resourceClient: ResourceGraphClient, subscriptions: azureResource.AzureResourceSubscription[], resourceQuery: string): Promise<T[]> {
const allResources: T[] = []; const allResources: T[] = [];
let totalProcessed = 0; let totalProcessed = 0;
@@ -112,34 +104,31 @@ export async function queryGraphResources<T extends GraphData>(resourceClient: R
return allResources; return allResources;
} }
export abstract class ResourceServiceBase<T extends GraphData, U extends azureResource.AzureResource> implements IAzureResourceService<U> { export abstract class ResourceServiceBase<T extends GraphData> implements IAzureResourceServerService<T> {
constructor() {
}
/** /**
* The query to use - see https://docs.microsoft.com/azure/governance/resource-graph/concepts/query-language * 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 * 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[]> { public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: msRest.ServiceClientCredentials, account: AzureAccount): Promise<azureResource.AzureResource[]> {
const convertedResources: U[] = []; const convertedResources: azureResource.AzureResource[] = [];
const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint }); 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>(); const ids = new Set<string>();
graphResources.forEach((res) => { graphResources.forEach((res) => {
if (!ids.has(res.id)) { if (!ids.has(res.id)) {
ids.add(res.id); ids.add(res.id);
res.subscriptionName = subscriptions.find(sub => sub.id === res.subscriptionId)?.name; res.subscriptionName = subscriptions.find(sub => sub.id === res.subscriptionId)?.name;
const converted = this.convertResource(res); const converted = this.convertServerResource(res);
convertedResources.push(converted); convertedResources.push(converted!);
} }
}); });
return convertedResources; 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 { azureResource } from 'azurecore';
import { sqlInstanceQuery } from '../queryStringConstants'; 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 { export class SqlInstanceResourceService extends ResourceServiceBase<SqlInstanceGraphData> {
properties: {
fullyQualifiedDomainName: string;
administratorLogin: string;
};
}
export class SqlInstanceResourceService extends ResourceServiceBase<SqlInstanceGraphData, azureResource.AzureResourceDatabaseServer> { public override queryFilter: string = sqlInstanceQuery;
protected get query(): string { public override convertServerResource(resource: SqlInstanceGraphData): azureResource.AzureResourceDatabaseServer | undefined {
return sqlInstanceQuery;
}
protected convertResource(resource: SqlInstanceGraphData): azureResource.AzureResourceDatabaseServer {
return { return {
id: resource.id, id: resource.id,
name: resource.name, name: resource.name,
provider: SQLINSTANCE_PROVIDER_ID,
fullName: resource.properties.fullyQualifiedDomainName, fullName: resource.properties.fullyQualifiedDomainName,
loginName: resource.properties.administratorLogin, loginName: resource.properties.administratorLogin,
defaultDatabaseName: 'master', defaultDatabaseName: 'master',

View File

@@ -8,27 +8,26 @@ import { TreeItemCollapsibleState, ExtensionContext } from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
import { AzureResourceItemType, mssqlProvider } from '../../constants'; import { AzureResourceItemType, AzureResourcePrefixes, mssqlProvider } from '../../constants';
import { generateGuid } from '../../utils'; import { generateGuid } from '../../utils';
import { IAzureResourceService } from '../../interfaces'; import { GraphData, SqlInstanceGraphData } from '../../interfaces';
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
import { AzureAccount, azureResource } from 'azurecore'; 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 containerId = 'azure.resource.providers.sqlInstanceContainer';
private static readonly containerLabel = localize('azure.resource.providers.sqlInstanceContainerLabel', "Azure SQL DB managed instance"); private static readonly containerLabel = localize('azure.resource.providers.sqlInstanceContainerLabel', "Azure SQL DB managed instance");
public constructor( public constructor(
databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>, databaseServerService: azureResource.IAzureResourceService,
private _extensionContext: ExtensionContext private _extensionContext: ExtensionContext
) { ) {
super(databaseServerService); super(databaseServerService);
} }
public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem {
protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem {
return { 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, label: this.browseConnectionMode ? `${databaseServer.name} (${SqlInstanceTreeDataProvider.containerLabel}, ${databaseServer.subscription.name})` : databaseServer.name,
iconPath: { iconPath: {
dark: this._extensionContext.asAbsolutePath('resources/dark/sql_instance_inverse.svg'), 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. * 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 { azureResource } from 'azurecore';
import { sqlInstanceArcQuery } from '../queryStringConstants'; import { sqlInstanceArcQuery } from '../queryStringConstants';
import { SqlInstanceArcGraphData } from '../../interfaces';
import { SQLINSTANCE_ARC_PROVIDER_ID } from '../../../constants';
export interface SqlInstanceArcGraphData extends GraphData { export class SqlInstanceArcResourceService extends ResourceServiceBase<SqlInstanceArcGraphData> {
properties: {
admin: string;
hybridDataManager: string;
};
}
export class SqlInstanceArcResourceService extends ResourceServiceBase<SqlInstanceArcGraphData, azureResource.AzureResourceDatabaseServer> { public override queryFilter: string = sqlInstanceArcQuery;
protected get query(): string { public override convertServerResource(resource: SqlInstanceArcGraphData): azureResource.AzureResourceDatabaseServer | undefined {
return sqlInstanceArcQuery;
}
protected convertResource(resource: SqlInstanceArcGraphData): azureResource.AzureResourceDatabaseServer {
return { return {
id: resource.id, id: resource.id,
name: resource.name, name: resource.name,
provider: SQLINSTANCE_ARC_PROVIDER_ID,
fullName: resource.name, fullName: resource.name,
loginName: resource.properties.admin, loginName: resource.properties.admin,
defaultDatabaseName: 'master', defaultDatabaseName: 'master',

View File

@@ -8,28 +8,27 @@ import { TreeItemCollapsibleState, ExtensionContext } from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
import { AzureResourceItemType, mssqlProvider } from '../../constants'; import { AzureResourceItemType, AzureResourcePrefixes, mssqlProvider } from '../../constants';
import { generateGuid } from '../../utils'; import { generateGuid } from '../../utils';
import { IAzureResourceService } from '../../interfaces'; import { GraphData, SqlInstanceArcGraphData } from '../../interfaces';
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
import { AzureAccount, azureResource } from 'azurecore'; 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'; private static readonly containerId = 'azure.resource.providers.sqlInstanceArcContainer';
// allow-any-unicode-next-line // allow-any-unicode-next-line
private static readonly containerLabel = localize('azure.resource.providers.sqlInstanceArcContainerLabel', "SQL managed instance Azure Arc"); private static readonly containerLabel = localize('azure.resource.providers.sqlInstanceArcContainerLabel', "SQL managed instance Azure Arc");
public constructor( public constructor(
databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>, databaseServerService: azureResource.IAzureResourceService,
private _extensionContext: ExtensionContext private _extensionContext: ExtensionContext
) { ) {
super(databaseServerService); super(databaseServerService);
} }
public getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem {
protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem {
return { 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, label: this.browseConnectionMode ? `${databaseServer.name} (${SqlInstanceArcTreeDataProvider.containerLabel}, ${databaseServer.subscription.name})` : databaseServer.name,
iconPath: { iconPath: {
dark: this._extensionContext.asAbsolutePath('resources/dark/sql_instance_inverse.svg'), 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 { ServiceClientCredentials } from '@azure/ms-rest-js';
import { IAzureResourceService } from '../../interfaces'; import { GraphData, IAzureResourceDbService, SynapseGraphData, SynapseWorkspaceGraphData } from '../../interfaces';
import { SynapseWorkspaceGraphData } from '../synapseWorkspace/synapseWorkspaceService'; import { synapseWorkspacesQuery, synapseSqlPoolsQuery, where } from '../queryStringConstants';
import { synapseWorkspacesQuery, synapseSqlPoolsQuery } from '../queryStringConstants';
import { ResourceGraphClient } from '@azure/arm-resourcegraph'; import { ResourceGraphClient } from '@azure/arm-resourcegraph';
import { queryGraphResources, GraphData } from '../resourceTreeDataProviderBase'; import { queryGraphResources } from '../resourceTreeDataProviderBase';
import { AzureAccount, azureResource } from 'azurecore'; import { AzureAccount, azureResource } from 'azurecore';
import { SYNAPSE_SQL_POOL_PROVIDER_ID } from '../../../constants';
interface SynapseGraphData extends GraphData { export class AzureResourceSynapseService implements IAzureResourceDbService<SynapseWorkspaceGraphData, SynapseGraphData> {
kind: string;
// 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[]> { public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise<azureResource.AzureResourceDatabase[]> {
const databases: azureResource.AzureResourceDatabase[] = []; const databases: azureResource.AzureResourceDatabase[] = [];
const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint }); 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) // Query synapse servers, and databases in parallel (start all promises before waiting on the 1st)
let synapseQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, synapseSqlPoolsQuery); let synapseQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, where + this.queryFilter);
let synapseWorkspaceQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, synapseWorkspacesQuery); let synapseWorkspaceQueryPromise = queryGraphResources<GraphData>(resourceClient, subscriptions, where + synapseWorkspacesQuery);
let synapse = await synapseQueryPromise as SynapseGraphData[]; let synapse = await synapseQueryPromise as SynapseGraphData[];
let synapseWorkspaceByGraph: SynapseWorkspaceGraphData[] = await synapseWorkspaceQueryPromise as SynapseWorkspaceGraphData[]; let synapseWorkspaceByGraph: SynapseWorkspaceGraphData[] = await synapseWorkspaceQueryPromise as SynapseWorkspaceGraphData[];
@@ -50,19 +71,8 @@ export class AzureResourceSynapseService implements IAzureResourceService<azureR
const serverName = founds[2]; const serverName = founds[2];
let server = synapseWorkspaceByGraph.find(s => s.name === serverName); let server = synapseWorkspaceByGraph.find(s => s.name === serverName);
if (server) { if (server) {
databases.push({ let res = this.convertDatabaseResource(db, server);
name: db.name, databases.push(res!);
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
});
} }
} }
}); });

View File

@@ -8,27 +8,27 @@ import * as vscode from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
import { AzureResourceItemType, mssqlProvider } from '../../constants'; import { AzureResourceItemType, AzureResourcePrefixes, mssqlProvider } from '../../constants';
import { generateGuid } from '../../utils'; import { generateGuid } from '../../utils';
import { IAzureResourceService } from '../../interfaces'; import { SynapseGraphData, SynapseWorkspaceGraphData } from '../../interfaces';
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
import { AzureAccount, azureResource } from 'azurecore'; 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 containerId = 'azure.resource.providers.synapseSqlPool.treeDataProvider.synapseSqlPoolContainer';
private static readonly containerLabel = localize('azure.resource.providers.synapseSqlPool.treeDataProvider.synapseSqlPoolContainerLabel', "Dedicated SQL Pools"); private static readonly containerLabel = localize('azure.resource.providers.synapseSqlPool.treeDataProvider.synapseSqlPoolContainerLabel', "Dedicated SQL Pools");
public constructor( public constructor(
synapseSqlPoolService: IAzureResourceService<azureResource.AzureResourceDatabase>, synapseSqlPoolService: azureResource.IAzureResourceService,
private _extensionContext: vscode.ExtensionContext private _extensionContext: vscode.ExtensionContext
) { ) {
super(synapseSqlPoolService); super(synapseSqlPoolService);
} }
protected getTreeItemForResource(synapse: azureResource.AzureResourceDatabase, account: AzureAccount): TreeItem { public getTreeItemForResource(synapse: azureResource.AzureResourceDatabase, account: AzureAccount): TreeItem {
return { 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})`, label: this.browseConnectionMode ? `${synapse.serverName}/${synapse.name} (${AzureResourceSynapseSqlPoolTreeDataProvider.containerLabel}, ${synapse.subscription.name})` : `${synapse.name} (${synapse.serverName})`,
iconPath: { iconPath: {
dark: this._extensionContext.asAbsolutePath('resources/dark/sql_database_inverse.svg'), 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 { ServiceClientCredentials } from '@azure/ms-rest-js';
import { ResourceGraphClient } from '@azure/arm-resourcegraph'; import { ResourceGraphClient } from '@azure/arm-resourcegraph';
import { GraphData, queryGraphResources } from '../resourceTreeDataProviderBase'; import { queryGraphResources } from '../resourceTreeDataProviderBase';
import { azureResource, AzureAccount } from 'azurecore'; import { azureResource, AzureAccount } from 'azurecore';
import { IAzureResourceService } from '../../interfaces'; import { IAzureResourceServerService, SynapseWorkspaceGraphData } from '../../interfaces';
import { synapseWorkspacesQuery } from '../queryStringConstants'; import { synapseWorkspacesQuery, where } from '../queryStringConstants';
import { SYNAPSE_WORKSPACE_PROVIDER_ID } from '../../../constants';
/** export class AzureResourceSynapseWorkspaceService implements IAzureResourceServerService<SynapseWorkspaceGraphData> {
* 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 IAzureResourceService<azureResource.AzureResourceDatabaseServer> { public queryFilter: string = synapseWorkspacesQuery;
protected get query(): string {
return synapseWorkspacesQuery;
}
public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise<azureResource.AzureResourceDatabaseServer[]> { public async getResources(subscriptions: azureResource.AzureResourceSubscription[], credential: ServiceClientCredentials, account: AzureAccount): Promise<azureResource.AzureResourceDatabaseServer[]> {
const convertedResources: azureResource.AzureResourceDatabaseServer[] = []; const convertedResources: azureResource.AzureResourceDatabaseServer[] = [];
const resourceClient = new ResourceGraphClient(credential, { baseUri: account.properties.providerSettings.settings.armResource.endpoint }); 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>(); const ids = new Set<string>();
serverGraphResources.forEach((res) => { serverGraphResources.forEach((res) => {
if (!ids.has(res.id)) { if (!ids.has(res.id)) {
ids.add(res.id); ids.add(res.id);
res.subscriptionName = subscriptions.find(sub => sub.id === res.subscriptionId)?.name; res.subscriptionName = subscriptions.find(sub => sub.id === res.subscriptionId)?.name;
const converted = this.convertResource(res); const converted = this.convertServerResource(res);
convertedResources.push(converted); convertedResources.push(converted!);
} }
}); });
return convertedResources; return convertedResources;
} }
protected convertResource(resource: SynapseWorkspaceGraphData): azureResource.AzureResourceDatabaseServer { public convertServerResource(resource: SynapseWorkspaceGraphData): azureResource.AzureResourceDatabaseServer | undefined {
return { return {
id: resource.id, id: resource.id,
name: resource.name, name: resource.name,
provider: SYNAPSE_WORKSPACE_PROVIDER_ID,
fullName: resource.properties.connectivityEndpoints?.sql, fullName: resource.properties.connectivityEndpoints?.sql,
loginName: resource.properties.sqlAdministratorLogin, loginName: resource.properties.sqlAdministratorLogin,
defaultDatabaseName: 'master', defaultDatabaseName: 'master',

View File

@@ -8,26 +8,26 @@ import * as vscode from 'vscode';
import * as nls from 'vscode-nls'; import * as nls from 'vscode-nls';
const localize = nls.loadMessageBundle(); const localize = nls.loadMessageBundle();
import { AzureResourceItemType, mssqlProvider } from '../../constants'; import { AzureResourceItemType, AzureResourcePrefixes, mssqlProvider } from '../../constants';
import { generateGuid } from '../../utils'; import { generateGuid } from '../../utils';
import { IAzureResourceService } from '../../interfaces'; import { GraphData, SynapseWorkspaceGraphData } from '../../interfaces';
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
import { AzureAccount, azureResource } from 'azurecore'; 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 containerId = 'azure.resource.providers.synapseWorkspace.treeDataProvider.synapseWorkspaceContainer';
private static readonly containerLabel = localize('azure.resource.providers.synapseWorkspace.treeDataProvider.synapseWorkspaceContainerLabel', "Azure Synapse Analytics"); private static readonly containerLabel = localize('azure.resource.providers.synapseWorkspace.treeDataProvider.synapseWorkspaceContainerLabel', "Azure Synapse Analytics");
public constructor( public constructor(
synapseWorkspaceService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>, synapseWorkspaceService: azureResource.IAzureResourceService,
private _extensionContext: vscode.ExtensionContext private _extensionContext: vscode.ExtensionContext
) { ) {
super(synapseWorkspaceService); super(synapseWorkspaceService);
} }
protected getTreeItemForResource(synapseWorkspace: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem { public getTreeItemForResource(synapseWorkspace: azureResource.AzureResourceDatabaseServer, account: AzureAccount): TreeItem {
return { 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, label: this.browseConnectionMode ? `${synapseWorkspace.name} (${AzureResourceSynapseWorkspaceTreeDataProvider.containerLabel}, ${synapseWorkspace.subscription.name})` : synapseWorkspace.name,
iconPath: { iconPath: {
dark: this._extensionContext.asAbsolutePath('resources/dark/sql_server_inverse.svg'), 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. * 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 { IAzureResourceNodeWithProviderId } from './interfaces';
import { AzureAccount, azureResource } from 'azurecore'; import { AzureAccount, azureResource } from 'azurecore';
import { UNIVERSAL_PROVIDER_ID } from '../constants';
export class AzureResourceService { export class AzureResourceService {
private _areResourceProvidersLoaded: boolean = false; private _areResourceProvidersLoaded: boolean = false;
private _resourceProviders: { [resourceProviderId: string]: azureResource.IAzureResourceProvider } = {}; private _resourceProviders: { [resourceProviderId: string]: azureResource.IAzureResourceProvider } = {};
private _treeDataProviders: { [resourceProviderId: string]: azureResource.IAzureResourceTreeDataProvider } = {}; private _treeDataProviders: { [resourceProviderId: string]: azureResource.IAzureResourceTreeDataProvider } = {};
private _universalProvider: azureResource.IAzureUniversalResourceProvider | undefined = undefined;
public constructor() { public constructor() { }
}
public async listResourceProviderIds(): Promise<string[]> { public async listResourceProviderIds(): Promise<string[]> {
await this.ensureResourceProvidersRegistered(); await this.ensureResourceProvidersRegistered();
@@ -22,6 +24,11 @@ export class AzureResourceService {
return Object.keys(this._resourceProviders); 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 { public registerResourceProvider(resourceProvider: azureResource.IAzureResourceProvider): void {
this.doRegisterResourceProvider(resourceProvider); this.doRegisterResourceProvider(resourceProvider);
} }
@@ -35,13 +42,11 @@ export class AzureResourceService {
public async getRootChildren(resourceProviderId: string, account: AzureAccount, subscription: azureResource.AzureResourceSubscription): Promise<IAzureResourceNodeWithProviderId[]> { public async getRootChildren(resourceProviderId: string, account: AzureAccount, subscription: azureResource.AzureResourceSubscription): Promise<IAzureResourceNodeWithProviderId[]> {
await this.ensureResourceProvidersRegistered(); 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}`); throw new Error(`Azure resource provider doesn't exist. Id: ${resourceProviderId}`);
} }
const treeDataProvider = this._treeDataProviders[resourceProviderId]; const rootChildren = <azdata.TreeItem[]>await this._treeDataProviders[resourceProviderId]?.getRootChildren();
const rootChildren = await treeDataProvider.getRootChildren();
return rootChildren.map(rootChild => { return rootChildren.map(rootChild => {
return { return {
resourceProviderId, resourceProviderId,
@@ -58,13 +63,13 @@ export class AzureResourceService {
public async getChildren(resourceProviderId: string, element: azureResource.IAzureResourceNode, browseConnectionMode: boolean = false): Promise<IAzureResourceNodeWithProviderId[]> { public async getChildren(resourceProviderId: string, element: azureResource.IAzureResourceNode, browseConnectionMode: boolean = false): Promise<IAzureResourceNodeWithProviderId[]> {
await this.ensureResourceProvidersRegistered(); 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}`); 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; treeDataProvider.browseConnectionMode = browseConnectionMode;
const children = await treeDataProvider.getChildren(element); const children = <azureResource.IAzureResourceNode[]>await treeDataProvider.getChildren(element);
return children.map((child) => <IAzureResourceNodeWithProviderId>{ return children.map((child) => <IAzureResourceNodeWithProviderId>{
resourceProviderId: resourceProviderId, 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(); 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)) { return children.map((child) => <IAzureResourceNodeWithProviderId>{
throw new Error(`Azure resource provider doesn't exist. Id: ${resourceProviderId}`); resourceProviderId: UNIVERSAL_PROVIDER_ID,
} resourceNode: child
});
const treeDataProvider = this._treeDataProviders[resourceProviderId];
return treeDataProvider.getResourceTreeItem(element);
} }
public get areResourceProvidersLoaded(): boolean { public get areResourceProvidersLoaded(): boolean {
@@ -107,11 +113,14 @@ export class AzureResourceService {
if (extension.exports && extension.exports.provideResources) { if (extension.exports && extension.exports.provideResources) {
for (const resourceProvider of <azureResource.IAzureResourceProvider[]>extension.exports.provideResources()) { for (const resourceProvider of <azureResource.IAzureResourceProvider[]>extension.exports.provideResources()) {
if (resourceProvider) { if (resourceProvider && resourceProvider.providerId !== UNIVERSAL_PROVIDER_ID) {
this.doRegisterResourceProvider(resourceProvider); 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._resourceProviders[resourceProvider.providerId] = resourceProvider;
this._treeDataProviders[resourceProvider.providerId] = resourceProvider.getTreeDataProvider(); this._treeDataProviders[resourceProvider.providerId] = resourceProvider.getTreeDataProvider();
} }
} }

View File

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

View File

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

View File

@@ -43,8 +43,7 @@ export class FlatAccountTreeNode extends AzureResourceContainerTreeNodeBase {
this.treeChangeHandler.notifyNodeChanged(this); this.treeChangeHandler.notifyNodeChanged(this);
}); });
this._loader.onLoadingStatusChanged(async () => { this._loader.onLoadingStatusChanged(() => {
await this.updateLabel();
this.treeChangeHandler.notifyNodeChanged(this); this.treeChangeHandler.notifyNodeChanged(this);
}); });
} }
@@ -69,6 +68,7 @@ export class FlatAccountTreeNode extends AzureResourceContainerTreeNodeBase {
public async getChildren(): Promise<TreeNode[]> { public async getChildren(): Promise<TreeNode[]> {
if (this._isClearingCache) { if (this._isClearingCache) {
await this.updateLabel();
this._loader.start().catch(err => console.error('Error loading Azure FlatAccountTreeNodes ', err)); this._loader.start().catch(err => console.error('Error loading Azure FlatAccountTreeNodes ', err));
this._isClearingCache = false; this._isClearingCache = false;
return []; return [];
@@ -183,7 +183,6 @@ class FlatAccountTreeNodeLoader {
try { try {
// Authenticate to tenants to filter out subscriptions that are not accessible. // Authenticate to tenants to filter out subscriptions that are not accessible.
let tenants = this._account.properties.tenants; let tenants = this._account.properties.tenants;
// Filter out tenants that we can't authenticate to. // Filter out tenants that we can't authenticate to.
tenants = tenants.filter(async tenant => { tenants = tenants.filter(async tenant => {
@@ -209,12 +208,7 @@ class FlatAccountTreeNodeLoader {
}); });
} }
const resourceProviderIds = await this._resourceService.listResourceProviderIds(); const resources = await this._resourceService.getAllChildren(this._account, subscriptions, true);
for (const subscription of subscriptions) {
for (const providerId of resourceProviderIds) {
const resourceTypes = await this._resourceService.getRootChildren(providerId, this._account, subscription);
for (const resourceType of resourceTypes) {
const resources = await this._resourceService.getChildren(providerId, resourceType.resourceNode, true);
if (resources?.length > 0) { if (resources?.length > 0) {
this._nodes.push(...resources.map(dr => new AzureResourceResourceTreeNode(dr, this._accountNode, this.appContext))); this._nodes.push(...resources.map(dr => new AzureResourceResourceTreeNode(dr, this._accountNode, this.appContext)));
this._nodes = this.nodes.sort((a, b) => { this._nodes = this.nodes.sort((a, b) => {
@@ -222,9 +216,6 @@ class FlatAccountTreeNodeLoader {
}); });
newNodesAvailable = true; newNodesAvailable = true;
} }
}
}
}
// Create "No Resources Found" message node if no resources found under azure account. // Create "No Resources Found" message node if no resources found under azure account.
if (this._nodes.length === 0) { if (this._nodes.length === 0) {
this._nodes.push(AzureResourceMessageTreeNode.create(localize('azure.resource.flatAccountTreeNode.noResourcesLabel', "No Resources found."), this._accountNode)) 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. * 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 { ResourceGraphClient } from '@azure/arm-resourcegraph';
import { TokenCredentials } from '@azure/ms-rest-js'; 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 { AzureRestResponse, GetResourceGroupsResult, GetSubscriptionsResult, ResourceQueryResult, GetBlobContainersResult, GetFileSharesResult, HttpRequestMethod, GetLocationsResult, GetManagedDatabasesResult, CreateResourceGroupResult, GetBlobsResult, GetStorageAccountAccessKeyResult, AzureAccount, azureResource, AzureAccountProviderMetadata, AzureNetworkResponse } from 'azurecore';
import { EOL } from 'os'; import { EOL } from 'os';
import * as nls from 'vscode-nls';
import { AppContext } from '../appContext'; import { AppContext } from '../appContext';
import { invalidAzureAccount, invalidTenant, unableToFetchTokenError } from '../localizedConstants'; import { invalidAzureAccount, invalidTenant, unableToFetchTokenError } from '../localizedConstants';
import { AzureResourceServiceNames } from './constants'; import { AzureResourceServiceNames } from './constants';
@@ -16,12 +18,36 @@ import { IAzureResourceSubscriptionFilterService, IAzureResourceSubscriptionServ
import { AzureResourceGroupService } from './providers/resourceGroup/resourceGroupService'; import { AzureResourceGroupService } from './providers/resourceGroup/resourceGroupService';
import { BlobServiceClient, StorageSharedKeyCredential } from '@azure/storage-blob'; import { BlobServiceClient, StorageSharedKeyCredential } from '@azure/storage-blob';
import providerSettings from '../account-provider/providerSettings'; import providerSettings from '../account-provider/providerSettings';
import * as Constants from '../constants';
import { getProxyEnabledHttpClient } from '../utils'; import { getProxyEnabledHttpClient } from '../utils';
import { HttpClient } from '../account-provider/auths/httpClient'; import { HttpClient } from '../account-provider/auths/httpClient';
import { NetworkRequestOptions } from '@azure/msal-common'; import { NetworkRequestOptions } from '@azure/msal-common';
import { ErrorResponseBody } from '@azure/arm-subscriptions/esm/models'; import { ErrorResponseBody } from '@azure/arm-subscriptions/esm/models';
import { TenantIgnoredError } from '../utils/TenantIgnoredError'; 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(); const localize = nls.loadMessageBundle();
@@ -233,6 +259,30 @@ export async function getLocations(appContext: AppContext, account?: AzureAccoun
return result; 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>( export async function runResourceQuery<T extends azureResource.AzureGraphResource>(
account: AzureAccount, account: AzureAccount,
subscriptions: azureResource.AzureResourceSubscription[], subscriptions: azureResource.AzureResourceSubscription[],

View File

@@ -6,6 +6,7 @@
declare module 'azurecore' { declare module 'azurecore' {
import * as azdata from 'azdata'; import * as azdata from 'azdata';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import * as msRest from '@azure/ms-rest-js';
import { BlobItem } from '@azure/storage-blob'; import { BlobItem } from '@azure/storage-blob';
/** /**
@@ -327,6 +328,7 @@ declare module 'azurecore' {
getRegionDisplayName(region?: string): string; getRegionDisplayName(region?: string): string;
getProviderMetadataForAccount(account: AzureAccount): AzureAccountProviderMetadata; getProviderMetadataForAccount(account: AzureAccount): AzureAccountProviderMetadata;
provideResources(): azureResource.IAzureResourceProvider[]; provideResources(): azureResource.IAzureResourceProvider[];
getUniversalProvider(): azureResource.IAzureUniversalResourceProvider;
runGraphQuery<T extends azureResource.AzureGraphResource>(account: AzureAccount, subscriptions: azureResource.AzureResourceSubscription[], ignoreErrors: boolean, query: string): Promise<ResourceQueryResult<T>>; 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. * Event emitted when MSAL cache encryption keys are updated in credential store.
@@ -383,11 +385,23 @@ declare module 'azurecore' {
mysqlFlexibleServer = 'microsoft.dbformysql/flexibleservers' 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 { export interface IAzureResourceProvider extends azdata.DataProvider {
getTreeDataProvider(): IAzureResourceTreeDataProvider; getTreeDataProvider(): IAzureResourceTreeDataProvider;
} }
export interface IAzureResourceTreeDataProvider { export interface IAzureResourceTreeDataProvider {
getService(): azureResource.IAzureResourceService;
/** /**
* Gets the root tree item nodes for this provider - these will be used as * Gets the root tree item nodes for this provider - these will be used as
* direct children of the Account node in the Azure tree view. * direct children of the Account node in the Azure tree view.
@@ -399,10 +413,11 @@ declare module 'azurecore' {
*/ */
getChildren(element: IAzureResourceNode): Promise<IAzureResourceNode[]>; getChildren(element: IAzureResourceNode): Promise<IAzureResourceNode[]>;
/** /**
* Gets the tree item to display for a given {@link IAzureResourceNode} * Converts resource to VS Code treeItem
* @param element The resource node to get the TreeItem for * @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; browseConnectionMode: boolean;
} }
@@ -422,10 +437,16 @@ declare module 'azurecore' {
name: string; name: string;
id: string; id: string;
subscription: IAzureSubscriptionInfo; subscription: IAzureSubscriptionInfo;
provider?: string,
resourceGroup?: string; resourceGroup?: string;
tenant?: 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'> { export interface AzureResourceSubscription extends Omit<AzureResource, 'subscription'> {
} }

View File

@@ -134,3 +134,18 @@ export enum Platform {
Mac = 'darwin', Mac = 'darwin',
Linux = 'linux' 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 { AppContext } from './appContext';
import { AzureAccountProviderService } from './account-provider/azureAccountProviderService'; 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 { AzureResourceService } from './azureResource/resourceService';
import { IAzureResourceCacheService, IAzureResourceSubscriptionService, IAzureResourceSubscriptionFilterService, IAzureTerminalService } from './azureResource/interfaces'; import { IAzureResourceCacheService, IAzureResourceSubscriptionService, IAzureResourceSubscriptionFilterService, IAzureTerminalService } from './azureResource/interfaces';
import { AzureResourceServiceNames } from './azureResource/constants'; import { AzureResourceServiceNames } from './azureResource/constants';
@@ -24,23 +19,7 @@ import { AzureResourceSubscriptionFilterService } from './azureResource/services
import { AzureResourceCacheService } from './azureResource/services/cacheService'; import { AzureResourceCacheService } from './azureResource/services/cacheService';
import { registerAzureResourceCommands } from './azureResource/commands'; import { registerAzureResourceCommands } from './azureResource/commands';
import { AzureResourceTreeProvider } from './azureResource/tree/treeProvider'; 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 { 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 azurecore from 'azurecore';
import * as azureResourceUtils from './azureResource/utils'; import * as azureResourceUtils from './azureResource/utils';
import * as utils from './utils'; import * as utils from './utils';
@@ -50,10 +29,11 @@ import { AzureResourceGroupService } from './azureResource/providers/resourceGro
import { Logger } from './utils/Logger'; import { Logger } from './utils/Logger';
import { ConnectionDialogTreeProvider } from './azureResource/tree/connectionDialogTreeProvider'; import { ConnectionDialogTreeProvider } from './azureResource/tree/connectionDialogTreeProvider';
import { AzureDataGridProvider } from './azureDataGridProvider'; import { AzureDataGridProvider } from './azureDataGridProvider';
import { AzureResourceSynapseSqlPoolProvider } from './azureResource/providers/synapseSqlPool/synapseSqlPoolProvider'; // import { AzureResourceUniversalService } from './azureResource/providers/universal/universalService';
import { AzureResourceSynapseWorkspaceProvider } from './azureResource/providers/synapseWorkspace/synapseWorkspaceProvider'; import { AzureResourceUniversalService } from './azureResource/providers/universal/universalService';
import { AzureResourceSynapseWorkspaceService } from './azureResource/providers/synapseWorkspace/synapseWorkspaceService'; import { AzureResourceUniversalTreeDataProvider } from './azureResource/providers/universal/universalTreeDataProvider';
import { AzureResourceSynapseService } from './azureResource/providers/synapseSqlPool/synapseSqlPoolService'; import { AzureResourceUniversalResourceProvider } from './azureResource/providers/universal/universalProvider';
// import { AzureResourceUniversalTreeDataProvider } from './azureResource/providers/universal/universalTreeDataProvider';
let extensionContext: vscode.ExtensionContext; let extensionContext: vscode.ExtensionContext;
@@ -151,26 +131,15 @@ export async function activate(context: vscode.ExtensionContext): Promise<azurec
return azureResourceUtils.getLocations(appContext, account, subscription, ignoreErrors); return azureResourceUtils.getLocations(appContext, account, subscription, ignoreErrors);
}, },
provideResources(): azurecore.azureResource.IAzureResourceProvider[] { provideResources(): azurecore.azureResource.IAzureResourceProvider[] {
const arcFeaturedEnabled = vscode.workspace.getConfiguration(Constants.AzureSection).get(Constants.EnableArcFeaturesSection); return azureResourceUtils.getAllResourceProviders(extensionContext);
const providers: azurecore.azureResource.IAzureResourceProvider[] = [ },
new KustoProvider(new KustoResourceService(), extensionContext), getUniversalProvider(): azurecore.azureResource.IAzureUniversalResourceProvider {
new AzureMonitorProvider(new AzureMonitorResourceService(), extensionContext), let providers = azureResourceUtils.getAllResourceProviders(extensionContext);
new AzureResourceDatabaseServerProvider(new AzureResourceDatabaseServerService(), extensionContext), let treeDataProviders = new Map<string, azurecore.azureResource.IAzureResourceTreeDataProvider>();
new AzureResourceDatabaseProvider(new AzureResourceDatabaseService(), extensionContext), providers.forEach(provider => {
new AzureResourceSynapseSqlPoolProvider(new AzureResourceSynapseService(), extensionContext), treeDataProviders.set(provider.providerId, provider.getTreeDataProvider());
new AzureResourceSynapseWorkspaceProvider(new AzureResourceSynapseWorkspaceService(), extensionContext), })
new SqlInstanceProvider(new SqlInstanceResourceService(), extensionContext), return new AzureResourceUniversalResourceProvider(Constants.UNIVERSAL_PROVIDER_ID, new AzureResourceUniversalTreeDataProvider(new AzureResourceUniversalService(treeDataProviders)));
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;
}, },
getSqlManagedInstances(account: azurecore.AzureAccount, getSqlManagedInstances(account: azurecore.AzureAccount,
subscriptions: azurecore.azureResource.AzureResourceSubscription[], subscriptions: azurecore.azureResource.AzureResourceSubscription[],

View File

@@ -12,12 +12,12 @@ import 'mocha';
import { AzureResourceDatabaseTreeDataProvider } from '../../../../azureResource/providers/database/databaseTreeDataProvider'; import { AzureResourceDatabaseTreeDataProvider } from '../../../../azureResource/providers/database/databaseTreeDataProvider';
import { AzureResourceItemType } from '../../../../azureResource/constants'; import { AzureResourceItemType } from '../../../../azureResource/constants';
import { IAzureResourceService } from '../../../../azureResource/interfaces';
import { AzureAccount, azureResource } from 'azurecore'; import { AzureAccount, azureResource } from 'azurecore';
import settings from '../../../../account-provider/providerSettings'; import settings from '../../../../account-provider/providerSettings';
import { DATABASE_PROVIDER_ID } from '../../../../constants';
// Mock services // Mock services
let mockDatabaseService: TypeMoq.IMock<IAzureResourceService<azureResource.AzureResourceDatabase>>; let mockDatabaseService: TypeMoq.IMock<azureResource.IAzureResourceService>;
let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>; let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
// Mock test data // Mock test data
@@ -76,6 +76,7 @@ const mockDatabases: azureResource.AzureResourceDatabase[] = [
{ {
name: 'mock database 1', name: 'mock database 1',
id: 'mock-id-1', id: 'mock-id-1',
provider: DATABASE_PROVIDER_ID,
serverName: 'mock database server 1', serverName: 'mock database server 1',
serverFullName: 'mock database server full name 1', serverFullName: 'mock database server full name 1',
loginName: 'mock login', loginName: 'mock login',
@@ -88,6 +89,7 @@ const mockDatabases: azureResource.AzureResourceDatabase[] = [
{ {
name: 'mock database 2', name: 'mock database 2',
id: 'mock-id-2', id: 'mock-id-2',
provider: DATABASE_PROVIDER_ID,
serverName: 'mock database server 2', serverName: 'mock database server 2',
serverFullName: 'mock database server full name 2', serverFullName: 'mock database server full name 2',
loginName: 'mock login', loginName: 'mock login',
@@ -101,14 +103,12 @@ const mockDatabases: azureResource.AzureResourceDatabase[] = [
describe('AzureResourceDatabaseTreeDataProvider.info', function (): void { describe('AzureResourceDatabaseTreeDataProvider.info', function (): void {
beforeEach(() => { beforeEach(() => {
mockDatabaseService = TypeMoq.Mock.ofType<IAzureResourceService<azureResource.AzureResourceDatabase>>(); mockDatabaseService = TypeMoq.Mock.ofType<azureResource.IAzureResourceService>();
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>(); mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
}); });
it('Should be correct when created.', async function (): Promise<void> { it('Should be correct when created.', async function (): Promise<void> {
const treeDataProvider = new AzureResourceDatabaseTreeDataProvider(mockDatabaseService.object, mockExtensionContext.object); const treeItem = mockResourceRootNode.treeItem;
const treeItem = await treeDataProvider.getResourceTreeItem(mockResourceRootNode);
should(treeItem.id).equal(mockResourceRootNode.treeItem.id); should(treeItem.id).equal(mockResourceRootNode.treeItem.id);
should(treeItem.label).equal(mockResourceRootNode.treeItem.label); should(treeItem.label).equal(mockResourceRootNode.treeItem.label);
should(treeItem.collapsibleState).equal(mockResourceRootNode.treeItem.collapsibleState); should(treeItem.collapsibleState).equal(mockResourceRootNode.treeItem.collapsibleState);
@@ -118,7 +118,7 @@ describe('AzureResourceDatabaseTreeDataProvider.info', function (): void {
describe('AzureResourceDatabaseTreeDataProvider.getChildren', function (): void { describe('AzureResourceDatabaseTreeDataProvider.getChildren', function (): void {
beforeEach(() => { beforeEach(() => {
mockDatabaseService = TypeMoq.Mock.ofType<IAzureResourceService<azureResource.AzureResourceDatabase>>(); mockDatabaseService = TypeMoq.Mock.ofType<azureResource.IAzureResourceService>();
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>(); mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
sinon.stub(azdata.accounts, 'getAccountSecurityToken').returns(Promise.resolve(mockToken)); 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.account).equal(mockAccount);
should(child.subscription).equal(mockSubscription); should(child.subscription).equal(mockSubscription);
should(child.tenantId).equal(mockTenantId); 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.label).equal(`${database.name} (${database.serverName})`);
should(child.treeItem.collapsibleState).equal(vscode.TreeItemCollapsibleState.Collapsed); should(child.treeItem.collapsibleState).equal(vscode.TreeItemCollapsibleState.Collapsed);
should(child.treeItem.contextValue).equal(AzureResourceItemType.database); should(child.treeItem.contextValue).equal(AzureResourceItemType.database);

View File

@@ -12,13 +12,13 @@ import 'mocha';
import { AzureResourceDatabaseServerTreeDataProvider } from '../../../../azureResource/providers/databaseServer/databaseServerTreeDataProvider'; import { AzureResourceDatabaseServerTreeDataProvider } from '../../../../azureResource/providers/databaseServer/databaseServerTreeDataProvider';
import { AzureResourceItemType } from '../../../../azureResource/constants'; import { AzureResourceItemType } from '../../../../azureResource/constants';
import { IAzureResourceService } from '../../../../azureResource/interfaces';
// Mock services // Mock services
let mockDatabaseServerService: TypeMoq.IMock<IAzureResourceService<azureResource.AzureResourceDatabaseServer>>; let mockDatabaseServerService: TypeMoq.IMock<azureResource.IAzureResourceService>;
let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>; let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
import settings from '../../../../account-provider/providerSettings'; import settings from '../../../../account-provider/providerSettings';
import { AzureAccount, azureResource } from 'azurecore'; import { AzureAccount, azureResource } from 'azurecore';
import { DATABASE_SERVER_PROVIDER_ID } from '../../../../constants';
// Mock test data // Mock test data
const mockAccount: AzureAccount = { const mockAccount: AzureAccount = {
@@ -75,6 +75,7 @@ const mockDatabaseServers: azureResource.AzureResourceDatabaseServer[] = [
{ {
name: 'mock database server 1', name: 'mock database server 1',
id: 'mock-id-1', id: 'mock-id-1',
provider: DATABASE_SERVER_PROVIDER_ID,
fullName: 'mock database server full name 1', fullName: 'mock database server full name 1',
loginName: 'mock login', loginName: 'mock login',
defaultDatabaseName: 'master', defaultDatabaseName: 'master',
@@ -87,6 +88,7 @@ const mockDatabaseServers: azureResource.AzureResourceDatabaseServer[] = [
{ {
name: 'mock database server 2', name: 'mock database server 2',
id: 'mock-id-2', id: 'mock-id-2',
provider: DATABASE_SERVER_PROVIDER_ID,
fullName: 'mock database server full name 2', fullName: 'mock database server full name 2',
loginName: 'mock login', loginName: 'mock login',
defaultDatabaseName: 'master', defaultDatabaseName: 'master',
@@ -100,14 +102,12 @@ const mockDatabaseServers: azureResource.AzureResourceDatabaseServer[] = [
describe('AzureResourceDatabaseServerTreeDataProvider.info', function (): void { describe('AzureResourceDatabaseServerTreeDataProvider.info', function (): void {
beforeEach(() => { beforeEach(() => {
mockDatabaseServerService = TypeMoq.Mock.ofType<IAzureResourceService<azureResource.AzureResourceDatabaseServer>>(); mockDatabaseServerService = TypeMoq.Mock.ofType<azureResource.IAzureResourceService>();
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>(); mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
}); });
it('Should be correct when created.', async function (): Promise<void> { it('Should be correct when created.', async function (): Promise<void> {
const treeDataProvider = new AzureResourceDatabaseServerTreeDataProvider(mockDatabaseServerService.object, mockExtensionContext.object); const treeItem = mockResourceRootNode.treeItem;
const treeItem = await treeDataProvider.getResourceTreeItem(mockResourceRootNode);
should(treeItem.id).equal(mockResourceRootNode.treeItem.id); should(treeItem.id).equal(mockResourceRootNode.treeItem.id);
should(treeItem.label).equal(mockResourceRootNode.treeItem.label); should(treeItem.label).equal(mockResourceRootNode.treeItem.label);
should(treeItem.collapsibleState).equal(mockResourceRootNode.treeItem.collapsibleState); should(treeItem.collapsibleState).equal(mockResourceRootNode.treeItem.collapsibleState);
@@ -117,7 +117,7 @@ describe('AzureResourceDatabaseServerTreeDataProvider.info', function (): void {
describe('AzureResourceDatabaseServerTreeDataProvider.getChildren', function (): void { describe('AzureResourceDatabaseServerTreeDataProvider.getChildren', function (): void {
beforeEach(() => { beforeEach(() => {
mockDatabaseServerService = TypeMoq.Mock.ofType<IAzureResourceService<azureResource.AzureResourceDatabaseServer>>(); mockDatabaseServerService = TypeMoq.Mock.ofType<azureResource.IAzureResourceService>();
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>(); mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
sinon.stub(azdata.accounts, 'getAccountSecurityToken').returns(Promise.resolve(mockToken)); sinon.stub(azdata.accounts, 'getAccountSecurityToken').returns(Promise.resolve(mockToken));
@@ -159,7 +159,7 @@ describe('AzureResourceDatabaseServerTreeDataProvider.getChildren', function ():
should(child.account).equal(mockAccount); should(child.account).equal(mockAccount);
should(child.subscription).equal(mockSubscription); should(child.subscription).equal(mockSubscription);
should(child.tenantId).equal(mockTenantId); 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.label).equal(databaseServer.name);
should(child.treeItem.collapsibleState).equal(vscode.TreeItemCollapsibleState.Collapsed); should(child.treeItem.collapsibleState).equal(vscode.TreeItemCollapsibleState.Collapsed);
should(child.treeItem.contextValue).equal(AzureResourceItemType.databaseServer); should(child.treeItem.contextValue).equal(AzureResourceItemType.databaseServer);

View File

@@ -58,14 +58,12 @@ describe('AzureResourceService.listResourceProviderIds', function (): void {
beforeEach(() => { beforeEach(() => {
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>(); mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object])); mockResourceTreeDataProvider1.setup((o) => o.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 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1'); mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1');
mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object); mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object);
mockResourceTreeDataProvider2 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>(); mockResourceTreeDataProvider2 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider2.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object])); mockResourceTreeDataProvider2.setup((o) => o.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 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider2.setup((o) => o.providerId).returns(() => 'mockResourceProvider2'); mockResourceProvider2.setup((o) => o.providerId).returns(() => 'mockResourceProvider2');
mockResourceProvider2.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider2.object); mockResourceProvider2.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider2.object);
@@ -95,7 +93,6 @@ describe('AzureResourceService.getRootChildren', function (): void {
beforeEach(() => { beforeEach(() => {
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>(); mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object])); mockResourceTreeDataProvider1.setup((o) => o.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 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1'); mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1');
mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object); mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object);
@@ -129,7 +126,6 @@ describe('AzureResourceService.getChildren', function (): void {
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>(); mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([TypeMoq.Mock.ofType<azdata.TreeItem>().object])); mockResourceTreeDataProvider1.setup((o) => o.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.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 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1'); mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1');
mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object); mockResourceProvider1.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider1.object);
@@ -156,36 +152,3 @@ describe('AzureResourceService.getChildren', function (): void {
fail(); 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 { describe('AzureResourceResourceTreeNode.info', function (): void {
beforeEach(() => { beforeEach(() => {
mockResourceTreeDataProvider = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>(); 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)); mockResourceTreeDataProvider.setup((o) => o.getChildren(mockResourceRootNode)).returns(() => Promise.resolve(mockResourceNodes));
mockResourceProvider = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>(); mockResourceProvider = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();

View File

@@ -124,7 +124,6 @@ describe('AzureResourceSubscriptionTreeNode.getChildren', function(): void {
mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>(); mockResourceTreeDataProvider1 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider1.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([{ label: 'Item1' }] as azdata.TreeItem[])); mockResourceTreeDataProvider1.setup((o) => o.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 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1'); mockResourceProvider1.setup((o) => o.providerId).returns(() => 'mockResourceProvider1');
@@ -132,7 +131,6 @@ describe('AzureResourceSubscriptionTreeNode.getChildren', function(): void {
mockResourceTreeDataProvider2 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>(); mockResourceTreeDataProvider2 = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
mockResourceTreeDataProvider2.setup((o) => o.getRootChildren()).returns(() => Promise.resolve([{ label: 'Item2' }] as azdata.TreeItem[])); mockResourceTreeDataProvider2.setup((o) => o.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 = TypeMoq.Mock.ofType<azureResource.IAzureResourceProvider>();
mockResourceProvider2.setup((o) => o.providerId).returns(() => 'mockResourceProvider2'); mockResourceProvider2.setup((o) => o.providerId).returns(() => 'mockResourceProvider2');
mockResourceProvider2.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider2.object); mockResourceProvider2.setup((o) => o.getTreeDataProvider()).returns(() => mockResourceTreeDataProvider2.object);

View File

@@ -61,6 +61,9 @@ export class AzurecoreApiStub implements azurecore.IExtension {
provideResources(): azurecore.azureResource.IAzureResourceProvider[] { provideResources(): azurecore.azureResource.IAzureResourceProvider[] {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }
getUniversalProvider(): azurecore.azureResource.IAzureUniversalResourceProvider {
throw new Error('Method not implemented.');
}
onEncryptionKeysUpdated: any onEncryptionKeysUpdated: any
getEncryptionKeys(): Promise<azurecore.CacheEncryptionKeys> { getEncryptionKeys(): Promise<azurecore.CacheEncryptionKeys> {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');