mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Open in azure portal (#10535)
* initial commit to get it working * Change configuration to use package.nls.json * Make the necessary plumbing * Support multi cloud * Move the menu item to the bottom * Fix failing tests * Fix the tests
This commit is contained in:
@@ -151,6 +151,10 @@
|
||||
"light": "resources/light/console.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "azure.resource.openInAzurePortal",
|
||||
"title": "%azure.openInAzurePortal.title%"
|
||||
},
|
||||
{
|
||||
"command": "azure.resource.connectsqlserver",
|
||||
"title": "%azure.resource.connectsqlserver.title%",
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"azure.accounts.getSubscriptions.title": "Get Azure Account Subscriptions",
|
||||
"azure.accounts.getResourceGroups.title": "Get Azure Account Subscription Resource Groups",
|
||||
|
||||
"azure.openInAzurePortal.title": "Open in Azure Portal",
|
||||
|
||||
"config.azureAccountConfigurationSection": "Azure Account Configuration",
|
||||
"config.enablePublicCloudDescription": "Should Azure public cloud integration be enabled",
|
||||
"config.enableUsGovCloudDescription": "Should US Government Azure cloud (Fairfax) integration be enabled",
|
||||
|
||||
@@ -118,6 +118,8 @@ interface Settings {
|
||||
redirectUri?: string;
|
||||
|
||||
scopes?: string[]
|
||||
|
||||
portalEndpoint?: string
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -52,7 +52,8 @@ const publicAzureSettings: ProviderSettings = {
|
||||
scopes: [
|
||||
'openid', 'email', 'profile', 'offline_access',
|
||||
'https://management.azure.com/user_impersonation',
|
||||
]
|
||||
],
|
||||
portalEndpoint: 'https://portal.azure.com'
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -101,7 +102,8 @@ const usGovAzureSettings: ProviderSettings = {
|
||||
scopes: [
|
||||
'openid', 'email', 'profile', 'offline_access',
|
||||
'https://management.usgovcloudapi.net/user_impersonation'
|
||||
]
|
||||
],
|
||||
portalEndpoint: 'https://portal.azure.us'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -24,6 +24,7 @@ export namespace azureResource {
|
||||
export interface AzureResource {
|
||||
name: string;
|
||||
id: string;
|
||||
tenant?: string;
|
||||
}
|
||||
|
||||
export interface AzureResourceSubscription extends AzureResource {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
import { window, QuickPickItem } from 'vscode';
|
||||
import { window, QuickPickItem, env, Uri } from 'vscode';
|
||||
import * as azdata from 'azdata';
|
||||
import { TokenCredentials } from '@azure/ms-rest-js';
|
||||
import * as nls from 'vscode-nls';
|
||||
@@ -237,4 +237,23 @@ export function registerAzureResourceCommands(appContext: AppContext, tree: Azur
|
||||
appContext.apiWrapper.executeCommand('workbench.view.connections');
|
||||
}
|
||||
});
|
||||
|
||||
appContext.apiWrapper.registerCommand('azure.resource.openInAzurePortal', async (node?: TreeNode) => {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
const treeItem: azdata.TreeItem = await node.getTreeItem();
|
||||
if (!treeItem.payload) {
|
||||
return;
|
||||
}
|
||||
let connectionProfile = Object.assign({}, treeItem.payload, { saveProfile: true });
|
||||
|
||||
if (!connectionProfile.azureResourceId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const urlToOpen = `${connectionProfile.azurePortalEndpoint}//${connectionProfile.azureTenantId}/#resource/${connectionProfile.azureResourceId}`;
|
||||
env.openExternal(Uri.parse(urlToOpen));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -50,7 +50,8 @@ export class AzureResourceDatabaseService implements IAzureResourceService<azure
|
||||
id: db.id,
|
||||
serverName: server.name,
|
||||
serverFullName: server.properties.fullyQualifiedDomainName,
|
||||
loginName: server.properties.administratorLogin
|
||||
loginName: server.properties.administratorLogin,
|
||||
tenant: db.tenantId
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,10 @@ export class AzureResourceDatabaseTreeDataProvider extends ResourceTreeDataProvi
|
||||
providerName: 'MSSQL',
|
||||
saveProfile: false,
|
||||
options: {},
|
||||
azureAccount: account.key.accountId
|
||||
azureAccount: account.key.accountId,
|
||||
azureResourceId: database.id,
|
||||
azureTenantId: database.tenant,
|
||||
azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint
|
||||
},
|
||||
childProvider: 'MSSQL',
|
||||
type: ExtensionNodeType.Database
|
||||
|
||||
@@ -29,7 +29,8 @@ export class AzureResourceDatabaseServerService extends ResourceServiceBase<DbSe
|
||||
name: resource.name,
|
||||
fullName: resource.properties.fullyQualifiedDomainName,
|
||||
loginName: resource.properties.administratorLogin,
|
||||
defaultDatabaseName: 'master'
|
||||
defaultDatabaseName: 'master',
|
||||
tenant: resource.tenantId
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,10 @@ export class AzureResourceDatabaseServerTreeDataProvider extends ResourceTreeDat
|
||||
providerName: 'MSSQL',
|
||||
saveProfile: false,
|
||||
options: {},
|
||||
azureAccount: account.key.accountId
|
||||
azureAccount: account.key.accountId,
|
||||
azureTenantId: databaseServer.tenant,
|
||||
azureResourceId: databaseServer.id,
|
||||
azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint
|
||||
},
|
||||
childProvider: 'MSSQL',
|
||||
type: ExtensionNodeType.Server
|
||||
|
||||
@@ -26,7 +26,8 @@ export class PostgresServerArcService extends ResourceServiceBase<PostgresArcSer
|
||||
name: resource.name,
|
||||
fullName: resource.name,
|
||||
loginName: resource.properties.admin,
|
||||
defaultDatabaseName: 'postgres'
|
||||
defaultDatabaseName: 'postgres',
|
||||
tenant: resource.tenantId
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,10 @@ export class PostgresServerArcTreeDataProvider extends ResourceTreeDataProviderB
|
||||
// Set default for SSL or will get error complaining about it not being set correctly
|
||||
'sslmode': 'require'
|
||||
},
|
||||
azureAccount: account.key.accountId
|
||||
azureAccount: account.key.accountId,
|
||||
azureTenantId: databaseServer.tenant,
|
||||
azureResourceId: databaseServer.id,
|
||||
azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint
|
||||
},
|
||||
childProvider: 'PGSQL',
|
||||
type: ExtensionNodeType.Server
|
||||
|
||||
@@ -29,7 +29,8 @@ export class PostgresServerService extends ResourceServiceBase<DbServerGraphData
|
||||
name: resource.name,
|
||||
fullName: resource.properties.fullyQualifiedDomainName,
|
||||
loginName: resource.properties.administratorLogin,
|
||||
defaultDatabaseName: 'postgres'
|
||||
defaultDatabaseName: 'postgres',
|
||||
tenant: resource.tenantId
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,10 @@ export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase
|
||||
// Set default for SSL or will get error complaining about it not being set correctly
|
||||
'sslmode': 'require'
|
||||
},
|
||||
azureAccount: account.key.accountId
|
||||
azureAccount: account.key.accountId,
|
||||
azureTenantId: databaseServer.tenant,
|
||||
azureResourceId: databaseServer.id,
|
||||
azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint
|
||||
},
|
||||
childProvider: 'PGSQL',
|
||||
type: ExtensionNodeType.Server
|
||||
|
||||
@@ -58,6 +58,7 @@ export abstract class ResourceTreeDataProviderBase<T extends azureResource.Azure
|
||||
}
|
||||
|
||||
export interface GraphData {
|
||||
tenantId: string;
|
||||
id: string;
|
||||
name: string;
|
||||
location: string;
|
||||
|
||||
@@ -27,7 +27,8 @@ export class SqlInstanceResourceService extends ResourceServiceBase<SqlInstanceG
|
||||
name: resource.name,
|
||||
fullName: resource.properties.fullyQualifiedDomainName,
|
||||
loginName: resource.properties.administratorLogin,
|
||||
defaultDatabaseName: 'master'
|
||||
defaultDatabaseName: 'master',
|
||||
tenant: resource.tenantId
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,10 @@ export class SqlInstanceTreeDataProvider extends ResourceTreeDataProviderBase<az
|
||||
providerName: 'MSSQL',
|
||||
saveProfile: false,
|
||||
options: {},
|
||||
azureAccount: account.key.accountId
|
||||
azureAccount: account.key.accountId,
|
||||
azureTenantId: databaseServer.tenant,
|
||||
azureResourceId: databaseServer.id,
|
||||
azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint
|
||||
},
|
||||
childProvider: 'MSSQL',
|
||||
type: ExtensionNodeType.Server
|
||||
|
||||
@@ -26,7 +26,8 @@ export class SqlInstanceArcResourceService extends ResourceServiceBase<SqlInstan
|
||||
name: resource.name,
|
||||
fullName: resource.name,
|
||||
loginName: resource.properties.admin,
|
||||
defaultDatabaseName: 'master'
|
||||
defaultDatabaseName: 'master',
|
||||
tenant: resource.tenantId
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,10 @@ export class SqlInstanceArcTreeDataProvider extends ResourceTreeDataProviderBase
|
||||
providerName: 'MSSQL',
|
||||
saveProfile: false,
|
||||
options: {},
|
||||
azureAccount: account.key.accountId
|
||||
azureAccount: account.key.accountId,
|
||||
azureTenantId: databaseServer.tenant,
|
||||
azureResourceId: databaseServer.id,
|
||||
azurePortalEndpoint: account.properties.providerSettings.settings.portalEndpoint
|
||||
},
|
||||
childProvider: 'MSSQL',
|
||||
type: ExtensionNodeType.Server
|
||||
|
||||
@@ -14,6 +14,8 @@ import { ApiWrapper } from '../../../../apiWrapper';
|
||||
import { AzureResourceDatabaseTreeDataProvider } from '../../../../azureResource/providers/database/databaseTreeDataProvider';
|
||||
import { AzureResourceItemType } from '../../../../azureResource/constants';
|
||||
import { IAzureResourceService } from '../../../../azureResource/interfaces';
|
||||
import { AzureAccount } from '../../../../account-provider/interfaces';
|
||||
import settings from '../../../../account-provider/providerSettings';
|
||||
|
||||
// Mock services
|
||||
let mockDatabaseService: TypeMoq.IMock<IAzureResourceService<azureResource.AzureResourceDatabase>>;
|
||||
@@ -21,7 +23,7 @@ let mockApiWrapper: TypeMoq.IMock<ApiWrapper>;
|
||||
let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
|
||||
|
||||
// Mock test data
|
||||
const mockAccount: azdata.Account = {
|
||||
const mockAccount: AzureAccount = {
|
||||
key: {
|
||||
accountId: 'mock_account',
|
||||
providerId: 'mock_provider'
|
||||
@@ -32,7 +34,11 @@ const mockAccount: azdata.Account = {
|
||||
contextualDisplayName: 'test',
|
||||
userId: 'test@email.com'
|
||||
},
|
||||
properties: undefined,
|
||||
properties: {
|
||||
providerSettings: settings[0].metadata,
|
||||
isMsAccount: true,
|
||||
tenants: []
|
||||
},
|
||||
isStale: false
|
||||
};
|
||||
|
||||
|
||||
@@ -19,9 +19,11 @@ import { IAzureResourceService } from '../../../../azureResource/interfaces';
|
||||
let mockDatabaseServerService: TypeMoq.IMock<IAzureResourceService<azureResource.AzureResourceDatabaseServer>>;
|
||||
let mockApiWrapper: TypeMoq.IMock<ApiWrapper>;
|
||||
let mockExtensionContext: TypeMoq.IMock<vscode.ExtensionContext>;
|
||||
import settings from '../../../../account-provider/providerSettings';
|
||||
import { AzureAccount } from '../../../../account-provider/interfaces';
|
||||
|
||||
// Mock test data
|
||||
const mockAccount: azdata.Account = {
|
||||
const mockAccount: AzureAccount = {
|
||||
key: {
|
||||
accountId: 'mock_account',
|
||||
providerId: 'mock_provider'
|
||||
@@ -32,7 +34,11 @@ const mockAccount: azdata.Account = {
|
||||
contextualDisplayName: 'test',
|
||||
userId: 'test@email.com'
|
||||
},
|
||||
properties: undefined,
|
||||
properties: {
|
||||
providerSettings: settings[0].metadata,
|
||||
isMsAccount: true,
|
||||
tenants: []
|
||||
},
|
||||
isStale: false
|
||||
};
|
||||
|
||||
|
||||
@@ -5,15 +5,16 @@
|
||||
|
||||
import * as should from 'should';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import * as azdata from 'azdata';
|
||||
import 'mocha';
|
||||
import { fail } from 'assert';
|
||||
|
||||
import { azureResource } from '../../azureResource/azure-resource';
|
||||
import { AzureResourceService } from '../../azureResource/resourceService';
|
||||
import { AzureAccount } from '../../account-provider/interfaces';
|
||||
import settings from '../../account-provider/providerSettings';
|
||||
|
||||
// Mock test data
|
||||
const mockAccount: azdata.Account = {
|
||||
const mockAccount: AzureAccount = {
|
||||
key: {
|
||||
accountId: 'mock_account',
|
||||
providerId: 'mock_provider'
|
||||
@@ -24,7 +25,11 @@ const mockAccount: azdata.Account = {
|
||||
contextualDisplayName: 'test',
|
||||
userId: 'test@email.com'
|
||||
},
|
||||
properties: undefined,
|
||||
properties: {
|
||||
providerSettings: settings[0].metadata,
|
||||
isMsAccount: true,
|
||||
tenants: []
|
||||
},
|
||||
isStale: false
|
||||
};
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import * as should from 'should';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import 'mocha';
|
||||
|
||||
@@ -15,9 +14,11 @@ import { AzureResourceResourceTreeNode } from '../../azureResource/resourceTreeN
|
||||
import { AppContext } from '../../appContext';
|
||||
import { ApiWrapper } from '../../apiWrapper';
|
||||
import { AzureResourceServiceNames } from '../../azureResource/constants';
|
||||
import settings from '../../account-provider/providerSettings';
|
||||
import { AzureAccount } from '../../account-provider/interfaces';
|
||||
|
||||
// Mock test data
|
||||
const mockAccount: azdata.Account = {
|
||||
const mockAccount: AzureAccount = {
|
||||
key: {
|
||||
accountId: 'mock_account',
|
||||
providerId: 'mock_provider'
|
||||
@@ -28,7 +29,11 @@ const mockAccount: azdata.Account = {
|
||||
contextualDisplayName: 'test',
|
||||
userId: 'test@email.com'
|
||||
},
|
||||
properties: undefined,
|
||||
properties: {
|
||||
providerSettings: settings[0].metadata,
|
||||
isMsAccount: true,
|
||||
tenants: []
|
||||
},
|
||||
isStale: false
|
||||
};
|
||||
|
||||
@@ -87,7 +92,7 @@ let mockResourceProvider: TypeMoq.IMock<azureResource.IAzureResourceProvider>;
|
||||
let resourceService: AzureResourceService;
|
||||
let appContext: AppContext;
|
||||
|
||||
describe('AzureResourceResourceTreeNode.info', function(): void {
|
||||
describe('AzureResourceResourceTreeNode.info', function (): void {
|
||||
beforeEach(() => {
|
||||
mockResourceTreeDataProvider = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
|
||||
mockResourceTreeDataProvider.setup((o) => o.getTreeItem(mockResourceRootNode)).returns(() => mockResourceRootNode.treeItem);
|
||||
@@ -105,7 +110,7 @@ describe('AzureResourceResourceTreeNode.info', function(): void {
|
||||
appContext.registerService(AzureResourceServiceNames.resourceService, resourceService);
|
||||
});
|
||||
|
||||
it('Should be correct when created.', async function(): Promise<void> {
|
||||
it('Should be correct when created.', async function (): Promise<void> {
|
||||
const resourceTreeNode = new AzureResourceResourceTreeNode({
|
||||
resourceProviderId: mockResourceProviderId,
|
||||
resourceNode: mockResourceRootNode
|
||||
@@ -127,7 +132,7 @@ describe('AzureResourceResourceTreeNode.info', function(): void {
|
||||
});
|
||||
});
|
||||
|
||||
describe('AzureResourceResourceTreeNode.getChildren', function(): void {
|
||||
describe('AzureResourceResourceTreeNode.getChildren', function (): void {
|
||||
beforeEach(() => {
|
||||
mockResourceTreeDataProvider = TypeMoq.Mock.ofType<azureResource.IAzureResourceTreeDataProvider>();
|
||||
mockResourceTreeDataProvider.setup((o) => o.getChildren(mockResourceRootNode)).returns(() => Promise.resolve(mockResourceNodes));
|
||||
@@ -145,12 +150,12 @@ describe('AzureResourceResourceTreeNode.getChildren', function(): void {
|
||||
appContext.registerService(AzureResourceServiceNames.resourceService, resourceService);
|
||||
});
|
||||
|
||||
it('Should return resource nodes when it is container node.', async function(): Promise<void> {
|
||||
it('Should return resource nodes when it is container node.', async function (): Promise<void> {
|
||||
const resourceTreeNode = new AzureResourceResourceTreeNode({
|
||||
resourceProviderId: mockResourceProviderId,
|
||||
resourceNode: mockResourceRootNode
|
||||
},
|
||||
undefined, appContext);
|
||||
undefined, appContext);
|
||||
|
||||
const children = await resourceTreeNode.getChildren();
|
||||
|
||||
@@ -176,7 +181,7 @@ describe('AzureResourceResourceTreeNode.getChildren', function(): void {
|
||||
}
|
||||
});
|
||||
|
||||
it('Should return empty when it is leaf node.', async function(): Promise<void> {
|
||||
it('Should return empty when it is leaf node.', async function (): Promise<void> {
|
||||
const resourceTreeNode = new AzureResourceResourceTreeNode({
|
||||
resourceProviderId: mockResourceProviderId,
|
||||
resourceNode: mockResourceNode1
|
||||
|
||||
Reference in New Issue
Block a user