From cbf3cd7445b8471f32b998cefd9281070afe2217 Mon Sep 17 00:00:00 2001 From: Charles Gagnon Date: Mon, 13 Jul 2020 18:11:54 -0700 Subject: [PATCH] Remove AzureCore ApiWrapper (#11335) --- extensions/azurecore/package.json | 2 + extensions/azurecore/src/apiWrapper.ts | 220 ------------------ extensions/azurecore/src/appContext.ts | 5 +- .../azurecore/src/azureResource/commands.ts | 47 ++-- .../azurecore/src/azureResource/interfaces.ts | 8 +- .../providers/database/databaseProvider.ts | 4 +- .../database/databaseTreeDataProvider.ts | 4 +- .../databaseServer/databaseServerProvider.ts | 4 +- .../databaseServerTreeDataProvider.ts | 4 +- .../postgresServerProvider.ts | 4 +- .../postgresServerTreeDataProvider.ts | 4 +- .../postgresServer/postgresServerProvider.ts | 4 +- .../postgresServerTreeDataProvider.ts | 4 +- .../providers/resourceTreeDataProviderBase.ts | 8 +- .../sqlinstance/sqlInstanceProvider.ts | 4 +- .../sqlInstanceTreeDataProvider.ts | 4 +- .../sqlinstanceArc/sqlInstanceArcProvider.ts | 4 +- .../sqlInstanceArcTreeDataProvider.ts | 4 +- .../azureResource/services/accountService.ts | 30 --- .../src/azureResource/tree/accountTreeNode.ts | 16 +- .../src/azureResource/tree/treeProvider.ts | 52 ++--- .../azurecore/src/azureResource/utils.ts | 2 +- extensions/azurecore/src/extension.ts | 41 ++-- .../database/databaseTreeDataProvider.test.ts | 17 +- .../databaseServerTreeDataProvider.test.ts | 17 +- .../azureResource/resourceTreeNode.test.ts | 5 +- .../tree/accountTreeNode.test.ts | 34 +-- .../tree/subscriptionTreeNode.test.ts | 8 +- .../azureResource/tree/treeProvider.test.ts | 21 +- extensions/azurecore/yarn.lock | 104 +++++++++ 30 files changed, 246 insertions(+), 439 deletions(-) delete mode 100644 extensions/azurecore/src/apiWrapper.ts delete mode 100644 extensions/azurecore/src/azureResource/services/accountService.ts diff --git a/extensions/azurecore/package.json b/extensions/azurecore/package.json index 17197017bf..89a02939f6 100644 --- a/extensions/azurecore/package.json +++ b/extensions/azurecore/package.json @@ -276,11 +276,13 @@ "@types/node": "^12.11.7", "@types/qs": "^6.9.1", "@types/request": "^2.48.1", + "@types/sinon": "^9.0.4", "@types/ws": "^6.0.4", "mocha": "^5.2.0", "mocha-junit-reporter": "^1.17.0", "mocha-multi-reporters": "^1.1.7", "should": "^13.2.1", + "sinon": "^9.0.2", "typemoq": "^2.1.0", "vscodetestcover": "^1.0.9" } diff --git a/extensions/azurecore/src/apiWrapper.ts b/extensions/azurecore/src/apiWrapper.ts deleted file mode 100644 index 344f905512..0000000000 --- a/extensions/azurecore/src/apiWrapper.ts +++ /dev/null @@ -1,220 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import * as azdata from 'azdata'; - -import * as constants from './constants'; - -/** - * Wrapper class to act as a facade over VSCode and Data APIs and allow us to test / mock callbacks into - * this API from our code - * - * @export - */ -export class ApiWrapper { - // Data APIs - public registerConnectionProvider(provider: azdata.ConnectionProvider): vscode.Disposable { - return azdata.dataprotocol.registerConnectionProvider(provider); - } - - public registerObjectExplorerProvider(provider: azdata.ObjectExplorerProvider): vscode.Disposable { - return azdata.dataprotocol.registerObjectExplorerProvider(provider); - } - - public registerTaskServicesProvider(provider: azdata.TaskServicesProvider): vscode.Disposable { - return azdata.dataprotocol.registerTaskServicesProvider(provider); - } - - public registerFileBrowserProvider(provider: azdata.FileBrowserProvider): vscode.Disposable { - return azdata.dataprotocol.registerFileBrowserProvider(provider); - } - - public registerCapabilitiesServiceProvider(provider: azdata.CapabilitiesProvider): vscode.Disposable { - return azdata.dataprotocol.registerCapabilitiesServiceProvider(provider); - } - - public registerModelViewProvider(widgetId: string, handler: (modelView: azdata.ModelView) => void): void { - return azdata.ui.registerModelViewProvider(widgetId, handler); - } - - public registerWebviewProvider(widgetId: string, handler: (webview: azdata.DashboardWebview) => void): void { - return azdata.dashboard.registerWebviewProvider(widgetId, handler); - } - - public createDialog(title: string): azdata.window.Dialog { - return azdata.window.createModelViewDialog(title); - } - - public openDialog(dialog: azdata.window.Dialog): void { - return azdata.window.openDialog(dialog); - } - - public closeDialog(dialog: azdata.window.Dialog): void { - return azdata.window.closeDialog(dialog); - } - - public registerTaskHandler(taskId: string, handler: (profile: azdata.IConnectionProfile) => void): void { - azdata.tasks.registerTask(taskId, handler); - } - - public startBackgroundOperation(operationInfo: azdata.BackgroundOperationInfo): void { - azdata.tasks.startBackgroundOperation(operationInfo); - } - - public getActiveConnections(): Thenable { - return azdata.connection.getActiveConnections(); - } - - public getCurrentConnection(): Thenable { - return azdata.connection.getCurrentConnection(); - } - - public createModelViewEditor(title: string, options?: azdata.ModelViewEditorOptions): azdata.workspace.ModelViewEditor { - return azdata.workspace.createModelViewEditor(title, options); - } - - // VSCode APIs - public createTerminal(name?: string, shellPath?: string, shellArgs?: string[]): vscode.Terminal { - return vscode.window.createTerminal(name, shellPath, shellArgs); - } - - public createTerminalWithOptions(options: vscode.TerminalOptions): vscode.Terminal { - return vscode.window.createTerminal(options); - } - - public executeCommand(command: string, ...rest: any[]): Thenable { - return vscode.commands.executeCommand(command, ...rest); - } - - public getFilePathRelativeToWorkspace(uri: vscode.Uri): string { - return vscode.workspace.asRelativePath(uri); - } - - public getWorkspaceFolders(): readonly vscode.WorkspaceFolder[] { - return vscode.workspace.workspaceFolders; - } - - public getWorkspacePathFromUri(uri: vscode.Uri): string | undefined { - let workspaceFolder = vscode.workspace.getWorkspaceFolder(uri); - return workspaceFolder ? workspaceFolder.uri.fsPath : undefined; - } - - public registerCommand(command: string, callback: (...args: any[]) => any, thisArg?: any): vscode.Disposable { - return vscode.commands.registerCommand(command, callback, thisArg); - } - - public registerDocumentOpenHandler(handler: (doc: vscode.TextDocument) => any): vscode.Disposable { - return vscode.workspace.onDidOpenTextDocument(handler); - } - - public registerTreeDataProvider(viewId: string, treeDataProvider: vscode.TreeDataProvider): vscode.Disposable { - return vscode.window.registerTreeDataProvider(viewId, treeDataProvider); - } - - public setCommandContext(key: string, value: any): Thenable { - return vscode.commands.executeCommand(constants.BuiltInCommands.SetContext, key, value); - } - - /** - * Get the configuration for a extensionName - * @param extensionName The string name of the extension to get the configuration for - * @param resource The optional URI, as a URI object or a string, to use to get resource-scoped configurations - */ - public getConfiguration(extensionName?: string, resource?: vscode.Uri | string): vscode.WorkspaceConfiguration { - if (typeof resource === 'string') { - try { - resource = this.parseUri(resource); - } catch (e) { - resource = undefined; - } - } - return vscode.workspace.getConfiguration(extensionName, resource as vscode.Uri); - } - - public getExtensionConfiguration(): vscode.WorkspaceConfiguration { - return this.getConfiguration(constants.extensionConfigSectionName); - } - - public get onDidChangeConfiguration(): vscode.Event { - return vscode.workspace.onDidChangeConfiguration; - } - - /** - * Parse uri - */ - public parseUri(uri: string): vscode.Uri { - return vscode.Uri.parse(uri); - } - - public showOpenDialog(options: vscode.OpenDialogOptions): Thenable { - return vscode.window.showOpenDialog(options); - } - - public showSaveDialog(options: vscode.SaveDialogOptions): Thenable { - return vscode.window.showSaveDialog(options); - } - - public showTextDocument(document: vscode.TextDocument, column?: vscode.ViewColumn, preserveFocus?: boolean, preview?: boolean): Thenable { - let options: vscode.TextDocumentShowOptions = { - viewColumn: column, - preserveFocus: preserveFocus, - preview: preview - }; - return vscode.window.showTextDocument(document, options); - } - - public showErrorMessage(message: string, ...items: string[]): Thenable { - return vscode.window.showErrorMessage(message, ...items); - } - - public showWarningMessage(message: string, ...items: string[]): Thenable { - return vscode.window.showWarningMessage(message, ...items); - } - - public showInformationMessage(message: string, ...items: string[]): Thenable { - return vscode.window.showInformationMessage(message, ...items); - } - - public createStatusBarItem(alignment?: vscode.StatusBarAlignment, priority?: number): vscode.StatusBarItem { - return vscode.window.createStatusBarItem(alignment, priority); - } - - public get workspaceFolders(): readonly vscode.WorkspaceFolder[] { - return vscode.workspace.workspaceFolders; - } - - public createOutputChannel(name: string): vscode.OutputChannel { - return vscode.window.createOutputChannel(name); - } - - public createWizardPage(title: string): azdata.window.WizardPage { - return azdata.window.createWizardPage(title); - } - - public registerCompletionItemProvider(selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, ...triggerCharacters: string[]): vscode.Disposable { - return vscode.languages.registerCompletionItemProvider(selector, provider, ...triggerCharacters); - } - - public createTab(title: string): azdata.window.DialogTab { - return azdata.window.createTab(title); - } - - // Account APIs - public getAllAccounts(): Thenable { - return azdata.accounts.getAllAccounts(); - } - - public getSecurityToken(account: azdata.Account, resource: azdata.AzureResource): Thenable<{ [key: string]: any }> { - return azdata.accounts.getSecurityToken(account, resource); - } - - public readonly onDidChangeAccounts = azdata.accounts.onDidChangeAccounts; - - // Connection APIs - public openConnectionDialog(providers: string[], initialConnectionProfile?: azdata.IConnectionProfile, connectionCompletionOptions?: azdata.IConnectionCompletionOptions): Thenable { - return azdata.connection.openConnectionDialog(providers, initialConnectionProfile, connectionCompletionOptions); - } -} diff --git a/extensions/azurecore/src/appContext.ts b/extensions/azurecore/src/appContext.ts index df03917215..4a20fe30f2 100644 --- a/extensions/azurecore/src/appContext.ts +++ b/extensions/azurecore/src/appContext.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { ApiWrapper } from './apiWrapper'; /** * Global context for the application @@ -12,9 +11,7 @@ import { ApiWrapper } from './apiWrapper'; export class AppContext { private serviceMap: Map = new Map(); - constructor(public readonly extensionContext: vscode.ExtensionContext, public readonly apiWrapper: ApiWrapper) { - this.apiWrapper = apiWrapper || new ApiWrapper(); - } + constructor(public readonly extensionContext: vscode.ExtensionContext) { } public getService(serviceName: string): T { return this.serviceMap.get(serviceName) as T; diff --git a/extensions/azurecore/src/azureResource/commands.ts b/extensions/azurecore/src/azureResource/commands.ts index 7ff78bf9ed..7c4883cd33 100644 --- a/extensions/azurecore/src/azureResource/commands.ts +++ b/extensions/azurecore/src/azureResource/commands.ts @@ -3,9 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - -import { window, QuickPickItem, env, Uri } from 'vscode'; import * as azdata from 'azdata'; +import * as vscode from 'vscode'; import { TokenCredentials } from '@azure/ms-rest-js'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); @@ -21,12 +20,12 @@ import { AzureResourceServiceNames } from './constants'; import { AzureAccount, Tenant } from '../account-provider/interfaces'; export function registerAzureResourceCommands(appContext: AppContext, tree: AzureResourceTreeProvider): void { - appContext.apiWrapper.registerCommand('azure.resource.startterminal', async (node?: TreeNode) => { + vscode.commands.registerCommand('azure.resource.startterminal', async (node?: TreeNode) => { try { - const enablePreviewFeatures = appContext.apiWrapper.getConfiguration('workbench').get('enablePreviewFeatures'); + const enablePreviewFeatures = vscode.workspace.getConfiguration('workbench').get('enablePreviewFeatures'); if (!enablePreviewFeatures) { const msg = localize('azure.cloudTerminalPreview', "You must enable preview features in order to use Azure Cloud Shell."); - appContext.apiWrapper.showInformationMessage(msg); + vscode.window.showInformationMessage(msg); return; } if (!node || !(node instanceof AzureResourceAccountTreeNode)) { @@ -36,28 +35,28 @@ export function registerAzureResourceCommands(appContext: AppContext, tree: Azur const accountNode = node as AzureResourceAccountTreeNode; const azureAccount = accountNode.account as AzureAccount; - const tokens = await appContext.apiWrapper.getSecurityToken(azureAccount, azdata.AzureResource.MicrosoftResourceManagement); + const tokens = await azdata.accounts.getSecurityToken(azureAccount, azdata.AzureResource.MicrosoftResourceManagement); const terminalService = appContext.getService(AzureResourceServiceNames.terminalService); const listOfTenants = azureAccount.properties.tenants.map(t => t.displayName); if (listOfTenants.length === 0) { - window.showErrorMessage(localize('azure.noTenants', "A tenant is required for this feature. Your Azure subscription seems to have no tenants.")); + vscode.window.showErrorMessage(localize('azure.noTenants', "A tenant is required for this feature. Your Azure subscription seems to have no tenants.")); return; } let tenant: Tenant; - window.setStatusBarMessage(localize('azure.startingCloudShell', "Starting cloud shell…"), 5000); + vscode.window.setStatusBarMessage(localize('azure.startingCloudShell', "Starting cloud shell…"), 5000); if (listOfTenants.length === 1) { // Don't show quickpick for a single option tenant = azureAccount.properties.tenants[0]; } else { - const pickedTenant = await window.showQuickPick(listOfTenants, { canPickMany: false }); + const pickedTenant = await vscode.window.showQuickPick(listOfTenants, { canPickMany: false }); if (!pickedTenant) { - window.showErrorMessage(localize('azure.mustPickTenant', "You must select a tenant for this feature to work.")); + vscode.window.showErrorMessage(localize('azure.mustPickTenant', "You must select a tenant for this feature to work.")); return; } @@ -68,13 +67,13 @@ export function registerAzureResourceCommands(appContext: AppContext, tree: Azur await terminalService.getOrCreateCloudConsole(azureAccount, tenant, tokens); } catch (ex) { console.error(ex); - window.showErrorMessage(ex); + vscode.window.showErrorMessage(ex); } }); // Resource Tree commands - appContext.apiWrapper.registerCommand('azure.resource.selectsubscriptions', async (node?: TreeNode) => { + vscode.commands.registerCommand('azure.resource.selectsubscriptions', async (node?: TreeNode) => { if (!(node instanceof AzureResourceAccountTreeNode)) { return; } @@ -92,7 +91,7 @@ export function registerAzureResourceCommands(appContext: AppContext, tree: Azur const subscriptions = (await accountNode.getCachedSubscriptions()) || []; if (subscriptions.length === 0) { try { - const tokens = await this.servicePool.apiWrapper.getSecurityToken(account, azdata.AzureResource.ResourceManagement); + const tokens = await azdata.accounts.getSecurityToken(account, azdata.AzureResource.ResourceManagement); for (const tenant of account.properties.tenants) { const token = tokens[tenant.id].token; @@ -119,7 +118,7 @@ export function registerAzureResourceCommands(appContext: AppContext, tree: Azur selectedSubscriptionIds.push(...subscriptions.map((subscription) => subscription.id)); } - interface AzureResourceSubscriptionQuickPickItem extends QuickPickItem { + interface AzureResourceSubscriptionQuickPickItem extends vscode.QuickPickItem { subscription: azureResource.AzureResourceSubscription; } @@ -131,7 +130,7 @@ export function registerAzureResourceCommands(appContext: AppContext, tree: Azur }; }).sort((a, b) => a.label.localeCompare(b.label)); - const selectedSubscriptionQuickPickItems = await window.showQuickPick(subscriptionQuickPickItems, { canPickMany: true }); + const selectedSubscriptionQuickPickItems = await vscode.window.showQuickPick(subscriptionQuickPickItems, { canPickMany: true }); if (selectedSubscriptionQuickPickItems && selectedSubscriptionQuickPickItems.length > 0) { await tree.refresh(node, false); @@ -140,17 +139,17 @@ export function registerAzureResourceCommands(appContext: AppContext, tree: Azur } }); - appContext.apiWrapper.registerCommand('azure.resource.refreshall', () => tree.notifyNodeChanged(undefined)); + vscode.commands.registerCommand('azure.resource.refreshall', () => tree.notifyNodeChanged(undefined)); - appContext.apiWrapper.registerCommand('azure.resource.refresh', async (node?: TreeNode) => { + vscode.commands.registerCommand('azure.resource.refresh', async (node?: TreeNode) => { await tree.refresh(node, true); }); - appContext.apiWrapper.registerCommand('azure.resource.signin', async (node?: TreeNode) => { - appContext.apiWrapper.executeCommand('workbench.actions.modal.linkedAccount'); + vscode.commands.registerCommand('azure.resource.signin', async (node?: TreeNode) => { + vscode.commands.executeCommand('workbench.actions.modal.linkedAccount'); }); - appContext.apiWrapper.registerCommand('azure.resource.connectsqlserver', async (node?: TreeNode) => { + vscode.commands.registerCommand('azure.resource.connectsqlserver', async (node?: TreeNode) => { if (!node) { return; } @@ -161,13 +160,13 @@ export function registerAzureResourceCommands(appContext: AppContext, tree: Azur } // Ensure connection is saved to the Connections list, then open connection dialog let connectionProfile = Object.assign({}, treeItem.payload, { saveProfile: true }); - const conn = await appContext.apiWrapper.openConnectionDialog(undefined, connectionProfile, { saveConnection: true, showDashboard: true }); + const conn = await azdata.connection.openConnectionDialog(undefined, connectionProfile, { saveConnection: true, showDashboard: true }); if (conn) { - appContext.apiWrapper.executeCommand('workbench.view.connections'); + vscode.commands.executeCommand('workbench.view.connections'); } }); - appContext.apiWrapper.registerCommand('azure.resource.openInAzurePortal', async (connectionProfile: azdata.IConnectionProfile) => { + vscode.commands.registerCommand('azure.resource.openInAzurePortal', async (connectionProfile: azdata.IConnectionProfile) => { if ( !connectionProfile.azureResourceId || @@ -178,6 +177,6 @@ export function registerAzureResourceCommands(appContext: AppContext, tree: Azur } const urlToOpen = `${connectionProfile.azurePortalEndpoint}//${connectionProfile.azureTenantId}/#resource/${connectionProfile.azureResourceId}`; - env.openExternal(Uri.parse(urlToOpen)); + vscode.env.openExternal(vscode.Uri.parse(urlToOpen)); }); } diff --git a/extensions/azurecore/src/azureResource/interfaces.ts b/extensions/azurecore/src/azureResource/interfaces.ts index 35a11026d2..ce2139a375 100644 --- a/extensions/azurecore/src/azureResource/interfaces.ts +++ b/extensions/azurecore/src/azureResource/interfaces.ts @@ -5,17 +5,11 @@ import * as msRest from '@azure/ms-rest-js'; -import { Account, DidChangeAccountsParams } from 'azdata'; -import { Event } from 'vscode'; +import { Account } from 'azdata'; import { azureResource } from './azure-resource'; import { AzureAccount, AzureAccountSecurityToken, Tenant } from '../account-provider/interfaces'; -export interface IAzureResourceAccountService { - getAccounts(): Promise; - readonly onDidChangeAccounts: Event; -} - export interface IAzureResourceSubscriptionService { getSubscriptions(account: Account, credential: msRest.ServiceClientCredentials): Promise; } diff --git a/extensions/azurecore/src/azureResource/providers/database/databaseProvider.ts b/extensions/azurecore/src/azureResource/providers/database/databaseProvider.ts index f63f4e5d99..565f01ff5b 100644 --- a/extensions/azurecore/src/azureResource/providers/database/databaseProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/database/databaseProvider.ts @@ -5,7 +5,6 @@ import { ExtensionContext } from 'vscode'; -import { ApiWrapper } from '../../../apiWrapper'; import { azureResource } from '../../azure-resource'; import { AzureResourceDatabaseTreeDataProvider } from './databaseTreeDataProvider'; @@ -14,13 +13,12 @@ import { IAzureResourceService } from '../../interfaces'; export class AzureResourceDatabaseProvider implements azureResource.IAzureResourceProvider { public constructor( private _databaseService: IAzureResourceService, - private _apiWrapper: ApiWrapper, private _extensionContext: ExtensionContext ) { } public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { - return new AzureResourceDatabaseTreeDataProvider(this._databaseService, this._apiWrapper, this._extensionContext); + return new AzureResourceDatabaseTreeDataProvider(this._databaseService, this._extensionContext); } public get providerId(): string { diff --git a/extensions/azurecore/src/azureResource/providers/database/databaseTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/database/databaseTreeDataProvider.ts index da655898b5..143e3ae531 100644 --- a/extensions/azurecore/src/azureResource/providers/database/databaseTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/database/databaseTreeDataProvider.ts @@ -10,7 +10,6 @@ const localize = nls.loadMessageBundle(); import { azureResource } from '../../azure-resource'; import { AzureResourceItemType } from '../../../azureResource/constants'; -import { ApiWrapper } from '../../../apiWrapper'; import { generateGuid } from '../../utils'; import { IAzureResourceService } from '../../interfaces'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; @@ -22,10 +21,9 @@ export class AzureResourceDatabaseTreeDataProvider extends ResourceTreeDataProvi public constructor( databaseService: IAzureResourceService, - apiWrapper: ApiWrapper, private _extensionContext: ExtensionContext ) { - super(databaseService, apiWrapper); + super(databaseService); } protected getTreeItemForResource(database: azureResource.AzureResourceDatabase, account: Account): TreeItem { return { diff --git a/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerProvider.ts b/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerProvider.ts index 832e276c93..63ba9cac08 100644 --- a/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerProvider.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { ExtensionContext } from 'vscode'; -import { ApiWrapper } from '../../../apiWrapper'; import { azureResource } from '../../azure-resource'; import { IAzureResourceService } from '../../interfaces'; @@ -13,13 +12,12 @@ import { AzureResourceDatabaseServerTreeDataProvider } from './databaseServerTre export class AzureResourceDatabaseServerProvider implements azureResource.IAzureResourceProvider { public constructor( private _databaseServerService: IAzureResourceService, - private _apiWrapper: ApiWrapper, private _extensionContext: ExtensionContext ) { } public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { - return new AzureResourceDatabaseServerTreeDataProvider(this._databaseServerService, this._apiWrapper, this._extensionContext); + return new AzureResourceDatabaseServerTreeDataProvider(this._databaseServerService, this._extensionContext); } public get providerId(): string { diff --git a/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerTreeDataProvider.ts index 7b868ee0e6..3f8a8f2882 100644 --- a/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/databaseServer/databaseServerTreeDataProvider.ts @@ -9,7 +9,6 @@ import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); import { AzureResourceItemType } from '../../../azureResource/constants'; -import { ApiWrapper } from '../../../apiWrapper'; import { generateGuid } from '../../utils'; import { IAzureResourceService } from '../../interfaces'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; @@ -21,10 +20,9 @@ export class AzureResourceDatabaseServerTreeDataProvider extends ResourceTreeDat public constructor( databaseServerService: IAzureResourceService, - apiWrapper: ApiWrapper, private _extensionContext: ExtensionContext ) { - super(databaseServerService, apiWrapper); + super(databaseServerService); } diff --git a/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerProvider.ts b/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerProvider.ts index 4ea82c61c9..8b6e47af44 100644 --- a/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerProvider.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { ExtensionContext } from 'vscode'; -import { ApiWrapper } from '../../../apiWrapper'; import { azureResource } from '../../azure-resource'; import { IAzureResourceService } from '../../interfaces'; @@ -13,13 +12,12 @@ import { PostgresServerArcTreeDataProvider as PostgresServerArcTreeDataProvider export class PostgresServerArcProvider implements azureResource.IAzureResourceProvider { public constructor( private _databaseServerService: IAzureResourceService, - private _apiWrapper: ApiWrapper, private _extensionContext: ExtensionContext ) { } public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { - return new PostgresServerArcTreeDataProvider(this._databaseServerService, this._apiWrapper, this._extensionContext); + return new PostgresServerArcTreeDataProvider(this._databaseServerService, this._extensionContext); } public get providerId(): string { diff --git a/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerTreeDataProvider.ts index b25d3cf9e5..4eb71c285c 100644 --- a/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/postgresArcServer/postgresServerTreeDataProvider.ts @@ -9,7 +9,6 @@ import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); import { AzureResourceItemType } from '../../constants'; -import { ApiWrapper } from '../../../apiWrapper'; import { generateGuid } from '../../utils'; import { IAzureResourceService } from '../../interfaces'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; @@ -21,10 +20,9 @@ export class PostgresServerArcTreeDataProvider extends ResourceTreeDataProviderB public constructor( databaseServerService: IAzureResourceService, - apiWrapper: ApiWrapper, private _extensionContext: ExtensionContext ) { - super(databaseServerService, apiWrapper); + super(databaseServerService); } diff --git a/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerProvider.ts b/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerProvider.ts index 21b248874f..58b3d64da9 100644 --- a/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerProvider.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { ExtensionContext } from 'vscode'; -import { ApiWrapper } from '../../../apiWrapper'; import { azureResource } from '../../azure-resource'; import { IAzureResourceService } from '../../interfaces'; @@ -13,13 +12,12 @@ import { PostgresServerTreeDataProvider as PostgresServerTreeDataProvider } from export class PostgresServerProvider implements azureResource.IAzureResourceProvider { public constructor( private _databaseServerService: IAzureResourceService, - private _apiWrapper: ApiWrapper, private _extensionContext: ExtensionContext ) { } public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { - return new PostgresServerTreeDataProvider(this._databaseServerService, this._apiWrapper, this._extensionContext); + return new PostgresServerTreeDataProvider(this._databaseServerService, this._extensionContext); } public get providerId(): string { diff --git a/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerTreeDataProvider.ts index 7f2d254da2..e90b1d4142 100644 --- a/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/postgresServer/postgresServerTreeDataProvider.ts @@ -9,7 +9,6 @@ import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); import { AzureResourceItemType } from '../../constants'; -import { ApiWrapper } from '../../../apiWrapper'; import { generateGuid } from '../../utils'; import { IAzureResourceService } from '../../interfaces'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; @@ -21,10 +20,9 @@ export class PostgresServerTreeDataProvider extends ResourceTreeDataProviderBase public constructor( databaseServerService: IAzureResourceService, - apiWrapper: ApiWrapper, private _extensionContext: ExtensionContext ) { - super(databaseServerService, apiWrapper); + super(databaseServerService); } diff --git a/extensions/azurecore/src/azureResource/providers/resourceTreeDataProviderBase.ts b/extensions/azurecore/src/azureResource/providers/resourceTreeDataProviderBase.ts index d3bb2b79da..5e62cb43e5 100644 --- a/extensions/azurecore/src/azureResource/providers/resourceTreeDataProviderBase.ts +++ b/extensions/azurecore/src/azureResource/providers/resourceTreeDataProviderBase.ts @@ -7,17 +7,13 @@ import * as azdata from 'azdata'; import * as msRest from '@azure/ms-rest-js'; import { azureResource } from '../azure-resource'; -import { ApiWrapper } from '../../apiWrapper'; import { IAzureResourceService } from '../interfaces'; import { AzureResourceErrorMessageUtil } from '../utils'; import { ResourceGraphClient } from '@azure/arm-resourcegraph'; export abstract class ResourceTreeDataProviderBase implements azureResource.IAzureResourceTreeDataProvider { - public constructor( - protected _resourceService: IAzureResourceService, - protected _apiWrapper: ApiWrapper - ) { + public constructor(protected _resourceService: IAzureResourceService) { } public getTreeItem(element: azureResource.IAzureResourceNode): azdata.TreeItem | Thenable { @@ -45,7 +41,7 @@ export abstract class ResourceTreeDataProviderBase { - const tokens = await this._apiWrapper.getSecurityToken(element.account, azdata.AzureResource.ResourceManagement); + const tokens = await azdata.accounts.getSecurityToken(element.account, azdata.AzureResource.ResourceManagement); const credential = new msRest.TokenCredentials(tokens[element.tenantId].token, tokens[element.tenantId].tokenType); const resources: T[] = await this._resourceService.getResources(element.subscription, credential, element.account) || []; diff --git a/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceProvider.ts b/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceProvider.ts index 09b4e7c8d8..1dc7e8cc86 100644 --- a/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceProvider.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { ExtensionContext } from 'vscode'; -import { ApiWrapper } from '../../../apiWrapper'; import { azureResource } from '../../azure-resource'; import { IAzureResourceService } from '../../interfaces'; @@ -13,13 +12,12 @@ import { SqlInstanceTreeDataProvider as SqlInstanceTreeDataProvider } from './sq 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); + return new SqlInstanceTreeDataProvider(this._service, this._extensionContext); } public get providerId(): string { diff --git a/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceTreeDataProvider.ts index ca6870c416..d1285d3555 100644 --- a/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/sqlinstance/sqlInstanceTreeDataProvider.ts @@ -9,7 +9,6 @@ import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); import { AzureResourceItemType } from '../../constants'; -import { ApiWrapper } from '../../../apiWrapper'; import { generateGuid } from '../../utils'; import { IAzureResourceService } from '../../interfaces'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; @@ -21,10 +20,9 @@ export class SqlInstanceTreeDataProvider extends ResourceTreeDataProviderBase, - apiWrapper: ApiWrapper, private _extensionContext: ExtensionContext ) { - super(databaseServerService, apiWrapper); + super(databaseServerService); } diff --git a/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcProvider.ts b/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcProvider.ts index a73d91fad4..6a9acf0840 100644 --- a/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcProvider.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { ExtensionContext } from 'vscode'; -import { ApiWrapper } from '../../../apiWrapper'; import { azureResource } from '../../azure-resource'; import { IAzureResourceService } from '../../interfaces'; @@ -13,13 +12,12 @@ import { SqlInstanceArcTreeDataProvider as SqlInstanceArcTreeDataProvider } from export class SqlInstanceArcProvider implements azureResource.IAzureResourceProvider { public constructor( private _service: IAzureResourceService, - private _apiWrapper: ApiWrapper, private _extensionContext: ExtensionContext ) { } public getTreeDataProvider(): azureResource.IAzureResourceTreeDataProvider { - return new SqlInstanceArcTreeDataProvider(this._service, this._apiWrapper, this._extensionContext); + return new SqlInstanceArcTreeDataProvider(this._service, this._extensionContext); } public get providerId(): string { diff --git a/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcTreeDataProvider.ts b/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcTreeDataProvider.ts index 1d4d9d7a81..9edac29a9e 100644 --- a/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcTreeDataProvider.ts +++ b/extensions/azurecore/src/azureResource/providers/sqlinstanceArc/sqlInstanceArcTreeDataProvider.ts @@ -9,7 +9,6 @@ import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); import { AzureResourceItemType } from '../../constants'; -import { ApiWrapper } from '../../../apiWrapper'; import { generateGuid } from '../../utils'; import { IAzureResourceService } from '../../interfaces'; import { ResourceTreeDataProviderBase } from '../resourceTreeDataProviderBase'; @@ -21,10 +20,9 @@ export class SqlInstanceArcTreeDataProvider extends ResourceTreeDataProviderBase public constructor( databaseServerService: IAzureResourceService, - apiWrapper: ApiWrapper, private _extensionContext: ExtensionContext ) { - super(databaseServerService, apiWrapper); + super(databaseServerService); } diff --git a/extensions/azurecore/src/azureResource/services/accountService.ts b/extensions/azurecore/src/azureResource/services/accountService.ts deleted file mode 100644 index 4d8acab001..0000000000 --- a/extensions/azurecore/src/azureResource/services/accountService.ts +++ /dev/null @@ -1,30 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Event } from 'vscode'; -import { Account, DidChangeAccountsParams } from 'azdata'; -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 { - return await this._apiWrapper.getAllAccounts(); - } - - public get onDidChangeAccounts(): Event { - return this._onDidChangeAccounts; - } - - private _apiWrapper: ApiWrapper = undefined; - private _onDidChangeAccounts: Event = undefined; -} diff --git a/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts b/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts index fbc32fb663..946785f398 100644 --- a/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts +++ b/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts @@ -3,8 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TreeItem, TreeItemCollapsibleState } from 'vscode'; -import { Account, NodeInfo, AzureResource } from 'azdata'; +import * as vscode from 'vscode'; +import * as azdata from 'azdata'; import { TokenCredentials } from '@azure/ms-rest-js'; import * as nls from 'vscode-nls'; @@ -24,7 +24,7 @@ import { IAzureResourceSubscriptionService, IAzureResourceSubscriptionFilterServ export class AzureResourceAccountTreeNode extends AzureResourceContainerTreeNodeBase { public constructor( - public readonly account: Account, + public readonly account: azdata.Account, appContext: AppContext, treeChangeHandler: IAzureResourceTreeChangeHandler ) { @@ -42,7 +42,7 @@ export class AzureResourceAccountTreeNode extends AzureResourceContainerTreeNode public async getChildren(): Promise { try { let subscriptions: azureResource.AzureResourceSubscription[] = []; - const tokens = await this.appContext.apiWrapper.getSecurityToken(this.account, AzureResource.ResourceManagement); + const tokens = await azdata.accounts.getSecurityToken(this.account, azdata.AzureResource.ResourceManagement); if (this._isClearingCache) { try { @@ -99,7 +99,7 @@ export class AzureResourceAccountTreeNode extends AzureResourceContainerTreeNode } } catch (error) { if (error instanceof AzureResourceCredentialError) { - this.appContext.apiWrapper.executeCommand('azure.resource.signin'); + vscode.commands.executeCommand('azure.resource.signin'); } return [AzureResourceMessageTreeNode.create(AzureResourceErrorMessageUtil.getErrorMessage(error), this)]; } @@ -109,8 +109,8 @@ export class AzureResourceAccountTreeNode extends AzureResourceContainerTreeNode return this.getCache(); } - public getTreeItem(): TreeItem | Promise { - const item = new TreeItem(this._label, TreeItemCollapsibleState.Collapsed); + public getTreeItem(): vscode.TreeItem | Promise { + const item = new vscode.TreeItem(this._label, vscode.TreeItemCollapsibleState.Collapsed); item.id = this._id; item.contextValue = AzureResourceItemType.account; item.iconPath = { @@ -120,7 +120,7 @@ export class AzureResourceAccountTreeNode extends AzureResourceContainerTreeNode return item; } - public getNodeInfo(): NodeInfo { + public getNodeInfo(): azdata.NodeInfo { return { label: this._label, isLeaf: false, diff --git a/extensions/azurecore/src/azureResource/tree/treeProvider.ts b/extensions/azurecore/src/azureResource/tree/treeProvider.ts index febf5ddfd1..6a97de8b7e 100644 --- a/extensions/azurecore/src/azureResource/tree/treeProvider.ts +++ b/extensions/azurecore/src/azureResource/tree/treeProvider.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TreeDataProvider, EventEmitter, Event, TreeItem } from 'vscode'; +import * as vscode from 'vscode'; import * as azdata from 'azdata'; import { AppContext } from '../../appContext'; import * as nls from 'vscode-nls'; @@ -16,41 +16,29 @@ import { AzureResourceMessageTreeNode } from '../messageTreeNode'; import { AzureResourceContainerTreeNodeBase } from './baseTreeNodes'; import { AzureResourceErrorMessageUtil, equals } from '../utils'; import { IAzureResourceTreeChangeHandler } from './treeChangeHandler'; -import { IAzureResourceAccountService } from '../../azureResource/interfaces'; -import { AzureResourceServiceNames } from '../constants'; -export class AzureResourceTreeProvider implements TreeDataProvider, IAzureResourceTreeChangeHandler { +export class AzureResourceTreeProvider implements vscode.TreeDataProvider, IAzureResourceTreeChangeHandler { public isSystemInitialized: boolean = false; - private accountService: IAzureResourceAccountService; private accounts: azdata.Account[]; - private _onDidChangeTreeData = new EventEmitter(); + private _onDidChangeTreeData = new vscode.EventEmitter(); private loadingAccountsPromise: Promise; - public constructor(public readonly appContext: AppContext) { - if (appContext) { - this.hookAccountService(appContext); - } - } - - private hookAccountService(appContext: AppContext): void { - this.accountService = appContext.getService(AzureResourceServiceNames.accountService); - if (this.accountService) { - this.accountService.onDidChangeAccounts(async (e: azdata.DidChangeAccountsParams) => { - // This event sends it per provider, we need to make sure we get all the azure related accounts - let accounts = await this.accountService.getAccounts(); - accounts = accounts.filter(a => a.key.providerId.startsWith('azure')); - // the onDidChangeAccounts event will trigger in many cases where the accounts didn't actually change - // the notifyNodeChanged event triggers a refresh which triggers a getChildren which can trigger this callback - // this below check short-circuits the infinite callback loop - this.setSystemInitialized(); - if (!equals(accounts, this.accounts)) { - this.accounts = accounts; - this.notifyNodeChanged(undefined); - } - }); - } + public constructor(private readonly appContext: AppContext) { + azdata.accounts.onDidChangeAccounts(async (e: azdata.DidChangeAccountsParams) => { + // This event sends it per provider, we need to make sure we get all the azure related accounts + let accounts = await azdata.accounts.getAllAccounts(); + accounts = accounts.filter(a => a.key.providerId.startsWith('azure')); + // the onDidChangeAccounts event will trigger in many cases where the accounts didn't actually change + // the notifyNodeChanged event triggers a refresh which triggers a getChildren which can trigger this callback + // this below check short-circuits the infinite callback loop + this.setSystemInitialized(); + if (!equals(accounts, this.accounts)) { + this.accounts = accounts; + this.notifyNodeChanged(undefined); + } + }); } public async getChildren(element?: TreeNode): Promise { @@ -78,7 +66,7 @@ export class AzureResourceTreeProvider implements TreeDataProvider, IA private async loadAccounts(): Promise { try { - this.accounts = await this.appContext.getService(AzureResourceServiceNames.accountService).getAccounts(); + this.accounts = await azdata.accounts.getAllAccounts(); // System has been initialized this.setSystemInitialized(); this._onDidChangeTreeData.fire(undefined); @@ -93,7 +81,7 @@ export class AzureResourceTreeProvider implements TreeDataProvider, IA this.loadingAccountsPromise = undefined; } - public get onDidChangeTreeData(): Event { + public get onDidChangeTreeData(): vscode.Event { return this._onDidChangeTreeData.event; } @@ -111,7 +99,7 @@ export class AzureResourceTreeProvider implements TreeDataProvider, IA this._onDidChangeTreeData.fire(node); } - public getTreeItem(element: TreeNode): TreeItem | Thenable { + public getTreeItem(element: TreeNode): vscode.TreeItem | Thenable { return element.getTreeItem(); } } diff --git a/extensions/azurecore/src/azureResource/utils.ts b/extensions/azurecore/src/azureResource/utils.ts index ecadcc4a59..605f69bbe6 100644 --- a/extensions/azurecore/src/azureResource/utils.ts +++ b/extensions/azurecore/src/azureResource/utils.ts @@ -151,7 +151,7 @@ export async function getSubscriptions(appContext: AppContext, account?: azdata. } const subscriptionService = appContext.getService(AzureResourceServiceNames.subscriptionService); - const tokens = await appContext.apiWrapper.getSecurityToken(account, azdata.AzureResource.ResourceManagement); + const tokens = await azdata.accounts.getSecurityToken(account, azdata.AzureResource.ResourceManagement); await Promise.all(account.properties.tenants.map(async (tenant: { id: string | number; }) => { try { const token = tokens[tenant.id].token; diff --git a/extensions/azurecore/src/extension.ts b/extensions/azurecore/src/extension.ts index a5e98cddb0..89d7add7f9 100644 --- a/extensions/azurecore/src/extension.ts +++ b/extensions/azurecore/src/extension.ts @@ -10,7 +10,6 @@ import * as path from 'path'; import * as os from 'os'; import { AppContext } from './appContext'; -import { ApiWrapper } from './apiWrapper'; import { AzureAccountProviderService } from './account-provider/azureAccountProviderService'; import { AzureResourceDatabaseServerProvider } from './azureResource/providers/databaseServer/databaseServerProvider'; @@ -18,9 +17,8 @@ import { AzureResourceDatabaseServerService } from './azureResource/providers/da import { AzureResourceDatabaseProvider } from './azureResource/providers/database/databaseProvider'; import { AzureResourceDatabaseService } from './azureResource/providers/database/databaseService'; import { AzureResourceService } from './azureResource/resourceService'; -import { IAzureResourceCacheService, IAzureResourceAccountService, IAzureResourceSubscriptionService, IAzureResourceSubscriptionFilterService, IAzureResourceTenantService, IAzureTerminalService } from './azureResource/interfaces'; +import { IAzureResourceCacheService, IAzureResourceSubscriptionService, IAzureResourceSubscriptionFilterService, IAzureResourceTenantService, IAzureTerminalService } from './azureResource/interfaces'; import { AzureResourceServiceNames } from './azureResource/constants'; -import { AzureResourceAccountService } from './azureResource/services/accountService'; import { AzureResourceSubscriptionService } from './azureResource/services/subscriptionService'; import { AzureResourceSubscriptionFilterService } from './azureResource/services/subscriptionFilterService'; import { AzureResourceCacheService } from './azureResource/services/cacheService'; @@ -41,6 +39,7 @@ import * as azurecore from './azurecore'; import * as azureResourceUtils from './azureResource/utils'; import * as utils from './utils'; import * as loc from './localizedConstants'; +import * as constants from './constants'; import { AzureResourceGroupService } from './azureResource/providers/resourceGroup/resourceGroupService'; import { Logger } from './utils/Logger'; @@ -70,39 +69,38 @@ function pushDisposable(disposable: vscode.Disposable): void { // your extension is activated the very first time the command is executed export async function activate(context: vscode.ExtensionContext): Promise { extensionContext = context; - const apiWrapper = new ApiWrapper(); - let appContext = new AppContext(extensionContext, apiWrapper); + let appContext = new AppContext(extensionContext); let storagePath = await findOrMakeStoragePath(); if (!storagePath) { return undefined; } - updatePiiLoggingLevel(apiWrapper); + updatePiiLoggingLevel(); // Create the provider service and activate initAzureAccountProvider(extensionContext, storagePath).catch((err) => console.log(err)); registerAzureServices(appContext); const azureResourceTree = new AzureResourceTreeProvider(appContext); - pushDisposable(apiWrapper.registerTreeDataProvider('azureResourceExplorer', azureResourceTree)); - pushDisposable(apiWrapper.onDidChangeConfiguration(e => onDidChangeConfiguration(e, apiWrapper), this)); + pushDisposable(vscode.window.registerTreeDataProvider('azureResourceExplorer', azureResourceTree)); + pushDisposable(vscode.workspace.onDidChangeConfiguration(e => onDidChangeConfiguration(e), this)); registerAzureResourceCommands(appContext, azureResourceTree); return { getSubscriptions(account?: azdata.Account, ignoreErrors?: boolean): Thenable { return azureResourceUtils.getSubscriptions(appContext, account, ignoreErrors); }, getResourceGroups(account?: azdata.Account, subscription?: azureResource.AzureResourceSubscription, ignoreErrors?: boolean): Thenable { return azureResourceUtils.getResourceGroups(appContext, account, subscription, ignoreErrors); }, provideResources(): azureResource.IAzureResourceProvider[] { - const arcFeaturedEnabled = apiWrapper.getExtensionConfiguration().get('enableArcFeatures'); + const arcFeaturedEnabled = vscode.workspace.getConfiguration(constants.extensionConfigSectionName).get('enableArcFeatures'); const providers: azureResource.IAzureResourceProvider[] = [ - new AzureResourceDatabaseServerProvider(new AzureResourceDatabaseServerService(), apiWrapper, extensionContext), - new AzureResourceDatabaseProvider(new AzureResourceDatabaseService(), apiWrapper, extensionContext), - new SqlInstanceProvider(new SqlInstanceResourceService(), apiWrapper, extensionContext), - new PostgresServerProvider(new PostgresServerService(), apiWrapper, extensionContext), + new AzureResourceDatabaseServerProvider(new AzureResourceDatabaseServerService(), extensionContext), + new AzureResourceDatabaseProvider(new AzureResourceDatabaseService(), extensionContext), + new SqlInstanceProvider(new SqlInstanceResourceService(), extensionContext), + new PostgresServerProvider(new PostgresServerService(), extensionContext), ]; if (arcFeaturedEnabled) { providers.push( - new SqlInstanceArcProvider(new SqlInstanceArcResourceService(), apiWrapper, extensionContext), - new PostgresServerArcProvider(new PostgresServerArcService(), apiWrapper, extensionContext) + new SqlInstanceArcProvider(new SqlInstanceArcResourceService(), extensionContext), + new PostgresServerArcProvider(new PostgresServerArcService(), extensionContext) ); } return providers; @@ -152,7 +150,6 @@ async function initAzureAccountProvider(extensionContext: vscode.ExtensionContex function registerAzureServices(appContext: AppContext): void { appContext.registerService(AzureResourceServiceNames.resourceService, new AzureResourceService()); appContext.registerService(AzureResourceServiceNames.resourceGroupService, new AzureResourceGroupService()); - appContext.registerService(AzureResourceServiceNames.accountService, new AzureResourceAccountService(appContext.apiWrapper)); appContext.registerService(AzureResourceServiceNames.cacheService, new AzureResourceCacheService(extensionContext)); appContext.registerService(AzureResourceServiceNames.subscriptionService, new AzureResourceSubscriptionService()); appContext.registerService(AzureResourceServiceNames.subscriptionFilterService, new AzureResourceSubscriptionFilterService(new AzureResourceCacheService(extensionContext))); @@ -160,21 +157,21 @@ function registerAzureServices(appContext: AppContext): void { appContext.registerService(AzureResourceServiceNames.terminalService, new AzureTerminalService(extensionContext)); } -async function onDidChangeConfiguration(e: vscode.ConfigurationChangeEvent, apiWrapper: ApiWrapper): Promise { +async function onDidChangeConfiguration(e: vscode.ConfigurationChangeEvent): Promise { if (e.affectsConfiguration('azure.enableArcFeatures')) { - const response = await apiWrapper.showInformationMessage(loc.requiresReload, loc.reload); + const response = await vscode.window.showInformationMessage(loc.requiresReload, loc.reload); if (response === loc.reload) { - await apiWrapper.executeCommand('workbench.action.reloadWindow'); + await vscode.commands.executeCommand('workbench.action.reloadWindow'); } return; } if (e.affectsConfiguration('azure.piiLogging')) { - updatePiiLoggingLevel(apiWrapper); + updatePiiLoggingLevel(); } } -function updatePiiLoggingLevel(apiWrapper: ApiWrapper) { - const piiLogging: boolean = apiWrapper.getExtensionConfiguration().get('piiLogging'); +function updatePiiLoggingLevel() { + const piiLogging: boolean = vscode.workspace.getConfiguration(constants.extensionConfigSectionName).get('piiLogging'); Logger.piiLogging = piiLogging; } diff --git a/extensions/azurecore/src/test/azureResource/providers/database/databaseTreeDataProvider.test.ts b/extensions/azurecore/src/test/azureResource/providers/database/databaseTreeDataProvider.test.ts index 69fea6ea22..e40fbddfa4 100644 --- a/extensions/azurecore/src/test/azureResource/providers/database/databaseTreeDataProvider.test.ts +++ b/extensions/azurecore/src/test/azureResource/providers/database/databaseTreeDataProvider.test.ts @@ -7,10 +7,10 @@ import * as should from 'should'; import * as TypeMoq from 'typemoq'; import * as azdata from 'azdata'; import * as vscode from 'vscode'; +import * as sinon from 'sinon'; import 'mocha'; import { azureResource } from '../../../../azureResource/azure-resource'; -import { ApiWrapper } from '../../../../apiWrapper'; import { AzureResourceDatabaseTreeDataProvider } from '../../../../azureResource/providers/database/databaseTreeDataProvider'; import { AzureResourceItemType } from '../../../../azureResource/constants'; import { IAzureResourceService } from '../../../../azureResource/interfaces'; @@ -19,7 +19,6 @@ import settings from '../../../../account-provider/providerSettings'; // Mock services let mockDatabaseService: TypeMoq.IMock>; -let mockApiWrapper: TypeMoq.IMock; let mockExtensionContext: TypeMoq.IMock; // Mock test data @@ -88,12 +87,11 @@ const mockDatabases: azureResource.AzureResourceDatabase[] = [ describe('AzureResourceDatabaseTreeDataProvider.info', function (): void { beforeEach(() => { mockDatabaseService = TypeMoq.Mock.ofType>(); - mockApiWrapper = TypeMoq.Mock.ofType(); mockExtensionContext = TypeMoq.Mock.ofType(); }); it('Should be correct when created.', async function (): Promise { - const treeDataProvider = new AzureResourceDatabaseTreeDataProvider(mockDatabaseService.object, mockApiWrapper.object, mockExtensionContext.object); + const treeDataProvider = new AzureResourceDatabaseTreeDataProvider(mockDatabaseService.object, mockExtensionContext.object); const treeItem = await treeDataProvider.getTreeItem(mockResourceRootNode); should(treeItem.id).equal(mockResourceRootNode.treeItem.id); @@ -106,16 +104,19 @@ describe('AzureResourceDatabaseTreeDataProvider.info', function (): void { describe('AzureResourceDatabaseTreeDataProvider.getChildren', function (): void { beforeEach(() => { mockDatabaseService = TypeMoq.Mock.ofType>(); - mockApiWrapper = TypeMoq.Mock.ofType(); mockExtensionContext = TypeMoq.Mock.ofType(); - mockApiWrapper.setup((o) => o.getSecurityToken(mockAccount, azdata.AzureResource.ResourceManagement)).returns(() => Promise.resolve(mockTokens)); + sinon.stub(azdata.accounts, 'getSecurityToken').returns(Promise.resolve(mockTokens)); mockDatabaseService.setup((o) => o.getResources(mockSubscription, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(mockDatabases)); mockExtensionContext.setup((o) => o.asAbsolutePath(TypeMoq.It.isAnyString())).returns(() => TypeMoq.It.isAnyString()); }); + afterEach(function (): void { + sinon.restore(); + }); + it('Should return container node when element is undefined.', async function (): Promise { - const treeDataProvider = new AzureResourceDatabaseTreeDataProvider(mockDatabaseService.object, mockApiWrapper.object, mockExtensionContext.object); + const treeDataProvider = new AzureResourceDatabaseTreeDataProvider(mockDatabaseService.object, mockExtensionContext.object); const children = await treeDataProvider.getChildren(); @@ -133,7 +134,7 @@ describe('AzureResourceDatabaseTreeDataProvider.getChildren', function (): void }); it('Should return resource nodes when it is container node.', async function (): Promise { - const treeDataProvider = new AzureResourceDatabaseTreeDataProvider(mockDatabaseService.object, mockApiWrapper.object, mockExtensionContext.object); + const treeDataProvider = new AzureResourceDatabaseTreeDataProvider(mockDatabaseService.object, mockExtensionContext.object); const children = await treeDataProvider.getChildren(mockResourceRootNode); diff --git a/extensions/azurecore/src/test/azureResource/providers/databaseServer/databaseServerTreeDataProvider.test.ts b/extensions/azurecore/src/test/azureResource/providers/databaseServer/databaseServerTreeDataProvider.test.ts index 64c69dfaa2..330b47b2cf 100644 --- a/extensions/azurecore/src/test/azureResource/providers/databaseServer/databaseServerTreeDataProvider.test.ts +++ b/extensions/azurecore/src/test/azureResource/providers/databaseServer/databaseServerTreeDataProvider.test.ts @@ -7,17 +7,16 @@ import * as should from 'should'; import * as TypeMoq from 'typemoq'; import * as azdata from 'azdata'; import * as vscode from 'vscode'; +import * as sinon from 'sinon'; import 'mocha'; import { azureResource } from '../../../../azureResource/azure-resource'; -import { ApiWrapper } from '../../../../apiWrapper'; import { AzureResourceDatabaseServerTreeDataProvider } from '../../../../azureResource/providers/databaseServer/databaseServerTreeDataProvider'; import { AzureResourceItemType } from '../../../../azureResource/constants'; import { IAzureResourceService } from '../../../../azureResource/interfaces'; // Mock services let mockDatabaseServerService: TypeMoq.IMock>; -let mockApiWrapper: TypeMoq.IMock; let mockExtensionContext: TypeMoq.IMock; import settings from '../../../../account-provider/providerSettings'; import { AzureAccount } from '../../../../account-provider/interfaces'; @@ -88,12 +87,11 @@ const mockDatabaseServers: azureResource.AzureResourceDatabaseServer[] = [ describe('AzureResourceDatabaseServerTreeDataProvider.info', function (): void { beforeEach(() => { mockDatabaseServerService = TypeMoq.Mock.ofType>(); - mockApiWrapper = TypeMoq.Mock.ofType(); mockExtensionContext = TypeMoq.Mock.ofType(); }); it('Should be correct when created.', async function (): Promise { - const treeDataProvider = new AzureResourceDatabaseServerTreeDataProvider(mockDatabaseServerService.object, mockApiWrapper.object, mockExtensionContext.object); + const treeDataProvider = new AzureResourceDatabaseServerTreeDataProvider(mockDatabaseServerService.object, mockExtensionContext.object); const treeItem = await treeDataProvider.getTreeItem(mockResourceRootNode); should(treeItem.id).equal(mockResourceRootNode.treeItem.id); @@ -106,16 +104,19 @@ describe('AzureResourceDatabaseServerTreeDataProvider.info', function (): void { describe('AzureResourceDatabaseServerTreeDataProvider.getChildren', function (): void { beforeEach(() => { mockDatabaseServerService = TypeMoq.Mock.ofType>(); - mockApiWrapper = TypeMoq.Mock.ofType(); mockExtensionContext = TypeMoq.Mock.ofType(); - mockApiWrapper.setup((o) => o.getSecurityToken(mockAccount, azdata.AzureResource.ResourceManagement)).returns(() => Promise.resolve(mockTokens)); + sinon.stub(azdata.accounts, 'getSecurityToken').returns(Promise.resolve(mockTokens)); mockDatabaseServerService.setup((o) => o.getResources(mockSubscription, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(mockDatabaseServers)); mockExtensionContext.setup((o) => o.asAbsolutePath(TypeMoq.It.isAnyString())).returns(() => TypeMoq.It.isAnyString()); }); + afterEach(function (): void { + sinon.restore(); + }); + it('Should return container node when element is undefined.', async function (): Promise { - const treeDataProvider = new AzureResourceDatabaseServerTreeDataProvider(mockDatabaseServerService.object, mockApiWrapper.object, mockExtensionContext.object); + const treeDataProvider = new AzureResourceDatabaseServerTreeDataProvider(mockDatabaseServerService.object, mockExtensionContext.object); const children = await treeDataProvider.getChildren(); @@ -133,7 +134,7 @@ describe('AzureResourceDatabaseServerTreeDataProvider.getChildren', function (): }); it('Should return resource nodes when it is container node.', async function (): Promise { - const treeDataProvider = new AzureResourceDatabaseServerTreeDataProvider(mockDatabaseServerService.object, mockApiWrapper.object, mockExtensionContext.object); + const treeDataProvider = new AzureResourceDatabaseServerTreeDataProvider(mockDatabaseServerService.object, mockExtensionContext.object); const children = await treeDataProvider.getChildren(mockResourceRootNode); diff --git a/extensions/azurecore/src/test/azureResource/resourceTreeNode.test.ts b/extensions/azurecore/src/test/azureResource/resourceTreeNode.test.ts index 6fa491ff3d..f16a10a9fa 100644 --- a/extensions/azurecore/src/test/azureResource/resourceTreeNode.test.ts +++ b/extensions/azurecore/src/test/azureResource/resourceTreeNode.test.ts @@ -12,7 +12,6 @@ import { azureResource } from '../../azureResource/azure-resource'; import { AzureResourceService } from '../../azureResource/resourceService'; import { AzureResourceResourceTreeNode } from '../../azureResource/resourceTreeNode'; 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'; @@ -106,7 +105,7 @@ describe('AzureResourceResourceTreeNode.info', function (): void { resourceService.registerResourceProvider(mockResourceProvider.object); resourceService.areResourceProvidersLoaded = true; - appContext = new AppContext(undefined, new ApiWrapper()); + appContext = new AppContext(undefined); appContext.registerService(AzureResourceServiceNames.resourceService, resourceService); }); @@ -146,7 +145,7 @@ describe('AzureResourceResourceTreeNode.getChildren', function (): void { resourceService.registerResourceProvider(mockResourceProvider.object); resourceService.areResourceProvidersLoaded = true; - appContext = new AppContext(undefined, new ApiWrapper()); + appContext = new AppContext(undefined); appContext.registerService(AzureResourceServiceNames.resourceService, resourceService); }); diff --git a/extensions/azurecore/src/test/azureResource/tree/accountTreeNode.test.ts b/extensions/azurecore/src/test/azureResource/tree/accountTreeNode.test.ts index 0ad524d8fb..541d99afcd 100644 --- a/extensions/azurecore/src/test/azureResource/tree/accountTreeNode.test.ts +++ b/extensions/azurecore/src/test/azureResource/tree/accountTreeNode.test.ts @@ -7,6 +7,7 @@ import * as should from 'should'; import * as TypeMoq from 'typemoq'; import * as azdata from 'azdata'; import * as vscode from 'vscode'; +import * as sinon from 'sinon'; import 'mocha'; import { TokenCredentials } from '@azure/ms-rest-js'; import { AppContext } from '../../../appContext'; @@ -23,18 +24,16 @@ import { AzureResourceAccountTreeNode } from '../../../azureResource/tree/accoun import { AzureResourceSubscriptionTreeNode } from '../../../azureResource/tree/subscriptionTreeNode'; import { AzureResourceItemType, AzureResourceServiceNames } from '../../../azureResource/constants'; import { AzureResourceMessageTreeNode } from '../../../azureResource/messageTreeNode'; -import { ApiWrapper } from '../../../apiWrapper'; import { generateGuid } from '../../../azureResource/utils'; // Mock services let mockExtensionContext: TypeMoq.IMock; -let mockApiWrapper: TypeMoq.IMock; let mockCacheService: TypeMoq.IMock; let mockSubscriptionService: TypeMoq.IMock; let mockSubscriptionFilterService: TypeMoq.IMock; let mockTenantService: TypeMoq.IMock; let mockAppContext: AppContext; - +let getSecurityTokenStub: sinon.SinonStub; let mockTreeChangeHandler: TypeMoq.IMock; // Mock test data @@ -91,7 +90,6 @@ let mockSubscriptionCache: azureResource.AzureResourceSubscription[] = []; describe('AzureResourceAccountTreeNode.info', function (): void { beforeEach(() => { mockExtensionContext = TypeMoq.Mock.ofType(); - mockApiWrapper = TypeMoq.Mock.ofType(); mockCacheService = TypeMoq.Mock.ofType(); mockSubscriptionService = TypeMoq.Mock.ofType(); mockSubscriptionFilterService = TypeMoq.Mock.ofType(); @@ -101,19 +99,23 @@ describe('AzureResourceAccountTreeNode.info', function (): void { mockSubscriptionCache = []; - mockAppContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object); + mockAppContext = new AppContext(mockExtensionContext.object); mockAppContext.registerService(AzureResourceServiceNames.cacheService, mockCacheService.object); mockAppContext.registerService(AzureResourceServiceNames.subscriptionService, mockSubscriptionService.object); mockAppContext.registerService(AzureResourceServiceNames.subscriptionFilterService, mockSubscriptionFilterService.object); mockAppContext.registerService(AzureResourceServiceNames.tenantService, mockTenantService.object); - mockApiWrapper.setup((o) => o.getSecurityToken(mockAccount, azdata.AzureResource.ResourceManagement)).returns(() => Promise.resolve(mockTokens)); + getSecurityTokenStub = sinon.stub(azdata.accounts, 'getSecurityToken').returns(Promise.resolve(mockTokens)); mockCacheService.setup((o) => o.generateKey(TypeMoq.It.isAnyString())).returns(() => generateGuid()); mockCacheService.setup((o) => o.get(TypeMoq.It.isAnyString())).returns(() => mockSubscriptionCache); mockCacheService.setup((o) => o.update(TypeMoq.It.isAnyString(), TypeMoq.It.isAny())).returns(() => mockSubscriptionCache = mockSubscriptions); mockTenantService.setup((o) => o.getTenantId(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(mockTenantId)); }); + afterEach(function (): void { + sinon.restore(); + }); + it('Should be correct when created.', async function (): Promise { const accountTreeNode = new AzureResourceAccountTreeNode(mockAccount, mockAppContext, mockTreeChangeHandler.object); @@ -179,7 +181,6 @@ describe('AzureResourceAccountTreeNode.info', function (): void { describe('AzureResourceAccountTreeNode.getChildren', function (): void { beforeEach(() => { mockExtensionContext = TypeMoq.Mock.ofType(); - mockApiWrapper = TypeMoq.Mock.ofType(); mockCacheService = TypeMoq.Mock.ofType(); mockSubscriptionService = TypeMoq.Mock.ofType(); mockSubscriptionFilterService = TypeMoq.Mock.ofType(); @@ -189,19 +190,23 @@ describe('AzureResourceAccountTreeNode.getChildren', function (): void { mockSubscriptionCache = []; - mockAppContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object); + mockAppContext = new AppContext(mockExtensionContext.object); mockAppContext.registerService(AzureResourceServiceNames.cacheService, mockCacheService.object); mockAppContext.registerService(AzureResourceServiceNames.subscriptionService, mockSubscriptionService.object); mockAppContext.registerService(AzureResourceServiceNames.subscriptionFilterService, mockSubscriptionFilterService.object); mockAppContext.registerService(AzureResourceServiceNames.tenantService, mockTenantService.object); - mockApiWrapper.setup((o) => o.getSecurityToken(mockAccount, azdata.AzureResource.ResourceManagement)).returns(() => Promise.resolve(mockTokens)); + sinon.stub(azdata.accounts, 'getSecurityToken').returns(Promise.resolve(mockTokens)); mockCacheService.setup((o) => o.generateKey(TypeMoq.It.isAnyString())).returns(() => generateGuid()); mockCacheService.setup((o) => o.get(TypeMoq.It.isAnyString())).returns(() => mockSubscriptionCache); mockCacheService.setup((o) => o.update(TypeMoq.It.isAnyString(), TypeMoq.It.isAny())).returns(() => mockSubscriptionCache = mockSubscriptions); mockTenantService.setup((o) => o.getTenantId(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(mockTenantId)); }); + afterEach(function (): void { + sinon.restore(); + }); + it('Should load subscriptions from scratch and update cache when it is clearing cache.', async function (): Promise { mockSubscriptionService.setup((o) => o.getSubscriptions(mockAccount, mockCredential)).returns(() => Promise.resolve(mockSubscriptions)); mockSubscriptionFilterService.setup((o) => o.getSelectedSubscriptions(mockAccount)).returns(() => Promise.resolve([])); @@ -300,7 +305,7 @@ describe('AzureResourceAccountTreeNode.getChildren', function (): void { const children = await accountTreeNode.getChildren(); - mockApiWrapper.verify((o) => o.getSecurityToken(mockAccount, azdata.AzureResource.ResourceManagement), TypeMoq.Times.once()); + should(getSecurityTokenStub.calledOnce).be.true('getSecurityToken should have been called exactly once'); mockSubscriptionService.verify((o) => o.getSubscriptions(mockAccount, mockCredential), TypeMoq.Times.once()); mockSubscriptionFilterService.verify((o) => o.getSelectedSubscriptions(mockAccount), TypeMoq.Times.once()); mockCacheService.verify((o) => o.get(TypeMoq.It.isAnyString()), TypeMoq.Times.never()); @@ -317,7 +322,6 @@ describe('AzureResourceAccountTreeNode.getChildren', function (): void { describe('AzureResourceAccountTreeNode.clearCache', function (): void { beforeEach(() => { mockExtensionContext = TypeMoq.Mock.ofType(); - mockApiWrapper = TypeMoq.Mock.ofType(); mockCacheService = TypeMoq.Mock.ofType(); mockSubscriptionService = TypeMoq.Mock.ofType(); mockSubscriptionFilterService = TypeMoq.Mock.ofType(); @@ -327,19 +331,23 @@ describe('AzureResourceAccountTreeNode.clearCache', function (): void { mockSubscriptionCache = []; - mockAppContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object); + mockAppContext = new AppContext(mockExtensionContext.object); mockAppContext.registerService(AzureResourceServiceNames.cacheService, mockCacheService.object); mockAppContext.registerService(AzureResourceServiceNames.subscriptionService, mockSubscriptionService.object); mockAppContext.registerService(AzureResourceServiceNames.subscriptionFilterService, mockSubscriptionFilterService.object); mockAppContext.registerService(AzureResourceServiceNames.tenantService, mockTenantService.object); - mockApiWrapper.setup((o,) => o.getSecurityToken(mockAccount, azdata.AzureResource.ResourceManagement)).returns(() => Promise.resolve(mockTokens)); + sinon.stub(azdata.accounts, 'getSecurityToken').returns(Promise.resolve(mockTokens)); mockCacheService.setup((o) => o.generateKey(TypeMoq.It.isAnyString())).returns(() => generateGuid()); mockCacheService.setup((o) => o.get(TypeMoq.It.isAnyString())).returns(() => mockSubscriptionCache); mockCacheService.setup((o) => o.update(TypeMoq.It.isAnyString(), TypeMoq.It.isAny())).returns(() => mockSubscriptionCache = mockSubscriptions); mockTenantService.setup((o) => o.getTenantId(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(mockTenantId)); }); + afterEach(function (): void { + sinon.restore(); + }); + it('Should clear cache.', async function (): Promise { const accountTreeNode = new AzureResourceAccountTreeNode(mockAccount, mockAppContext, mockTreeChangeHandler.object); accountTreeNode.clearCache(); diff --git a/extensions/azurecore/src/test/azureResource/tree/subscriptionTreeNode.test.ts b/extensions/azurecore/src/test/azureResource/tree/subscriptionTreeNode.test.ts index d0b627dcd4..49ce2c37b4 100644 --- a/extensions/azurecore/src/test/azureResource/tree/subscriptionTreeNode.test.ts +++ b/extensions/azurecore/src/test/azureResource/tree/subscriptionTreeNode.test.ts @@ -9,7 +9,6 @@ import * as azdata from 'azdata'; import * as vscode from 'vscode'; import 'mocha'; import { AppContext } from '../../../appContext'; -import { ApiWrapper } from '../../../apiWrapper'; import { azureResource } from '../../../azureResource/azure-resource'; import { IAzureResourceTreeChangeHandler } from '../../../azureResource/tree/treeChangeHandler'; @@ -24,7 +23,6 @@ import { generateGuid } from '../../../azureResource/utils'; let appContext: AppContext; let mockExtensionContext: TypeMoq.IMock; -let mockApiWrapper: TypeMoq.IMock; let mockCacheService: TypeMoq.IMock; let mockTreeChangeHandler: TypeMoq.IMock; @@ -63,7 +61,6 @@ const resourceService: AzureResourceService = new AzureResourceService(); describe('AzureResourceSubscriptionTreeNode.info', function(): void { beforeEach(() => { mockExtensionContext = TypeMoq.Mock.ofType(); - mockApiWrapper = TypeMoq.Mock.ofType(); mockCacheService = TypeMoq.Mock.ofType(); mockCacheService.setup((o) => o.generateKey(TypeMoq.It.isAnyString())).returns(() => generateGuid()); @@ -89,7 +86,7 @@ describe('AzureResourceSubscriptionTreeNode.info', function(): void { resourceService.registerResourceProvider(mockResourceProvider2.object); resourceService.areResourceProvidersLoaded = true; - appContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object); + appContext = new AppContext(mockExtensionContext.object); appContext.registerService(AzureResourceServiceNames.cacheService, mockCacheService.object); appContext.registerService(AzureResourceServiceNames.resourceService, resourceService); @@ -116,7 +113,6 @@ describe('AzureResourceSubscriptionTreeNode.info', function(): void { describe('AzureResourceSubscriptionTreeNode.getChildren', function(): void { beforeEach(() => { mockExtensionContext = TypeMoq.Mock.ofType(); - mockApiWrapper = TypeMoq.Mock.ofType(); mockCacheService = TypeMoq.Mock.ofType(); mockCacheService.setup((o) => o.generateKey(TypeMoq.It.isAnyString())).returns(() => generateGuid()); @@ -142,7 +138,7 @@ describe('AzureResourceSubscriptionTreeNode.getChildren', function(): void { resourceService.registerResourceProvider(mockResourceProvider2.object); resourceService.areResourceProvidersLoaded = true; - appContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object); + appContext = new AppContext(mockExtensionContext.object); appContext.registerService(AzureResourceServiceNames.cacheService, mockCacheService.object); appContext.registerService(AzureResourceServiceNames.resourceService, resourceService); diff --git a/extensions/azurecore/src/test/azureResource/tree/treeProvider.test.ts b/extensions/azurecore/src/test/azureResource/tree/treeProvider.test.ts index 0e84d406bf..ad3a2e61e0 100644 --- a/extensions/azurecore/src/test/azureResource/tree/treeProvider.test.ts +++ b/extensions/azurecore/src/test/azureResource/tree/treeProvider.test.ts @@ -7,11 +7,11 @@ import * as vscode from 'vscode'; import * as should from 'should'; import * as TypeMoq from 'typemoq'; import * as azdata from 'azdata'; +import * as sinon from 'sinon'; import 'mocha'; import { AppContext } from '../../../appContext'; -import { ApiWrapper } from '../../../apiWrapper'; -import { IAzureResourceCacheService, IAzureResourceAccountService } from '../../../azureResource/interfaces'; +import { IAzureResourceCacheService } from '../../../azureResource/interfaces'; import { AzureResourceTreeProvider } from '../../../azureResource/tree/treeProvider'; import { AzureResourceAccountTreeNode } from '../../../azureResource/tree/accountTreeNode'; import { AzureResourceAccountNotSignedInTreeNode } from '../../../azureResource/tree/accountNotSignedInTreeNode'; @@ -22,9 +22,7 @@ import { generateGuid } from '../../../azureResource/utils'; let mockAppContext: AppContext; let mockExtensionContext: TypeMoq.IMock; -let mockApiWrapper: TypeMoq.IMock; let mockCacheService: TypeMoq.IMock; -let mockAccountService: TypeMoq.IMock; // Mock test data const mockAccount1: azdata.Account = { @@ -60,27 +58,28 @@ const mockAccounts = [mockAccount1, mockAccount2]; describe('AzureResourceTreeProvider.getChildren', function (): void { beforeEach(() => { mockExtensionContext = TypeMoq.Mock.ofType(); - mockApiWrapper = TypeMoq.Mock.ofType(); mockCacheService = TypeMoq.Mock.ofType(); - mockAccountService = TypeMoq.Mock.ofType(); - mockAppContext = new AppContext(mockExtensionContext.object, mockApiWrapper.object); + mockAppContext = new AppContext(mockExtensionContext.object); mockAppContext.registerService(AzureResourceServiceNames.cacheService, mockCacheService.object); - mockAppContext.registerService(AzureResourceServiceNames.accountService, mockAccountService.object); mockCacheService.setup((o) => o.generateKey(TypeMoq.It.isAnyString())).returns(() => generateGuid()); }); + afterEach(function(): void { + sinon.restore(); + }); + it('Should load accounts.', async function (): Promise { - mockAccountService.setup((o) => o.getAccounts()).returns(() => Promise.resolve(mockAccounts)); + const getAllAccountsStub = sinon.stub(azdata.accounts, 'getAllAccounts').returns(Promise.resolve(mockAccounts)); const treeProvider = new AzureResourceTreeProvider(mockAppContext); await treeProvider.getChildren(undefined); // Load account promise const children = await treeProvider.getChildren(undefined); // Actual accounts - mockAccountService.verify((o) => o.getAccounts(), TypeMoq.Times.once()); + should(getAllAccountsStub.calledOnce).be.true('getAllAccounts should have been called exactly once'); should(children).Array(); should(children.length).equal(mockAccounts.length); @@ -94,7 +93,7 @@ describe('AzureResourceTreeProvider.getChildren', function (): void { }); it('Should handle when there is no accounts.', async function (): Promise { - mockAccountService.setup((o) => o.getAccounts()).returns(() => Promise.resolve(undefined)); + sinon.stub(azdata.accounts, 'getAllAccounts').returns(Promise.resolve(undefined)); const treeProvider = new AzureResourceTreeProvider(mockAppContext); treeProvider.isSystemInitialized = true; diff --git a/extensions/azurecore/yarn.lock b/extensions/azurecore/yarn.lock index afd5436703..b1e534f75b 100644 --- a/extensions/azurecore/yarn.lock +++ b/extensions/azurecore/yarn.lock @@ -222,6 +222,42 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== +"@sinonjs/commons@^1", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.7.2": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.0.tgz#c8d68821a854c555bba172f3b06959a0039b236d" + integrity sha512-wEj54PfsZ5jGSwMX68G8ZXFawcSglQSXqCftWX3ec8MDUzQdHgcKvw97awHbY0efQEL5iKUOAmmVtoYgmrSG4Q== + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^6.0.0", "@sinonjs/fake-timers@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@sinonjs/formatio@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-5.0.1.tgz#f13e713cb3313b1ab965901b01b0828ea6b77089" + integrity sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ== + dependencies: + "@sinonjs/commons" "^1" + "@sinonjs/samsam" "^5.0.2" + +"@sinonjs/samsam@^5.0.2", "@sinonjs/samsam@^5.0.3": + version "5.0.3" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.0.3.tgz#86f21bdb3d52480faf0892a480c9906aa5a52938" + integrity sha512-QucHkc2uMJ0pFGjJUDP3F9dq5dx8QIaqISl9QgwLOh6P9yv877uONPGXh/OH/0zmM3tW1JjuJltAZV2l7zU+uQ== + dependencies: + "@sinonjs/commons" "^1.6.0" + lodash.get "^4.4.2" + type-detect "^4.0.8" + +"@sinonjs/text-encoding@^0.7.1": + version "0.7.1" + resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" + integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== + "@types/caseless@*": version "0.12.2" resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8" @@ -264,6 +300,18 @@ "@types/tough-cookie" "*" form-data "^2.5.0" +"@types/sinon@^9.0.4": + version "9.0.4" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.4.tgz#e934f904606632287a6e7f7ab0ce3f08a0dad4b1" + integrity sha512-sJmb32asJZY6Z2u09bl0G2wglSxDlROlAejCjsnor+LzBMz17gu8IU7vKC/vWDnv9zEq2wqADHVXFjf4eE8Gdw== + dependencies: + "@types/sinonjs__fake-timers" "*" + +"@types/sinonjs__fake-timers@*": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz#681df970358c82836b42f989188d133e218c458e" + integrity sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA== + "@types/tough-cookie@*": version "2.3.6" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.6.tgz#c880579e087d7a0db13777ff8af689f4ffc7b0d5" @@ -531,6 +579,11 @@ diff@3.5.0: resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== +diff@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -697,6 +750,11 @@ is-fullwidth-code-point@^2.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -777,6 +835,11 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" +just-extend@^4.0.2: + version "4.1.0" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.1.0.tgz#7278a4027d889601640ee0ce0e5a00b992467da4" + integrity sha512-ApcjaOdVTJ7y4r08xI5wIqpvwS48Q0PBG4DJROcEkH1f8MdAiNFyFxz3xoL0LWAVwjrwPYZdVHHxhRHcx/uGLA== + keytar@*: version "5.4.0" resolved "https://registry.yarnpkg.com/keytar/-/keytar-5.4.0.tgz#71d8209e7dd2fe99008c243791350a6bd6ceab67" @@ -785,6 +848,11 @@ keytar@*: nan "2.14.0" prebuild-install "5.3.3" +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= + lodash@^4.16.4, lodash@^4.17.13, lodash@^4.17.4: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" @@ -923,6 +991,17 @@ napi-build-utils@^1.0.1: resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== +nise@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/nise/-/nise-4.0.4.tgz#d73dea3e5731e6561992b8f570be9e363c4512dd" + integrity sha512-bTTRUNlemx6deJa+ZyoCUTRvH3liK5+N6VQZ4NIw90AgDXY6iPnsqplNFf6STcj+ePk0H/xqxnP75Lr0J0Fq3A== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@sinonjs/fake-timers" "^6.0.0" + "@sinonjs/text-encoding" "^0.7.1" + just-extend "^4.0.2" + path-to-regexp "^1.7.0" + node-abi@^2.7.0: version "2.15.0" resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.15.0.tgz#51d55cc711bd9e4a24a572ace13b9231945ccb10" @@ -972,6 +1051,13 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-to-regexp@^1.7.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" + integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA== + dependencies: + isarray "0.0.1" + pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" @@ -1170,6 +1256,19 @@ simple-get@^3.0.3: once "^1.3.1" simple-concat "^1.0.0" +sinon@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.0.2.tgz#b9017e24633f4b1c98dfb6e784a5f0509f5fd85d" + integrity sha512-0uF8Q/QHkizNUmbK3LRFqx5cpTttEVXudywY9Uwzy8bTfZUhljZ7ARzSxnRHWYWtVTeh4Cw+tTb3iU21FQVO9A== + dependencies: + "@sinonjs/commons" "^1.7.2" + "@sinonjs/fake-timers" "^6.0.1" + "@sinonjs/formatio" "^5.0.1" + "@sinonjs/samsam" "^5.0.3" + diff "^4.0.2" + nise "^4.0.1" + supports-color "^7.1.0" + source-map@^0.5.0: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -1307,6 +1406,11 @@ tunnel@0.0.6: resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== +type-detect@4.0.8, type-detect@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + typemoq@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/typemoq/-/typemoq-2.1.0.tgz#4452ce360d92cf2a1a180f0c29de2803f87af1e8"