mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Support for removing hardcoded deployment options (#8986)
* Support for removing hardcode deployment options * Add missing file * Generalize errors and make name and id required. Sort dropdowns. * Fix test * Capitalize text
This commit is contained in:
@@ -65,6 +65,16 @@
|
|||||||
"title": "%accounts.clearTokenCache%",
|
"title": "%accounts.clearTokenCache%",
|
||||||
"category": "Azure Accounts"
|
"category": "Azure Accounts"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "azure.accounts.getSubscriptions",
|
||||||
|
"title": "%azure.accounts.getSubscriptions.title%",
|
||||||
|
"category": "Azure Accounts"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "azure.accounts.getResourceGroups",
|
||||||
|
"title": "%azure.accounts.getResourceGroups.title%",
|
||||||
|
"category": "Azure Accounts"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "azure.resource.signin",
|
"command": "azure.resource.signin",
|
||||||
"title": "%azure.resource.signin.title%",
|
"title": "%azure.resource.signin.title%",
|
||||||
@@ -196,6 +206,6 @@
|
|||||||
"vscodetestcover": "github:corivera/vscodetestcover#1.0.4"
|
"vscodetestcover": "github:corivera/vscodetestcover#1.0.4"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"esprima": "^4.0.0"
|
"esprima": "^4.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
"azure.resource.connectsqldb.title": "Add to Servers",
|
"azure.resource.connectsqldb.title": "Add to Servers",
|
||||||
|
|
||||||
"accounts.clearTokenCache": "Clear Azure Account Token Cache",
|
"accounts.clearTokenCache": "Clear Azure Account Token Cache",
|
||||||
|
"azure.accounts.getSubscriptions.title": "Get Azure Account Subscriptions",
|
||||||
|
"azure.accounts.getResourceGroups.title": "Get Azure Account Subscription Resource Groups",
|
||||||
|
|
||||||
"config.enablePublicCloudDescription": "Should Azure public cloud integration be enabled",
|
"config.enablePublicCloudDescription": "Should Azure public cloud integration be enabled",
|
||||||
"config.enableUsGovCloudDescription": "Should US Government Azure cloud (Fairfax) integration be enabled",
|
"config.enableUsGovCloudDescription": "Should US Government Azure cloud (Fairfax) integration be enabled",
|
||||||
|
|||||||
@@ -21,8 +21,30 @@ export namespace azureResource {
|
|||||||
readonly treeItem: TreeItem;
|
readonly treeItem: TreeItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AzureResourceSubscription {
|
export interface AzureResource {
|
||||||
id: string;
|
|
||||||
name: string;
|
name: string;
|
||||||
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AzureResourceSubscription extends AzureResource {
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AzureSqlResource extends AzureResource {
|
||||||
|
loginName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AzureResourceResourceGroup extends AzureResource {
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AzureResourceDatabase extends AzureSqlResource {
|
||||||
|
serverName: string;
|
||||||
|
serverFullName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AzureResourceDatabaseServer extends AzureSqlResource {
|
||||||
|
fullName: string;
|
||||||
|
defaultDatabaseName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,47 @@ import { AzureResourceTreeProvider } from './tree/treeProvider';
|
|||||||
import { AzureResourceAccountTreeNode } from './tree/accountTreeNode';
|
import { AzureResourceAccountTreeNode } from './tree/accountTreeNode';
|
||||||
import { IAzureResourceSubscriptionService, IAzureResourceSubscriptionFilterService } from '../azureResource/interfaces';
|
import { IAzureResourceSubscriptionService, IAzureResourceSubscriptionFilterService } from '../azureResource/interfaces';
|
||||||
import { AzureResourceServiceNames } from './constants';
|
import { AzureResourceServiceNames } from './constants';
|
||||||
|
import { AzureResourceGroupService } from './providers/resourceGroup/resourceGroupService';
|
||||||
|
|
||||||
export function registerAzureResourceCommands(appContext: AppContext, tree: AzureResourceTreeProvider): void {
|
export function registerAzureResourceCommands(appContext: AppContext, tree: AzureResourceTreeProvider): void {
|
||||||
|
|
||||||
|
// Resource Management commands
|
||||||
|
appContext.apiWrapper.registerCommand('azure.accounts.getSubscriptions', async (account: azdata.Account) => {
|
||||||
|
const subscriptions = <azureResource.AzureResourceSubscription[]>[];
|
||||||
|
try {
|
||||||
|
const subscriptionService = appContext.getService<IAzureResourceSubscriptionService>(AzureResourceServiceNames.subscriptionService);
|
||||||
|
const tokens = await appContext.apiWrapper.getSecurityToken(account, azdata.AzureResource.ResourceManagement);
|
||||||
|
|
||||||
|
for (const tenant of account.properties.tenants) {
|
||||||
|
const token = tokens[tenant.id].token;
|
||||||
|
const tokenType = tokens[tenant.id].tokenType;
|
||||||
|
|
||||||
|
subscriptions.push(...await subscriptionService.getSubscriptions(account, new TokenCredentials(token, tokenType)));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(localize('azure.accounts.getSubscriptions.error', "Unexpected error occurred getting the subscriptions for account {0}. {1}", account.key.accountId, error));
|
||||||
|
}
|
||||||
|
return subscriptions;
|
||||||
|
});
|
||||||
|
|
||||||
|
appContext.apiWrapper.registerCommand('azure.accounts.getResourceGroups', async (account: azdata.Account, subscription: azureResource.AzureResourceSubscription) => {
|
||||||
|
try {
|
||||||
|
const service = new AzureResourceGroupService();
|
||||||
|
const resourceGroups: azureResource.AzureResourceResourceGroup[] = [];
|
||||||
|
for (const tenant of account.properties.tenants) {
|
||||||
|
const tokens = await appContext.apiWrapper.getSecurityToken(account, azdata.AzureResource.ResourceManagement);
|
||||||
|
const token = tokens[tenant.id].token;
|
||||||
|
const tokenType = tokens[tenant.id].tokenType;
|
||||||
|
|
||||||
|
resourceGroups.push(...await service.getResources(subscription, new TokenCredentials(token, tokenType)));
|
||||||
|
}
|
||||||
|
return resourceGroups;
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(localize('azure.accounts.getResourceGroups.error', "Unexpected error occurred getting the subscriptions for subscription {0} ({1}). {2}", subscription.name, subscription.id, error));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Resource Tree commands
|
||||||
appContext.apiWrapper.registerCommand('azure.resource.selectsubscriptions', async (node?: TreeNode) => {
|
appContext.apiWrapper.registerCommand('azure.resource.selectsubscriptions', async (node?: TreeNode) => {
|
||||||
if (!(node instanceof AzureResourceAccountTreeNode)) {
|
if (!(node instanceof AzureResourceAccountTreeNode)) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -41,23 +41,6 @@ export interface IAzureResourceNodeWithProviderId {
|
|||||||
resourceNode: azureResource.IAzureResourceNode;
|
resourceNode: azureResource.IAzureResourceNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AzureSqlResource {
|
export interface IAzureResourceService<T extends azureResource.AzureResource> {
|
||||||
name: string;
|
|
||||||
loginName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IAzureResourceService<T extends AzureSqlResource> {
|
|
||||||
getResources(subscription: azureResource.AzureResourceSubscription, credential: msRest.ServiceClientCredentials): Promise<T[]>;
|
getResources(subscription: azureResource.AzureResourceSubscription, credential: msRest.ServiceClientCredentials): Promise<T[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface AzureResourceDatabase extends AzureSqlResource {
|
|
||||||
serverName: string;
|
|
||||||
serverFullName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface AzureResourceDatabaseServer extends AzureSqlResource {
|
|
||||||
id?: string;
|
|
||||||
fullName: string;
|
|
||||||
defaultDatabaseName: string;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ import { ApiWrapper } from '../../../apiWrapper';
|
|||||||
|
|
||||||
import { azureResource } from '../../azure-resource';
|
import { azureResource } from '../../azure-resource';
|
||||||
import { AzureResourceDatabaseTreeDataProvider } from './databaseTreeDataProvider';
|
import { AzureResourceDatabaseTreeDataProvider } from './databaseTreeDataProvider';
|
||||||
import { IAzureResourceService, AzureResourceDatabase } from '../../interfaces';
|
import { IAzureResourceService } from '../../interfaces';
|
||||||
|
|
||||||
export class AzureResourceDatabaseProvider implements azureResource.IAzureResourceProvider {
|
export class AzureResourceDatabaseProvider implements azureResource.IAzureResourceProvider {
|
||||||
public constructor(
|
public constructor(
|
||||||
private _databaseService: IAzureResourceService<AzureResourceDatabase>,
|
private _databaseService: IAzureResourceService<azureResource.AzureResourceDatabase>,
|
||||||
private _apiWrapper: ApiWrapper,
|
private _apiWrapper: ApiWrapper,
|
||||||
private _extensionContext: ExtensionContext
|
private _extensionContext: ExtensionContext
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { ServiceClientCredentials } from '@azure/ms-rest-js';
|
import { ServiceClientCredentials } from '@azure/ms-rest-js';
|
||||||
import { azureResource } from '../../azure-resource';
|
import { azureResource } from '../../azure-resource';
|
||||||
import { IAzureResourceService, AzureResourceDatabase } from '../../interfaces';
|
import { IAzureResourceService } from '../../interfaces';
|
||||||
import { serversQuery, DbServerGraphData } from '../databaseServer/databaseServerService';
|
import { serversQuery, DbServerGraphData } from '../databaseServer/databaseServerService';
|
||||||
import { ResourceGraphClient } from '@azure/arm-resourcegraph';
|
import { ResourceGraphClient } from '@azure/arm-resourcegraph';
|
||||||
import { queryGraphResources, GraphData } from '../resourceTreeDataProviderBase';
|
import { queryGraphResources, GraphData } from '../resourceTreeDataProviderBase';
|
||||||
@@ -13,9 +13,9 @@ import { queryGraphResources, GraphData } from '../resourceTreeDataProviderBase'
|
|||||||
interface DatabaseGraphData extends GraphData {
|
interface DatabaseGraphData extends GraphData {
|
||||||
kind: string;
|
kind: string;
|
||||||
}
|
}
|
||||||
export class AzureResourceDatabaseService implements IAzureResourceService<AzureResourceDatabase> {
|
export class AzureResourceDatabaseService implements IAzureResourceService<azureResource.AzureResourceDatabase> {
|
||||||
public async getResources(subscription: azureResource.AzureResourceSubscription, credential: ServiceClientCredentials): Promise<AzureResourceDatabase[]> {
|
public async getResources(subscription: azureResource.AzureResourceSubscription, credential: ServiceClientCredentials): Promise<azureResource.AzureResourceDatabase[]> {
|
||||||
const databases: AzureResourceDatabase[] = [];
|
const databases: azureResource.AzureResourceDatabase[] = [];
|
||||||
const resourceClient = new ResourceGraphClient(credential);
|
const resourceClient = new ResourceGraphClient(credential);
|
||||||
|
|
||||||
// Query servers and databases in parallel (start both promises before waiting on the 1st)
|
// Query servers and databases in parallel (start both promises before waiting on the 1st)
|
||||||
@@ -46,6 +46,7 @@ export class AzureResourceDatabaseService implements IAzureResourceService<Azure
|
|||||||
if (server) {
|
if (server) {
|
||||||
databases.push({
|
databases.push({
|
||||||
name: db.name,
|
name: db.name,
|
||||||
|
id: db.id,
|
||||||
serverName: server.name,
|
serverName: server.name,
|
||||||
serverFullName: server.properties.fullyQualifiedDomainName,
|
serverFullName: server.properties.fullyQualifiedDomainName,
|
||||||
loginName: server.properties.administratorLogin
|
loginName: server.properties.administratorLogin
|
||||||
|
|||||||
@@ -12,22 +12,22 @@ import { azureResource } from '../../azure-resource';
|
|||||||
import { AzureResourceItemType } from '../../../azureResource/constants';
|
import { AzureResourceItemType } from '../../../azureResource/constants';
|
||||||
import { ApiWrapper } from '../../../apiWrapper';
|
import { ApiWrapper } from '../../../apiWrapper';
|
||||||
import { generateGuid } from '../../utils';
|
import { generateGuid } from '../../utils';
|
||||||
import { IAzureResourceService, AzureResourceDatabase } from '../../interfaces';
|
import { IAzureResourceService } from '../../interfaces';
|
||||||
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
|
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
|
||||||
|
|
||||||
export class AzureResourceDatabaseTreeDataProvider extends ResourceTreeDataProviderBase<AzureResourceDatabase> {
|
export class AzureResourceDatabaseTreeDataProvider extends ResourceTreeDataProviderBase<azureResource.AzureResourceDatabase> {
|
||||||
|
|
||||||
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 Databases");
|
private static readonly containerLabel = localize('azure.resource.providers.database.treeDataProvider.databaseContainerLabel', "SQL Databases");
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
databaseService: IAzureResourceService<AzureResourceDatabase>,
|
databaseService: IAzureResourceService<azureResource.AzureResourceDatabase>,
|
||||||
apiWrapper: ApiWrapper,
|
apiWrapper: ApiWrapper,
|
||||||
private _extensionContext: ExtensionContext
|
private _extensionContext: ExtensionContext
|
||||||
) {
|
) {
|
||||||
super(databaseService, apiWrapper);
|
super(databaseService, apiWrapper);
|
||||||
}
|
}
|
||||||
protected getTreeItemForResource(database: AzureResourceDatabase): TreeItem {
|
protected getTreeItemForResource(database: azureResource.AzureResourceDatabase): TreeItem {
|
||||||
return {
|
return {
|
||||||
id: `databaseServer_${database.serverFullName}.database_${database.name}`,
|
id: `databaseServer_${database.serverFullName}.database_${database.name}`,
|
||||||
label: `${database.name} (${database.serverName})`,
|
label: `${database.name} (${database.serverName})`,
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import { ExtensionContext } from 'vscode';
|
|||||||
import { ApiWrapper } from '../../../apiWrapper';
|
import { ApiWrapper } from '../../../apiWrapper';
|
||||||
|
|
||||||
import { azureResource } from '../../azure-resource';
|
import { azureResource } from '../../azure-resource';
|
||||||
import { IAzureResourceService, AzureResourceDatabaseServer } from '../../interfaces';
|
import { IAzureResourceService } from '../../interfaces';
|
||||||
import { AzureResourceDatabaseServerTreeDataProvider } from './databaseServerTreeDataProvider';
|
import { AzureResourceDatabaseServerTreeDataProvider } from './databaseServerTreeDataProvider';
|
||||||
|
|
||||||
export class AzureResourceDatabaseServerProvider implements azureResource.IAzureResourceProvider {
|
export class AzureResourceDatabaseServerProvider implements azureResource.IAzureResourceProvider {
|
||||||
public constructor(
|
public constructor(
|
||||||
private _databaseServerService: IAzureResourceService<AzureResourceDatabaseServer>,
|
private _databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>,
|
||||||
private _apiWrapper: ApiWrapper,
|
private _apiWrapper: ApiWrapper,
|
||||||
private _extensionContext: ExtensionContext
|
private _extensionContext: ExtensionContext
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase';
|
import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase';
|
||||||
import { AzureResourceDatabaseServer } from '../../interfaces';
|
import { azureResource } from '../../azure-resource';
|
||||||
|
|
||||||
|
|
||||||
export interface DbServerGraphData extends GraphData {
|
export interface DbServerGraphData extends GraphData {
|
||||||
@@ -17,13 +17,13 @@ export interface DbServerGraphData extends GraphData {
|
|||||||
|
|
||||||
export const serversQuery = 'where type == "microsoft.sql/servers"';
|
export const serversQuery = 'where type == "microsoft.sql/servers"';
|
||||||
|
|
||||||
export class AzureResourceDatabaseServerService extends ResourceServiceBase<DbServerGraphData, AzureResourceDatabaseServer> {
|
export class AzureResourceDatabaseServerService extends ResourceServiceBase<DbServerGraphData, azureResource.AzureResourceDatabaseServer> {
|
||||||
|
|
||||||
protected get query(): string {
|
protected get query(): string {
|
||||||
return serversQuery;
|
return serversQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected convertResource(resource: DbServerGraphData): AzureResourceDatabaseServer {
|
protected convertResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer {
|
||||||
return {
|
return {
|
||||||
id: resource.id,
|
id: resource.id,
|
||||||
name: resource.name,
|
name: resource.name,
|
||||||
|
|||||||
@@ -11,16 +11,16 @@ const localize = nls.loadMessageBundle();
|
|||||||
import { AzureResourceItemType } from '../../../azureResource/constants';
|
import { AzureResourceItemType } from '../../../azureResource/constants';
|
||||||
import { ApiWrapper } from '../../../apiWrapper';
|
import { ApiWrapper } from '../../../apiWrapper';
|
||||||
import { generateGuid } from '../../utils';
|
import { generateGuid } from '../../utils';
|
||||||
import { IAzureResourceService, AzureResourceDatabaseServer } from '../../interfaces';
|
import { IAzureResourceService } from '../../interfaces';
|
||||||
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
|
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
|
||||||
import { azureResource } from '../../azure-resource';
|
import { azureResource } from '../../azure-resource';
|
||||||
|
|
||||||
export class AzureResourceDatabaseServerTreeDataProvider extends ResourceTreeDataProviderBase<AzureResourceDatabaseServer> {
|
export class AzureResourceDatabaseServerTreeDataProvider extends ResourceTreeDataProviderBase<azureResource.AzureResourceDatabaseServer> {
|
||||||
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 Servers");
|
private static readonly containerLabel = localize('azure.resource.providers.databaseServer.treeDataProvider.databaseServerContainerLabel', "SQL Servers");
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
databaseServerService: IAzureResourceService<AzureResourceDatabaseServer>,
|
databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>,
|
||||||
apiWrapper: ApiWrapper,
|
apiWrapper: ApiWrapper,
|
||||||
private _extensionContext: ExtensionContext
|
private _extensionContext: ExtensionContext
|
||||||
) {
|
) {
|
||||||
@@ -28,7 +28,7 @@ export class AzureResourceDatabaseServerTreeDataProvider extends ResourceTreeDat
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected getTreeItemForResource(databaseServer: AzureResourceDatabaseServer): TreeItem {
|
protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer): TreeItem {
|
||||||
return {
|
return {
|
||||||
id: `databaseServer_${databaseServer.id ? databaseServer.id : databaseServer.name}`,
|
id: `databaseServer_${databaseServer.id ? databaseServer.id : databaseServer.name}`,
|
||||||
label: databaseServer.name,
|
label: databaseServer.name,
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import { ExtensionContext } from 'vscode';
|
|||||||
import { ApiWrapper } from '../../../apiWrapper';
|
import { ApiWrapper } from '../../../apiWrapper';
|
||||||
|
|
||||||
import { azureResource } from '../../azure-resource';
|
import { azureResource } from '../../azure-resource';
|
||||||
import { IAzureResourceService, AzureResourceDatabaseServer } from '../../interfaces';
|
import { IAzureResourceService } from '../../interfaces';
|
||||||
import { PostgresServerTreeDataProvider as PostgresServerTreeDataProvider } from './postgresServerTreeDataProvider';
|
import { PostgresServerTreeDataProvider as PostgresServerTreeDataProvider } from './postgresServerTreeDataProvider';
|
||||||
|
|
||||||
export class PostgresServerProvider implements azureResource.IAzureResourceProvider {
|
export class PostgresServerProvider implements azureResource.IAzureResourceProvider {
|
||||||
public constructor(
|
public constructor(
|
||||||
private _databaseServerService: IAzureResourceService<AzureResourceDatabaseServer>,
|
private _databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>,
|
||||||
private _apiWrapper: ApiWrapper,
|
private _apiWrapper: ApiWrapper,
|
||||||
private _extensionContext: ExtensionContext
|
private _extensionContext: ExtensionContext
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase';
|
import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase';
|
||||||
import { AzureResourceDatabaseServer } from '../../interfaces';
|
import { azureResource } from '../../azure-resource';
|
||||||
|
|
||||||
|
|
||||||
interface DbServerGraphData extends GraphData {
|
interface DbServerGraphData extends GraphData {
|
||||||
@@ -17,13 +17,13 @@ interface DbServerGraphData extends GraphData {
|
|||||||
|
|
||||||
const serversQuery = 'where type == "microsoft.dbforpostgresql/servers"';
|
const serversQuery = 'where type == "microsoft.dbforpostgresql/servers"';
|
||||||
|
|
||||||
export class PostgresServerService extends ResourceServiceBase<DbServerGraphData, AzureResourceDatabaseServer> {
|
export class PostgresServerService extends ResourceServiceBase<DbServerGraphData, azureResource.AzureResourceDatabaseServer> {
|
||||||
|
|
||||||
protected get query(): string {
|
protected get query(): string {
|
||||||
return serversQuery;
|
return serversQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected convertResource(resource: DbServerGraphData): AzureResourceDatabaseServer {
|
protected convertResource(resource: DbServerGraphData): azureResource.AzureResourceDatabaseServer {
|
||||||
return {
|
return {
|
||||||
id: resource.id,
|
id: resource.id,
|
||||||
name: resource.name,
|
name: resource.name,
|
||||||
|
|||||||
@@ -11,16 +11,16 @@ const localize = nls.loadMessageBundle();
|
|||||||
import { AzureResourceItemType } from '../../constants';
|
import { AzureResourceItemType } from '../../constants';
|
||||||
import { ApiWrapper } from '../../../apiWrapper';
|
import { ApiWrapper } from '../../../apiWrapper';
|
||||||
import { generateGuid } from '../../utils';
|
import { generateGuid } from '../../utils';
|
||||||
import { IAzureResourceService, AzureResourceDatabaseServer } from '../../interfaces';
|
import { IAzureResourceService } from '../../interfaces';
|
||||||
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
|
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
|
||||||
import { azureResource } from '../../azure-resource';
|
import { azureResource } from '../../azure-resource';
|
||||||
|
|
||||||
export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase<AzureResourceDatabaseServer> {
|
export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase<azureResource.AzureResourceDatabaseServer> {
|
||||||
private static readonly containerId = 'azure.resource.providers.databaseServer.treeDataProvider.postgresServerContainer';
|
private static readonly containerId = 'azure.resource.providers.databaseServer.treeDataProvider.postgresServerContainer';
|
||||||
private static readonly containerLabel = localize('azure.resource.providers.databaseServer.treeDataProvider.postgresServerContainerLabel', "Azure Database for PostgreSQL Servers");
|
private static readonly containerLabel = localize('azure.resource.providers.databaseServer.treeDataProvider.postgresServerContainerLabel', "Azure Database for PostgreSQL Servers");
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
databaseServerService: IAzureResourceService<AzureResourceDatabaseServer>,
|
databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>,
|
||||||
apiWrapper: ApiWrapper,
|
apiWrapper: ApiWrapper,
|
||||||
private _extensionContext: ExtensionContext
|
private _extensionContext: ExtensionContext
|
||||||
) {
|
) {
|
||||||
@@ -28,7 +28,7 @@ export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected getTreeItemForResource(databaseServer: AzureResourceDatabaseServer): TreeItem {
|
protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer): TreeItem {
|
||||||
return {
|
return {
|
||||||
id: `databaseServer_${databaseServer.id ? databaseServer.id : databaseServer.name}`,
|
id: `databaseServer_${databaseServer.id ? databaseServer.id : databaseServer.name}`,
|
||||||
label: databaseServer.name,
|
label: databaseServer.name,
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { DbServerGraphData } from '../databaseServer/databaseServerService';
|
||||||
|
import { azureResource } from '../../azure-resource';
|
||||||
|
import { ResourceServiceBase } from '../resourceTreeDataProviderBase';
|
||||||
|
|
||||||
|
export class AzureResourceGroupService extends ResourceServiceBase<DbServerGraphData, azureResource.AzureResourceResourceGroup> {
|
||||||
|
|
||||||
|
protected get query(): string {
|
||||||
|
return 'ResourceContainers | where type=="microsoft.resources/subscriptions/resourcegroups"';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected convertResource(resource: DbServerGraphData): azureResource.AzureResourceResourceGroup {
|
||||||
|
return {
|
||||||
|
id: resource.id,
|
||||||
|
name: resource.name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,16 +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 { AzureResource, TreeItem } from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as msRest from '@azure/ms-rest-js';
|
import * as msRest from '@azure/ms-rest-js';
|
||||||
|
|
||||||
import { azureResource } from '../azure-resource';
|
import { azureResource } from '../azure-resource';
|
||||||
import { ApiWrapper } from '../../apiWrapper';
|
import { ApiWrapper } from '../../apiWrapper';
|
||||||
import { IAzureResourceService, AzureSqlResource } from '../interfaces';
|
import { IAzureResourceService } from '../interfaces';
|
||||||
import { AzureResourceErrorMessageUtil } from '../utils';
|
import { AzureResourceErrorMessageUtil } from '../utils';
|
||||||
import { ResourceGraphClient } from '@azure/arm-resourcegraph';
|
import { ResourceGraphClient } from '@azure/arm-resourcegraph';
|
||||||
|
|
||||||
export abstract class ResourceTreeDataProviderBase<T extends AzureSqlResource> implements azureResource.IAzureResourceTreeDataProvider {
|
export abstract class ResourceTreeDataProviderBase<T extends azureResource.AzureResource> implements azureResource.IAzureResourceTreeDataProvider {
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
protected _resourceService: IAzureResourceService<T>,
|
protected _resourceService: IAzureResourceService<T>,
|
||||||
@@ -20,7 +20,7 @@ export abstract class ResourceTreeDataProviderBase<T extends AzureSqlResource> i
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTreeItem(element: azureResource.IAzureResourceNode): TreeItem | Thenable<TreeItem> {
|
public getTreeItem(element: azureResource.IAzureResourceNode): azdata.TreeItem | Thenable<azdata.TreeItem> {
|
||||||
return element.treeItem;
|
return element.treeItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,14 +45,14 @@ export abstract class ResourceTreeDataProviderBase<T extends AzureSqlResource> i
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getResources(element: azureResource.IAzureResourceNode): Promise<T[]> {
|
private async getResources(element: azureResource.IAzureResourceNode): Promise<T[]> {
|
||||||
const tokens = await this._apiWrapper.getSecurityToken(element.account, AzureResource.ResourceManagement);
|
const tokens = await this._apiWrapper.getSecurityToken(element.account, azdata.AzureResource.ResourceManagement);
|
||||||
const credential = new msRest.TokenCredentials(tokens[element.tenantId].token, tokens[element.tenantId].tokenType);
|
const credential = new msRest.TokenCredentials(tokens[element.tenantId].token, tokens[element.tenantId].tokenType);
|
||||||
|
|
||||||
const resources: T[] = await this._resourceService.getResources(element.subscription, credential) || <T[]>[];
|
const resources: T[] = await this._resourceService.getResources(element.subscription, credential) || <T[]>[];
|
||||||
return resources;
|
return resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract getTreeItemForResource(resource: T): TreeItem;
|
protected abstract getTreeItemForResource(resource: T): azdata.TreeItem;
|
||||||
|
|
||||||
protected abstract createContainerNode(): azureResource.IAzureResourceNode;
|
protected abstract createContainerNode(): azureResource.IAzureResourceNode;
|
||||||
}
|
}
|
||||||
@@ -89,10 +89,14 @@ export async function queryGraphResources<T extends GraphData>(resourceClient: R
|
|||||||
return allResources;
|
return allResources;
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class ResourceServiceBase<T extends GraphData, U extends AzureSqlResource> implements IAzureResourceService<U> {
|
export abstract class ResourceServiceBase<T extends GraphData, U extends azureResource.AzureResource> implements IAzureResourceService<U> {
|
||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The query to use - see https://docs.microsoft.com/azure/governance/resource-graph/concepts/query-language
|
||||||
|
* for more information on the supported syntax and tables/properties
|
||||||
|
*/
|
||||||
protected abstract get query(): string;
|
protected abstract get query(): string;
|
||||||
|
|
||||||
public async getResources(subscription: azureResource.AzureResourceSubscription, credential: msRest.ServiceClientCredentials): Promise<U[]> {
|
public async getResources(subscription: azureResource.AzureResourceSubscription, credential: msRest.ServiceClientCredentials): Promise<U[]> {
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import { ExtensionContext } from 'vscode';
|
|||||||
import { ApiWrapper } from '../../../apiWrapper';
|
import { ApiWrapper } from '../../../apiWrapper';
|
||||||
|
|
||||||
import { azureResource } from '../../azure-resource';
|
import { azureResource } from '../../azure-resource';
|
||||||
import { IAzureResourceService, AzureResourceDatabaseServer } from '../../interfaces';
|
import { IAzureResourceService } from '../../interfaces';
|
||||||
import { SqlInstanceTreeDataProvider as SqlInstanceTreeDataProvider } from './sqlInstanceTreeDataProvider';
|
import { SqlInstanceTreeDataProvider as SqlInstanceTreeDataProvider } from './sqlInstanceTreeDataProvider';
|
||||||
|
|
||||||
export class SqlInstanceProvider implements azureResource.IAzureResourceProvider {
|
export class SqlInstanceProvider implements azureResource.IAzureResourceProvider {
|
||||||
public constructor(
|
public constructor(
|
||||||
private _service: IAzureResourceService<AzureResourceDatabaseServer>,
|
private _service: IAzureResourceService<azureResource.AzureResourceDatabaseServer>,
|
||||||
private _apiWrapper: ApiWrapper,
|
private _apiWrapper: ApiWrapper,
|
||||||
private _extensionContext: ExtensionContext
|
private _extensionContext: ExtensionContext
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* 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 { AzureResourceDatabaseServer } from '../../interfaces';
|
import { azureResource } from '../../azure-resource';
|
||||||
import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase';
|
import { ResourceServiceBase, GraphData } from '../resourceTreeDataProviderBase';
|
||||||
|
|
||||||
interface SqlInstanceGraphData extends GraphData {
|
interface SqlInstanceGraphData extends GraphData {
|
||||||
@@ -15,13 +15,13 @@ interface SqlInstanceGraphData extends GraphData {
|
|||||||
|
|
||||||
const instanceQuery = 'where type == "microsoft.sql/managedinstances"';
|
const instanceQuery = 'where type == "microsoft.sql/managedinstances"';
|
||||||
|
|
||||||
export class SqlInstanceResourceService extends ResourceServiceBase<SqlInstanceGraphData, AzureResourceDatabaseServer> {
|
export class SqlInstanceResourceService extends ResourceServiceBase<SqlInstanceGraphData, azureResource.AzureResourceDatabaseServer> {
|
||||||
|
|
||||||
protected get query(): string {
|
protected get query(): string {
|
||||||
return instanceQuery;
|
return instanceQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected convertResource(resource: SqlInstanceGraphData): AzureResourceDatabaseServer {
|
protected convertResource(resource: SqlInstanceGraphData): azureResource.AzureResourceDatabaseServer {
|
||||||
return {
|
return {
|
||||||
id: resource.id,
|
id: resource.id,
|
||||||
name: resource.name,
|
name: resource.name,
|
||||||
|
|||||||
@@ -11,16 +11,16 @@ const localize = nls.loadMessageBundle();
|
|||||||
import { AzureResourceItemType } from '../../constants';
|
import { AzureResourceItemType } from '../../constants';
|
||||||
import { ApiWrapper } from '../../../apiWrapper';
|
import { ApiWrapper } from '../../../apiWrapper';
|
||||||
import { generateGuid } from '../../utils';
|
import { generateGuid } from '../../utils';
|
||||||
import { IAzureResourceService, AzureResourceDatabaseServer } from '../../interfaces';
|
import { IAzureResourceService } from '../../interfaces';
|
||||||
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
|
import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
|
||||||
import { azureResource } from '../../azure-resource';
|
import { azureResource } from '../../azure-resource';
|
||||||
|
|
||||||
export class SqlInstanceTreeDataProvider extends ResourceTreeDataProviderBase<AzureResourceDatabaseServer> {
|
export class SqlInstanceTreeDataProvider extends ResourceTreeDataProviderBase<azureResource.AzureResourceDatabaseServer> {
|
||||||
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', "SQL Managed Instances");
|
private static readonly containerLabel = localize('azure.resource.providers.sqlInstanceContainerLabel', "SQL Managed Instances");
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
databaseServerService: IAzureResourceService<AzureResourceDatabaseServer>,
|
databaseServerService: IAzureResourceService<azureResource.AzureResourceDatabaseServer>,
|
||||||
apiWrapper: ApiWrapper,
|
apiWrapper: ApiWrapper,
|
||||||
private _extensionContext: ExtensionContext
|
private _extensionContext: ExtensionContext
|
||||||
) {
|
) {
|
||||||
@@ -28,7 +28,7 @@ export class SqlInstanceTreeDataProvider extends ResourceTreeDataProviderBase<Az
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected getTreeItemForResource(databaseServer: AzureResourceDatabaseServer): TreeItem {
|
protected getTreeItemForResource(databaseServer: azureResource.AzureResourceDatabaseServer): TreeItem {
|
||||||
return {
|
return {
|
||||||
id: `sqlInstance_${databaseServer.id ? databaseServer.id : databaseServer.name}`,
|
id: `sqlInstance_${databaseServer.id ? databaseServer.id : databaseServer.name}`,
|
||||||
label: databaseServer.name,
|
label: databaseServer.name,
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ import { azureResource } from '../../../../azureResource/azure-resource';
|
|||||||
import { ApiWrapper } from '../../../../apiWrapper';
|
import { ApiWrapper } from '../../../../apiWrapper';
|
||||||
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, AzureResourceDatabase } from '../../../../azureResource/interfaces';
|
import { IAzureResourceService } from '../../../../azureResource/interfaces';
|
||||||
|
|
||||||
// Mock services
|
// Mock services
|
||||||
let mockDatabaseService: TypeMoq.IMock<IAzureResourceService<AzureResourceDatabase>>;
|
let mockDatabaseService: TypeMoq.IMock<IAzureResourceService<azureResource.AzureResourceDatabase>>;
|
||||||
let mockApiWrapper: TypeMoq.IMock<ApiWrapper>;
|
let mockApiWrapper: TypeMoq.IMock<ApiWrapper>;
|
||||||
let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
|
let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
|
||||||
|
|
||||||
@@ -62,15 +62,17 @@ mockTokens[mockTenantId] = {
|
|||||||
tokenType: 'Bearer'
|
tokenType: 'Bearer'
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockDatabases: AzureResourceDatabase[] = [
|
const mockDatabases: azureResource.AzureResourceDatabase[] = [
|
||||||
{
|
{
|
||||||
name: 'mock database 1',
|
name: 'mock database 1',
|
||||||
|
id: 'mock-id-1',
|
||||||
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'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'mock database 2',
|
name: 'mock database 2',
|
||||||
|
id: 'mock-id-2',
|
||||||
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'
|
||||||
@@ -79,7 +81,7 @@ const mockDatabases: AzureResourceDatabase[] = [
|
|||||||
|
|
||||||
describe('AzureResourceDatabaseTreeDataProvider.info', function (): void {
|
describe('AzureResourceDatabaseTreeDataProvider.info', function (): void {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockDatabaseService = TypeMoq.Mock.ofType<IAzureResourceService<AzureResourceDatabase>>();
|
mockDatabaseService = TypeMoq.Mock.ofType<IAzureResourceService<azureResource.AzureResourceDatabase>>();
|
||||||
mockApiWrapper = TypeMoq.Mock.ofType<ApiWrapper>();
|
mockApiWrapper = TypeMoq.Mock.ofType<ApiWrapper>();
|
||||||
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
|
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
|
||||||
});
|
});
|
||||||
@@ -97,7 +99,7 @@ describe('AzureResourceDatabaseTreeDataProvider.info', function (): void {
|
|||||||
|
|
||||||
describe('AzureResourceDatabaseTreeDataProvider.getChildren', function (): void {
|
describe('AzureResourceDatabaseTreeDataProvider.getChildren', function (): void {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockDatabaseService = TypeMoq.Mock.ofType<IAzureResourceService<AzureResourceDatabase>>();
|
mockDatabaseService = TypeMoq.Mock.ofType<IAzureResourceService<azureResource.AzureResourceDatabase>>();
|
||||||
mockApiWrapper = TypeMoq.Mock.ofType<ApiWrapper>();
|
mockApiWrapper = TypeMoq.Mock.ofType<ApiWrapper>();
|
||||||
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
|
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
|
||||||
|
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ import { azureResource } from '../../../../azureResource/azure-resource';
|
|||||||
import { ApiWrapper } from '../../../../apiWrapper';
|
import { ApiWrapper } from '../../../../apiWrapper';
|
||||||
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, AzureResourceDatabaseServer } from '../../../../azureResource/interfaces';
|
import { IAzureResourceService } from '../../../../azureResource/interfaces';
|
||||||
|
|
||||||
// Mock services
|
// Mock services
|
||||||
let mockDatabaseServerService: TypeMoq.IMock<IAzureResourceService<AzureResourceDatabaseServer>>;
|
let mockDatabaseServerService: TypeMoq.IMock<IAzureResourceService<azureResource.AzureResourceDatabaseServer>>;
|
||||||
let mockApiWrapper: TypeMoq.IMock<ApiWrapper>;
|
let mockApiWrapper: TypeMoq.IMock<ApiWrapper>;
|
||||||
let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
|
let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
|
||||||
|
|
||||||
@@ -62,15 +62,17 @@ mockTokens[mockTenantId] = {
|
|||||||
tokenType: 'Bearer'
|
tokenType: 'Bearer'
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockDatabaseServers: AzureResourceDatabaseServer[] = [
|
const mockDatabaseServers: azureResource.AzureResourceDatabaseServer[] = [
|
||||||
{
|
{
|
||||||
name: 'mock database server 1',
|
name: 'mock database server 1',
|
||||||
|
id: 'mock-id-1',
|
||||||
fullName: 'mock database server full name 1',
|
fullName: 'mock database server full name 1',
|
||||||
loginName: 'mock login',
|
loginName: 'mock login',
|
||||||
defaultDatabaseName: 'master'
|
defaultDatabaseName: 'master'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'mock database server 2',
|
name: 'mock database server 2',
|
||||||
|
id: 'mock-id-2',
|
||||||
fullName: 'mock database server full name 2',
|
fullName: 'mock database server full name 2',
|
||||||
loginName: 'mock login',
|
loginName: 'mock login',
|
||||||
defaultDatabaseName: 'master'
|
defaultDatabaseName: 'master'
|
||||||
@@ -79,7 +81,7 @@ const mockDatabaseServers: AzureResourceDatabaseServer[] = [
|
|||||||
|
|
||||||
describe('AzureResourceDatabaseServerTreeDataProvider.info', function (): void {
|
describe('AzureResourceDatabaseServerTreeDataProvider.info', function (): void {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockDatabaseServerService = TypeMoq.Mock.ofType<IAzureResourceService<AzureResourceDatabaseServer>>();
|
mockDatabaseServerService = TypeMoq.Mock.ofType<IAzureResourceService<azureResource.AzureResourceDatabaseServer>>();
|
||||||
mockApiWrapper = TypeMoq.Mock.ofType<ApiWrapper>();
|
mockApiWrapper = TypeMoq.Mock.ofType<ApiWrapper>();
|
||||||
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
|
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
|
||||||
});
|
});
|
||||||
@@ -97,7 +99,7 @@ describe('AzureResourceDatabaseServerTreeDataProvider.info', function (): void {
|
|||||||
|
|
||||||
describe('AzureResourceDatabaseServerTreeDataProvider.getChildren', function (): void {
|
describe('AzureResourceDatabaseServerTreeDataProvider.getChildren', function (): void {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockDatabaseServerService = TypeMoq.Mock.ofType<IAzureResourceService<AzureResourceDatabaseServer>>();
|
mockDatabaseServerService = TypeMoq.Mock.ofType<IAzureResourceService<azureResource.AzureResourceDatabaseServer>>();
|
||||||
mockApiWrapper = TypeMoq.Mock.ofType<ApiWrapper>();
|
mockApiWrapper = TypeMoq.Mock.ofType<ApiWrapper>();
|
||||||
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
|
mockExtensionContext = TypeMoq.Mock.ofType<vscode.ExtensionContext>();
|
||||||
|
|
||||||
@@ -139,7 +141,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.name}`);
|
should(child.treeItem.id).equal(`databaseServer_${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);
|
||||||
|
|||||||
@@ -172,6 +172,11 @@ export interface FieldInfo {
|
|||||||
editable?: boolean; // for editable dropdown
|
editable?: boolean; // for editable dropdown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AzureAccountFieldInfo extends FieldInfo {
|
||||||
|
subscriptionVariableName?: string;
|
||||||
|
resourceGroupVariableName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export const enum LabelPosition {
|
export const enum LabelPosition {
|
||||||
Top = 'top',
|
Top = 'top',
|
||||||
Left = 'left'
|
Left = 'left'
|
||||||
@@ -195,7 +200,8 @@ export enum FieldType {
|
|||||||
Password = 'password',
|
Password = 'password',
|
||||||
Options = 'options',
|
Options = 'options',
|
||||||
ReadonlyText = 'readonly_text',
|
ReadonlyText = 'readonly_text',
|
||||||
Checkbox = 'checkbox'
|
Checkbox = 'checkbox',
|
||||||
|
AzureAccount = 'azure_account'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NotebookInfo {
|
export interface NotebookInfo {
|
||||||
|
|||||||
12
extensions/resource-deployment/src/localizedConstants.ts
Normal file
12
extensions/resource-deployment/src/localizedConstants.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as nls from 'vscode-nls';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export const account = localize('azure.account', "Azure Account");
|
||||||
|
export const subscription = localize('azure.account.subscription', "Subscription");
|
||||||
|
export const resourceGroup = localize('azure.account.resourceGroup', "Resource Group");
|
||||||
@@ -6,4 +6,5 @@
|
|||||||
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
||||||
/// <reference path='../../../../src/sql/azdata.d.ts'/>
|
/// <reference path='../../../../src/sql/azdata.d.ts'/>
|
||||||
/// <reference path='../../../../src/sql/azdata.proposed.d.ts'/>
|
/// <reference path='../../../../src/sql/azdata.proposed.d.ts'/>
|
||||||
|
/// <reference path='../../../azurecore/src/azureResource/azure-resource.d.ts' />
|
||||||
/// <reference types='@types/node'/>
|
/// <reference types='@types/node'/>
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ export class AzureSettingsPage extends WizardPageBase<DeployClusterWizard> {
|
|||||||
self.wizard.registerDisposable(disposable);
|
self.wizard.registerDisposable(disposable);
|
||||||
},
|
},
|
||||||
onNewInputComponentCreated: (name: string, component: azdata.InputBoxComponent | azdata.DropDownComponent | azdata.CheckBoxComponent): void => {
|
onNewInputComponentCreated: (name: string, component: azdata.InputBoxComponent | azdata.DropDownComponent | azdata.CheckBoxComponent): void => {
|
||||||
self.inputComponents[name] = component;
|
self.inputComponents[name] = { component: component };
|
||||||
},
|
},
|
||||||
onNewValidatorCreated: (validator: Validator): void => {
|
onNewValidatorCreated: (validator: Validator): void => {
|
||||||
self.validators.push(validator);
|
self.validators.push(validator);
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ export class ClusterSettingsPage extends WizardPageBase<DeployClusterWizard> {
|
|||||||
self.wizard.registerDisposable(disposable);
|
self.wizard.registerDisposable(disposable);
|
||||||
},
|
},
|
||||||
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent): void => {
|
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent): void => {
|
||||||
self.inputComponents[name] = component;
|
self.inputComponents[name] = { component: component };
|
||||||
},
|
},
|
||||||
onNewValidatorCreated: (validator: Validator): void => {
|
onNewValidatorCreated: (validator: Validator): void => {
|
||||||
self.validators.push(validator);
|
self.validators.push(validator);
|
||||||
@@ -212,7 +212,7 @@ export class ClusterSettingsPage extends WizardPageBase<DeployClusterWizard> {
|
|||||||
self.wizard.registerDisposable(disposable);
|
self.wizard.registerDisposable(disposable);
|
||||||
},
|
},
|
||||||
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent): void => {
|
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent): void => {
|
||||||
self.inputComponents[name] = component;
|
self.inputComponents[name] = { component: component };
|
||||||
},
|
},
|
||||||
onNewValidatorCreated: (validator: Validator): void => {
|
onNewValidatorCreated: (validator: Validator): void => {
|
||||||
self.validators.push(validator);
|
self.validators.push(validator);
|
||||||
@@ -226,7 +226,7 @@ export class ClusterSettingsPage extends WizardPageBase<DeployClusterWizard> {
|
|||||||
self.wizard.registerDisposable(disposable);
|
self.wizard.registerDisposable(disposable);
|
||||||
},
|
},
|
||||||
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent): void => {
|
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent): void => {
|
||||||
self.inputComponents[name] = component;
|
self.inputComponents[name] = { component: component };
|
||||||
},
|
},
|
||||||
onNewValidatorCreated: (validator: Validator): void => {
|
onNewValidatorCreated: (validator: Validator): void => {
|
||||||
self.validators.push(validator);
|
self.validators.push(validator);
|
||||||
@@ -235,7 +235,7 @@ export class ClusterSettingsPage extends WizardPageBase<DeployClusterWizard> {
|
|||||||
const basicSettingsFormItem = { title: '', component: basicSettingsGroup };
|
const basicSettingsFormItem = { title: '', component: basicSettingsGroup };
|
||||||
const dockerSettingsFormItem = { title: '', component: dockerSettingsGroup };
|
const dockerSettingsFormItem = { title: '', component: dockerSettingsGroup };
|
||||||
this.activeDirectorySection = { title: '', component: activeDirectorySettingsGroup };
|
this.activeDirectorySection = { title: '', component: activeDirectorySettingsGroup };
|
||||||
const authModeDropdown = <azdata.DropDownComponent>this.inputComponents[VariableNames.AuthenticationMode_VariableName];
|
const authModeDropdown = <azdata.DropDownComponent>this.inputComponents[VariableNames.AuthenticationMode_VariableName].component;
|
||||||
this.formBuilder = view.modelBuilder.formContainer().withFormItems(
|
this.formBuilder = view.modelBuilder.formContainer().withFormItems(
|
||||||
[basicSettingsFormItem, dockerSettingsFormItem],
|
[basicSettingsFormItem, dockerSettingsFormItem],
|
||||||
{
|
{
|
||||||
@@ -290,7 +290,7 @@ export class ClusterSettingsPage extends WizardPageBase<DeployClusterWizard> {
|
|||||||
getInputBoxComponent(VariableNames.DockerRegistry_VariableName, this.inputComponents).value = this.wizard.model.getStringValue(VariableNames.DockerRegistry_VariableName);
|
getInputBoxComponent(VariableNames.DockerRegistry_VariableName, this.inputComponents).value = this.wizard.model.getStringValue(VariableNames.DockerRegistry_VariableName);
|
||||||
getInputBoxComponent(VariableNames.DockerRepository_VariableName, this.inputComponents).value = this.wizard.model.getStringValue(VariableNames.DockerRepository_VariableName);
|
getInputBoxComponent(VariableNames.DockerRepository_VariableName, this.inputComponents).value = this.wizard.model.getStringValue(VariableNames.DockerRepository_VariableName);
|
||||||
getInputBoxComponent(VariableNames.DockerImageTag_VariableName, this.inputComponents).value = this.wizard.model.getStringValue(VariableNames.DockerImageTag_VariableName);
|
getInputBoxComponent(VariableNames.DockerImageTag_VariableName, this.inputComponents).value = this.wizard.model.getStringValue(VariableNames.DockerImageTag_VariableName);
|
||||||
const authModeDropdown = <azdata.DropDownComponent>this.inputComponents[VariableNames.AuthenticationMode_VariableName];
|
const authModeDropdown = <azdata.DropDownComponent>this.inputComponents[VariableNames.AuthenticationMode_VariableName].component;
|
||||||
if (authModeDropdown) {
|
if (authModeDropdown) {
|
||||||
authModeDropdown.enabled = this.wizard.model.adAuthSupported;
|
authModeDropdown.enabled = this.wizard.model.adAuthSupported;
|
||||||
const adAuthSelected = (<azdata.CategoryValue>authModeDropdown.value).name === 'ad';
|
const adAuthSelected = (<azdata.CategoryValue>authModeDropdown.value).name === 'ad';
|
||||||
|
|||||||
@@ -296,7 +296,7 @@ export class ServiceSettingsPage extends WizardPageBase<DeployClusterWizard> {
|
|||||||
this.wizard.registerDisposable(disposable);
|
this.wizard.registerDisposable(disposable);
|
||||||
},
|
},
|
||||||
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent): void => {
|
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent): void => {
|
||||||
this.inputComponents[name] = component;
|
this.inputComponents[name] = { component: component };
|
||||||
},
|
},
|
||||||
onNewValidatorCreated: (validator: Validator): void => {
|
onNewValidatorCreated: (validator: Validator): void => {
|
||||||
}
|
}
|
||||||
@@ -348,43 +348,43 @@ export class ServiceSettingsPage extends WizardPageBase<DeployClusterWizard> {
|
|||||||
this.controllerDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.ControllerDNSName', "Controller DNS name"), required: false, width: inputWidth });
|
this.controllerDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.ControllerDNSName', "Controller DNS name"), required: false, width: inputWidth });
|
||||||
this.controllerPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.ControllerPortName', "Controller port"), required: true, width: NumberInputWidth, min: 1 });
|
this.controllerPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.ControllerPortName', "Controller port"), required: true, width: NumberInputWidth, min: 1 });
|
||||||
this.controllerEndpointRow = createFlexContainer(view, [this.controllerNameLabel, this.controllerDNSInput, this.controllerPortInput]);
|
this.controllerEndpointRow = createFlexContainer(view, [this.controllerNameLabel, this.controllerDNSInput, this.controllerPortInput]);
|
||||||
this.inputComponents[VariableNames.ControllerDNSName_VariableName] = this.controllerDNSInput;
|
this.inputComponents[VariableNames.ControllerDNSName_VariableName] = { component: this.controllerDNSInput };
|
||||||
this.inputComponents[VariableNames.ControllerPort_VariableName] = this.controllerPortInput;
|
this.inputComponents[VariableNames.ControllerPort_VariableName] = { component: this.controllerPortInput };
|
||||||
|
|
||||||
this.SqlServerNameLabel = createLabel(view, { text: localize('deployCluster.MasterSqlText', "SQL Server Master"), width: labelWidth, required: true });
|
this.SqlServerNameLabel = createLabel(view, { text: localize('deployCluster.MasterSqlText', "SQL Server Master"), width: labelWidth, required: true });
|
||||||
this.sqlServerDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.MasterSQLServerDNSName', "SQL Server Master DNS name"), required: false, width: inputWidth });
|
this.sqlServerDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.MasterSQLServerDNSName', "SQL Server Master DNS name"), required: false, width: inputWidth });
|
||||||
this.sqlServerPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.MasterSQLServerPortName', "SQL Server Master port"), required: true, width: NumberInputWidth, min: 1 });
|
this.sqlServerPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.MasterSQLServerPortName', "SQL Server Master port"), required: true, width: NumberInputWidth, min: 1 });
|
||||||
this.sqlServerEndpointRow = createFlexContainer(view, [this.SqlServerNameLabel, this.sqlServerDNSInput, this.sqlServerPortInput]);
|
this.sqlServerEndpointRow = createFlexContainer(view, [this.SqlServerNameLabel, this.sqlServerDNSInput, this.sqlServerPortInput]);
|
||||||
this.inputComponents[VariableNames.SQLServerDNSName_VariableName] = this.sqlServerDNSInput;
|
this.inputComponents[VariableNames.SQLServerDNSName_VariableName] = { component: this.sqlServerDNSInput };
|
||||||
this.inputComponents[VariableNames.SQLServerPort_VariableName] = this.sqlServerPortInput;
|
this.inputComponents[VariableNames.SQLServerPort_VariableName] = { component: this.sqlServerPortInput };
|
||||||
|
|
||||||
this.gatewayNameLabel = createLabel(view, { text: localize('deployCluster.GatewayText', "Gateway"), width: labelWidth, required: true });
|
this.gatewayNameLabel = createLabel(view, { text: localize('deployCluster.GatewayText', "Gateway"), width: labelWidth, required: true });
|
||||||
this.gatewayDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.GatewayDNSName', "Gateway DNS name"), required: false, width: inputWidth });
|
this.gatewayDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.GatewayDNSName', "Gateway DNS name"), required: false, width: inputWidth });
|
||||||
this.gatewayPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.GatewayPortName', "Gateway port"), required: true, width: NumberInputWidth, min: 1 });
|
this.gatewayPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.GatewayPortName', "Gateway port"), required: true, width: NumberInputWidth, min: 1 });
|
||||||
this.gatewayEndpointRow = createFlexContainer(view, [this.gatewayNameLabel, this.gatewayDNSInput, this.gatewayPortInput]);
|
this.gatewayEndpointRow = createFlexContainer(view, [this.gatewayNameLabel, this.gatewayDNSInput, this.gatewayPortInput]);
|
||||||
this.inputComponents[VariableNames.GatewayDNSName_VariableName] = this.gatewayDNSInput;
|
this.inputComponents[VariableNames.GatewayDNSName_VariableName] = { component: this.gatewayDNSInput };
|
||||||
this.inputComponents[VariableNames.GateWayPort_VariableName] = this.gatewayPortInput;
|
this.inputComponents[VariableNames.GateWayPort_VariableName] = { component: this.gatewayPortInput };
|
||||||
|
|
||||||
this.serviceProxyNameLabel = createLabel(view, { text: localize('deployCluster.ServiceProxyText', "Management proxy"), width: labelWidth, required: true });
|
this.serviceProxyNameLabel = createLabel(view, { text: localize('deployCluster.ServiceProxyText', "Management proxy"), width: labelWidth, required: true });
|
||||||
this.serviceProxyDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.ServiceProxyDNSName', "Management proxy DNS name"), required: false, width: inputWidth });
|
this.serviceProxyDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.ServiceProxyDNSName', "Management proxy DNS name"), required: false, width: inputWidth });
|
||||||
this.serviceProxyPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.ServiceProxyPortName', "Management proxy port"), required: true, width: NumberInputWidth, min: 1 });
|
this.serviceProxyPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.ServiceProxyPortName', "Management proxy port"), required: true, width: NumberInputWidth, min: 1 });
|
||||||
this.serviceProxyEndpointRow = createFlexContainer(view, [this.serviceProxyNameLabel, this.serviceProxyDNSInput, this.serviceProxyPortInput]);
|
this.serviceProxyEndpointRow = createFlexContainer(view, [this.serviceProxyNameLabel, this.serviceProxyDNSInput, this.serviceProxyPortInput]);
|
||||||
this.inputComponents[VariableNames.ServiceProxyDNSName_VariableName] = this.serviceProxyDNSInput;
|
this.inputComponents[VariableNames.ServiceProxyDNSName_VariableName] = { component: this.serviceProxyDNSInput };
|
||||||
this.inputComponents[VariableNames.ServiceProxyPort_VariableName] = this.serviceProxyPortInput;
|
this.inputComponents[VariableNames.ServiceProxyPort_VariableName] = { component: this.serviceProxyPortInput };
|
||||||
|
|
||||||
this.appServiceProxyNameLabel = createLabel(view, { text: localize('deployCluster.AppServiceProxyText', "Application proxy"), width: labelWidth, required: true });
|
this.appServiceProxyNameLabel = createLabel(view, { text: localize('deployCluster.AppServiceProxyText', "Application proxy"), width: labelWidth, required: true });
|
||||||
this.appServiceProxyDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.AppServiceProxyDNSName', "Application proxy DNS name"), required: false, width: inputWidth });
|
this.appServiceProxyDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.AppServiceProxyDNSName', "Application proxy DNS name"), required: false, width: inputWidth });
|
||||||
this.appServiceProxyPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.AppServiceProxyPortName', "Application proxy port"), required: true, width: NumberInputWidth, min: 1 });
|
this.appServiceProxyPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.AppServiceProxyPortName', "Application proxy port"), required: true, width: NumberInputWidth, min: 1 });
|
||||||
this.appServiceProxyEndpointRow = createFlexContainer(view, [this.appServiceProxyNameLabel, this.appServiceProxyDNSInput, this.appServiceProxyPortInput]);
|
this.appServiceProxyEndpointRow = createFlexContainer(view, [this.appServiceProxyNameLabel, this.appServiceProxyDNSInput, this.appServiceProxyPortInput]);
|
||||||
this.inputComponents[VariableNames.AppServiceProxyDNSName_VariableName] = this.appServiceProxyDNSInput;
|
this.inputComponents[VariableNames.AppServiceProxyDNSName_VariableName] = { component: this.appServiceProxyDNSInput };
|
||||||
this.inputComponents[VariableNames.AppServiceProxyPort_VariableName] = this.appServiceProxyPortInput;
|
this.inputComponents[VariableNames.AppServiceProxyPort_VariableName] = { component: this.appServiceProxyPortInput };
|
||||||
|
|
||||||
this.readableSecondaryNameLabel = createLabel(view, { text: localize('deployCluster.ReadableSecondaryText', "Readable secondary"), width: labelWidth, required: true });
|
this.readableSecondaryNameLabel = createLabel(view, { text: localize('deployCluster.ReadableSecondaryText', "Readable secondary"), width: labelWidth, required: true });
|
||||||
this.readableSecondaryDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.ReadableSecondaryDNSName', "Readable secondary DNS name"), required: false, width: inputWidth });
|
this.readableSecondaryDNSInput = createTextInput(view, { ariaLabel: localize('deployCluster.ReadableSecondaryDNSName', "Readable secondary DNS name"), required: false, width: inputWidth });
|
||||||
this.readableSecondaryPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.ReadableSecondaryPortName', "Readable secondary port"), required: false, width: NumberInputWidth, min: 1 });
|
this.readableSecondaryPortInput = createNumberInput(view, { ariaLabel: localize('deployCluster.ReadableSecondaryPortName', "Readable secondary port"), required: false, width: NumberInputWidth, min: 1 });
|
||||||
this.readableSecondaryEndpointRow = createFlexContainer(view, [this.readableSecondaryNameLabel, this.readableSecondaryDNSInput, this.readableSecondaryPortInput]);
|
this.readableSecondaryEndpointRow = createFlexContainer(view, [this.readableSecondaryNameLabel, this.readableSecondaryDNSInput, this.readableSecondaryPortInput]);
|
||||||
this.inputComponents[VariableNames.ReadableSecondaryDNSName_VariableName] = this.readableSecondaryDNSInput;
|
this.inputComponents[VariableNames.ReadableSecondaryDNSName_VariableName] = { component: this.readableSecondaryDNSInput };
|
||||||
this.inputComponents[VariableNames.ReadableSecondaryPort_VariableName] = this.readableSecondaryPortInput;
|
this.inputComponents[VariableNames.ReadableSecondaryPort_VariableName] = { component: this.readableSecondaryPortInput };
|
||||||
|
|
||||||
return createGroupContainer(view, [this.endpointHeaderRow, this.controllerEndpointRow, this.sqlServerEndpointRow, this.gatewayEndpointRow, this.serviceProxyEndpointRow, this.appServiceProxyEndpointRow, this.readableSecondaryEndpointRow], {
|
return createGroupContainer(view, [this.endpointHeaderRow, this.controllerEndpointRow, this.sqlServerEndpointRow, this.gatewayEndpointRow, this.serviceProxyEndpointRow, this.appServiceProxyEndpointRow, this.readableSecondaryEndpointRow], {
|
||||||
header: localize('deployCluster.EndpointSettings', "Endpoint settings"),
|
header: localize('deployCluster.EndpointSettings', "Endpoint settings"),
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import * as nls from 'vscode-nls';
|
|||||||
import { DialogBase } from './dialogBase';
|
import { DialogBase } from './dialogBase';
|
||||||
import { INotebookService } from '../services/notebookService';
|
import { INotebookService } from '../services/notebookService';
|
||||||
import { DialogInfo, instanceOfNotebookBasedDialogInfo, NotebookBasedDialogInfo } from '../interfaces';
|
import { DialogInfo, instanceOfNotebookBasedDialogInfo, NotebookBasedDialogInfo } from '../interfaces';
|
||||||
import { Validator, initializeDialog, InputComponents, setModelValues } from './modelViewUtils';
|
import { Validator, initializeDialog, InputComponents, setModelValues, InputValueTransformer } from './modelViewUtils';
|
||||||
import { Model } from './model';
|
import { Model } from './model';
|
||||||
import { EOL } from 'os';
|
import { EOL } from 'os';
|
||||||
import { getDateTimeString, getErrorMessage } from '../utils';
|
import { getDateTimeString, getErrorMessage } from '../utils';
|
||||||
@@ -46,8 +46,8 @@ export class DeploymentInputDialog extends DialogBase {
|
|||||||
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
|
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
|
||||||
this._toDispose.push(disposable);
|
this._toDispose.push(disposable);
|
||||||
},
|
},
|
||||||
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent): void => {
|
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent, inputValueTransformer?: InputValueTransformer): void => {
|
||||||
this.inputComponents[name] = component;
|
this.inputComponents[name] = { component: component, inputValueTransformer: inputValueTransformer };
|
||||||
},
|
},
|
||||||
onNewValidatorCreated: (validator: Validator): void => {
|
onNewValidatorCreated: (validator: Validator): void => {
|
||||||
validators.push(validator);
|
validators.push(validator);
|
||||||
|
|||||||
@@ -6,25 +6,28 @@
|
|||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { DialogInfoBase, FieldType, FieldInfo, SectionInfo, LabelPosition, FontWeight, FontStyle } from '../interfaces';
|
import { DialogInfoBase, FieldType, FieldInfo, SectionInfo, LabelPosition, FontWeight, FontStyle, AzureAccountFieldInfo } from '../interfaces';
|
||||||
import { Model } from './model';
|
import { Model } from './model';
|
||||||
import { getDateTimeString } from '../utils';
|
import { getDateTimeString } from '../utils';
|
||||||
|
import { azureResource } from '../../../azurecore/src/azureResource/azure-resource';
|
||||||
|
import * as loc from '../localizedConstants';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
export type Validator = () => { valid: boolean, message: string };
|
export type Validator = () => { valid: boolean, message: string };
|
||||||
export type InputComponents = { [s: string]: azdata.InputBoxComponent | azdata.DropDownComponent | azdata.CheckBoxComponent; };
|
export type InputValueTransformer = (inputValue: string) => string;
|
||||||
|
export type InputComponents = { [s: string]: { component: azdata.InputBoxComponent | azdata.DropDownComponent | azdata.CheckBoxComponent; inputValueTransformer?: InputValueTransformer } };
|
||||||
|
|
||||||
export function getInputBoxComponent(name: string, inputComponents: InputComponents): azdata.InputBoxComponent {
|
export function getInputBoxComponent(name: string, inputComponents: InputComponents): azdata.InputBoxComponent {
|
||||||
return <azdata.InputBoxComponent>inputComponents[name];
|
return <azdata.InputBoxComponent>inputComponents[name].component;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDropdownComponent(name: string, inputComponents: InputComponents): azdata.DropDownComponent {
|
export function getDropdownComponent(name: string, inputComponents: InputComponents): azdata.DropDownComponent {
|
||||||
return <azdata.DropDownComponent>inputComponents[name];
|
return <azdata.DropDownComponent>inputComponents[name].component;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCheckboxComponent(name: string, inputComponents: InputComponents): azdata.CheckBoxComponent {
|
export function getCheckboxComponent(name: string, inputComponents: InputComponents): azdata.CheckBoxComponent {
|
||||||
return <azdata.CheckBoxComponent>inputComponents[name];
|
return <azdata.CheckBoxComponent>inputComponents[name].component;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DefaultInputComponentWidth = '400px';
|
export const DefaultInputComponentWidth = '400px';
|
||||||
@@ -52,11 +55,15 @@ interface FieldContext extends CreateContext {
|
|||||||
view: azdata.ModelView;
|
view: azdata.ModelView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface AzureAccountFieldContext extends FieldContext {
|
||||||
|
fieldInfo: AzureAccountFieldInfo;
|
||||||
|
}
|
||||||
|
|
||||||
interface CreateContext {
|
interface CreateContext {
|
||||||
container: azdata.window.Dialog | azdata.window.Wizard;
|
container: azdata.window.Dialog | azdata.window.Wizard;
|
||||||
onNewValidatorCreated: (validator: Validator) => void;
|
onNewValidatorCreated: (validator: Validator) => void;
|
||||||
onNewDisposableCreated: (disposable: vscode.Disposable) => void;
|
onNewDisposableCreated: (disposable: vscode.Disposable) => void;
|
||||||
onNewInputComponentCreated: (name: string, component: azdata.InputBoxComponent | azdata.DropDownComponent | azdata.CheckBoxComponent) => void;
|
onNewInputComponentCreated: (name: string, component: azdata.InputBoxComponent | azdata.DropDownComponent | azdata.CheckBoxComponent, inputValueTransformer?: InputValueTransformer) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createTextInput(view: azdata.ModelView, inputInfo: { defaultValue?: string, ariaLabel: string, required?: boolean, placeHolder?: string, width?: string }): azdata.InputBoxComponent {
|
export function createTextInput(view: azdata.ModelView, inputInfo: { defaultValue?: string, ariaLabel: string, required?: boolean, placeHolder?: string, width?: string }): azdata.InputBoxComponent {
|
||||||
@@ -260,6 +267,9 @@ function processField(context: FieldContext): void {
|
|||||||
case FieldType.Checkbox:
|
case FieldType.Checkbox:
|
||||||
processCheckboxField(context);
|
processCheckboxField(context);
|
||||||
break;
|
break;
|
||||||
|
case FieldType.AzureAccount:
|
||||||
|
processAzureAccountField(context);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(localize('UnknownFieldTypeError', "Unknown field type: \"{0}\"", context.fieldInfo.type));
|
throw new Error(localize('UnknownFieldTypeError', "Unknown field type: \"{0}\"", context.fieldInfo.type));
|
||||||
}
|
}
|
||||||
@@ -411,6 +421,119 @@ function processCheckboxField(context: FieldContext): void {
|
|||||||
context.onNewInputComponentCreated(context.fieldInfo.variableName!, checkbox);
|
context.onNewInputComponentCreated(context.fieldInfo.variableName!, checkbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Values used for the Azure Account field inputs
|
||||||
|
let selectedAccount: azdata.Account | undefined;
|
||||||
|
let subscriptionDropdown: azdata.DropDownComponent;
|
||||||
|
let resourceGroupDropdown: azdata.DropDownComponent;
|
||||||
|
const accountValueToAccountMap = new Map<string, azdata.Account>();
|
||||||
|
const subscriptionValueToSubscriptionMap = new Map<string, azureResource.AzureResourceSubscription>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An Azure Account field consists of 3 separate dropdown fields - Account, Subscription and Resource Group
|
||||||
|
* @param context The context to use to create the field
|
||||||
|
*/
|
||||||
|
function processAzureAccountField(context: AzureAccountFieldContext): void {
|
||||||
|
if (subscriptionDropdown) {
|
||||||
|
throw new Error(localize('onlyOneAzureAccountField', "Only one Azure Account field is supported at this time"));
|
||||||
|
}
|
||||||
|
const accountDropdown = createAzureAccountDropdown(context);
|
||||||
|
createAzureSubscriptionDropdown(context, accountDropdown);
|
||||||
|
createAzureResourceGroupsDropdown(context, subscriptionDropdown);
|
||||||
|
azdata.accounts.getAllAccounts().then((accounts: azdata.Account[]) => {
|
||||||
|
accountDropdown.values = accounts.map(account => {
|
||||||
|
const displayName = `${account.displayInfo.displayName} (${account.displayInfo.userId})`;
|
||||||
|
accountValueToAccountMap.set(displayName, account);
|
||||||
|
return displayName;
|
||||||
|
});
|
||||||
|
selectedAccount = accounts.length > 0 ? accounts[0] : undefined;
|
||||||
|
handleSelectedAccountChanged();
|
||||||
|
}, (err: any) => console.log(`Unexpected error fetching accounts: ${err}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAzureAccountDropdown(context: AzureAccountFieldContext): azdata.DropDownComponent {
|
||||||
|
const label = createLabel(context.view, {
|
||||||
|
text: loc.account,
|
||||||
|
description: context.fieldInfo.description,
|
||||||
|
required: context.fieldInfo.required,
|
||||||
|
width: context.fieldInfo.labelWidth,
|
||||||
|
fontWeight: context.fieldInfo.labelFontWeight
|
||||||
|
});
|
||||||
|
const accountDropdown = createDropdown(context.view, {
|
||||||
|
width: context.fieldInfo.inputWidth,
|
||||||
|
editable: false,
|
||||||
|
required: context.fieldInfo.required,
|
||||||
|
label: loc.account
|
||||||
|
});
|
||||||
|
context.onNewInputComponentCreated('', accountDropdown);
|
||||||
|
addLabelInputPairToContainer(context.view, context.components, label, accountDropdown, context.fieldInfo.labelPosition);
|
||||||
|
return accountDropdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAzureSubscriptionDropdown(context: AzureAccountFieldContext, accountDropdown: azdata.DropDownComponent): void {
|
||||||
|
const label = createLabel(context.view, {
|
||||||
|
text: loc.subscription,
|
||||||
|
required: context.fieldInfo.required,
|
||||||
|
width: context.fieldInfo.labelWidth,
|
||||||
|
fontWeight: context.fieldInfo.labelFontWeight
|
||||||
|
});
|
||||||
|
subscriptionDropdown = createDropdown(context.view, {
|
||||||
|
width: context.fieldInfo.inputWidth,
|
||||||
|
editable: false,
|
||||||
|
required: context.fieldInfo.required,
|
||||||
|
label: loc.subscription
|
||||||
|
});
|
||||||
|
context.onNewInputComponentCreated(context.fieldInfo.subscriptionVariableName!, subscriptionDropdown, (inputValue: string) => {
|
||||||
|
return subscriptionValueToSubscriptionMap.get(inputValue)?.id || inputValue;
|
||||||
|
});
|
||||||
|
addLabelInputPairToContainer(context.view, context.components, label, subscriptionDropdown, context.fieldInfo.labelPosition);
|
||||||
|
accountDropdown.onValueChanged(selectedItem => {
|
||||||
|
selectedAccount = accountValueToAccountMap.get(selectedItem.selected);
|
||||||
|
handleSelectedAccountChanged();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSelectedAccountChanged(): void {
|
||||||
|
subscriptionValueToSubscriptionMap.clear();
|
||||||
|
subscriptionDropdown.values = [];
|
||||||
|
vscode.commands.executeCommand('azure.accounts.getSubscriptions', selectedAccount).then(subscriptions => {
|
||||||
|
subscriptionDropdown.values = (<azureResource.AzureResourceSubscription[]>subscriptions).map(subscription => {
|
||||||
|
const displayName = `${subscription.name} (${subscription.id})`;
|
||||||
|
subscriptionValueToSubscriptionMap.set(displayName, subscription);
|
||||||
|
return displayName;
|
||||||
|
}).sort((a: string, b: string) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase()));
|
||||||
|
const selectedSubscription = subscriptionDropdown.values.length > 0 ? subscriptionValueToSubscriptionMap.get(subscriptionDropdown.values[0]) : undefined;
|
||||||
|
handleSelectedSubscriptionChanged(selectedSubscription);
|
||||||
|
}, err => { console.log(`Unexpected error fetching subscriptions for account ${selectedAccount?.displayInfo.displayName} (${selectedAccount?.key.accountId}): ${err}`); });
|
||||||
|
}
|
||||||
|
|
||||||
|
function createAzureResourceGroupsDropdown(context: AzureAccountFieldContext, subscriptionDropdown: azdata.DropDownComponent): void {
|
||||||
|
const label = createLabel(context.view, {
|
||||||
|
text: loc.resourceGroup,
|
||||||
|
required: context.fieldInfo.required,
|
||||||
|
width: context.fieldInfo.labelWidth,
|
||||||
|
fontWeight: context.fieldInfo.labelFontWeight
|
||||||
|
});
|
||||||
|
resourceGroupDropdown = createDropdown(context.view, {
|
||||||
|
width: context.fieldInfo.inputWidth,
|
||||||
|
editable: false,
|
||||||
|
required: context.fieldInfo.required,
|
||||||
|
label: loc.resourceGroup
|
||||||
|
});
|
||||||
|
context.onNewInputComponentCreated(context.fieldInfo.resourceGroupVariableName!, resourceGroupDropdown);
|
||||||
|
addLabelInputPairToContainer(context.view, context.components, label, resourceGroupDropdown, context.fieldInfo.labelPosition);
|
||||||
|
subscriptionDropdown.onValueChanged(selectedItem => {
|
||||||
|
const selectedSubscription = subscriptionValueToSubscriptionMap.get(selectedItem.selected);
|
||||||
|
handleSelectedSubscriptionChanged(selectedSubscription);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSelectedSubscriptionChanged(selectedSubscription: azureResource.AzureResourceSubscription | undefined): void {
|
||||||
|
resourceGroupDropdown.values = [];
|
||||||
|
vscode.commands.executeCommand('azure.accounts.getResourceGroups', selectedAccount, selectedSubscription).then(resourceGroups => {
|
||||||
|
resourceGroupDropdown.values = (<azureResource.AzureResourceSubscription[]>resourceGroups).map(resourceGroup => resourceGroup.name).sort((a: string, b: string) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase()));
|
||||||
|
}, err => { console.log(`Unexpected error fetching resource groups for subscription ${selectedSubscription?.name} (${selectedSubscription?.id}): ${err}`); });
|
||||||
|
}
|
||||||
|
|
||||||
export function isValidSQLPassword(password: string, userName: string = 'sa'): boolean {
|
export function isValidSQLPassword(password: string, userName: string = 'sa'): boolean {
|
||||||
// Validate SQL Server password
|
// Validate SQL Server password
|
||||||
const containsUserName = password && userName !== undefined && password.toUpperCase().includes(userName.toUpperCase());
|
const containsUserName = password && userName !== undefined && password.toUpperCase().includes(userName.toUpperCase());
|
||||||
@@ -441,7 +564,7 @@ export function getPasswordMismatchMessage(fieldName: string): string {
|
|||||||
export function setModelValues(inputComponents: InputComponents, model: Model): void {
|
export function setModelValues(inputComponents: InputComponents, model: Model): void {
|
||||||
Object.keys(inputComponents).forEach(key => {
|
Object.keys(inputComponents).forEach(key => {
|
||||||
let value;
|
let value;
|
||||||
const input = inputComponents[key];
|
const input = inputComponents[key].component;
|
||||||
if ('checked' in input) { // CheckBoxComponent
|
if ('checked' in input) { // CheckBoxComponent
|
||||||
value = input.checked ? 'true' : 'false';
|
value = input.checked ? 'true' : 'false';
|
||||||
} else if ('value' in input) { // InputBoxComponent or DropDownComponent
|
} else if ('value' in input) { // InputBoxComponent or DropDownComponent
|
||||||
@@ -455,6 +578,10 @@ export function setModelValues(inputComponents: InputComponents, model: Model):
|
|||||||
throw new Error(`Unknown input type with ID ${input.id}`);
|
throw new Error(`Unknown input type with ID ${input.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const inputValueTransformer = inputComponents[key].inputValueTransformer;
|
||||||
|
if (inputValueTransformer) {
|
||||||
|
value = inputValueTransformer(value || '');
|
||||||
|
}
|
||||||
model.setPropertyValue(key, value);
|
model.setPropertyValue(key, value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user