diff --git a/extensions/azurecore/resources/dark/sql_instance_inverse.svg b/extensions/azurecore/resources/dark/sql_instance_inverse.svg
new file mode 100644
index 0000000000..0ba62f0cf1
--- /dev/null
+++ b/extensions/azurecore/resources/dark/sql_instance_inverse.svg
@@ -0,0 +1,8 @@
+
diff --git a/extensions/azurecore/resources/light/sql_instance.svg b/extensions/azurecore/resources/light/sql_instance.svg
new file mode 100644
index 0000000000..e6c8ee21da
--- /dev/null
+++ b/extensions/azurecore/resources/light/sql_instance.svg
@@ -0,0 +1,8 @@
+
diff --git a/extensions/azurecore/src/azureResource/commands.ts b/extensions/azurecore/src/azureResource/commands.ts
index 2782941e6d..00a156e8a6 100644
--- a/extensions/azurecore/src/azureResource/commands.ts
+++ b/extensions/azurecore/src/azureResource/commands.ts
@@ -65,7 +65,7 @@ export function registerAzureResourceCommands(appContext: AppContext, tree: Azur
picked: selectedSubscriptionIds.indexOf(subscription.id) !== -1,
subscription: subscription
};
- });
+ }).sort((a, b) => a.label.localeCompare(b.label));
const selectedSubscriptionQuickPickItems = (await window.showQuickPick(subscriptionQuickPickItems, { canPickMany: true }));
if (selectedSubscriptionQuickPickItems && selectedSubscriptionQuickPickItems.length > 0) {
diff --git a/extensions/azurecore/src/azureResource/providers/resourceTreeDataProviderBase.ts b/extensions/azurecore/src/azureResource/providers/resourceTreeDataProviderBase.ts
index 3ab46b1d77..f14c95975b 100644
--- a/extensions/azurecore/src/azureResource/providers/resourceTreeDataProviderBase.ts
+++ b/extensions/azurecore/src/azureResource/providers/resourceTreeDataProviderBase.ts
@@ -30,14 +30,14 @@ export abstract class ResourceTreeDataProviderBase i
const tokens = await this._apiWrapper.getSecurityToken(element.account, AzureResource.ResourceManagement);
const credential = new TokenCredentials(tokens[element.tenantId].token, tokens[element.tenantId].tokenType);
- const resources: T[] = (await this._resourceService.getResources(element.subscription, credential)) || [];
+ const resources: T[] = await this._resourceService.getResources(element.subscription, credential) || [];
return resources.map((resource) => {
account: element.account,
subscription: element.subscription,
tenantId: element.tenantId,
treeItem: this.getTreeItemForResource(resource)
- });
+ }).sort((a, b) => a.treeItem.label.localeCompare(b.treeItem.label));
}
protected abstract getTreeItemForResource(resource: T): TreeItem;
diff --git a/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceProvider.ts b/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceProvider.ts
new file mode 100644
index 0000000000..ec3ea673fe
--- /dev/null
+++ b/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceProvider.ts
@@ -0,0 +1,28 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { ExtensionContext } from 'vscode';
+import { ApiWrapper } from '../../../apiWrapper';
+
+import { azureResource } from '../../azure-resource';
+import { IAzureResourceService, AzureResourceDatabaseServer } from '../../interfaces';
+import { SqlInstanceTreeDataProvider as SqlInstanceTreeDataProvider } from './sqlInstanceTreeDataProvider';
+
+export class SqlInstanceProvider implements azureResource.IAzureResourceProvider {
+ public constructor(
+ private _service: IAzureResourceService,
+ private _apiWrapper: ApiWrapper,
+ private _extensionContext: ExtensionContext
+ ) {
+ }
+
+ public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider {
+ return new SqlInstanceTreeDataProvider(this._service, this._apiWrapper, this._extensionContext);
+ }
+
+ public get providerId(): string {
+ return 'azure.resource.providers.sqlInstance';
+ }
+}
diff --git a/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceService.ts b/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceService.ts
new file mode 100644
index 0000000000..83aa8f22b7
--- /dev/null
+++ b/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceService.ts
@@ -0,0 +1,27 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { ServiceClientCredentials as OldSc } from 'ms-rest';
+import { SqlManagementClient } from 'azure-arm-sql';
+
+import { azureResource } from '../../azure-resource';
+import { IAzureResourceService, AzureResourceDatabaseServer } from '../../interfaces';
+
+export class SqlInstanceResourceService implements IAzureResourceService {
+ public async getResources(subscription: azureResource.AzureResourceSubscription, credential: OldSc): Promise {
+ const databaseServers: AzureResourceDatabaseServer[] = [];
+ const sqlManagementClient = new SqlManagementClient(credential, subscription.id);
+ const svrs = await sqlManagementClient.managedInstances.list();
+
+ svrs.forEach((svr) => databaseServers.push({
+ name: svr.name,
+ fullName: svr.fullyQualifiedDomainName,
+ loginName: svr.administratorLogin,
+ defaultDatabaseName: 'master'
+ }));
+
+ return databaseServers;
+ }
+}
diff --git a/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceTreeDataProvider.ts
new file mode 100644
index 0000000000..14516a683f
--- /dev/null
+++ b/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceTreeDataProvider.ts
@@ -0,0 +1,78 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { ExtensionNodeType, TreeItem } from 'azdata';
+import { TreeItemCollapsibleState, ExtensionContext } from 'vscode';
+import * as nls from 'vscode-nls';
+const localize = nls.loadMessageBundle();
+
+import { AzureResourceItemType } from '../../constants';
+import { ApiWrapper } from '../../../apiWrapper';
+import { generateGuid } from '../../utils';
+import { IAzureResourceService, AzureResourceDatabaseServer } from '../../interfaces';
+import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase';
+import { azureResource } from '../../azure-resource';
+
+export class SqlInstanceTreeDataProvider extends ResourceTreeDataProviderBase {
+ private static readonly containerId = 'azure.resource.providers.sqlInstanceContainer';
+ private static readonly containerLabel = localize('azure.resource.providers.sqlInstanceContainerLabel', "SQL Instances");
+
+ public constructor(
+ databaseServerService: IAzureResourceService,
+ apiWrapper: ApiWrapper,
+ private _extensionContext: ExtensionContext
+ ) {
+ super(databaseServerService, apiWrapper);
+ }
+
+
+ protected getTreeItemForResource(databaseServer: AzureResourceDatabaseServer): TreeItem {
+ return {
+ id: `sqlInstance_${databaseServer.name}`,
+ label: databaseServer.name,
+ iconPath: {
+ dark: this._extensionContext.asAbsolutePath('resources/dark/sql_instance_inverse.svg'),
+ light: this._extensionContext.asAbsolutePath('resources/light/sql_instance.svg')
+ },
+ collapsibleState: TreeItemCollapsibleState.Collapsed,
+ contextValue: AzureResourceItemType.databaseServer,
+ payload: {
+ id: generateGuid(),
+ connectionName: undefined,
+ serverName: databaseServer.fullName,
+ databaseName: databaseServer.defaultDatabaseName,
+ userName: databaseServer.loginName,
+ password: '',
+ authenticationType: 'SqlLogin',
+ savePassword: true,
+ groupFullName: '',
+ groupId: '',
+ providerName: 'MSSQL',
+ saveProfile: false,
+ options: {}
+ },
+ childProvider: 'MSSQL',
+ type: ExtensionNodeType.Server
+ };
+ }
+
+ protected createContainerNode(): azureResource.IAzureResourceNode {
+ return {
+ account: undefined,
+ subscription: undefined,
+ tenantId: undefined,
+ treeItem: {
+ id: SqlInstanceTreeDataProvider.containerId,
+ label: SqlInstanceTreeDataProvider.containerLabel,
+ iconPath: {
+ dark: this._extensionContext.asAbsolutePath('resources/dark/folder_inverse.svg'),
+ light: this._extensionContext.asAbsolutePath('resources/light/folder.svg')
+ },
+ collapsibleState: TreeItemCollapsibleState.Collapsed,
+ contextValue: AzureResourceItemType.databaseServerContainer
+ }
+ };
+ }
+}
diff --git a/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts b/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts
index 6e140cfad6..cf84497772 100644
--- a/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts
+++ b/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts
@@ -80,11 +80,12 @@ export class AzureResourceAccountTreeNode extends AzureResourceContainerTreeNode
if (subscriptions.length === 0) {
return [AzureResourceMessageTreeNode.create(AzureResourceAccountTreeNode.noSubscriptionsLabel, this)];
} else {
- return await Promise.all(subscriptions.map(async (subscription) => {
+ let subTreeNodes = await Promise.all(subscriptions.map(async (subscription) => {
const tenantId = await this._tenantService.getTenantId(subscription);
return new AzureResourceSubscriptionTreeNode(this.account, subscription, tenantId, this.appContext, this.treeChangeHandler, this);
}));
+ return subTreeNodes.sort((a, b) => a.subscription.name.localeCompare(b.subscription.name));
}
} catch (error) {
if (error instanceof AzureResourceCredentialError) {
@@ -163,4 +164,4 @@ export class AzureResourceAccountTreeNode extends AzureResourceContainerTreeNode
private _selectedSubscriptionCount = 0;
private static readonly noSubscriptionsLabel = localize('azure.resource.tree.accountTreeNode.noSubscriptionsLabel', "No Subscriptions found.");
-}
\ No newline at end of file
+}
diff --git a/extensions/azurecore/src/azureResource/tree/subscriptionTreeNode.ts b/extensions/azurecore/src/azureResource/tree/subscriptionTreeNode.ts
index c166a81bdf..02130cbff5 100644
--- a/extensions/azurecore/src/azureResource/tree/subscriptionTreeNode.ts
+++ b/extensions/azurecore/src/azureResource/tree/subscriptionTreeNode.ts
@@ -54,7 +54,7 @@ export class AzureResourceSubscriptionTreeNode extends AzureResourceContainerTre
// To make tree node's id unique, otherwise, treeModel.js would complain 'item already registered'
child.resourceNode.treeItem.id = `${this._id}.${child.resourceNode.treeItem.id}`;
return new AzureResourceResourceTreeNode(child, this, this.appContext);
- });
+ }).sort((a, b) => a.nodePathValue.localeCompare(b.nodePathValue));
}
} catch (error) {
return [AzureResourceMessageTreeNode.create(AzureResourceErrorMessageUtil.getErrorMessage(error), this)];
diff --git a/extensions/azurecore/src/extension.ts b/extensions/azurecore/src/extension.ts
index 5b1fd10bc0..17bd5af621 100644
--- a/extensions/azurecore/src/extension.ts
+++ b/extensions/azurecore/src/extension.ts
@@ -27,6 +27,8 @@ import { AzureResourceCacheService } from './azureResource/services/cacheService
import { AzureResourceTenantService } from './azureResource/services/tenantService';
import { registerAzureResourceCommands } from './azureResource/commands';
import { AzureResourceTreeProvider } from './azureResource/tree/treeProvider';
+import { SqlInstanceResourceService } from './azureResource/providers/sqlinstance/sqlInstanceService';
+import { SqlInstanceProvider } from './azureResource/providers/sqlinstance/sqlInstanceProvider';
let extensionContext: vscode.ExtensionContext;
@@ -74,7 +76,8 @@ export async function activate(context: vscode.ExtensionContext) {
provideResources() {
return [
new AzureResourceDatabaseServerProvider(new AzureResourceDatabaseServerService(), apiWrapper, extensionContext),
- new AzureResourceDatabaseProvider(new AzureResourceDatabaseService(), apiWrapper, extensionContext)
+ new AzureResourceDatabaseProvider(new AzureResourceDatabaseService(), apiWrapper, extensionContext),
+ new SqlInstanceProvider(new SqlInstanceResourceService(), apiWrapper, extensionContext)
];
}
};