mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Revert "Port the Azure Resource Explorer extension to core." (#2770)
* Revert "change how query plan is handled (#2735)" This reverts commit0693080630. * Revert "center the icon (#2760)" This reverts commit75d27837c2. * Revert "Alanren/edit data improvement (#2748)" This reverts commit597f29e90a. * Revert "Port the Azure Resource Explorer extension to core. (#2701)" This reverts commita77bb50b9e.
This commit is contained in:
@@ -1,120 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { window, QuickPickItem } from 'vscode';
|
||||
import { IConnectionProfile } from 'sqlops';
|
||||
import { generateGuid } from './utils';
|
||||
import { ApiWrapper } from '../apiWrapper';
|
||||
import { TreeNode } from '../treeNodes';
|
||||
|
||||
import { AzureResourceTreeProvider } from './tree/treeProvider';
|
||||
import { AzureResourceDatabaseServerTreeNode } from './tree/databaseServerTreeNode';
|
||||
import { AzureResourceDatabaseTreeNode } from './tree/databaseTreeNode';
|
||||
import { AzureResourceAccountTreeNode } from './tree/accountTreeNode';
|
||||
import { AzureResourceServicePool } from './servicePool';
|
||||
import { AzureResourceSubscription } from './models';
|
||||
|
||||
export function registerAzureResourceCommands(apiWrapper: ApiWrapper, tree: AzureResourceTreeProvider): void {
|
||||
apiWrapper.registerCommand('azureresource.selectsubscriptions', async (node?: TreeNode) => {
|
||||
if (!(node instanceof AzureResourceAccountTreeNode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const accountNode = node as AzureResourceAccountTreeNode;
|
||||
|
||||
const servicePool = AzureResourceServicePool.getInstance();
|
||||
|
||||
let subscriptions = await accountNode.getCachedSubscriptions();
|
||||
if (!subscriptions || subscriptions.length === 0) {
|
||||
const credentials = await servicePool.credentialService.getCredentials(accountNode.account);
|
||||
subscriptions = await servicePool.subscriptionService.getSubscriptions(accountNode.account, credentials);
|
||||
}
|
||||
|
||||
const selectedSubscriptions = (await servicePool.subscriptionFilterService.getSelectedSubscriptions(accountNode.account)) || <AzureResourceSubscription[]>[];
|
||||
const selectedSubscriptionIds: string[] = [];
|
||||
if (selectedSubscriptions.length > 0) {
|
||||
selectedSubscriptionIds.push(...selectedSubscriptions.map((subscription) => subscription.id));
|
||||
} else {
|
||||
// ALL subscriptions are selected by default
|
||||
selectedSubscriptionIds.push(...subscriptions.map((subscription) => subscription.id));
|
||||
}
|
||||
|
||||
interface SubscriptionQuickPickItem extends QuickPickItem {
|
||||
subscription: AzureResourceSubscription;
|
||||
}
|
||||
|
||||
const subscriptionItems: SubscriptionQuickPickItem[] = subscriptions.map((subscription) => {
|
||||
return {
|
||||
label: subscription.name,
|
||||
picked: selectedSubscriptionIds.indexOf(subscription.id) !== -1,
|
||||
subscription: subscription
|
||||
};
|
||||
});
|
||||
|
||||
const pickedSubscriptionItems = (await window.showQuickPick(subscriptionItems, { canPickMany: true }));
|
||||
if (pickedSubscriptionItems && pickedSubscriptionItems.length > 0) {
|
||||
tree.refresh(node, false);
|
||||
|
||||
const pickedSubscriptions = pickedSubscriptionItems.map((subscriptionItem) => subscriptionItem.subscription);
|
||||
await servicePool.subscriptionFilterService.saveSelectedSubscriptions(accountNode.account, pickedSubscriptions);
|
||||
}
|
||||
});
|
||||
|
||||
apiWrapper.registerCommand('azureresource.refreshall', () => tree.notifyNodeChanged(undefined));
|
||||
|
||||
apiWrapper.registerCommand('azureresource.refresh', async (node?: TreeNode) => {
|
||||
tree.refresh(node, true);
|
||||
});
|
||||
|
||||
apiWrapper.registerCommand('azureresource.connectsqldb', async (node?: TreeNode) => {
|
||||
let connectionProfile: IConnectionProfile = {
|
||||
id: generateGuid(),
|
||||
connectionName: undefined,
|
||||
serverName: undefined,
|
||||
databaseName: undefined,
|
||||
userName: undefined,
|
||||
password: '',
|
||||
authenticationType: undefined,
|
||||
savePassword: true,
|
||||
groupFullName: '',
|
||||
groupId: '',
|
||||
providerName: undefined,
|
||||
saveProfile: true,
|
||||
options: {
|
||||
}
|
||||
};
|
||||
|
||||
if (node instanceof AzureResourceDatabaseServerTreeNode) {
|
||||
let databaseServer = node.databaseServer;
|
||||
connectionProfile.connectionName = `connection to '${databaseServer.defaultDatabaseName}' on '${databaseServer.fullName}'`;
|
||||
connectionProfile.serverName = databaseServer.fullName;
|
||||
connectionProfile.databaseName = databaseServer.defaultDatabaseName;
|
||||
connectionProfile.userName = databaseServer.loginName;
|
||||
connectionProfile.authenticationType = 'SqlLogin';
|
||||
connectionProfile.providerName = 'MSSQL';
|
||||
}
|
||||
|
||||
if (node instanceof AzureResourceDatabaseTreeNode) {
|
||||
let database = node.database;
|
||||
connectionProfile.connectionName = `connection to '${database.name}' on '${database.serverFullName}'`;
|
||||
connectionProfile.serverName = database.serverFullName;
|
||||
connectionProfile.databaseName = database.name;
|
||||
connectionProfile.userName = database.loginName;
|
||||
connectionProfile.authenticationType = 'SqlLogin';
|
||||
connectionProfile.providerName = 'MSSQL';
|
||||
}
|
||||
|
||||
const conn = await apiWrapper.openConnectionDialog(undefined, connectionProfile, { saveConnection: true, showDashboard: true });
|
||||
if (conn) {
|
||||
apiWrapper.executeCommand('workbench.view.connections');
|
||||
}
|
||||
});
|
||||
|
||||
apiWrapper.registerCommand('azureresource.signin', async (node?: TreeNode) => {
|
||||
apiWrapper.executeCommand('sql.action.accounts.manageLinkedAccount');
|
||||
});
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
export enum AzureResourceItemType {
|
||||
account = 'azureResource.itemType.account',
|
||||
subscription = 'azureResource.itemType.subscription',
|
||||
databaseContainer = 'azureResource.itemType.databaseContainer',
|
||||
database = 'azureResource.itemType.database',
|
||||
databaseServerContainer = 'azureResource.itemType.databaseServerContainer',
|
||||
databaseServer = 'azureResource.itemType.databaseServer',
|
||||
message = 'azureResource.itemType.message'
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
export class AzureResourceCredentialError extends Error {
|
||||
constructor(
|
||||
message: string,
|
||||
public innerError: Error
|
||||
) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { ServiceClientCredentials } from 'ms-rest';
|
||||
import { Account, DidChangeAccountsParams } from 'sqlops';
|
||||
import { Event } from 'vscode';
|
||||
|
||||
import { AzureResourceSubscription, AzureResourceDatabaseServer, AzureResourceDatabase } from './models';
|
||||
|
||||
export interface IAzureResourceAccountService {
|
||||
getAccounts(): Promise<Account[]>;
|
||||
|
||||
readonly onDidChangeAccounts: Event<DidChangeAccountsParams>;
|
||||
}
|
||||
|
||||
export interface IAzureResourceCredentialService {
|
||||
getCredentials(account: Account): Promise<ServiceClientCredentials[]>;
|
||||
}
|
||||
|
||||
export interface IAzureResourceSubscriptionService {
|
||||
getSubscriptions(account: Account, credentials: ServiceClientCredentials[]): Promise<AzureResourceSubscription[]>;
|
||||
}
|
||||
|
||||
export interface IAzureResourceSubscriptionFilterService {
|
||||
getSelectedSubscriptions(account: Account): Promise<AzureResourceSubscription[]>;
|
||||
|
||||
saveSelectedSubscriptions(account: Account, selectedSubscriptions: AzureResourceSubscription[]): Promise<void>;
|
||||
}
|
||||
|
||||
export interface IAzureResourceDatabaseServerService {
|
||||
getDatabaseServers(subscription: AzureResourceSubscription, credentials: ServiceClientCredentials[]): Promise<AzureResourceDatabaseServer[]>;
|
||||
}
|
||||
|
||||
export interface IAzureResourceDatabaseService {
|
||||
getDatabases(subscription: AzureResourceSubscription, credentials: ServiceClientCredentials[]): Promise<AzureResourceDatabase[]>;
|
||||
}
|
||||
|
||||
export interface IAzureResourceCacheService {
|
||||
get<T>(key: string): T | undefined;
|
||||
|
||||
update<T>(key: string, value: T): void;
|
||||
}
|
||||
|
||||
export interface IAzureResourceContextService {
|
||||
getAbsolutePath(relativePath: string): string;
|
||||
|
||||
executeCommand(commandId: string, ...args: any[]): void;
|
||||
|
||||
showErrorMessage(errorMessage: string): void;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
export interface AzureResourceSubscription {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface AzureResourceDatabaseServer {
|
||||
name: string;
|
||||
fullName: string;
|
||||
loginName: string;
|
||||
defaultDatabaseName: string;
|
||||
}
|
||||
|
||||
export interface AzureResourceDatabase {
|
||||
name: string;
|
||||
serverName: string;
|
||||
serverFullName: string;
|
||||
loginName: string;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import {
|
||||
IAzureResourceAccountService,
|
||||
IAzureResourceCredentialService,
|
||||
IAzureResourceSubscriptionService,
|
||||
IAzureResourceSubscriptionFilterService,
|
||||
IAzureResourceDatabaseService,
|
||||
IAzureResourceDatabaseServerService,
|
||||
IAzureResourceCacheService,
|
||||
IAzureResourceContextService } from './interfaces';
|
||||
|
||||
export class AzureResourceServicePool {
|
||||
private constructor() { }
|
||||
|
||||
public static getInstance(): AzureResourceServicePool {
|
||||
return AzureResourceServicePool._instance;
|
||||
}
|
||||
|
||||
public contextService: IAzureResourceContextService;
|
||||
public cacheService: IAzureResourceCacheService;
|
||||
public accountService: IAzureResourceAccountService;
|
||||
public credentialService: IAzureResourceCredentialService;
|
||||
public subscriptionService: IAzureResourceSubscriptionService;
|
||||
public subscriptionFilterService: IAzureResourceSubscriptionFilterService;
|
||||
public databaseService: IAzureResourceDatabaseService;
|
||||
public databaseServerService: IAzureResourceDatabaseServerService;
|
||||
|
||||
private static readonly _instance = new AzureResourceServicePool();
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Event } from 'vscode';
|
||||
import { Account, DidChangeAccountsParams } from 'sqlops';
|
||||
import { ApiWrapper } from '../../apiWrapper';
|
||||
|
||||
import { IAzureResourceAccountService } from '../interfaces';
|
||||
|
||||
export class AzureResourceAccountService implements IAzureResourceAccountService {
|
||||
public constructor(
|
||||
apiWrapper: ApiWrapper
|
||||
) {
|
||||
this._apiWrapper = apiWrapper;
|
||||
this._onDidChangeAccounts = this._apiWrapper.onDidChangeAccounts;
|
||||
}
|
||||
|
||||
public async getAccounts(): Promise<Account[]> {
|
||||
return await this._apiWrapper.getAllAccounts();
|
||||
}
|
||||
|
||||
public get onDidChangeAccounts(): Event<DidChangeAccountsParams> {
|
||||
return this._onDidChangeAccounts;
|
||||
}
|
||||
|
||||
private _apiWrapper: ApiWrapper = undefined;
|
||||
private _onDidChangeAccounts: Event<DidChangeAccountsParams> = undefined;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { ExtensionContext } from "vscode";
|
||||
|
||||
import { IAzureResourceCacheService } from "../interfaces";
|
||||
|
||||
export class AzureResourceCacheService implements IAzureResourceCacheService {
|
||||
public constructor(
|
||||
public readonly context: ExtensionContext
|
||||
) {
|
||||
}
|
||||
|
||||
public get<T>(key: string): T | undefined {
|
||||
return this.context.workspaceState.get(key);
|
||||
}
|
||||
|
||||
public update<T>(key: string, value: T): void {
|
||||
this.context.workspaceState.update(key, value);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { ExtensionContext } from "vscode";
|
||||
import { ApiWrapper } from "../../apiWrapper";
|
||||
|
||||
import { IAzureResourceContextService } from "../interfaces";
|
||||
|
||||
export class AzureResourceContextService implements IAzureResourceContextService {
|
||||
public constructor(
|
||||
context: ExtensionContext,
|
||||
apiWrapper: ApiWrapper
|
||||
) {
|
||||
this._context = context;
|
||||
this._apiWrapper = apiWrapper;
|
||||
}
|
||||
|
||||
public getAbsolutePath(relativePath: string): string {
|
||||
return this._context.asAbsolutePath(relativePath);
|
||||
}
|
||||
|
||||
public executeCommand(commandId: string, ...args: any[]): void {
|
||||
this._apiWrapper.executeCommand(commandId, args);
|
||||
}
|
||||
|
||||
public showErrorMessage(errorMessage: string): void {
|
||||
this._apiWrapper.showErrorMessage(errorMessage);
|
||||
}
|
||||
|
||||
private _context: ExtensionContext = undefined;
|
||||
private _apiWrapper: ApiWrapper = undefined;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Account } from 'sqlops';
|
||||
import { TokenCredentials, ServiceClientCredentials } from 'ms-rest';
|
||||
import { ApiWrapper } from '../../apiWrapper';
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
import { IAzureResourceCredentialService } from '../interfaces';
|
||||
import { AzureResourceCredentialError } from '../errors';
|
||||
|
||||
export class AzureResourceCredentialService implements IAzureResourceCredentialService {
|
||||
public constructor(
|
||||
apiWrapper: ApiWrapper
|
||||
) {
|
||||
this._apiWrapper = apiWrapper;
|
||||
}
|
||||
|
||||
public async getCredentials(account: Account): Promise<ServiceClientCredentials[]> {
|
||||
try {
|
||||
let credentials: TokenCredentials[] = [];
|
||||
let tokens = await this._apiWrapper.getSecurityToken(account);
|
||||
|
||||
for (let tenant of account.properties.tenants) {
|
||||
let token = tokens[tenant.id].token;
|
||||
let tokenType = tokens[tenant.id].tokenType;
|
||||
|
||||
credentials.push(new TokenCredentials(token, tokenType));
|
||||
}
|
||||
|
||||
return credentials;
|
||||
} catch (error) {
|
||||
throw new AzureResourceCredentialError(localize('azureResource.services.credentialService.credentialError', 'Failed to get credential for account {0}. Please refresh the account.', account.key.accountId), error);
|
||||
}
|
||||
}
|
||||
|
||||
private _apiWrapper: ApiWrapper = undefined;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { ServiceClientCredentials } from 'ms-rest';
|
||||
import { SqlManagementClient } from 'azure-arm-sql';
|
||||
|
||||
import { IAzureResourceDatabaseServerService } from '../interfaces';
|
||||
import { AzureResourceSubscription, AzureResourceDatabaseServer } from '../models';
|
||||
|
||||
export class AzureResourceDatabaseServerService implements IAzureResourceDatabaseServerService {
|
||||
public async getDatabaseServers(subscription: AzureResourceSubscription, credentials: ServiceClientCredentials[]): Promise<AzureResourceDatabaseServer[]> {
|
||||
let databaseServers: AzureResourceDatabaseServer[] = [];
|
||||
for (let cred of credentials) {
|
||||
let sqlManagementClient = new SqlManagementClient(cred, subscription.id);
|
||||
try {
|
||||
let svrs = await sqlManagementClient.servers.list();
|
||||
svrs.forEach((svr) => databaseServers.push({
|
||||
name: svr.name,
|
||||
fullName: svr.fullyQualifiedDomainName,
|
||||
loginName: svr.administratorLogin,
|
||||
defaultDatabaseName: 'master'
|
||||
}));
|
||||
} catch (error) {
|
||||
if (error.code === 'InvalidAuthenticationTokenTenant' && error.statusCode === 401) {
|
||||
/**
|
||||
* There may be multiple tenants for an account and it may throw exceptions like following. Just swallow the exception here.
|
||||
* The access token is from the wrong issuer. It must match one of the tenants associated with this subscription.
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return databaseServers;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { ServiceClientCredentials } from 'ms-rest';
|
||||
import { SqlManagementClient } from 'azure-arm-sql';
|
||||
|
||||
import { IAzureResourceDatabaseService } from '../interfaces';
|
||||
import { AzureResourceSubscription, AzureResourceDatabase } from '../models';
|
||||
|
||||
export class AzureResourceDatabaseService implements IAzureResourceDatabaseService {
|
||||
public async getDatabases(subscription: AzureResourceSubscription, credentials: ServiceClientCredentials[]): Promise<AzureResourceDatabase[]> {
|
||||
let databases: AzureResourceDatabase[] = [];
|
||||
for (let cred of credentials) {
|
||||
let sqlManagementClient = new SqlManagementClient(cred, subscription.id);
|
||||
try {
|
||||
let svrs = await sqlManagementClient.servers.list();
|
||||
for (let svr of svrs) {
|
||||
// Extract resource group name from svr.id
|
||||
let svrIdRegExp = new RegExp(`\/subscriptions\/${subscription.id}\/resourceGroups\/(.+)\/providers\/Microsoft\.Sql\/servers\/${svr.name}`);
|
||||
if (!svrIdRegExp.test(svr.id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let founds = svrIdRegExp.exec(svr.id);
|
||||
let resouceGroup = founds[1];
|
||||
|
||||
let dbs = await sqlManagementClient.databases.listByServer(resouceGroup, svr.name);
|
||||
dbs.forEach((db) => databases.push({
|
||||
name: db.name,
|
||||
serverName: svr.name,
|
||||
serverFullName: svr.fullyQualifiedDomainName,
|
||||
loginName: svr.administratorLogin
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.code === 'InvalidAuthenticationTokenTenant' && error.statusCode === 401) {
|
||||
/**
|
||||
* There may be multiple tenants for an account and it may throw exceptions like following. Just swallow the exception here.
|
||||
* The access token is from the wrong issuer. It must match one of the tenants associated with this subscription.
|
||||
*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return databases;
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { WorkspaceConfiguration, ConfigurationTarget } from 'vscode';
|
||||
import { Account } from 'sqlops';
|
||||
|
||||
import { IAzureResourceSubscriptionFilterService, IAzureResourceCacheService } from '../interfaces';
|
||||
import { AzureResourceSubscription } from '../models';
|
||||
|
||||
interface AzureResourceSelectedSubscriptionsCache {
|
||||
selectedSubscriptions: { [accountId: string]: AzureResourceSubscription[]};
|
||||
}
|
||||
|
||||
export class AzureResourceSubscriptionFilterService implements IAzureResourceSubscriptionFilterService {
|
||||
public constructor(
|
||||
cacheService: IAzureResourceCacheService
|
||||
) {
|
||||
this._cacheService = cacheService;
|
||||
}
|
||||
|
||||
public async getSelectedSubscriptions(account: Account): Promise<AzureResourceSubscription[]> {
|
||||
let selectedSubscriptions: AzureResourceSubscription[] = [];
|
||||
|
||||
const cache = this._cacheService.get<AzureResourceSelectedSubscriptionsCache>(AzureResourceSubscriptionFilterService.CacheKey);
|
||||
if (cache) {
|
||||
selectedSubscriptions = cache.selectedSubscriptions[account.key.accountId];
|
||||
}
|
||||
|
||||
return selectedSubscriptions;
|
||||
}
|
||||
|
||||
public async saveSelectedSubscriptions(account: Account, selectedSubscriptions: AzureResourceSubscription[]): Promise<void> {
|
||||
let selectedSubscriptionsCache: { [accountId: string]: AzureResourceSubscription[]} = {};
|
||||
|
||||
const cache = this._cacheService.get<AzureResourceSelectedSubscriptionsCache>(AzureResourceSubscriptionFilterService.CacheKey);
|
||||
if (cache) {
|
||||
selectedSubscriptionsCache = cache.selectedSubscriptions;
|
||||
}
|
||||
|
||||
if (!selectedSubscriptionsCache) {
|
||||
selectedSubscriptionsCache = {};
|
||||
}
|
||||
|
||||
selectedSubscriptionsCache[account.key.accountId] = selectedSubscriptions;
|
||||
|
||||
this._cacheService.update<AzureResourceSelectedSubscriptionsCache>(AzureResourceSubscriptionFilterService.CacheKey, { selectedSubscriptions: selectedSubscriptionsCache });
|
||||
|
||||
const filters: string[] = [];
|
||||
for (const accountId in selectedSubscriptionsCache) {
|
||||
filters.push(...selectedSubscriptionsCache[accountId].map((subcription) => `${accountId}/${subcription.id}/${subcription.name}`));
|
||||
}
|
||||
|
||||
const resourceFilterConfig = this._config.inspect<string[]>(AzureResourceSubscriptionFilterService.FilterConfigName);
|
||||
let configTarget = ConfigurationTarget.Global;
|
||||
if (resourceFilterConfig) {
|
||||
if (resourceFilterConfig.workspaceFolderValue) {
|
||||
configTarget = ConfigurationTarget.WorkspaceFolder;
|
||||
} else if (resourceFilterConfig.workspaceValue) {
|
||||
configTarget = ConfigurationTarget.Workspace;
|
||||
} else if (resourceFilterConfig.globalValue) {
|
||||
configTarget = ConfigurationTarget.Global;
|
||||
}
|
||||
}
|
||||
|
||||
await this._config.update(AzureResourceSubscriptionFilterService.FilterConfigName, filters, configTarget);
|
||||
}
|
||||
|
||||
private _config: WorkspaceConfiguration = undefined;
|
||||
private _cacheService: IAzureResourceCacheService = undefined;
|
||||
|
||||
private static readonly FilterConfigName = 'resourceFilter';
|
||||
private static readonly CacheKey = 'azureResource.cache.selectedSubscriptions';
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Account } from 'sqlops';
|
||||
import { ServiceClientCredentials } from 'ms-rest';
|
||||
import { SubscriptionClient } from 'azure-arm-resource';
|
||||
|
||||
import { IAzureResourceSubscriptionService } from '../interfaces';
|
||||
import { AzureResourceSubscription } from '../models';
|
||||
|
||||
export class AzureResourceSubscriptionService implements IAzureResourceSubscriptionService {
|
||||
public async getSubscriptions(account: Account, credentials: ServiceClientCredentials[]): Promise<AzureResourceSubscription[]> {
|
||||
let subscriptions: AzureResourceSubscription[] = [];
|
||||
for (let cred of credentials) {
|
||||
let subClient = new SubscriptionClient.SubscriptionClient(cred);
|
||||
try {
|
||||
let subs = await subClient.subscriptions.list();
|
||||
subs.forEach((sub) => subscriptions.push({
|
||||
id: sub.subscriptionId,
|
||||
name: sub.displayName
|
||||
}));
|
||||
} catch (error) {
|
||||
// Swallow the exception here.
|
||||
}
|
||||
}
|
||||
|
||||
return subscriptions;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { NodeInfo } from 'sqlops';
|
||||
import { TreeNode } from '../../treeNodes';
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
import { AzureResourceItemType } from '../constants';
|
||||
|
||||
export class AzureResourceAccountNotSignedInTreeNode extends TreeNode {
|
||||
public getChildren(): TreeNode[] | Promise<TreeNode[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
public getTreeItem(): TreeItem | Promise<TreeItem> {
|
||||
let item = new TreeItem(AzureResourceAccountNotSignedInTreeNode.SignInLabel, TreeItemCollapsibleState.None);
|
||||
item.contextValue = AzureResourceItemType.message;
|
||||
item.command = {
|
||||
title: AzureResourceAccountNotSignedInTreeNode.SignInLabel,
|
||||
command: 'azureresource.signin',
|
||||
arguments: [this]
|
||||
};
|
||||
return item;
|
||||
}
|
||||
|
||||
public getNodeInfo(): NodeInfo {
|
||||
return {
|
||||
label: AzureResourceAccountNotSignedInTreeNode.SignInLabel,
|
||||
isLeaf: true,
|
||||
errorMessage: undefined,
|
||||
metadata: undefined,
|
||||
nodePath: this.generateNodePath(),
|
||||
nodeStatus: undefined,
|
||||
nodeType: AzureResourceItemType.message,
|
||||
nodeSubType: undefined,
|
||||
iconType: AzureResourceItemType.message
|
||||
};
|
||||
}
|
||||
|
||||
public get nodePathValue(): string {
|
||||
return 'message_accountNotSignedIn';
|
||||
}
|
||||
|
||||
private static readonly SignInLabel = localize('azureResource.tree.accountNotSignedInTreeNode.signIn', 'Sign in to Azure ...');
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { Account, NodeInfo } from 'sqlops';
|
||||
import { TreeNode } from '../../treeNodes';
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
import { AzureResourceContainerTreeNodeBase } from './baseTreeNodes';
|
||||
import { AzureResourceItemType } from '../constants';
|
||||
import { AzureResourceSubscriptionTreeNode } from './subscriptionTreeNode';
|
||||
import { AzureResourceMessageTreeNode } from './messageTreeNode';
|
||||
import { AzureResourceErrorMessageUtil } from '../utils';
|
||||
import { AzureResourceSubscription } from '../models';
|
||||
import { IAzureResourceTreeChangeHandler } from './treeProvider';
|
||||
|
||||
export class AzureResourceAccountTreeNode extends AzureResourceContainerTreeNodeBase {
|
||||
public constructor(
|
||||
account: Account,
|
||||
treeChangeHandler: IAzureResourceTreeChangeHandler
|
||||
) {
|
||||
super(account, treeChangeHandler, undefined);
|
||||
|
||||
this._id = `account_${this.account.key.accountId}`;
|
||||
this._label = this.generateLabel();
|
||||
}
|
||||
|
||||
public async getChildren(): Promise<TreeNode[]> {
|
||||
try {
|
||||
let subscriptions: AzureResourceSubscription[] = [];
|
||||
|
||||
if (this._isClearingCache) {
|
||||
const credentials = await this.getCredentials();
|
||||
subscriptions = (await this.servicePool.subscriptionService.getSubscriptions(this.account, credentials)) || <AzureResourceSubscription[]>[];
|
||||
|
||||
let cache = this.getCache<AzureResourceSubscriptionsCache>();
|
||||
if (!cache) {
|
||||
cache = { subscriptions: { } };
|
||||
}
|
||||
cache.subscriptions[this.account.key.accountId] = subscriptions;
|
||||
this.updateCache<AzureResourceSubscriptionsCache>(cache);
|
||||
|
||||
this._isClearingCache = false;
|
||||
} else {
|
||||
subscriptions = await this.getCachedSubscriptions();
|
||||
}
|
||||
|
||||
this._totalSubscriptionCount = subscriptions.length;
|
||||
|
||||
let selectedSubscriptions = await this.servicePool.subscriptionFilterService.getSelectedSubscriptions(this.account);
|
||||
let selectedSubscriptionIds = (selectedSubscriptions || <AzureResourceSubscription[]>[]).map((subscription) => subscription.id);
|
||||
if (selectedSubscriptionIds.length > 0) {
|
||||
subscriptions = subscriptions.filter((subscription) => selectedSubscriptionIds.indexOf(subscription.id) !== -1);
|
||||
this._selectedSubscriptionCount = selectedSubscriptionIds.length;
|
||||
} else {
|
||||
// ALL subscriptions are listed by default
|
||||
this._selectedSubscriptionCount = this._totalSubscriptionCount;
|
||||
}
|
||||
|
||||
this.refreshLabel();
|
||||
|
||||
if (subscriptions.length === 0) {
|
||||
return [AzureResourceMessageTreeNode.create(AzureResourceAccountTreeNode.NoSubscriptions, this)];
|
||||
} else {
|
||||
return subscriptions.map((subscription) => new AzureResourceSubscriptionTreeNode(subscription, this.account, this.treeChangeHandler, this));
|
||||
}
|
||||
} catch (error) {
|
||||
return [AzureResourceMessageTreeNode.create(AzureResourceErrorMessageUtil.getErrorMessage(error), this)];
|
||||
}
|
||||
}
|
||||
|
||||
public async getCachedSubscriptions(): Promise<AzureResourceSubscription[]> {
|
||||
const subscriptions: AzureResourceSubscription[] = [];
|
||||
const cache = this.getCache<AzureResourceSubscriptionsCache>();
|
||||
if (cache) {
|
||||
subscriptions.push(...cache.subscriptions[this.account.key.accountId]);
|
||||
}
|
||||
return subscriptions;
|
||||
}
|
||||
|
||||
public getTreeItem(): TreeItem | Promise<TreeItem> {
|
||||
let item = new TreeItem(this._label, TreeItemCollapsibleState.Collapsed);
|
||||
item.id = this._id;
|
||||
item.contextValue = AzureResourceItemType.account;
|
||||
item.iconPath = {
|
||||
dark: this.servicePool.contextService.getAbsolutePath('resources/dark/account_inverse.svg'),
|
||||
light: this.servicePool.contextService.getAbsolutePath('resources/light/account.svg')
|
||||
};
|
||||
return item;
|
||||
}
|
||||
|
||||
public getNodeInfo(): NodeInfo {
|
||||
return {
|
||||
label: this._label,
|
||||
isLeaf: false,
|
||||
errorMessage: undefined,
|
||||
metadata: undefined,
|
||||
nodePath: this.generateNodePath(),
|
||||
nodeStatus: undefined,
|
||||
nodeType: AzureResourceItemType.account,
|
||||
nodeSubType: undefined,
|
||||
iconType: AzureResourceItemType.account
|
||||
};
|
||||
}
|
||||
|
||||
public get nodePathValue(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get totalSubscriptionCount(): number {
|
||||
return this._totalSubscriptionCount;
|
||||
}
|
||||
|
||||
public get selectedSubscriptionCount(): number {
|
||||
return this._selectedSubscriptionCount;
|
||||
}
|
||||
|
||||
protected refreshLabel(): void {
|
||||
const newLabel = this.generateLabel();
|
||||
if (this._label !== newLabel) {
|
||||
this._label = newLabel;
|
||||
this.treeChangeHandler.notifyNodeChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected get cacheKey(): string {
|
||||
return 'azureResource.cache.subscriptions';
|
||||
}
|
||||
|
||||
private generateLabel(): string {
|
||||
let label = `${this.account.displayInfo.displayName} (${this.account.key.accountId})`;
|
||||
|
||||
if (this._totalSubscriptionCount !== 0) {
|
||||
label += ` (${this._selectedSubscriptionCount} / ${this._totalSubscriptionCount} subscriptions)`;
|
||||
}
|
||||
|
||||
return label;
|
||||
}
|
||||
|
||||
private _id: string = undefined;
|
||||
private _label: string = undefined;
|
||||
private _totalSubscriptionCount = 0;
|
||||
private _selectedSubscriptionCount = 0;
|
||||
|
||||
private static readonly NoSubscriptions = localize('azureResource.tree.accountTreeNode.noSubscriptions', 'No Subscriptions found.');
|
||||
}
|
||||
|
||||
interface AzureResourceSubscriptionsCache {
|
||||
subscriptions: { [accountId: string]: AzureResourceSubscription[] };
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Account } from 'sqlops';
|
||||
import { ServiceClientCredentials } from 'ms-rest';
|
||||
import { TreeNode } from '../../treeNodes';
|
||||
|
||||
import { AzureResourceServicePool } from '../servicePool';
|
||||
import { AzureResourceCredentialError } from '../errors';
|
||||
import { IAzureResourceTreeChangeHandler } from './treeChangeHandler';
|
||||
|
||||
export abstract class AzureResourceTreeNodeBase extends TreeNode {
|
||||
public constructor(
|
||||
public readonly treeChangeHandler: IAzureResourceTreeChangeHandler,
|
||||
parent: TreeNode
|
||||
) {
|
||||
super();
|
||||
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public readonly servicePool = AzureResourceServicePool.getInstance();
|
||||
}
|
||||
|
||||
export abstract class AzureResourceContainerTreeNodeBase extends AzureResourceTreeNodeBase {
|
||||
public constructor(
|
||||
public readonly account: Account,
|
||||
treeChangeHandler: IAzureResourceTreeChangeHandler,
|
||||
parent: TreeNode
|
||||
) {
|
||||
super(treeChangeHandler, parent);
|
||||
}
|
||||
|
||||
public clearCache(): void {
|
||||
this._isClearingCache = true;
|
||||
}
|
||||
|
||||
public get isClearingCache(): boolean {
|
||||
return this._isClearingCache;
|
||||
}
|
||||
|
||||
protected async getCredentials(): Promise<ServiceClientCredentials[]> {
|
||||
try {
|
||||
return await this.servicePool.credentialService.getCredentials(this.account);
|
||||
} catch (error) {
|
||||
if (error instanceof AzureResourceCredentialError) {
|
||||
this.servicePool.contextService.showErrorMessage(error.message);
|
||||
|
||||
this.servicePool.contextService.executeCommand('azureresource.signin');
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected updateCache<T>(cache: T): void {
|
||||
this.servicePool.cacheService.update<T>(this.cacheKey, cache);
|
||||
}
|
||||
|
||||
protected getCache<T>(): T {
|
||||
return this.servicePool.cacheService.get<T>(this.cacheKey);
|
||||
}
|
||||
|
||||
protected abstract get cacheKey(): string;
|
||||
|
||||
protected _isClearingCache = true;
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { Account, NodeInfo } from 'sqlops';
|
||||
import { TreeNode } from '../../treeNodes';
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
import { AzureResourceContainerTreeNodeBase } from './baseTreeNodes';
|
||||
import { AzureResourceItemType } from '../constants';
|
||||
import { AzureResourceErrorMessageUtil } from '../utils';
|
||||
import { AzureResourceDatabaseTreeNode } from './databaseTreeNode';
|
||||
import { AzureResourceMessageTreeNode } from './messageTreeNode';
|
||||
import { AzureResourceSubscription, AzureResourceDatabase } from '../models';
|
||||
import { IAzureResourceTreeChangeHandler } from './treeProvider';
|
||||
|
||||
export class AzureResourceDatabaseContainerTreeNode extends AzureResourceContainerTreeNodeBase {
|
||||
public constructor(
|
||||
public readonly subscription: AzureResourceSubscription,
|
||||
account: Account,
|
||||
treeChangeHandler: IAzureResourceTreeChangeHandler,
|
||||
parent: TreeNode
|
||||
) {
|
||||
super(account, treeChangeHandler, parent);
|
||||
}
|
||||
|
||||
public async getChildren(): Promise<TreeNode[]> {
|
||||
try {
|
||||
let databases: AzureResourceDatabase[] = [];
|
||||
|
||||
if (this._isClearingCache) {
|
||||
let credentials = await this.getCredentials();
|
||||
databases = (await this.servicePool.databaseService.getDatabases(this.subscription, credentials)) || <AzureResourceDatabase[]>[];
|
||||
|
||||
let cache = this.getCache<AzureResourceDatabasesCache>();
|
||||
if (!cache) {
|
||||
cache = { databases: { } };
|
||||
}
|
||||
cache.databases[this.subscription.id] = databases;
|
||||
this.updateCache(cache);
|
||||
|
||||
this._isClearingCache = false;
|
||||
} else {
|
||||
const cache = this.getCache<AzureResourceDatabasesCache>();
|
||||
if (cache) {
|
||||
databases = cache.databases[this.subscription.id] || <AzureResourceDatabase[]>[];
|
||||
}
|
||||
}
|
||||
|
||||
if (databases.length === 0) {
|
||||
return [AzureResourceMessageTreeNode.create(AzureResourceDatabaseContainerTreeNode.NoDatabases, this)];
|
||||
} else {
|
||||
return databases.map((database) => new AzureResourceDatabaseTreeNode(database, this.treeChangeHandler, this));
|
||||
}
|
||||
} catch (error) {
|
||||
return [AzureResourceMessageTreeNode.create(AzureResourceErrorMessageUtil.getErrorMessage(error), this)];
|
||||
}
|
||||
}
|
||||
|
||||
public getTreeItem(): TreeItem | Promise<TreeItem> {
|
||||
let item = new TreeItem(AzureResourceDatabaseContainerTreeNode.Label, TreeItemCollapsibleState.Collapsed);
|
||||
item.contextValue = AzureResourceItemType.databaseContainer;
|
||||
item.iconPath = {
|
||||
dark: this.servicePool.contextService.getAbsolutePath('resources/dark/folder_inverse.svg'),
|
||||
light: this.servicePool.contextService.getAbsolutePath('resources/light/folder.svg')
|
||||
};
|
||||
return item;
|
||||
}
|
||||
|
||||
public getNodeInfo(): NodeInfo {
|
||||
return {
|
||||
label: AzureResourceDatabaseContainerTreeNode.Label,
|
||||
isLeaf: false,
|
||||
errorMessage: undefined,
|
||||
metadata: undefined,
|
||||
nodePath: this.generateNodePath(),
|
||||
nodeStatus: undefined,
|
||||
nodeType: AzureResourceItemType.databaseContainer,
|
||||
nodeSubType: undefined,
|
||||
iconType: AzureResourceItemType.databaseContainer
|
||||
};
|
||||
}
|
||||
|
||||
public get nodePathValue(): string {
|
||||
return 'databaseContainer';
|
||||
}
|
||||
|
||||
protected get cacheKey(): string {
|
||||
return 'azureResource.cache.databases';
|
||||
}
|
||||
|
||||
private static readonly Label = localize('azureResource.tree.databaseContainerTreeNode.label', 'SQL Databases');
|
||||
private static readonly NoDatabases = localize('azureResource.tree.databaseContainerTreeNode.noDatabases', 'No SQL Databases found.');
|
||||
}
|
||||
|
||||
interface AzureResourceDatabasesCache {
|
||||
databases: { [subscriptionId: string]: AzureResourceDatabase[] };
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { Account, NodeInfo } from 'sqlops';
|
||||
import { TreeNode } from '../../treeNodes';
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
import { AzureResourceContainerTreeNodeBase } from './baseTreeNodes';
|
||||
import { AzureResourceItemType } from '../constants';
|
||||
import { AzureResourceMessageTreeNode } from './messageTreeNode';
|
||||
import { AzureResourceErrorMessageUtil } from '../utils';
|
||||
import { AzureResourceSubscription, AzureResourceDatabaseServer } from '../models';
|
||||
import { AzureResourceDatabaseServerTreeNode } from './databaseServerTreeNode';
|
||||
import { IAzureResourceTreeChangeHandler } from './treeProvider';
|
||||
|
||||
export class AzureResourceDatabaseServerContainerTreeNode extends AzureResourceContainerTreeNodeBase {
|
||||
public constructor(
|
||||
public readonly subscription: AzureResourceSubscription,
|
||||
account: Account,
|
||||
treeChangeHandler: IAzureResourceTreeChangeHandler,
|
||||
parent: TreeNode
|
||||
) {
|
||||
super(account, treeChangeHandler, parent);
|
||||
}
|
||||
|
||||
public async getChildren(): Promise<TreeNode[]> {
|
||||
try {
|
||||
let databaseServers: AzureResourceDatabaseServer[] = [];
|
||||
|
||||
if (this._isClearingCache) {
|
||||
let credentials = await this.getCredentials();
|
||||
databaseServers = (await this.servicePool.databaseServerService.getDatabaseServers(this.subscription, credentials)) || <AzureResourceDatabaseServer[]>[];
|
||||
|
||||
let cache = this.getCache<AzureResourceDatabaseServersCache>();
|
||||
if (!cache) {
|
||||
cache = { databaseServers: { } };
|
||||
}
|
||||
cache.databaseServers[this.subscription.id] = databaseServers;
|
||||
this.updateCache<AzureResourceDatabaseServersCache>(cache);
|
||||
|
||||
this._isClearingCache = false;
|
||||
} else {
|
||||
const cache = this.getCache<AzureResourceDatabaseServersCache>();
|
||||
if (cache) {
|
||||
databaseServers = cache.databaseServers[this.subscription.id] || <AzureResourceDatabaseServer[]>[];
|
||||
}
|
||||
}
|
||||
|
||||
if (databaseServers.length === 0) {
|
||||
return [AzureResourceMessageTreeNode.create(AzureResourceDatabaseServerContainerTreeNode.NoDatabaseServers, this)];
|
||||
} else {
|
||||
return databaseServers.map((server) => new AzureResourceDatabaseServerTreeNode(server, this.treeChangeHandler, this));
|
||||
}
|
||||
} catch (error) {
|
||||
return [AzureResourceMessageTreeNode.create(AzureResourceErrorMessageUtil.getErrorMessage(error), this)];
|
||||
}
|
||||
}
|
||||
|
||||
public getTreeItem(): TreeItem | Promise<TreeItem> {
|
||||
let item = new TreeItem(AzureResourceDatabaseServerContainerTreeNode.Label, TreeItemCollapsibleState.Collapsed);
|
||||
item.contextValue = AzureResourceItemType.databaseServerContainer;
|
||||
item.iconPath = {
|
||||
dark: this.servicePool.contextService.getAbsolutePath('resources/dark/folder_inverse.svg'),
|
||||
light: this.servicePool.contextService.getAbsolutePath('resources/light/folder.svg')
|
||||
};
|
||||
return item;
|
||||
}
|
||||
|
||||
public getNodeInfo(): NodeInfo {
|
||||
return {
|
||||
label: AzureResourceDatabaseServerContainerTreeNode.Label,
|
||||
isLeaf: false,
|
||||
errorMessage: undefined,
|
||||
metadata: undefined,
|
||||
nodePath: this.generateNodePath(),
|
||||
nodeStatus: undefined,
|
||||
nodeType: AzureResourceItemType.databaseServerContainer,
|
||||
nodeSubType: undefined,
|
||||
iconType: AzureResourceItemType.databaseServerContainer
|
||||
};
|
||||
}
|
||||
|
||||
public get nodePathValue(): string {
|
||||
return 'databaseServerContainer';
|
||||
}
|
||||
|
||||
protected get cacheKey(): string {
|
||||
return 'azureResource.cache.databaseServers';
|
||||
}
|
||||
|
||||
private static readonly Label = localize('azureResource.tree.databaseServerContainerTreeNode.label', 'SQL Servers');
|
||||
private static readonly NoDatabaseServers = localize('azureResource.tree.databaseContainerTreeNode.noDatabaseServers', 'No SQL Servers found.');
|
||||
}
|
||||
|
||||
interface AzureResourceDatabaseServersCache {
|
||||
databaseServers: { [subscriptionId: string]: AzureResourceDatabaseServer[] };
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { NodeInfo } from 'sqlops';
|
||||
import { TreeNode } from '../../treeNodes';
|
||||
|
||||
import { AzureResourceTreeNodeBase } from './baseTreeNodes';
|
||||
import { AzureResourceItemType } from '../constants';
|
||||
import { AzureResourceDatabaseServer } from '../models';
|
||||
import { IAzureResourceTreeChangeHandler } from './treeProvider';
|
||||
|
||||
export class AzureResourceDatabaseServerTreeNode extends AzureResourceTreeNodeBase {
|
||||
public constructor(
|
||||
public readonly databaseServer: AzureResourceDatabaseServer,
|
||||
treeChangeHandler: IAzureResourceTreeChangeHandler,
|
||||
parent: TreeNode
|
||||
) {
|
||||
super(treeChangeHandler, parent);
|
||||
}
|
||||
|
||||
public async getChildren(): Promise<TreeNode[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
public getTreeItem(): TreeItem | Promise<TreeItem> {
|
||||
let item = new TreeItem(this.databaseServer.name, TreeItemCollapsibleState.None);
|
||||
item.contextValue = AzureResourceItemType.databaseServer;
|
||||
item.iconPath = {
|
||||
dark: this.servicePool.contextService.getAbsolutePath('resources/dark/sql_server_inverse.svg'),
|
||||
light: this.servicePool.contextService.getAbsolutePath('resources/light/sql_server.svg')
|
||||
};
|
||||
return item;
|
||||
}
|
||||
|
||||
public getNodeInfo(): NodeInfo {
|
||||
return {
|
||||
label: this.databaseServer.name,
|
||||
isLeaf: true,
|
||||
errorMessage: undefined,
|
||||
metadata: undefined,
|
||||
nodePath: this.generateNodePath(),
|
||||
nodeStatus: undefined,
|
||||
nodeType: AzureResourceItemType.databaseServer,
|
||||
nodeSubType: undefined,
|
||||
iconType: AzureResourceItemType.databaseServer
|
||||
};
|
||||
}
|
||||
|
||||
public get nodePathValue(): string {
|
||||
return `databaseServer_${this.databaseServer.name}`;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { NodeInfo } from 'sqlops';
|
||||
import { TreeNode } from '../../treeNodes';
|
||||
|
||||
import { AzureResourceTreeNodeBase } from './baseTreeNodes';
|
||||
import { AzureResourceItemType } from '../constants';
|
||||
import { AzureResourceDatabase } from '../models';
|
||||
import { IAzureResourceTreeChangeHandler } from './treeProvider';
|
||||
|
||||
export class AzureResourceDatabaseTreeNode extends AzureResourceTreeNodeBase {
|
||||
public constructor(
|
||||
public readonly database: AzureResourceDatabase,
|
||||
treeChangeHandler: IAzureResourceTreeChangeHandler,
|
||||
parent: TreeNode
|
||||
) {
|
||||
super(treeChangeHandler, parent);
|
||||
|
||||
this._label = `${this.database.name} (${this.database.serverName})`;
|
||||
}
|
||||
|
||||
public async getChildren(): Promise<TreeNode[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
public getTreeItem(): TreeItem | Promise<TreeItem> {
|
||||
let item = new TreeItem(this._label, TreeItemCollapsibleState.None);
|
||||
item.contextValue = AzureResourceItemType.database;
|
||||
item.iconPath = {
|
||||
dark: this.servicePool.contextService.getAbsolutePath('resources/dark/sql_database_inverse.svg'),
|
||||
light: this.servicePool.contextService.getAbsolutePath('resources/light/sql_database.svg')
|
||||
};
|
||||
return item;
|
||||
}
|
||||
|
||||
public getNodeInfo(): NodeInfo {
|
||||
return {
|
||||
label: this._label,
|
||||
isLeaf: true,
|
||||
errorMessage: undefined,
|
||||
metadata: undefined,
|
||||
nodePath: this.generateNodePath(),
|
||||
nodeStatus: undefined,
|
||||
nodeType: AzureResourceItemType.database,
|
||||
nodeSubType: undefined,
|
||||
iconType: AzureResourceItemType.database
|
||||
};
|
||||
}
|
||||
|
||||
public get nodePathValue(): string {
|
||||
return `database_${this.database.name}`;
|
||||
}
|
||||
|
||||
private _label: string = undefined;
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { NodeInfo } from 'sqlops';
|
||||
import { TreeNode } from '../../treeNodes';
|
||||
|
||||
import { AzureResourceItemType } from '../constants';
|
||||
|
||||
export class AzureResourceMessageTreeNode extends TreeNode {
|
||||
public constructor(
|
||||
public readonly message: string,
|
||||
parent: TreeNode
|
||||
) {
|
||||
super();
|
||||
|
||||
this.parent = parent;
|
||||
this._id = `message_${AzureResourceMessageTreeNode._messageNum++}`;
|
||||
}
|
||||
|
||||
public static create(message: string, parent: TreeNode): AzureResourceMessageTreeNode {
|
||||
return new AzureResourceMessageTreeNode(message, parent);
|
||||
}
|
||||
|
||||
public getChildren(): TreeNode[] | Promise<TreeNode[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
public getTreeItem(): TreeItem | Promise<TreeItem> {
|
||||
let item = new TreeItem(this.message, TreeItemCollapsibleState.None);
|
||||
item.contextValue = AzureResourceItemType.message;
|
||||
return item;
|
||||
}
|
||||
|
||||
public getNodeInfo(): NodeInfo {
|
||||
return {
|
||||
label: this.message,
|
||||
isLeaf: true,
|
||||
errorMessage: undefined,
|
||||
metadata: undefined,
|
||||
nodePath: this.generateNodePath(),
|
||||
nodeStatus: undefined,
|
||||
nodeType: AzureResourceItemType.message,
|
||||
nodeSubType: undefined,
|
||||
iconType: AzureResourceItemType.message
|
||||
};
|
||||
}
|
||||
|
||||
public get nodePathValue(): string {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
private _id: string;
|
||||
|
||||
private static _messageNum: number = 0;
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
|
||||
import { Account, NodeInfo } from 'sqlops';
|
||||
import { TreeNode } from '../../treeNodes';
|
||||
|
||||
import { AzureResourceTreeNodeBase, AzureResourceContainerTreeNodeBase } from './baseTreeNodes';
|
||||
import { AzureResourceItemType } from '../constants';
|
||||
import { AzureResourceDatabaseContainerTreeNode } from './databaseContainerTreeNode';
|
||||
import { AzureResourceDatabaseServerContainerTreeNode } from './databaseServerContainerTreeNode';
|
||||
import { AzureResourceSubscription } from '../models';
|
||||
import { IAzureResourceTreeChangeHandler } from './treeChangeHandler';
|
||||
|
||||
export class AzureResourceSubscriptionTreeNode extends AzureResourceTreeNodeBase {
|
||||
public constructor(
|
||||
public readonly subscription: AzureResourceSubscription,
|
||||
account: Account,
|
||||
treeChangeHandler: IAzureResourceTreeChangeHandler,
|
||||
parent: TreeNode
|
||||
) {
|
||||
super(treeChangeHandler, parent);
|
||||
|
||||
this._children.push(new AzureResourceDatabaseContainerTreeNode(subscription, account, treeChangeHandler, this));
|
||||
this._children.push(new AzureResourceDatabaseServerContainerTreeNode(subscription, account, treeChangeHandler, this));
|
||||
}
|
||||
|
||||
public async getChildren(): Promise<TreeNode[]> {
|
||||
return this._children;
|
||||
}
|
||||
|
||||
public getTreeItem(): TreeItem | Promise<TreeItem> {
|
||||
let item = new TreeItem(this.subscription.name, TreeItemCollapsibleState.Collapsed);
|
||||
item.contextValue = AzureResourceItemType.subscription;
|
||||
item.iconPath = {
|
||||
dark: this.servicePool.contextService.getAbsolutePath('resources/dark/subscription_inverse.svg'),
|
||||
light: this.servicePool.contextService.getAbsolutePath('resources/light/subscription.svg')
|
||||
};
|
||||
return item;
|
||||
}
|
||||
|
||||
public getNodeInfo(): NodeInfo {
|
||||
return {
|
||||
label: this.subscription.name,
|
||||
isLeaf: false,
|
||||
errorMessage: undefined,
|
||||
metadata: undefined,
|
||||
nodePath: this.generateNodePath(),
|
||||
nodeStatus: undefined,
|
||||
nodeType: AzureResourceItemType.subscription,
|
||||
nodeSubType: undefined,
|
||||
iconType: AzureResourceItemType.subscription
|
||||
};
|
||||
}
|
||||
|
||||
public get nodePathValue(): string {
|
||||
return `subscription_${this.subscription.id}`;
|
||||
}
|
||||
|
||||
private _children: AzureResourceContainerTreeNodeBase[] = [];
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TreeNode } from '../../treeNodes';
|
||||
|
||||
export interface IAzureResourceTreeChangeHandler {
|
||||
notifyNodeChanged(node: TreeNode): void;
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { TreeDataProvider, EventEmitter, Event, TreeItem } from 'vscode';
|
||||
import { DidChangeAccountsParams } from 'sqlops';
|
||||
import { TreeNode } from '../../treeNodes';
|
||||
import { setInterval, clearInterval } from 'timers';
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
import { AzureResourceServicePool } from '../servicePool';
|
||||
import { AzureResourceAccountTreeNode } from './accountTreeNode';
|
||||
import { AzureResourceAccountNotSignedInTreeNode } from './accountNotSignedInTreeNode';
|
||||
import { AzureResourceMessageTreeNode } from './messageTreeNode';
|
||||
import { AzureResourceContainerTreeNodeBase, AzureResourceTreeNodeBase } from './baseTreeNodes';
|
||||
import { AzureResourceErrorMessageUtil } from '../utils';
|
||||
|
||||
export interface IAzureResourceTreeChangeHandler {
|
||||
notifyNodeChanged(node: TreeNode): void;
|
||||
}
|
||||
|
||||
export class AzureResourceTreeProvider implements TreeDataProvider<TreeNode>, IAzureResourceTreeChangeHandler {
|
||||
public constructor() {
|
||||
AzureResourceServicePool.getInstance().accountService.onDidChangeAccounts((e: DidChangeAccountsParams) => { this._onDidChangeTreeData.fire(undefined); });
|
||||
}
|
||||
|
||||
public async getChildren(element?: TreeNode): Promise<TreeNode[]> {
|
||||
if (element) {
|
||||
return element.getChildren(true);
|
||||
}
|
||||
|
||||
if (!this.isSystemInitialized) {
|
||||
this._loadingTimer = setInterval(async () => {
|
||||
try {
|
||||
// Call sqlops.accounts.getAllAccounts() to determine whether the system has been initialized.
|
||||
await AzureResourceServicePool.getInstance().accountService.getAccounts();
|
||||
|
||||
// System has been initialized
|
||||
this.isSystemInitialized = true;
|
||||
|
||||
if (this._loadingTimer) {
|
||||
clearInterval(this._loadingTimer);
|
||||
}
|
||||
|
||||
this._onDidChangeTreeData.fire(undefined);
|
||||
} catch (error) {
|
||||
// System not initialized yet
|
||||
this.isSystemInitialized = false;
|
||||
}
|
||||
}, AzureResourceTreeProvider.LoadingTimerInterval);
|
||||
|
||||
return [AzureResourceMessageTreeNode.create(AzureResourceTreeProvider.Loading, undefined)];
|
||||
}
|
||||
|
||||
try {
|
||||
const accounts = await AzureResourceServicePool.getInstance().accountService.getAccounts();
|
||||
|
||||
if (accounts && accounts.length > 0) {
|
||||
return accounts.map((account) => new AzureResourceAccountTreeNode(account, this));
|
||||
} else {
|
||||
return [new AzureResourceAccountNotSignedInTreeNode()];
|
||||
}
|
||||
} catch (error) {
|
||||
return [AzureResourceMessageTreeNode.create(AzureResourceErrorMessageUtil.getErrorMessage(error), undefined)];
|
||||
}
|
||||
}
|
||||
|
||||
public get onDidChangeTreeData(): Event<TreeNode> {
|
||||
return this._onDidChangeTreeData.event;
|
||||
}
|
||||
|
||||
public notifyNodeChanged(node: TreeNode): void {
|
||||
this._onDidChangeTreeData.fire(node);
|
||||
}
|
||||
|
||||
public async refresh(node: TreeNode, isClearingCache: boolean): Promise<void> {
|
||||
if (isClearingCache) {
|
||||
if ((node instanceof AzureResourceContainerTreeNodeBase)) {
|
||||
node.clearCache();
|
||||
}
|
||||
}
|
||||
|
||||
this._onDidChangeTreeData.fire(node);
|
||||
}
|
||||
|
||||
public getTreeItem(element: TreeNode): TreeItem | Thenable<TreeItem> {
|
||||
return element.getTreeItem();
|
||||
}
|
||||
|
||||
public isSystemInitialized: boolean = false;
|
||||
|
||||
private _loadingTimer: NodeJS.Timer = undefined;
|
||||
private _onDidChangeTreeData = new EventEmitter<TreeNode>();
|
||||
|
||||
private static readonly Loading = localize('azureResource.tree.treeProvider.loading', 'Loading ...');
|
||||
private static readonly LoadingTimerInterval = 5000;
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export function getErrorMessage(error: Error | string): string {
|
||||
return (error instanceof Error) ? error.message : error;
|
||||
}
|
||||
|
||||
|
||||
export class AzureResourceErrorMessageUtil {
|
||||
public static getErrorMessage(error: Error | string): string {
|
||||
return localize('azureResource.error', 'Error: {0}', getErrorMessage(error));
|
||||
}
|
||||
}
|
||||
|
||||
export function generateGuid(): string {
|
||||
let hexValues: string[] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
|
||||
// c.f. rfc4122 (UUID version 4 = xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx)
|
||||
let oct: string = '';
|
||||
let tmp: number;
|
||||
/* tslint:disable:no-bitwise */
|
||||
for (let a: number = 0; a < 4; a++) {
|
||||
tmp = (4294967296 * Math.random()) | 0;
|
||||
oct += hexValues[tmp & 0xF] +
|
||||
hexValues[tmp >> 4 & 0xF] +
|
||||
hexValues[tmp >> 8 & 0xF] +
|
||||
hexValues[tmp >> 12 & 0xF] +
|
||||
hexValues[tmp >> 16 & 0xF] +
|
||||
hexValues[tmp >> 20 & 0xF] +
|
||||
hexValues[tmp >> 24 & 0xF] +
|
||||
hexValues[tmp >> 28 & 0xF];
|
||||
}
|
||||
|
||||
// 'Set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively'
|
||||
let clockSequenceHi: string = hexValues[8 + (Math.random() * 4) | 0];
|
||||
return oct.substr(0, 8) + '-' + oct.substr(9, 4) + '-4' + oct.substr(13, 3) + '-' + clockSequenceHi + oct.substr(16, 3) + '-' + oct.substr(19, 12);
|
||||
/* tslint:enable:no-bitwise */
|
||||
}
|
||||
Reference in New Issue
Block a user