Inital platform relayering (#6385)

* moving test files and inital refactoring

* relayer extension host code

* fix imports

* make insights work

* relayer dashboard

* relayer notebooks

* moveing more code around

* formatting

* accept angular as browser

* fix serializer

* add missing files

* remove declarations from extensions

* fix build errors

* more relayering

* change urls to relative to help code relayering

* remove layering to prep for merge

* fix hygiene errors

* fix hygiene errors

* fix tests
This commit is contained in:
Anthony Dresser
2019-07-18 17:29:17 -07:00
committed by GitHub
parent 45c13116de
commit c23738f935
576 changed files with 2090 additions and 2788 deletions

View File

@@ -32,7 +32,7 @@ import { AddAccountAction } from 'sql/platform/accounts/common/accountActions';
import { AccountListRenderer, AccountListDelegate } from 'sql/platform/accounts/browser/accountListRenderer';
import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/platform/accounts/common/eventTypes';
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import { ILogService } from 'vs/platform/log/common/log';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';

View File

@@ -19,7 +19,7 @@ import { Modal } from 'sql/workbench/browser/modal/modal';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
import { attachModalDialogStyler, attachButtonStyler } from 'sql/platform/theme/common/styler';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { ILogService } from 'vs/platform/log/common/log';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';

View File

@@ -26,7 +26,7 @@ import { FirewallRuleViewModel } from 'sql/platform/accounts/common/firewallRule
import { attachModalDialogStyler, attachButtonStyler } from 'sql/platform/theme/common/styler';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
import { IAccountPickerService } from 'sql/platform/accounts/common/accountPicker';
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import { ILogService } from 'vs/platform/log/common/log';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';

View File

@@ -0,0 +1,103 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as TypeMoq from 'typemoq';
import { Emitter } from 'vs/base/common/event';
import { AccountDialog } from 'sql/platform/accounts/browser/accountDialog';
import { AccountDialogController } from 'sql/platform/accounts/browser/accountDialogController';
import { AccountViewModel } from 'sql/platform/accounts/common/accountViewModel';
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
import { TestErrorMessageService } from 'sql/platform/errorMessage/test/common/testErrorMessageService';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { AccountListRenderer } from 'sql/platform/accounts/browser/accountListRenderer';
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
// TESTS ///////////////////////////////////////////////////////////////////
suite('Account Management Dialog Controller Tests', () => {
test('Open Account Dialog - Dialog Doesn\'t Exist', () => {
// Setup: Create instance of the controller
let instantiationService = createInstantiationService();
let controller = new AccountDialogController(instantiationService, undefined);
assert.strictEqual(controller.accountDialog, undefined);
// If: I open the account dialog when one hasn't been opened
controller.openAccountDialog();
// Then:
// ... The account dialog should be defined
assert.notStrictEqual(controller.accountDialog, undefined);
});
test('Open Account Dialog - Dialog Exists', () => {
// Setup: Create instance of the controller with an account dialog already loaded
let instantiationService = createInstantiationService();
let controller = new AccountDialogController(instantiationService, undefined);
controller.openAccountDialog();
let accountDialog = controller.accountDialog;
// If: I open the account dialog when one has already been opened
controller.openAccountDialog();
// Then: It should be the same dialog that already existed
assert.equal(controller.accountDialog, accountDialog);
});
test('Add Account Failure - Error Message Shown', () => {
// Setup:
// ... Create instantiation service that returns mock emitter for account dialog
let mockEventEmitter = new Emitter<string>();
let instantiationService = createInstantiationService(mockEventEmitter);
// ... Create a mock instance of the error message service
let errorMessageServiceStub = new TestErrorMessageService();
let mockErrorMessageService = TypeMoq.Mock.ofInstance(errorMessageServiceStub);
mockErrorMessageService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()));
// ... Create instance of the controller with an opened dialog
let controller = new AccountDialogController(instantiationService, mockErrorMessageService.object);
controller.openAccountDialog();
// If: The account dialog reports a failure adding an account
mockEventEmitter.fire('Error message');
// Then: An error dialog should have been opened
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
});
});
function createInstantiationService(addAccountFailureEmitter?: Emitter<string>): InstantiationService {
// Create a mock account dialog view model
let accountViewModel = new AccountViewModel(new TestAccountManagementService());
let mockAccountViewModel = TypeMoq.Mock.ofInstance(accountViewModel);
let mockEvent = new Emitter<any>();
mockAccountViewModel.setup(x => x.addProviderEvent).returns(() => mockEvent.event);
mockAccountViewModel.setup(x => x.removeProviderEvent).returns(() => mockEvent.event);
mockAccountViewModel.setup(x => x.updateAccountListEvent).returns(() => mockEvent.event);
mockAccountViewModel.setup(x => x.initialize()).returns(() => Promise.resolve([]));
// Create a mocked out instantiation service
let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Strict);
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountViewModel)))
.returns(() => mockAccountViewModel.object);
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountListRenderer)))
.returns(() => undefined);
// Create a mock account dialog
let accountDialog = new AccountDialog(null, null, instantiationService.object, null, null, null, null, new MockContextKeyService(), null, undefined);
let mockAccountDialog = TypeMoq.Mock.ofInstance(accountDialog);
mockAccountDialog.setup(x => x.onAddAccountErrorEvent)
.returns(() => { return addAccountFailureEmitter ? addAccountFailureEmitter.event : mockEvent.event; });
mockAccountDialog.setup(x => x.onCloseEvent)
.returns(() => mockEvent.event);
mockAccountDialog.setup(x => x.render())
.returns(() => undefined);
mockAccountDialog.setup(x => x.open())
.returns(() => undefined);
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountDialog)))
.returns(() => mockAccountDialog.object);
return instantiationService.object;
}

View File

@@ -0,0 +1,120 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as assert from 'assert';
import * as TypeMoq from 'typemoq';
import { EventVerifierSingle } from 'sqltest/utils/eventVerifier';
import { Emitter } from 'vs/base/common/event';
import { AccountPicker } from 'sql/platform/accounts/browser/accountPicker';
import { AccountPickerService } from 'sql/platform/accounts/browser/accountPickerService';
import { AccountPickerViewModel } from 'sql/platform/accounts/common/accountPickerViewModel';
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
// SUITE STATE /////////////////////////////////////////////////////////////
let mockAddAccountCompleteEmitter: Emitter<void>;
let mockAddAccountErrorEmitter: Emitter<string>;
let mockAddAccountStartEmitter: Emitter<void>;
let mockOnAccountSelectionChangeEvent: Emitter<azdata.Account>;
// TESTS ///////////////////////////////////////////////////////////////////
suite('Account picker service tests', () => {
setup(() => {
// Setup event mocks for the account picker service
mockAddAccountCompleteEmitter = new Emitter<void>();
mockAddAccountErrorEmitter = new Emitter<string>();
mockAddAccountStartEmitter = new Emitter<void>();
mockOnAccountSelectionChangeEvent = new Emitter<azdata.Account>();
});
test('Construction - Events are properly defined', () => {
// Setup:
// ... Create instantiation service
let instantiationService = createInstantiationService();
// ... Create instance of the service and reder account picker
let service = new AccountPickerService(instantiationService);
service.renderAccountPicker(TypeMoq.It.isAny());
// Then:
// ... All the events for the view models should be properly initialized
assert.notEqual(service.addAccountCompleteEvent, undefined);
assert.notEqual(service.addAccountErrorEvent, undefined);
assert.notEqual(service.addAccountStartEvent, undefined);
assert.notEqual(service.onAccountSelectionChangeEvent, undefined);
// ... All the events should properly fire
let evAddAccountCompleteEvent = new EventVerifierSingle<void>();
service.addAccountCompleteEvent(evAddAccountCompleteEvent.eventHandler);
mockAddAccountCompleteEmitter.fire();
evAddAccountCompleteEvent.assertFired();
let errorMsg = 'Error';
let evAddAccountErrorEvent = new EventVerifierSingle<string>();
service.addAccountErrorEvent(evAddAccountErrorEvent.eventHandler);
mockAddAccountErrorEmitter.fire(errorMsg);
evAddAccountErrorEvent.assertFired(errorMsg);
let evAddAccountStartEvent = new EventVerifierSingle<void>();
service.addAccountStartEvent(evAddAccountStartEvent.eventHandler);
mockAddAccountStartEmitter.fire();
evAddAccountStartEvent.assertFired();
let account = {
key: { providerId: 'azure', accountId: 'account1' },
name: 'Account 1',
displayInfo: {
contextualDisplayName: 'Microsoft Account',
accountType: 'microsoft',
displayName: 'Account 1',
userId: 'user@email.com'
},
properties: [],
isStale: false
};
let evOnAccountSelectionChangeEvent = new EventVerifierSingle<azdata.Account>();
service.onAccountSelectionChangeEvent(evOnAccountSelectionChangeEvent.eventHandler);
mockOnAccountSelectionChangeEvent.fire(account);
evOnAccountSelectionChangeEvent.assertFired(account);
});
});
function createInstantiationService(): InstantiationService {
// Create a mock account picker view model
let providerId = 'azure';
let accountPickerViewModel = new AccountPickerViewModel(providerId, new TestAccountManagementService());
let mockAccountViewModel = TypeMoq.Mock.ofInstance(accountPickerViewModel);
let mockEvent = new Emitter<any>();
mockAccountViewModel.setup(x => x.updateAccountListEvent).returns(() => mockEvent.event);
// Create a mocked out instantiation service
let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Strict);
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountPickerViewModel), TypeMoq.It.isAny()))
.returns(() => mockAccountViewModel.object);
// Create a mock account picker
let accountPicker = new AccountPicker(null, null, instantiationService.object, null);
let mockAccountDialog = TypeMoq.Mock.ofInstance(accountPicker);
mockAccountDialog.setup(x => x.addAccountCompleteEvent)
.returns(() => mockAddAccountCompleteEmitter.event);
mockAccountDialog.setup(x => x.addAccountErrorEvent)
.returns((msg) => mockAddAccountErrorEmitter.event);
mockAccountDialog.setup(x => x.addAccountStartEvent)
.returns(() => mockAddAccountStartEmitter.event);
mockAccountDialog.setup(x => x.onAccountSelectionChangeEvent)
.returns((account) => mockOnAccountSelectionChangeEvent.event);
mockAccountDialog.setup(x => x.render(TypeMoq.It.isAny()))
.returns((container) => undefined);
mockAccountDialog.setup(x => x.createAccountPickerComponent());
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountPicker), TypeMoq.It.isAny()))
.returns(() => mockAccountDialog.object);
return instantiationService.object;
}

View File

@@ -0,0 +1,139 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as TypeMoq from 'typemoq';
import { Emitter } from 'vs/base/common/event';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { AutoOAuthDialog } from 'sql/platform/accounts/browser/autoOAuthDialog';
import { AutoOAuthDialogController } from 'sql/platform/accounts/browser/autoOAuthDialogController';
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
import { TestErrorMessageService } from 'sql/platform/errorMessage/test/common/testErrorMessageService';
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
// TESTS ///////////////////////////////////////////////////////////////////
suite('auto OAuth dialog controller tests', () => {
let instantiationService: TypeMoq.Mock<InstantiationService>;
let mockAutoOAuthDialog: TypeMoq.Mock<AutoOAuthDialog>;
let mockAccountManagementService: TypeMoq.Mock<TestAccountManagementService>;
let mockErrorMessageService: TypeMoq.Mock<TestErrorMessageService>;
let autoOAuthDialogController: AutoOAuthDialogController;
let mockOnCancelEvent: Emitter<void>;
let mockOnAddAccountEvent: Emitter<void>;
let mockOnCloseEvent: Emitter<void>;
let providerId = 'azure';
let title = 'Add Account';
let message = 'This is the dialog description';
let userCode = 'abcde';
let uri = 'uri';
setup(() => {
mockOnCancelEvent = new Emitter<void>();
mockOnAddAccountEvent = new Emitter<void>();
mockOnCloseEvent = new Emitter<void>();
// Create a mock auto OAuth dialog
let autoOAuthDialog = new AutoOAuthDialog(null, null, null, null, new MockContextKeyService(), null, undefined);
mockAutoOAuthDialog = TypeMoq.Mock.ofInstance(autoOAuthDialog);
mockAutoOAuthDialog.setup(x => x.onCancel).returns(() => mockOnCancelEvent.event);
mockAutoOAuthDialog.setup(x => x.onHandleAddAccount).returns(() => mockOnAddAccountEvent.event);
mockAutoOAuthDialog.setup(x => x.onCloseEvent).returns(() => mockOnCloseEvent.event);
mockAutoOAuthDialog.setup(x => x.render());
mockAutoOAuthDialog.setup(x => x.open(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()));
mockAutoOAuthDialog.setup(x => x.close()).callback(() => {
mockOnCloseEvent.fire();
});
// Create a mocked out instantiation service
instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Strict);
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AutoOAuthDialog)))
.returns(() => mockAutoOAuthDialog.object);
// Create a mocked account management service
let accountManagementTestService = new TestAccountManagementService();
mockAccountManagementService = TypeMoq.Mock.ofInstance(accountManagementTestService);
mockAccountManagementService.setup(x => x.copyUserCodeAndOpenBrowser(TypeMoq.It.isAny(), TypeMoq.It.isAny()));
// Create a mocked error message service
let errorMessageServiceStub = new TestErrorMessageService();
mockErrorMessageService = TypeMoq.Mock.ofInstance(errorMessageServiceStub);
mockErrorMessageService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()));
// Create a mocked auto OAuth dialog controller
autoOAuthDialogController = new AutoOAuthDialogController(instantiationService.object, mockAccountManagementService.object, mockErrorMessageService.object);
});
test('Open auto OAuth when the flyout is already open, return an error', (done) => {
// If: Open auto OAuth dialog first time
autoOAuthDialogController.openAutoOAuthDialog(providerId, title, message, userCode, uri);
// Then: It should open the flyout successfully
mockAutoOAuthDialog.verify(x => x.open(title, message, userCode, uri), TypeMoq.Times.once());
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never());
// If: a oauth flyout is already open
autoOAuthDialogController.openAutoOAuthDialog(providerId, title, message, userCode, uri)
.then(success => done('Failure: Expected error on 2nd dialog open'), error => done());
// Then: An error dialog should have been opened
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
});
test('Close auto OAuth dialog successfully', () => {
let title = 'Add Account';
let message = 'This is the dialog description';
let userCode = 'abcde';
let uri = 'uri';
autoOAuthDialogController.openAutoOAuthDialog(providerId, title, message, userCode, uri);
// If: closeAutoOAuthDialog is called
autoOAuthDialogController.closeAutoOAuthDialog();
// Then: it should close the dialog
mockAutoOAuthDialog.verify(x => x.close(), TypeMoq.Times.once());
});
test('Open and close auto OAuth dialog multiple times should work properly', () => {
let title = 'Add Account';
let message = 'This is the dialog description';
let userCode = 'abcde';
let uri = 'uri';
autoOAuthDialogController.openAutoOAuthDialog(providerId, title, message, userCode, uri);
autoOAuthDialogController.closeAutoOAuthDialog();
// If: Open the flyout second time
autoOAuthDialogController.openAutoOAuthDialog(providerId, title, message, userCode, uri);
// Then: It should open the flyout twice successfully
mockAutoOAuthDialog.verify(x => x.open(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.exactly(2));
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never());
});
test('Copy and open button in auto OAuth dialog should work properly', () => {
let title = 'Add Account';
let message = 'This is the dialog description';
let userCode = 'abcde';
let uri = 'uri';
autoOAuthDialogController.openAutoOAuthDialog(providerId, title, message, userCode, uri);
// If: the 'copy & open' button in auto Oauth dialog is selected
mockOnAddAccountEvent.fire();
// Then: copyUserCodeAndOpenBrowser should get called
mockAccountManagementService.verify(x => x.copyUserCodeAndOpenBrowser(userCode, uri), TypeMoq.Times.once());
});
// TODO: Test for cancel button
});

View File

@@ -0,0 +1,256 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as TypeMoq from 'typemoq';
import { Emitter } from 'vs/base/common/event';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { FirewallRuleDialog } from 'sql/platform/accounts/browser/firewallRuleDialog';
import { FirewallRuleViewModel } from 'sql/platform/accounts/common/firewallRuleViewModel';
import { FirewallRuleDialogController } from 'sql/platform/accounts/browser/firewallRuleDialogController';
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
import { TestResourceProvider } from 'sql/workbench/services/resourceProvider/test/common/testResourceProviderService';
import { TestErrorMessageService } from 'sql/platform/errorMessage/test/common/testErrorMessageService';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { Deferred } from 'sql/base/common/promise';
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
// TESTS ///////////////////////////////////////////////////////////////////
suite('Firewall rule dialog controller tests', () => {
let connectionProfile: IConnectionProfile;
let account: azdata.Account;
let IPAddress = '250.222.155.198';
let mockOnAddAccountErrorEvent: Emitter<string>;
let mockOnCreateFirewallRule: Emitter<void>;
let instantiationService: TypeMoq.Mock<InstantiationService>;
let mockFirewallRuleViewModel: TypeMoq.Mock<FirewallRuleViewModel>;
let mockFirewallRuleDialog: TypeMoq.Mock<FirewallRuleDialog>;
setup(() => {
account = {
key: { providerId: 'azure', accountId: 'account1' },
displayInfo: {
contextualDisplayName: 'Microsoft Account',
accountType: 'microsoft',
displayName: 'Account 1',
userId: 'user@email.com'
},
properties: [],
isStale: false
};
mockOnAddAccountErrorEvent = new Emitter<string>();
mockOnCreateFirewallRule = new Emitter<void>();
// Create a mock firewall rule view model
let firewallRuleViewModel = new FirewallRuleViewModel();
mockFirewallRuleViewModel = TypeMoq.Mock.ofInstance(firewallRuleViewModel);
mockFirewallRuleViewModel.setup(x => x.updateDefaultValues(TypeMoq.It.isAny()))
.returns((ipAddress) => undefined);
mockFirewallRuleViewModel.object.selectedAccount = account;
mockFirewallRuleViewModel.object.isIPAddressSelected = true;
// Create a mocked out instantiation service
instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Strict);
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(FirewallRuleViewModel)))
.returns(() => mockFirewallRuleViewModel.object);
// Create a mock account picker
let firewallRuleDialog = new FirewallRuleDialog(null, null, null, instantiationService.object, null, null, new MockContextKeyService(), null, null, undefined);
mockFirewallRuleDialog = TypeMoq.Mock.ofInstance(firewallRuleDialog);
let mockEvent = new Emitter<any>();
mockFirewallRuleDialog.setup(x => x.onCancel)
.returns(() => mockEvent.event);
mockFirewallRuleDialog.setup(x => x.onCreateFirewallRule)
.returns(() => mockOnCreateFirewallRule.event);
mockFirewallRuleDialog.setup(x => x.onAddAccountErrorEvent)
.returns((msg) => mockOnAddAccountErrorEvent.event);
mockFirewallRuleDialog.setup(x => x.render());
mockFirewallRuleDialog.setup(x => x.open());
mockFirewallRuleDialog.setup(x => x.close());
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(FirewallRuleDialog)))
.returns(() => mockFirewallRuleDialog.object);
connectionProfile = {
connectionName: 'new name',
serverName: 'new server',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g2/g2-2',
groupId: 'group id',
getOptionsKey: undefined,
matches: undefined,
providerName: mssqlProviderName,
options: {},
saveProfile: true,
id: undefined
};
});
test('Add Account Failure - Error Message Shown', () => {
// ... Create a mock instance of the error message service
let errorMessageServiceStub = new TestErrorMessageService();
let mockErrorMessageService = TypeMoq.Mock.ofInstance(errorMessageServiceStub);
mockErrorMessageService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()));
// ... Create instance of the controller with an opened dialog
let controller = new FirewallRuleDialogController(instantiationService.object, null, null, mockErrorMessageService.object);
controller.openFirewallRuleDialog(connectionProfile, IPAddress, 'resourceID');
// If: The firewall rule dialog reports a failure
mockOnAddAccountErrorEvent.fire('Error message');
// Then: An error dialog should have been opened
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
});
test('create firewall rule success', (done) => {
let deferredPromise = new Deferred();
mockFirewallRuleDialog.setup(x => x.onServiceComplete())
.callback(() => {
deferredPromise.resolve(true);
});
// ... Create a mock instance of the account management test service
let mockAccountManagementService = getMockAccountManagementService(true);
// ... Create a mock instance of the resource provider
let mockResourceProvider = getMockResourceProvider(true, { result: true, errorMessage: '' });
// ... Create instance of the controller with an opened dialog
let controller = new FirewallRuleDialogController(instantiationService.object, mockResourceProvider.object, mockAccountManagementService.object, null);
controller.openFirewallRuleDialog(connectionProfile, IPAddress, 'resourceID');
// If: The firewall rule dialog's create firewall rule get fired
mockOnCreateFirewallRule.fire();
// Then: it should get security token from account management service and call create firewall rule in resource provider
deferredPromise.promise.then(() => {
mockAccountManagementService.verify(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
mockResourceProvider.verify(x => x.createFirewallRule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
mockFirewallRuleDialog.verify(x => x.close(), TypeMoq.Times.once());
mockFirewallRuleDialog.verify(x => x.onServiceComplete(), TypeMoq.Times.once());
done();
});
});
test('create firewall rule fails during getSecurity', (done) => {
let deferredPromise = new Deferred();
// ... Create a mock instance of the error message service
let mockErrorMessageService = getMockErrorMessageService(deferredPromise);
// ... Create a mock instance of the account management test service
let mockAccountManagementService = getMockAccountManagementService(false);
// ... Create a mock instance of the resource provider
let mockResourceProvider = getMockResourceProvider(true, { result: true, errorMessage: '' });
// ... Create instance of the controller with an opened dialog
let controller = new FirewallRuleDialogController(instantiationService.object, mockResourceProvider.object, mockAccountManagementService.object, mockErrorMessageService.object);
controller.openFirewallRuleDialog(connectionProfile, IPAddress, 'resourceID');
// If: The firewall rule dialog's create firewall rule get fired
mockOnCreateFirewallRule.fire();
// Then: it should get security token from account management service and an error dialog should have been opened
deferredPromise.promise.then(() => {
mockAccountManagementService.verify(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
mockResourceProvider.verify(x => x.createFirewallRule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never());
done();
});
});
test('create firewall rule fails during createFirewallRule in ResourceProvider - result is false', (done) => {
let deferredPromise = new Deferred();
// ... Create a mock instance of the error message service
let mockErrorMessageService = getMockErrorMessageService(deferredPromise);
// ... Create a mock instance of the account management test service
let mockAccountManagementService = getMockAccountManagementService(true);
// ... Create a mock instance of the resource provider
let mockResourceProvider = getMockResourceProvider(true, { result: false, errorMessage: '' });
// ... Create instance of the controller with an opened dialog
let controller = new FirewallRuleDialogController(instantiationService.object, mockResourceProvider.object, mockAccountManagementService.object, mockErrorMessageService.object);
controller.openFirewallRuleDialog(connectionProfile, IPAddress, 'resourceID');
// If: The firewall rule dialog's create firewall rule get fired
mockOnCreateFirewallRule.fire();
// Then: it should get security token from account management service and an error dialog should have been opened
deferredPromise.promise.then(() => {
mockAccountManagementService.verify(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
mockResourceProvider.verify(x => x.createFirewallRule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
done();
});
});
test('create firewall rule fails during createFirewallRule in ResourceProvider - reject promise', (done) => {
let deferredPromise = new Deferred();
// ... Create a mock instance of the error message service
let mockErrorMessageService = getMockErrorMessageService(deferredPromise);
// ... Create a mock instance of the account management test service
let mockAccountManagementService = getMockAccountManagementService(true);
// ... Create a mock instance of the resource provider
let mockResourceProvider = getMockResourceProvider(false);
// ... Create instance of the controller with an opened dialog
let controller = new FirewallRuleDialogController(instantiationService.object, mockResourceProvider.object, mockAccountManagementService.object, mockErrorMessageService.object);
controller.openFirewallRuleDialog(connectionProfile, IPAddress, 'resourceID');
// If: The firewall rule dialog's create firewall rule get fired
mockOnCreateFirewallRule.fire();
// Then: it should get security token from account management service and an error dialog should have been opened
deferredPromise.promise.then(() => {
mockAccountManagementService.verify(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
mockResourceProvider.verify(x => x.createFirewallRule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
done();
});
});
});
function getMockAccountManagementService(resolveSecurityToken: boolean): TypeMoq.Mock<TestAccountManagementService> {
let accountManagementTestService = new TestAccountManagementService();
let mockAccountManagementService = TypeMoq.Mock.ofInstance(accountManagementTestService);
mockAccountManagementService.setup(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.returns(() => resolveSecurityToken ? Promise.resolve({}) : Promise.reject(null).then());
return mockAccountManagementService;
}
function getMockResourceProvider(resolveCreateFirewallRule: boolean, response?: azdata.CreateFirewallRuleResponse): TypeMoq.Mock<TestResourceProvider> {
let resourceProviderStub = new TestResourceProvider();
let mockResourceProvider = TypeMoq.Mock.ofInstance(resourceProviderStub);
mockResourceProvider.setup(x => x.createFirewallRule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.returns(() => resolveCreateFirewallRule ? Promise.resolve(response) : Promise.reject(null).then());
return mockResourceProvider;
}
function getMockErrorMessageService(deferredPromise: Deferred<{}>): TypeMoq.Mock<TestErrorMessageService> {
let errorMessageServiceStub = new TestErrorMessageService();
let mockErrorMessageService = TypeMoq.Mock.ofInstance(errorMessageServiceStub);
mockErrorMessageService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).callback(() => {
deferredPromise.resolve(true);
});
return mockErrorMessageService;
}

View File

@@ -0,0 +1,190 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as azdata from 'azdata';
import * as TypeMoq from 'typemoq';
import { AddAccountAction, RemoveAccountAction } from 'sql/platform/accounts/common/accountActions';
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
// import { MessageServiceStub } from 'sqltest/stubs/messageServiceStub';
import { TestErrorMessageService } from 'sql/platform/errorMessage/test/common/testErrorMessageService';
let testAccount = <azdata.Account>{
key: {
providerId: 'azure',
accountId: 'testAccount'
},
displayInfo: {
accountType: 'test',
displayName: 'Test Account',
contextualDisplayName: 'Azure Account'
},
isStale: false
};
suite('Account Management Dialog Actions Tests', () => {
test('AddAccount - Success', (done) => {
done();
// // Setup: Create an AddAccountAction object
// let param = 'azure';
// let mocks = createAddAccountAction(true, true, param);
// // If: I run the action when it will resolve
// mocks.action.run()
// .then(result => {
// // Then:
// // ... I should have gotten true back
// assert.ok(result);
// // ... The account management service should have gotten a add account request
// mocks.accountMock.verify(x => x.addAccount(param), TypeMoq.Times.once());
// })
// .then(
// () => done(),
// err => done(err)
// );
});
// test('AddAccount - Failure', (done) => {
// // // Setup: Create an AddAccountAction object
// // let param = 'azure';
// // let mocks = createAddAccountAction(false, true, param);
// // // If: I run the action when it will reject
// // mocks.action.run().then(result => {
// // // Then:
// // // ... The result should be false since the operation failed
// // assert.ok(!result);
// // // ... The account management service should have gotten a add account request
// // mocks.accountMock.verify(x => x.addAccount(param), TypeMoq.Times.once());
// // done();
// // }, error => {
// // // Should fail as rejected actions cause the debugger to crash
// // done(error);
// // });
// });
// test('RemoveAccount - Confirm Success', (done) => {
// // // Setup: Create an AddAccountAction object
// // let ams = getMockAccountManagementService(true);
// // let ms = getMockMessageService(true);
// // let es = getMockErrorMessageService();
// // let action = new RemoveAccountAction(testAccount, ms.object, es.object, ams.object);
// // // If: I run the action when it will resolve
// // action.run()
// // .then(result => {
// // // Then:
// // // ... I should have gotten true back
// // assert.ok(result);
// // // ... A confirmation dialog should have opened
// // ms.verify(x => x.confirm(TypeMoq.It.isAny()), TypeMoq.Times.once());
// // // ... The account management service should have gotten a remove account request
// // ams.verify(x => x.removeAccount(TypeMoq.It.isValue(testAccount.key)), TypeMoq.Times.once());
// // })
// // .then(
// // () => done(),
// // err => done(err)
// // );
// });
// test('RemoveAccount - Declined Success', (done) => {
// // // Setup: Create an AddAccountAction object
// // let ams = getMockAccountManagementService(true);
// // let ms = getMockMessageService(false);
// // let es = getMockErrorMessageService();
// // let action = new RemoveAccountAction(testAccount, ms.object, es.object, ams.object);
// // // If: I run the action when it will resolve
// // action.run()
// // .then(result => {
// // try {
// // // Then:
// // // ... I should have gotten false back
// // assert.ok(!result);
// // // ... A confirmation dialog should have opened
// // ms.verify(x => x.confirm(TypeMoq.It.isAny()), TypeMoq.Times.once());
// // // ... The account management service should not have gotten a remove account request
// // ams.verify(x => x.removeAccount(TypeMoq.It.isAny()), TypeMoq.Times.never());
// // done();
// // } catch (e) {
// // done(e);
// // }
// // });
// });
// test('RemoveAccount - Failure', (done) => {
// // // Setup: Create an AddAccountAction object
// // let ams = getMockAccountManagementService(false);
// // let ms = getMockMessageService(true);
// // let es = getMockErrorMessageService();
// // let action = new RemoveAccountAction(testAccount, ms.object, es.object, ams.object);
// // // If: I run the action when it will reject
// // action.run().then(result => {
// // // Then:
// // // ... The result should be false since the operation failed
// // assert.ok(!result);
// // // ... The account management service should have gotten a remove account request
// // ams.verify(x => x.removeAccount(TypeMoq.It.isValue(testAccount.key)), TypeMoq.Times.once());
// // done();
// // }, error => {
// // // Should fail as rejected actions cause the debugger to crash
// // done(error);
// // });
// });
});
// function createAddAccountAction(resolve: boolean, confirm: boolean, param: string): IAddActionMocks {
// let ams = getMockAccountManagementService(resolve);
// let mockMessageService = getMockMessageService(confirm);
// let mockErrorMessageService = getMockErrorMessageService();
// return {
// accountMock: ams,
// messageMock: mockMessageService,
// errorMessageMock: mockErrorMessageService,
// action: new AddAccountAction(param, mockMessageService.object,
// mockErrorMessageService.object, ams.object)
// };
// }
// function getMockAccountManagementService(resolve: boolean): TypeMoq.Mock<AccountManagementTestService> {
// let mockAccountManagementService = TypeMoq.Mock.ofType(AccountManagementTestService);
// mockAccountManagementService.setup(x => x.addAccount(TypeMoq.It.isAnyString()))
// .returns(resolve ? () => Promise.resolve(null) : () => Promise.reject(null));
// mockAccountManagementService.setup(x => x.removeAccount(TypeMoq.It.isAny()))
// .returns(resolve ? () => Promise.resolve(true) : () => Promise.reject(null).then());
// return mockAccountManagementService;
// }
// function getMockMessageService(confirm: boolean): TypeMoq.Mock<MessageServiceStub> {
// let mockMessageService = TypeMoq.Mock.ofType(MessageServiceStub);
// mockMessageService.setup(x => x.confirm(TypeMoq.It.isAny()))
// .returns(() => undefined);
// return mockMessageService;
// }
// function getMockErrorMessageService(): TypeMoq.Mock<ErrorMessageServiceStub> {
// let mockMessageService = TypeMoq.Mock.ofType(ErrorMessageServiceStub);
// mockMessageService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()));
// return mockMessageService;
// }
// interface IAddActionMocks
// {
// accountMock: TypeMoq.Mock<AccountManagementTestService>;
// messageMock: TypeMoq.Mock<MessageServiceStub>;
// errorMessageMock: TypeMoq.Mock<ErrorMessageServiceStub>;
// action: AddAccountAction;
// }

View File

@@ -0,0 +1,149 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as assert from 'assert';
import * as TypeMoq from 'typemoq';
import { EventVerifierSingle } from 'sqltest/utils/eventVerifier';
import { Emitter } from 'vs/base/common/event';
import { AccountPickerViewModel } from 'sql/platform/accounts/common/accountPickerViewModel';
import { UpdateAccountListEventParams } from 'sql/platform/accounts/common/eventTypes';
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
// SUITE STATE /////////////////////////////////////////////////////////////
let mockUpdateAccountEmitter: Emitter<UpdateAccountListEventParams>;
let providers: azdata.AccountProviderMetadata[];
let accounts: azdata.Account[];
suite('Account picker view model tests', () => {
setup(() => {
providers = [{
id: 'azure',
displayName: 'Azure'
}];
let account1 = {
key: { providerId: 'azure', accountId: 'account1' },
name: 'Account 1',
displayInfo: {
contextualDisplayName: 'Microsoft Account',
accountType: 'microsoft',
displayName: 'Account 1',
userId: 'user@email.com'
},
properties: [],
isStale: false
};
let account2 = {
key: { providerId: 'azure', accountId: 'account2' },
name: 'Account 2',
displayInfo: {
contextualDisplayName: 'Work/School Account',
accountType: 'microsoft',
displayName: 'Account 2',
userId: 'user@email.com'
},
properties: [],
isStale: true
};
accounts = [account1, account2];
// Setup event mocks
mockUpdateAccountEmitter = new Emitter<UpdateAccountListEventParams>();
});
test('Construction - Events are properly defined', () => {
// If: I create an account picker viewmodel
let mockAccountManagementService = getMockAccountManagementService(false, false);
let vm = new AccountPickerViewModel('azure', mockAccountManagementService.object);
// Then:
// ... The event for the view models should be properly initialized
assert.notEqual(vm.updateAccountListEvent, undefined);
// ... The event should properly fire
let argUpdateAccounts: UpdateAccountListEventParams = { providerId: providers[0].id, accountList: accounts };
let evUpdateAccounts = new EventVerifierSingle<UpdateAccountListEventParams>();
vm.updateAccountListEvent(evUpdateAccounts.eventHandler);
mockUpdateAccountEmitter.fire(argUpdateAccounts);
evUpdateAccounts.assertFired(argUpdateAccounts);
});
test('Initialize - Success', done => {
// Setup: Create a viewmodel with event handlers
let mockAccountManagementService = getMockAccountManagementService(true, true);
let evUpdateAccounts = new EventVerifierSingle<UpdateAccountListEventParams>();
let vm = getViewModel(mockAccountManagementService.object, evUpdateAccounts);
// If: I initialize the view model
vm.initialize()
.then(results => {
// Then:
// ... None of the events should have fired
evUpdateAccounts.assertNotFired();
// ... The account management service should have been called
mockAccountManagementService.verify(x => x.getAccountsForProvider(TypeMoq.It.isAny()), TypeMoq.Times.once());
// ... The results that were returned should be an array of account
assert.ok(Array.isArray(results));
assert.equal(results.length, 2);
assert.equal(results, accounts);
}).then(
() => done(),
err => done(err)
);
});
test('Initialize - Get accounts fails expects empty array', done => {
// Setup: Create a mock account management service that rejects the promise
let mockAccountManagementService = getMockAccountManagementService(true, false);
let evUpdateAccounts = new EventVerifierSingle<UpdateAccountListEventParams>();
let vm = getViewModel(mockAccountManagementService.object, evUpdateAccounts);
// If: I initialize the view model
vm.initialize()
.then(result => {
// Then:
// ... None of the events should have fired
evUpdateAccounts.assertNotFired();
// ... The account management service should have been called
mockAccountManagementService.verify(x => x.getAccountsForProvider(TypeMoq.It.isAny()), TypeMoq.Times.once());
// ... The results should be an empty array
assert.ok(Array.isArray(result));
assert.equal(result.length, 0);
assert.equal(result, []);
}).then(
() => done(),
err => done()
);
});
});
function getMockAccountManagementService(resolveProviders: boolean, resolveAccounts: boolean): TypeMoq.Mock<TestAccountManagementService> {
let mockAccountManagementService = TypeMoq.Mock.ofType(TestAccountManagementService);
mockAccountManagementService.setup(x => x.getAccountProviderMetadata())
.returns(() => resolveProviders ? Promise.resolve(providers) : Promise.reject(null).then());
mockAccountManagementService.setup(x => x.getAccountsForProvider(TypeMoq.It.isAny()))
.returns(() => resolveAccounts ? Promise.resolve(accounts) : Promise.reject(null).then());
mockAccountManagementService.setup(x => x.updateAccountListEvent)
.returns(() => mockUpdateAccountEmitter.event);
return mockAccountManagementService;
}
function getViewModel(
ams: TestAccountManagementService,
evUpdate: EventVerifierSingle<UpdateAccountListEventParams>
): AccountPickerViewModel {
let vm = new AccountPickerViewModel('azure', ams);
vm.updateAccountListEvent(evUpdate.eventHandler);
return vm;
}

View File

@@ -0,0 +1,440 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as azdata from 'azdata';
import AccountStore from 'sql/platform/accounts/common/accountStore';
import { EventVerifierSingle } from 'sqltest/utils/eventVerifier';
suite('Account Store Tests', () => {
test('AddOrUpdate - Uninitialized memento', done => {
// Setup: Create account store w/o initialized memento
let memento = {};
let as = new AccountStore(memento);
// If: I add an account to the store
as.addOrUpdate(account1)
.then(result => {
// Then:
// ... I should have gotten back a result indicating the account was added
assert.ok(result.accountAdded);
assert.ok(!result.accountModified);
assertAccountEqual(result.changedAccount, account1);
// ... The memento should have been initialized and account added
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 1);
assertAccountEqual(memento[AccountStore.MEMENTO_KEY][0], account1);
})
.then(
() => done(),
e => done(e)
);
});
test('AddOrUpdate - Adds to accounts', done => {
// Setup: Create account store with initialized memento with accounts
let memento = {};
memento[AccountStore.MEMENTO_KEY] = [];
let as = new AccountStore(memento);
// If: I add an account to the store
as.addOrUpdate(account1)
.then(result => {
// Then:
// ... I should have gotten back a result indicating the account was added
assert.ok(result.accountAdded);
assert.ok(!result.accountModified);
assertAccountEqual(result.changedAccount, account1);
// ... The memento should have the account added
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 1);
assertAccountEqual(memento[AccountStore.MEMENTO_KEY][0], account1);
})
.then(
() => done(),
e => done(e)
);
});
test('AddOrUpdate - Updates account', done => {
// Setup: Create account store with initialized memento with accounts
let memento = getTestMemento();
let as = new AccountStore(memento);
// If: I add an account to the store that already exists
let param = <azdata.Account>{
key: account2.key,
displayInfo: account1.displayInfo,
isStale: account1.isStale
};
as.addOrUpdate(param)
.then(result => {
// Then:
// ... I should have gotten back a result indicating the account was updated
assert.ok(result.accountModified);
assert.ok(!result.accountAdded);
assertAccountEqual(result.changedAccount, param);
// ... The memento should have been initialized and account updated
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 2);
assertAccountEqual(memento[AccountStore.MEMENTO_KEY][0], account1);
assertAccountEqual(memento[AccountStore.MEMENTO_KEY][1], param);
})
.then(
() => done(),
e => done(e)
);
});
test('GetAccountsByProvider - Uninitialized memento', done => {
// Setup: Create account store w/o initialized memento
let memento = {};
let as = new AccountStore(memento);
// If: I get accounts by provider
as.getAccountsByProvider('azure')
.then(result => {
// Then:
// ... I should get back an empty array
assert.ok(Array.isArray(result));
assert.equal(result.length, 0);
// ... Memento should not have been written
assert.equal(Object.keys(memento).length, 0);
})
.then(
() => done(),
e => done(e)
);
});
test('GetAccountsByProvider - No accounts', done => {
// Setup: Create account store with initialized memento with accounts
let memento = {};
memento[AccountStore.MEMENTO_KEY] = [];
let as = new AccountStore(memento);
// If: I get accounts when there aren't any accounts
as.getAccountsByProvider('azure')
.then(result => {
// Then: I should get back an empty array
assert.ok(Array.isArray(result));
assert.equal(result.length, 0);
})
.then(
() => done(),
e => done(e)
);
});
test('GetAccountsByProvider - Accounts, but no accounts for provider', done => {
// Setup: Create account store with initialized memento with accounts
let memento = getTestMemento();
let as = new AccountStore(memento);
// If: I get accounts by provider that doesn't have accounts
as.getAccountsByProvider('cloudycloud')
.then(result => {
// Then: I should get back an empty array
assert.ok(Array.isArray(result));
assert.equal(result.length, 0);
})
.then(
() => done(),
e => done(e)
);
});
test('GetAccountsByProvider - Accounts for provider', done => {
// Setup: Create account store with initialized memento with accounts
let memento = getTestMemento();
let as = new AccountStore(memento);
// If: I get accounts by provider that has accounts
as.getAccountsByProvider('azure')
.then(result => {
// Then: I should get the accounts
assert.ok(Array.isArray(result));
assert.equal(result.length, 2);
assertAccountEqual(result[0], memento[AccountStore.MEMENTO_KEY][0]);
assertAccountEqual(result[1], memento[AccountStore.MEMENTO_KEY][1]);
})
.then(
() => done(),
e => done(e)
);
});
test('GetAllAccounts - Uninitialized memento', done => {
// Setup: Create account store w/o initialized memento
let memento = {};
let as = new AccountStore(memento);
// If: I get accounts
as.getAllAccounts()
.then(result => {
// Then:
// ... I should get back an empty array
assert.ok(Array.isArray(result));
assert.equal(result.length, 0);
// ... Memento should not have been written
assert.equal(Object.keys(memento).length, 0);
})
.then(
() => done(),
e => done(e)
);
});
test('GetAllAccounts - No accounts', done => {
// Setup: Create account store with initialized memento with accounts
let memento = {};
memento[AccountStore.MEMENTO_KEY] = [];
let as = new AccountStore(memento);
// If: I get accounts when there aren't any accounts
as.getAllAccounts()
.then(result => {
// Then: I should get back an empty array
assert.ok(Array.isArray(result));
assert.equal(result.length, 0);
})
.then(
() => done(),
e => done(e)
);
});
test('GetAllAccounts - Accounts', done => {
// Setup: Create account store with initialized memento with accounts
let memento = getTestMemento();
let as = new AccountStore(memento);
// If: I get accounts
as.getAllAccounts()
.then(result => {
// Then: I should get the accounts
assert.ok(Array.isArray(result));
assert.equal(result.length, 2);
assertAccountEqual(result[0], memento[AccountStore.MEMENTO_KEY][0]);
assertAccountEqual(result[1], memento[AccountStore.MEMENTO_KEY][1]);
})
.then(
() => done(),
e => done(e)
);
});
test('Remove - Uninitialized menento', done => {
// Setup: Create account store w/o initialized memento
let memento = {};
let as = new AccountStore(memento);
// If: I remove an account when there's an uninitialized memento
as.remove(account1.key)
.then(result => {
// Then:
// ... I should get back false (no account removed)
assert.ok(!result);
// ... The memento should have been initialized
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 0);
})
.then(
() => done(),
e => done(e)
);
});
test('Remove - Account does not exist', done => {
// Setup: Create account store with initialized memento with accounts
let memento = {};
memento[AccountStore.MEMENTO_KEY] = [];
let as = new AccountStore(memento);
// If: I remove an account that doesn't exist
as.remove({ providerId: 'cloudyCloud', accountId: 'testyTest' })
.then(result => {
// Then:
// ... I should get back false (no account removed)
assert.ok(!result);
// ... The memento should still be empty
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 0);
})
.then(
() => done(),
e => done(e)
);
});
test('Remove - Account exists', done => {
// Setup: Create account store with initialized memento with accounts
let memento = getTestMemento();
let as = new AccountStore(memento);
// If: I remove an account that does exist
as.remove(account1.key)
.then(result => {
// Then:
// ... I should get back true (account removed)
assert.ok(result);
// ... The memento should have removed the first account
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 1);
assertAccountEqual(memento[AccountStore.MEMENTO_KEY][0], account2);
})
.then(
() => done(),
e => done(e)
);
});
test('Update - Uninitialized menento', done => {
// Setup:
// ... Create account store w/o initialized memento
let memento = {};
let as = new AccountStore(memento);
// ... Create a callback that we can verify was called
let updateCallback = new EventVerifierSingle<azdata.Account>();
// If: I update an account
as.update(account1.key, updateCallback.eventHandler)
.then(result => {
// Then:
// ... I should get back false (account did not change)
assert.ok(!result);
// ... The memento should have been initialized
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 0);
// ... The callback shouldn't have been called
updateCallback.assertNotFired();
})
.then(
() => done(),
e => done(e)
);
});
test('Update - Account does not exist', done => {
// Setup: Create account store with initialized memento with accounts
let memento = {};
memento[AccountStore.MEMENTO_KEY] = [];
let as = new AccountStore(memento);
// ... Create a callback that we can verify was called
let updateCallback = new EventVerifierSingle<azdata.Account>();
// If: I update an account that doesn't exist
as.update({ accountId: 'testyTest', providerId: 'cloudyCloud' }, updateCallback.eventHandler)
.then(result => {
// Then:
// ... I should get back false (account did not change)
assert.ok(!result);
// ... The memento should still be empty
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 0);
// ... The callback shouldn't have been called
updateCallback.assertNotFired();
})
.then(
() => done(),
e => done(e)
);
});
test('Update - Account exists', done => {
// Setup: Create account store with initialized memento with accounts
let memento = getTestMemento();
let as = new AccountStore(memento);
// ... Create a callback to update the account
let newDisplayName = 'Display Name Changed!';
let updateCallback = (arg: azdata.Account) => {
arg.displayInfo.displayName = newDisplayName;
};
// If: I update an account that exists
as.update(account1.key, updateCallback)
.then(result => {
// Then:
// ... I should get back true (account did change)
assert.ok(result);
// ... The memento still contains two accounts
assert.ok(Array.isArray(memento[AccountStore.MEMENTO_KEY]));
assert.equal(memento[AccountStore.MEMENTO_KEY].length, 2);
// ... Account 1 should have been updated
assert.equal(memento[AccountStore.MEMENTO_KEY][0].displayInfo.displayName, newDisplayName);
// ... Account 2 should have stayed the same
assertAccountEqual(memento[AccountStore.MEMENTO_KEY][1], account2);
})
.then(
() => done(),
e => done(e)
);
});
// TODO: Test to make sure operations occur sequentially
});
// TODO: Reinstate contextual logo once UI changes are checked in
const account1 = <azdata.Account>{
key: {
providerId: 'azure',
accountId: 'testAccount1'
},
displayInfo: {
displayName: 'Test Account 1',
accountType: 'test',
contextualDisplayName: 'Azure Account'
},
isStale: false
};
const account2 = <azdata.Account>{
key: {
providerId: 'azure',
accountId: 'testAccount2'
},
displayInfo: {
displayName: 'Test Account 2',
accountType: 'test',
contextualDisplayName: 'Azure Account'
},
isStale: false
};
function getTestMemento() {
let memento = {};
memento[AccountStore.MEMENTO_KEY] = [account1, account2];
return memento;
}
function assertAccountEqual(a: azdata.Account, b: azdata.Account) {
assert.equal(a.key.providerId, b.key.providerId);
assert.equal(a.key.accountId, b.key.accountId);
assert.equal(a.displayInfo.contextualDisplayName, b.displayInfo.contextualDisplayName);
assert.equal(a.displayInfo.accountType, b.displayInfo.accountType);
assert.equal(a.displayInfo.displayName, b.displayInfo.displayName);
assert.equal(a.isStale, b.isStale);
}

View File

@@ -0,0 +1,224 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as azdata from 'azdata';
import * as TypeMoq from 'typemoq';
import { EventVerifierSingle } from 'sqltest/utils/eventVerifier';
import { Emitter } from 'vs/base/common/event';
import { AccountViewModel } from 'sql/platform/accounts/common/accountViewModel';
import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/platform/accounts/common/eventTypes';
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
// SUITE STATE /////////////////////////////////////////////////////////////
let mockAddProviderEmitter: Emitter<AccountProviderAddedEventParams>;
let mockRemoveProviderEmitter: Emitter<azdata.AccountProviderMetadata>;
let mockUpdateAccountEmitter: Emitter<UpdateAccountListEventParams>;
let providers: azdata.AccountProviderMetadata[];
let accounts: azdata.Account[];
suite('Account Management Dialog ViewModel Tests', () => {
suiteSetup(() => {
providers = [{
id: 'azure',
displayName: 'Azure'
}];
let account1 = {
key: { providerId: 'azure', accountId: 'account1' },
name: 'Account 1',
displayInfo: {
contextualDisplayName: 'Microsoft Account',
accountType: 'microsoft',
displayName: 'Account 1',
userId: 'user@email.com'
},
properties: [],
isStale: false
};
let account2 = {
key: { providerId: 'azure', accountId: 'account2' },
name: 'Account 2',
displayInfo: {
contextualDisplayName: 'Work/School Account',
accountType: 'work_school',
displayName: 'Account 2',
userId: 'user@email.com'
},
properties: [],
isStale: true
};
accounts = [account1, account2];
// Setup event mocks for the account management service
mockAddProviderEmitter = new Emitter<AccountProviderAddedEventParams>();
mockRemoveProviderEmitter = new Emitter<azdata.AccountProviderMetadata>();
mockUpdateAccountEmitter = new Emitter<UpdateAccountListEventParams>();
});
test('Construction - Events are properly defined', () => {
// If: I create an account viewmodel
let mockAccountManagementService = getMockAccountManagementService(false, false);
let vm = new AccountViewModel(mockAccountManagementService.object);
// Then:
// ... All the events for the view models should be properly initialized
assert.notEqual(vm.addProviderEvent, undefined);
assert.notEqual(vm.removeProviderEvent, undefined);
assert.notEqual(vm.updateAccountListEvent, undefined);
// ... All the events should properly fire
let argAddProvider: AccountProviderAddedEventParams = { addedProvider: providers[0], initialAccounts: [] };
let evAddProvider = new EventVerifierSingle<AccountProviderAddedEventParams>();
vm.addProviderEvent(evAddProvider.eventHandler);
mockAddProviderEmitter.fire(argAddProvider);
evAddProvider.assertFired(argAddProvider);
let argRemoveProvider = providers[0];
let evRemoveProvider = new EventVerifierSingle<azdata.AccountProviderMetadata>();
vm.removeProviderEvent(evRemoveProvider.eventHandler);
mockRemoveProviderEmitter.fire(argRemoveProvider);
evRemoveProvider.assertFired(argRemoveProvider);
let argUpdateAccounts: UpdateAccountListEventParams = { providerId: providers[0].id, accountList: accounts };
let evUpdateAccounts = new EventVerifierSingle<UpdateAccountListEventParams>();
vm.updateAccountListEvent(evUpdateAccounts.eventHandler);
mockUpdateAccountEmitter.fire(argUpdateAccounts);
evUpdateAccounts.assertFired(argUpdateAccounts);
});
test('Initialize - Success', done => {
// Setup: Create a viewmodel with event handlers
let mockAccountManagementService = getMockAccountManagementService(true, true);
let evAddProvider = new EventVerifierSingle<AccountProviderAddedEventParams>();
let evRemoveProvider = new EventVerifierSingle<azdata.AccountProviderMetadata>();
let evUpdateAccounts = new EventVerifierSingle<UpdateAccountListEventParams>();
let vm = getViewModel(mockAccountManagementService.object, evAddProvider, evRemoveProvider, evUpdateAccounts);
// If: I initialize the view model
vm.initialize()
.then(results => {
// Then:
// ... None of the events should have fired
assertNoEventsFired(evAddProvider, evRemoveProvider, evUpdateAccounts);
// ... The account management service should have been called
mockAccountManagementService.verify(x => x.getAccountProviderMetadata(), TypeMoq.Times.once());
mockAccountManagementService.verify(x => x.getAccountsForProvider(TypeMoq.It.isAny()), TypeMoq.Times.once());
// ... The results that were returned should be an array of account provider added event params
assert.ok(Array.isArray(results));
assert.equal(results.length, 1);
assert.equal(results[0].addedProvider, providers[0]);
assert.equal(results[0].initialAccounts, accounts);
}).then(
() => done(),
err => done(err)
);
});
test('Initialize - Get providers fails', done => {
// Setup: Create a mock account management service that rejects looking up providers
let mockAccountManagementService = getMockAccountManagementService(false, true);
let evAddProvider = new EventVerifierSingle<AccountProviderAddedEventParams>();
let evRemoveProvider = new EventVerifierSingle<azdata.AccountProviderMetadata>();
let evUpdateAccounts = new EventVerifierSingle<UpdateAccountListEventParams>();
let vm = getViewModel(mockAccountManagementService.object, evAddProvider, evRemoveProvider, evUpdateAccounts);
// If: I initialize the view model
vm.initialize()
.then(results => {
// Then
// ... None of the events should have fired
assertNoEventsFired(evAddProvider, evRemoveProvider, evUpdateAccounts);
// ... The account management service should have been called for providers, but not accounts
mockAccountManagementService.verify(x => x.getAccountProviderMetadata(), TypeMoq.Times.once());
mockAccountManagementService.verify(x => x.getAccountsForProvider(TypeMoq.It.isAny()), TypeMoq.Times.never());
// ... The results that were returned should be an empty array
assert.ok(Array.isArray(results));
assert.equal(results.length, 0);
})
.then(
() => done(),
err => done(err)
);
});
test('Initialize - Get accounts fails', done => {
// Setup: Create a mock account management service that rejects the promise
let mockAccountManagementService = getMockAccountManagementService(true, false);
let evAddProvider = new EventVerifierSingle<AccountProviderAddedEventParams>();
let evRemoveProvider = new EventVerifierSingle<azdata.AccountProviderMetadata>();
let evUpdateAccounts = new EventVerifierSingle<UpdateAccountListEventParams>();
let vm = getViewModel(mockAccountManagementService.object, evAddProvider, evRemoveProvider, evUpdateAccounts);
// If: I initialize the view model
vm.initialize()
.then(result => {
// Then:
// ... None of the events should have fired
assertNoEventsFired(evAddProvider, evRemoveProvider, evUpdateAccounts);
// ... The account management service should have been called
mockAccountManagementService.verify(x => x.getAccountProviderMetadata(), TypeMoq.Times.once());
mockAccountManagementService.verify(x => x.getAccountsForProvider(TypeMoq.It.isAny()), TypeMoq.Times.once());
// ... The results should include the provider
assert.ok(Array.isArray(result));
assert.equal(result.length, 1);
assert.equal(result[0].addedProvider, providers[0]);
assert.equal(result[0].initialAccounts, accounts);
}).then(
() => done(),
err => done()
);
});
});
function getMockAccountManagementService(resolveProviders: boolean, resolveAccounts: boolean): TypeMoq.Mock<TestAccountManagementService> {
let mockAccountManagementService = TypeMoq.Mock.ofType(TestAccountManagementService);
mockAccountManagementService.setup(x => x.getAccountProviderMetadata())
.returns(() => resolveProviders ? Promise.resolve(providers) : Promise.reject(null).then());
mockAccountManagementService.setup(x => x.getAccountsForProvider(TypeMoq.It.isAny()))
.returns(() => resolveAccounts ? Promise.resolve(accounts) : Promise.reject(null).then());
mockAccountManagementService.setup(x => x.addAccountProviderEvent)
.returns(() => mockAddProviderEmitter.event);
mockAccountManagementService.setup(x => x.removeAccountProviderEvent)
.returns(() => mockRemoveProviderEmitter.event);
mockAccountManagementService.setup(x => x.updateAccountListEvent)
.returns(() => mockUpdateAccountEmitter.event);
return mockAccountManagementService;
}
function getViewModel(
ams: TestAccountManagementService,
evAdd: EventVerifierSingle<AccountProviderAddedEventParams>,
evRemove: EventVerifierSingle<azdata.AccountProviderMetadata>,
evUpdate: EventVerifierSingle<UpdateAccountListEventParams>
): AccountViewModel {
let vm = new AccountViewModel(ams);
vm.addProviderEvent(evAdd.eventHandler);
vm.removeProviderEvent(evRemove.eventHandler);
vm.updateAccountListEvent(evUpdate.eventHandler);
return vm;
}
function assertNoEventsFired(
evAdd: EventVerifierSingle<AccountProviderAddedEventParams>,
evRemove: EventVerifierSingle<azdata.AccountProviderMetadata>,
evUpdate: EventVerifierSingle<UpdateAccountListEventParams>
): void {
evAdd.assertNotFired();
evRemove.assertNotFired();
evUpdate.assertNotFired();
}

View File

@@ -0,0 +1,43 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { FirewallRuleViewModel } from 'sql/platform/accounts/common/firewallRuleViewModel';
suite('Firewall rule view model tests', () => {
let viewModel: FirewallRuleViewModel;
setup(() => {
viewModel = new FirewallRuleViewModel();
});
test('update default values to 250.222.155.198 should calculate the correct default subnet IP range', () => {
let IPAddress = '250.222.155.198';
viewModel.updateDefaultValues(IPAddress);
assert.equal(IPAddress, viewModel.defaultIPAddress);
assert.equal('250.222.155.0', viewModel.defaultFromSubnetIPRange);
assert.equal('250.222.155.255', viewModel.defaultToSubnetIPRange);
});
test('update default values to 250.222.155.0 should calculate the correct default subnet IP range', () => {
let IPAddress = '250.222.155.2';
viewModel.updateDefaultValues(IPAddress);
assert.equal(IPAddress, viewModel.defaultIPAddress);
assert.equal('250.222.155.0', viewModel.defaultFromSubnetIPRange);
assert.equal('250.222.155.255', viewModel.defaultToSubnetIPRange);
});
test('subnet IP range should return the correct values', () => {
let IPAddress = '250.222.155.198';
viewModel.updateDefaultValues(IPAddress);
assert.equal('250.222.155.0', viewModel.fromSubnetIPRange);
assert.equal('250.222.155.255', viewModel.toSubnetIPRange);
viewModel.fromSubnetIPRange = '250.222.155.100';
viewModel.toSubnetIPRange = '250.222.155.220';
assert.equal('250.222.155.100', viewModel.fromSubnetIPRange);
assert.equal('250.222.155.220', viewModel.toSubnetIPRange);
});
});

View File

@@ -0,0 +1,103 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import { Event } from 'vs/base/common/event';
import { IAccountManagementService } from 'sql/platform/accounts/common/interfaces';
import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/platform/accounts/common/eventTypes';
export class TestAccountManagementService implements IAccountManagementService {
_serviceBrand: any;
public get addAccountProviderEvent(): Event<AccountProviderAddedEventParams> { return () => { return undefined; }; }
public get removeAccountProviderEvent(): Event<azdata.AccountProviderMetadata> { return () => { return undefined; }; }
public get updateAccountListEvent(): Event<UpdateAccountListEventParams> { return () => { return undefined; }; }
accountUpdated(account: azdata.Account): Thenable<void> {
return undefined;
}
addAccount(providerId: string): Thenable<void> {
return undefined;
}
beginAutoOAuthDeviceCode(title: string, message: string, userCode: string, uri: string): Thenable<void> {
return undefined;
}
cancelAutoOAuthDeviceCode(providerId: string): void {
return undefined;
}
endAutoOAuthDeviceCode(): void {
return undefined;
}
copyUserCodeAndOpenBrowser(userCode: string, uri: string): void {
return undefined;
}
getAccountProviderMetadata(): Thenable<azdata.AccountProviderMetadata[]> {
return undefined;
}
getAccountsForProvider(providerId: string): Thenable<azdata.Account[]> {
return undefined;
}
getSecurityToken(account: azdata.Account, resource: azdata.AzureResource): Thenable<{}> {
return undefined;
}
removeAccount(accountKey: azdata.AccountKey): Thenable<boolean> {
return undefined;
}
refreshAccount(account: azdata.Account): Thenable<azdata.Account> {
return undefined;
}
openAccountListDialog(): Promise<any> {
return undefined;
}
registerProvider(providerMetadata: azdata.AccountProviderMetadata, provider: azdata.AccountProvider): void {
return undefined;
}
shutdown(): void {
return undefined;
}
unregisterProvider(providerMetadata: azdata.AccountProviderMetadata): void {
return undefined;
}
}
export class AccountProviderStub implements azdata.AccountProvider {
autoOAuthCancelled(): Thenable<void> {
return Promise.resolve();
}
clear(account: azdata.AccountKey): Thenable<void> {
return Promise.resolve();
}
getSecurityToken(account: azdata.Account, resource: azdata.AzureResource): Thenable<{}> {
return Promise.resolve({});
}
initialize(storedAccounts: azdata.Account[]): Thenable<azdata.Account[]> {
return Promise.resolve(storedAccounts);
}
prompt(): Thenable<azdata.Account> {
return Promise.resolve(undefined);
}
refresh(account: azdata.Account): Thenable<azdata.Account> {
return Promise.resolve(account);
}
}

View File

@@ -5,8 +5,8 @@
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import * as azdata from 'azdata';
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
import * as TelemetryUtils from 'sql/platform/telemetry/telemetryUtilities';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import * as TelemetryUtils from 'sql/platform/telemetry/common/telemetryUtilities';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IBackupService, TaskExecutionMode } from 'sql/platform/backup/common/backupService';
import { invalidProvider } from 'sql/base/common/errors';

View File

@@ -3,12 +3,13 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { NgModuleRef, enableProdMode, InjectionToken, Type, PlatformRef, Provider } from '@angular/core';
import { NgModuleRef, enableProdMode, PlatformRef, Provider } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { IInstantiationService, _util } from 'vs/platform/instantiation/common/instantiation';
import { IEditorInput } from 'vs/workbench/common/editor';
import { Trace } from 'vs/platform/instantiation/common/instantiationService';
import { values } from 'vs/base/common/map';
import { IModuleFactory, IBootstrapParams } from 'sql/platform/bootstrap/common/bootstrapParams';
const selectorCounter = new Map<string, number>();
@@ -24,14 +25,6 @@ export function providerIterator(service: IInstantiationService): Provider[] {
});
}
export const ISelector = new InjectionToken<string>('selector');
export const IBootstrapParams = new InjectionToken<IBootstrapParams>('bootstrap_params');
export interface IBootstrapParams {
}
export type IModuleFactory<T> = (params: IBootstrapParams, selector: string, service: IInstantiationService) => Type<T>;
function createUniqueSelector(selector: string): string {
let num: number;
if (selectorCounter.has(selector)) {

View File

@@ -8,15 +8,14 @@ import { Injectable, Inject } from '@angular/core';
import { Observable } from 'rxjs/Observable';
/* SQL imports */
import { IDefaultComponentParams } from 'sql/platform/bootstrap/node/bootstrapParams';
import { IBootstrapParams } from 'sql/platform/bootstrap/node/bootstrapService';
import { IDefaultComponentParams, IBootstrapParams } from 'sql/platform/bootstrap/common/bootstrapParams';
import { IMetadataService } from 'sql/platform/metadata/common/metadataService';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
import { IAdminService } from 'sql/workbench/services/admin/common/adminService';
import { IQueryManagementService } from 'sql/platform/query/common/queryManagement';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { AngularDisposable } from 'sql/base/node/lifecycle';
import { AngularDisposable } from 'sql/base/browser/lifecycle';
import { ConnectionContextKey } from 'sql/workbench/parts/connection/common/connectionContextKey';
import { ProviderMetadata, DatabaseInfo, SimpleExecuteResult } from 'azdata';

View File

@@ -7,8 +7,8 @@ import { DataService } from 'sql/workbench/parts/grid/services/dataService';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ConnectionContextKey } from 'sql/workbench/parts/connection/common/connectionContextKey';
import { IBootstrapParams } from 'sql/platform/bootstrap/node/bootstrapService';
import { Event } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
export interface IQueryComponentParams extends IBootstrapParams {
dataService: DataService;
@@ -39,3 +39,15 @@ export interface ITaskDialogComponentParams extends IBootstrapParams {
export interface IQueryPlanParams extends IBootstrapParams {
planXml: string;
}
export const ISelector = 'selector';
export const IBootstrapParams = 'bootstrap_params';
export interface IBootstrapParams {
}
export interface Type<T> {
new(...args: any[]): T;
}
export type IModuleFactory<T> = (params: IBootstrapParams, selector: string, service: IInstantiationService) => Type<T>;

View File

@@ -9,7 +9,7 @@ import {
} from '@angular/core';
import { Dropdown, IDropdownOptions } from 'sql/base/parts/editableDropdown/browser/dropdown';
import { AngularDisposable } from 'sql/base/node/lifecycle';
import { AngularDisposable } from 'sql/base/browser/lifecycle';
import { attachEditableDropdownStyler } from 'sql/platform/theme/common/styler';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';

View File

@@ -0,0 +1,83 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import {
Component, Inject, forwardRef, ElementRef, OnInit, Input,
Output, OnChanges, SimpleChanges, EventEmitter
} from '@angular/core';
import { InputBox as vsInputBox } from 'sql/base/browser/ui/inputBox/inputBox';
import { AngularDisposable } from 'sql/base/browser/lifecycle';
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
@Component({
selector: 'input-box',
template: ''
})
export class InputBox extends AngularDisposable implements OnInit, OnChanges {
private _inputbox: vsInputBox;
@Input() min: string;
@Input() max: string;
@Input() type: string;
@Input() placeholder: string;
@Input('aria-label') ariaLabel: string;
@Input() value: string;
@Output() onDidChange = new EventEmitter<string | number>();
constructor(
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
@Inject(IThemeService) private themeService: IThemeService,
@Inject(IContextViewService) private contextViewService: IContextViewService
) {
super();
}
ngOnInit(): void {
this._inputbox = new vsInputBox(this._el.nativeElement, this.contextViewService, {
min: this.min,
max: this.max,
type: this.type,
placeholder: this.placeholder,
ariaLabel: this.ariaLabel
});
if (this.value) {
this._inputbox.value = this.value;
}
this._inputbox.onDidChange(e => {
switch (this.type) {
case 'number':
if (e) {
this.onDidChange.emit(Number(e));
break;
}
default:
this.onDidChange.emit(e);
}
});
this._register(attachInputBoxStyler(this._inputbox, this.themeService));
}
ngOnChanges(changes: SimpleChanges): void {
if (this._inputbox) {
if (changes['min']) {
this._inputbox.inputElement.min = this.min;
}
if (changes['max']) {
this._inputbox.inputElement.max = this.max;
}
if (changes['type']) {
this._inputbox.inputElement.type = this.type;
}
if (changes['placeholder']) {
this._inputbox.inputElement.placeholder = this.placeholder;
}
}
}
}

View File

@@ -9,7 +9,7 @@ import {
} from '@angular/core';
import { SelectBox as vsSelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { AngularDisposable } from 'sql/base/node/lifecycle';
import { AngularDisposable } from 'sql/base/browser/lifecycle';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { ISelectData } from 'vs/base/browser/ui/selectBox/selectBox';

View File

@@ -0,0 +1,145 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
import { ICapabilitiesService, ProviderFeatures } from 'sql/platform/capabilities/common/capabilitiesService';
import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
import { Event, Emitter } from 'vs/base/common/event';
import { Action } from 'vs/base/common/actions';
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
export class TestCapabilitiesService implements ICapabilitiesService {
public _serviceBrand: any;
public capabilities: { [id: string]: ProviderFeatures } = {};
constructor() {
let connectionProvider: azdata.ConnectionOption[] = [
{
name: 'connectionName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.connectionName,
valueType: ServiceOptionType.string
},
{
name: 'serverName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.serverName,
valueType: ServiceOptionType.string
},
{
name: 'databaseName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.databaseName,
valueType: ServiceOptionType.string
},
{
name: 'userName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.userName,
valueType: ServiceOptionType.string
},
{
name: 'authenticationType',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.authType,
valueType: ServiceOptionType.string
},
{
name: 'password',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.password,
valueType: ServiceOptionType.string
}
];
let msSQLCapabilities = {
providerId: mssqlProviderName,
displayName: 'MSSQL',
connectionOptions: connectionProvider,
};
this.capabilities[mssqlProviderName] = { connection: msSQLCapabilities };
}
/**
* Retrieve a list of registered server capabilities
*/
public getCapabilities(provider: string): ProviderFeatures {
return this.capabilities[provider];
}
public getLegacyCapabilities(provider: string): azdata.DataProtocolServerCapabilities {
throw new Error('Method not implemented.');
}
public get providers(): { [id: string]: ProviderFeatures } {
return this.capabilities;
}
/**
* Register the capabilities provider and query the provider for its capabilities
*/
public registerProvider(provider: azdata.CapabilitiesProvider): void {
}
// Event Emitters
public get onProviderRegisteredEvent(): Event<azdata.DataProtocolServerCapabilities> {
return undefined;
}
public isFeatureAvailable(featureName: Action, connectionManagementInfo: ConnectionManagementInfo): boolean {
return true;
}
public onCapabilitiesReady(): Promise<void> {
return Promise.resolve(null);
}
public fireCapabilitiesRegistered(providerFeatures: ProviderFeatures): void {
this._onCapabilitiesRegistered.fire(providerFeatures);
}
private _onCapabilitiesRegistered = new Emitter<ProviderFeatures>();
public readonly onCapabilitiesRegistered = this._onCapabilitiesRegistered.event;
}

View File

@@ -18,10 +18,10 @@ import * as Constants from 'sql/platform/connection/common/constants';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import * as ConnectionContracts from 'sql/workbench/parts/connection/common/connection';
import { ConnectionStatusManager } from 'sql/platform/connection/common/connectionStatusManager';
import { DashboardInput } from 'sql/workbench/parts/dashboard/dashboardInput';
import { DashboardInput } from 'sql/workbench/parts/dashboard/common/dashboardInput';
import { ConnectionGlobalStatus } from 'sql/workbench/parts/connection/common/connectionGlobalStatus';
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
import * as TelemetryUtils from 'sql/platform/telemetry/telemetryUtilities';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import * as TelemetryUtils from 'sql/platform/telemetry/common/telemetryUtilities';
import { IResourceProviderService } from 'sql/workbench/services/resourceProvider/common/resourceProviderService';
import { IAngularEventingService, AngularEventType } from 'sql/platform/angularEventing/common/angularEventingService';
import * as QueryConstants from 'sql/workbench/parts/query/common/constants';

View File

@@ -12,7 +12,7 @@ import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/co
import { IConnectionProfile, IConnectionProfileStore } from 'sql/platform/connection/common/interfaces';
import { TestConfigurationService } from 'sql/platform/connection/test/common/testConfigurationService';
import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService';
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
import * as TypeMoq from 'typemoq';
import { Emitter } from 'vs/base/common/event';
import { deepClone, deepFreeze } from 'vs/base/common/objects';
@@ -119,7 +119,7 @@ suite('ConnectionConfig', () => {
]);
setup(() => {
capabilitiesService = TypeMoq.Mock.ofType(CapabilitiesTestService);
capabilitiesService = TypeMoq.Mock.ofType(TestCapabilitiesService);
capabilities = [];
let connectionProvider: azdata.ConnectionProviderOptions = {
options: [

View File

@@ -0,0 +1,994 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TestConnectionDialogService } from 'sql/workbench/services/connection/test/common/testConnectionDialogService';
import { ConnectionManagementService } from 'sql/platform/connection/common/connectionManagementService';
import { ConnectionStatusManager } from 'sql/platform/connection/common/connectionStatusManager';
import { ConnectionStore } from 'sql/platform/connection/common/connectionStore';
import {
INewConnectionParams, ConnectionType,
IConnectionCompletionOptions, IConnectionResult,
RunQueryOnConnectionMode
} from 'sql/platform/connection/common/connectionManagement';
import * as Constants from 'sql/platform/connection/common/constants';
import * as Utils from 'sql/platform/connection/common/utils';
import { IHandleFirewallRuleResult } from 'sql/workbench/services/resourceProvider/common/resourceProviderService';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
import { TestConnectionProvider } from 'sql/platform/connection/test/common/testConnectionProvider';
import { TestResourceProvider } from 'sql/workbench/services/resourceProvider/test/common/testResourceProviderService';
import * as azdata from 'azdata';
import * as assert from 'assert';
import * as TypeMoq from 'typemoq';
import { IConnectionProfileGroup, ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
import { TestStorageService, TestEnvironmentService, TestEditorService } from 'vs/workbench/test/workbenchTestServices';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { NullLogService } from 'vs/platform/log/common/log';
suite('SQL ConnectionManagementService tests', () => {
let capabilitiesService: TestCapabilitiesService;
let connectionDialogService: TypeMoq.Mock<TestConnectionDialogService>;
let connectionStore: TypeMoq.Mock<ConnectionStore>;
let workbenchEditorService: TypeMoq.Mock<TestEditorService>;
let connectionStatusManager: ConnectionStatusManager;
let mssqlConnectionProvider: TypeMoq.Mock<TestConnectionProvider>;
let workspaceConfigurationServiceMock: TypeMoq.Mock<TestConfigurationService>;
let resourceProviderStubMock: TypeMoq.Mock<TestResourceProvider>;
let accountManagementService: TypeMoq.Mock<TestAccountManagementService>;
let none: void;
let connectionProfile: IConnectionProfile = {
connectionName: 'new name',
serverName: 'new server',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: 'integrated',
savePassword: true,
groupFullName: 'g2/g2-2',
groupId: 'group id',
getOptionsKey: () => { return 'connectionId'; },
matches: undefined,
providerName: 'MSSQL',
options: {},
saveProfile: true,
id: undefined
};
let connectionProfileWithEmptySavedPassword: IConnectionProfile =
Object.assign({}, connectionProfile, { password: '', serverName: connectionProfile.serverName + 1 });
let connectionProfileWithEmptyUnsavedPassword: IConnectionProfile =
Object.assign({}, connectionProfile, { password: '', serverName: connectionProfile.serverName + 2, savePassword: false });
let connectionManagementService: ConnectionManagementService;
let configResult: { [key: string]: any } = {};
configResult['defaultEngine'] = 'MSSQL';
let handleFirewallRuleResult: IHandleFirewallRuleResult;
let resolveHandleFirewallRuleDialog: boolean;
let isFirewallRuleAdded: boolean;
setup(() => {
capabilitiesService = new TestCapabilitiesService();
connectionDialogService = TypeMoq.Mock.ofType(TestConnectionDialogService);
connectionStore = TypeMoq.Mock.ofType(ConnectionStore, TypeMoq.MockBehavior.Loose, new TestStorageService());
workbenchEditorService = TypeMoq.Mock.ofType(TestEditorService);
connectionStatusManager = new ConnectionStatusManager(capabilitiesService, new NullLogService(), TestEnvironmentService, new TestNotificationService());
mssqlConnectionProvider = TypeMoq.Mock.ofType(TestConnectionProvider);
let resourceProviderStub = new TestResourceProvider();
resourceProviderStubMock = TypeMoq.Mock.ofInstance(resourceProviderStub);
accountManagementService = TypeMoq.Mock.ofType(TestAccountManagementService);
let root = new ConnectionProfileGroup(ConnectionProfileGroup.RootGroupName, undefined, ConnectionProfileGroup.RootGroupName, undefined, undefined);
root.connections = [ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile)];
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, undefined)).returns(() => Promise.resolve(none));
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, TypeMoq.It.isAny())).returns(() => Promise.resolve(none));
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, undefined, TypeMoq.It.isAny())).returns(() => Promise.resolve(none));
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined, undefined, undefined)).returns(() => Promise.resolve(none));
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), undefined)).returns(() => Promise.resolve(none));
connectionDialogService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(none));
connectionStore.setup(x => x.addRecentConnection(TypeMoq.It.isAny())).returns(() => Promise.resolve());
connectionStore.setup(x => x.saveProfile(TypeMoq.It.isAny())).returns(() => Promise.resolve(connectionProfile));
workbenchEditorService.setup(x => x.openEditor(undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(undefined));
connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is<IConnectionProfile>(
c => c.serverName === connectionProfile.serverName))).returns(() => Promise.resolve({ profile: connectionProfile, savedCred: true }));
connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is<IConnectionProfile>(
c => c.serverName === connectionProfileWithEmptySavedPassword.serverName))).returns(
() => Promise.resolve({ profile: connectionProfileWithEmptySavedPassword, savedCred: true }));
connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is<IConnectionProfile>(
c => c.serverName === connectionProfileWithEmptyUnsavedPassword.serverName))).returns(
() => Promise.resolve({ profile: connectionProfileWithEmptyUnsavedPassword, savedCred: false }));
connectionStore.setup(x => x.isPasswordRequired(TypeMoq.It.isAny())).returns(() => true);
connectionStore.setup(x => x.getConnectionProfileGroups(false, undefined)).returns(() => [root]);
connectionStore.setup(x => x.savePassword(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
mssqlConnectionProvider.setup(x => x.connect(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => undefined);
// Setup resource provider
handleFirewallRuleResult = {
canHandleFirewallRule: false,
ipAddress: '123.123.123.123',
resourceProviderId: 'Azure'
};
resourceProviderStubMock.setup(x => x.handleFirewallRule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.returns(() => Promise.resolve(handleFirewallRuleResult));
resolveHandleFirewallRuleDialog = true;
isFirewallRuleAdded = true;
resourceProviderStubMock.setup(x => x.showFirewallRuleDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.returns(() => {
if (resolveHandleFirewallRuleDialog) {
return isFirewallRuleAdded ? Promise.resolve(true) : Promise.resolve(false);
} else {
return Promise.reject(null).then();
}
});
// Setup configuration to return a config that can be modified later.
workspaceConfigurationServiceMock = TypeMoq.Mock.ofType(TestConfigurationService);
workspaceConfigurationServiceMock.setup(x => x.getValue(Constants.sqlConfigSectionName))
.returns(() => configResult);
connectionManagementService = createConnectionManagementService();
connectionManagementService.registerProvider('MSSQL', mssqlConnectionProvider.object);
});
function createConnectionManagementService(): ConnectionManagementService {
let connectionManagementService = new ConnectionManagementService(
connectionStore.object,
undefined,
connectionDialogService.object,
undefined, // IServerGroupController
undefined, // IInstantiationService
workbenchEditorService.object,
undefined, // ITelemetryService
workspaceConfigurationServiceMock.object,
capabilitiesService,
undefined, // IQuickInputService
new TestNotificationService(),
resourceProviderStubMock.object,
undefined, // IAngularEventingService
accountManagementService.object,
new NullLogService(), // ILogService
undefined, // IStorageService
TestEnvironmentService
);
return connectionManagementService;
}
function verifyShowConnectionDialog(connectionProfile: IConnectionProfile, connectionType: ConnectionType, uri: string, options: boolean, connectionResult?: IConnectionResult, didShow: boolean = true): void {
if (connectionProfile) {
connectionDialogService.verify(x => x.showDialog(
TypeMoq.It.isAny(),
TypeMoq.It.is<INewConnectionParams>(p => p.connectionType === connectionType && (uri === undefined || p.input.uri === uri)),
TypeMoq.It.is<IConnectionProfile>(c => c !== undefined && c.serverName === connectionProfile.serverName),
connectionResult ? TypeMoq.It.is<IConnectionResult>(r => r.errorMessage === connectionResult.errorMessage && r.callStack === connectionResult.callStack) : undefined,
options ? TypeMoq.It.isAny() : undefined),
didShow ? TypeMoq.Times.once() : TypeMoq.Times.never());
} else {
connectionDialogService.verify(x => x.showDialog(
TypeMoq.It.isAny(),
TypeMoq.It.is<INewConnectionParams>(p => p.connectionType === connectionType && ((uri === undefined && p.input === undefined) || p.input.uri === uri)),
undefined,
connectionResult ? TypeMoq.It.is<IConnectionResult>(r => r.errorMessage === connectionResult.errorMessage && r.callStack === connectionResult.callStack) : undefined,
options ? TypeMoq.It.isAny() : undefined),
didShow ? TypeMoq.Times.once() : TypeMoq.Times.never());
}
}
function verifyShowFirewallRuleDialog(connectionProfile: IConnectionProfile, didShow: boolean = true): void {
resourceProviderStubMock.verify(x => x.showFirewallRuleDialog(
TypeMoq.It.is<IConnectionProfile>(c => c.serverName === connectionProfile.serverName),
TypeMoq.It.isAny(),
TypeMoq.It.isAny()),
didShow ? TypeMoq.Times.once() : TypeMoq.Times.never());
}
function verifyOptions(options?: IConnectionCompletionOptions, fromDialog?: boolean): void {
if (options) {
if (options.saveTheConnection) {
connectionStore.verify(x => x.saveProfile(TypeMoq.It.isAny()), TypeMoq.Times.once());
}
if (options.showDashboard) {
workbenchEditorService.verify(x => x.openEditor(undefined, TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
}
}
if (fromDialog !== undefined && !fromDialog) {
connectionStore.verify(x => x.addSavedPassword(TypeMoq.It.isAny()), TypeMoq.Times.once());
}
}
function connect(uri: string, options?: IConnectionCompletionOptions, fromDialog?: boolean, connection?: IConnectionProfile, error?: string, errorCode?: number, errorCallStack?: string): Promise<IConnectionResult> {
let connectionToUse = connection ? connection : connectionProfile;
return new Promise<IConnectionResult>((resolve, reject) => {
let id = connectionToUse.getOptionsKey();
let defaultUri = 'connection://' + (id ? id : connectionToUse.serverName + ':' + connectionToUse.databaseName);
connectionManagementService.onConnectionRequestSent(() => {
let info: azdata.ConnectionInfoSummary = {
connectionId: error ? undefined : 'id',
connectionSummary: {
databaseName: connectionToUse.databaseName,
serverName: connectionToUse.serverName,
userName: connectionToUse.userName
},
errorMessage: error,
errorNumber: errorCode,
messages: errorCallStack,
ownerUri: uri ? uri : defaultUri,
serverInfo: undefined
};
connectionManagementService.onConnectionComplete(0, info);
});
connectionManagementService.cancelConnectionForUri(uri).then(() => {
if (fromDialog) {
resolve(connectionManagementService.connectAndSaveProfile(connectionToUse, uri, options));
} else {
resolve(connectionManagementService.connect(connectionToUse, uri, options));
}
});
});
}
test('showConnectionDialog should open the dialog with default type given no parameters', done => {
connectionManagementService.showConnectionDialog().then(() => {
verifyShowConnectionDialog(undefined, ConnectionType.default, undefined, false);
done();
}).catch(err => {
done(err);
});
});
test('showConnectionDialog should open the dialog with given type given valid input', done => {
let params: INewConnectionParams = {
connectionType: ConnectionType.editor,
input: {
onConnectReject: undefined,
onConnectStart: undefined,
onDisconnect: undefined,
onConnectSuccess: undefined,
onConnectCanceled: undefined,
uri: 'Editor Uri'
},
runQueryOnCompletion: RunQueryOnConnectionMode.executeQuery
};
connectionManagementService.showConnectionDialog(params).then(() => {
verifyShowConnectionDialog(undefined, params.connectionType, params.input.uri, false);
done();
}).catch(err => {
done(err);
});
});
test('showConnectionDialog should pass the model to the dialog if there is a model assigned to the uri', done => {
let params: INewConnectionParams = {
connectionType: ConnectionType.editor,
input: {
onConnectReject: undefined,
onConnectStart: undefined,
onDisconnect: undefined,
onConnectSuccess: undefined,
onConnectCanceled: undefined,
uri: 'Editor Uri'
},
runQueryOnCompletion: RunQueryOnConnectionMode.executeQuery
};
connect(params.input.uri).then(() => {
let saveConnection = connectionManagementService.getConnectionProfile(params.input.uri);
assert.notEqual(saveConnection, undefined, `profile was not added to the connections`);
assert.equal(saveConnection.serverName, connectionProfile.serverName, `Server names are different`);
connectionManagementService.showConnectionDialog(params).then(() => {
verifyShowConnectionDialog(connectionProfile, params.connectionType, params.input.uri, false);
done();
}).catch(err => {
done(err);
});
}, err => done(err));
});
test('connect should save profile given options with saveProfile set to true', done => {
let uri: string = 'Editor Uri';
let options: IConnectionCompletionOptions = {
params: undefined,
saveTheConnection: true,
showDashboard: false,
showConnectionDialogOnError: false,
showFirewallRuleOnError: true
};
connect(uri, options).then(() => {
verifyOptions(options);
done();
}).catch(err => {
done(err);
});
});
test('getDefaultProviderId is MSSQL', done => {
let defaultProvider = connectionManagementService.getDefaultProviderId();
assert.equal(defaultProvider, 'MSSQL', `Default provider is not equal to MSSQL`);
done();
});
/* Andresse 10/5/17 commented this test out since it was only working before my changes by the chance of how Promises work
If we want to continue to test this, the connection logic needs to be rewritten to actually wait for everything to be done before it resolves */
// test('connect should show dashboard given options with showDashboard set to true', done => {
// let uri: string = 'Editor Uri';
// let options: IConnectionCompletionOptions = {
// params: undefined,
// saveTheConnection: false,
// showDashboard: true,
// showConnectionDialogOnError: false
// };
// connect(uri, options).then(() => {
// verifyOptions(options);
// done();
// }).catch(err => {
// done(err);
// });
// });
test('connect should pass the params in options to onConnectSuccess callback', done => {
let uri: string = 'Editor Uri';
let paramsInOnConnectSuccess: INewConnectionParams;
let options: IConnectionCompletionOptions = {
params: {
connectionType: ConnectionType.editor,
input: {
onConnectSuccess: (params?: INewConnectionParams) => {
paramsInOnConnectSuccess = params;
},
onConnectReject: undefined,
onConnectStart: undefined,
onDisconnect: undefined,
onConnectCanceled: undefined,
uri: uri
},
querySelection: undefined,
runQueryOnCompletion: RunQueryOnConnectionMode.none
},
saveTheConnection: true,
showDashboard: false,
showConnectionDialogOnError: true,
showFirewallRuleOnError: true
};
connect(uri, options).then(() => {
verifyOptions(options);
assert.notEqual(paramsInOnConnectSuccess, undefined);
assert.equal(paramsInOnConnectSuccess.connectionType, options.params.connectionType);
done();
}).catch(err => {
done(err);
});
});
test('connectAndSaveProfile should show not load the password', done => {
let uri: string = 'Editor Uri';
let options: IConnectionCompletionOptions = undefined;
connect(uri, options, true).then(() => {
verifyOptions(options, true);
done();
}).catch(err => {
done(err);
});
});
test('connect with undefined uri and options should connect using the default uri', done => {
let uri = undefined;
let options: IConnectionCompletionOptions = undefined;
connect(uri, options).then(() => {
assert.equal(connectionManagementService.isProfileConnected(connectionProfile), true);
done();
}).catch(err => {
done(err);
});
});
test('failed connection should open the dialog if connection fails', done => {
let uri = undefined;
let error: string = 'error';
let errorCode: number = 111;
let errorCallStack: string = 'error call stack';
let expectedConnection: boolean = false;
let options: IConnectionCompletionOptions = {
params: undefined,
saveTheConnection: false,
showDashboard: false,
showConnectionDialogOnError: true,
showFirewallRuleOnError: true
};
let connectionResult: IConnectionResult = {
connected: expectedConnection,
errorMessage: error,
errorCode: errorCode,
callStack: errorCallStack
};
connect(uri, options, false, connectionProfile, error, errorCode, errorCallStack).then(result => {
assert.equal(result.connected, expectedConnection);
assert.equal(result.errorMessage, connectionResult.errorMessage);
verifyShowFirewallRuleDialog(connectionProfile, false);
verifyShowConnectionDialog(connectionProfile, ConnectionType.default, uri, true, connectionResult);
done();
}).catch(err => {
done(err);
});
});
test('failed connection should not open the dialog if the option is set to false even if connection fails', done => {
let uri = undefined;
let error: string = 'error when options set to false';
let errorCode: number = 111;
let errorCallStack: string = 'error call stack';
let expectedConnection: boolean = false;
let options: IConnectionCompletionOptions = {
params: undefined,
saveTheConnection: false,
showDashboard: false,
showConnectionDialogOnError: false,
showFirewallRuleOnError: true
};
let connectionResult: IConnectionResult = {
connected: expectedConnection,
errorMessage: error,
errorCode: errorCode,
callStack: errorCallStack
};
connect(uri, options, false, connectionProfile, error, errorCode, errorCallStack).then(result => {
assert.equal(result.connected, expectedConnection);
assert.equal(result.errorMessage, connectionResult.errorMessage);
verifyShowFirewallRuleDialog(connectionProfile, false);
verifyShowConnectionDialog(connectionProfile, ConnectionType.default, uri, true, connectionResult, false);
done();
}).catch(err => {
done(err);
});
});
test('failed firewall rule should open the firewall rule dialog', done => {
handleFirewallRuleResult.canHandleFirewallRule = true;
resolveHandleFirewallRuleDialog = true;
isFirewallRuleAdded = true;
let uri = undefined;
let error: string = 'error';
let errorCode: number = 111;
let expectedConnection: boolean = false;
let expectedError: string = error;
let options: IConnectionCompletionOptions = {
params: undefined,
saveTheConnection: false,
showDashboard: false,
showConnectionDialogOnError: true,
showFirewallRuleOnError: true
};
connect(uri, options, false, connectionProfile, error, errorCode).then(result => {
assert.equal(result.connected, expectedConnection);
assert.equal(result.errorMessage, expectedError);
verifyShowFirewallRuleDialog(connectionProfile, true);
done();
}).catch(err => {
done(err);
});
});
test('failed firewall rule connection should not open the firewall rule dialog if the option is set to false even if connection fails', done => {
handleFirewallRuleResult.canHandleFirewallRule = true;
resolveHandleFirewallRuleDialog = true;
isFirewallRuleAdded = true;
let uri = undefined;
let error: string = 'error when options set to false';
let errorCallStack: string = 'error call stack';
let errorCode: number = 111;
let expectedConnection: boolean = false;
let options: IConnectionCompletionOptions = {
params: undefined,
saveTheConnection: false,
showDashboard: false,
showConnectionDialogOnError: false,
showFirewallRuleOnError: false
};
let connectionResult: IConnectionResult = {
connected: expectedConnection,
errorMessage: error,
errorCode: errorCode,
callStack: errorCallStack
};
connect(uri, options, false, connectionProfile, error, errorCode, errorCallStack).then(result => {
assert.equal(result.connected, expectedConnection);
assert.equal(result.errorMessage, connectionResult.errorMessage);
verifyShowFirewallRuleDialog(connectionProfile, false);
verifyShowConnectionDialog(connectionProfile, ConnectionType.default, uri, true, connectionResult, false);
done();
}).catch(err => {
done(err);
});
});
test('failed firewall rule connection and failed during open firewall rule should open the firewall rule dialog and connection dialog with error', done => {
handleFirewallRuleResult.canHandleFirewallRule = true;
resolveHandleFirewallRuleDialog = false;
isFirewallRuleAdded = true;
let uri = undefined;
let error: string = 'error when options set to false';
let errorCode: number = 111;
let errorCallStack: string = 'error call stack';
let expectedConnection: boolean = false;
let options: IConnectionCompletionOptions = {
params: undefined,
saveTheConnection: false,
showDashboard: false,
showConnectionDialogOnError: true,
showFirewallRuleOnError: true
};
let connectionResult: IConnectionResult = {
connected: expectedConnection,
errorMessage: error,
errorCode: errorCode,
callStack: errorCallStack
};
connect(uri, options, false, connectionProfile, error, errorCode, errorCallStack).then(result => {
assert.equal(result.connected, expectedConnection);
assert.equal(result.errorMessage, connectionResult.errorMessage);
verifyShowFirewallRuleDialog(connectionProfile, true);
verifyShowConnectionDialog(connectionProfile, ConnectionType.default, uri, true, connectionResult, true);
done();
}).catch(err => {
done(err);
});
});
test('failed firewall rule connection should open the firewall rule dialog. Then canceled firewall rule dialog should not open connection dialog', done => {
handleFirewallRuleResult.canHandleFirewallRule = true;
resolveHandleFirewallRuleDialog = true;
isFirewallRuleAdded = false;
let uri = undefined;
let error: string = 'error when options set to false';
let errorCallStack: string = 'error call stack';
let errorCode: number = 111;
let expectedConnection: boolean = false;
let options: IConnectionCompletionOptions = {
params: undefined,
saveTheConnection: false,
showDashboard: false,
showConnectionDialogOnError: true,
showFirewallRuleOnError: true
};
let connectionResult: IConnectionResult = {
connected: expectedConnection,
errorMessage: error,
errorCode: errorCode,
callStack: errorCallStack
};
connect(uri, options, false, connectionProfile, error, errorCode, errorCallStack).then(result => {
assert.equal(result.connected, expectedConnection);
assert.equal(result.errorMessage, connectionResult.errorMessage);
verifyShowFirewallRuleDialog(connectionProfile, true);
verifyShowConnectionDialog(connectionProfile, ConnectionType.default, uri, true, connectionResult, false);
done();
}).catch(err => {
done(err);
});
});
test('connect when password is empty and unsaved should open the dialog', done => {
let uri = undefined;
let expectedConnection: boolean = false;
let options: IConnectionCompletionOptions = {
params: undefined,
saveTheConnection: false,
showDashboard: false,
showConnectionDialogOnError: true,
showFirewallRuleOnError: true
};
let connectionResult: IConnectionResult = {
connected: expectedConnection,
errorMessage: undefined,
errorCode: undefined,
callStack: undefined
};
connect(uri, options, false, connectionProfileWithEmptyUnsavedPassword).then(result => {
assert.equal(result.connected, expectedConnection);
assert.equal(result.errorMessage, connectionResult.errorMessage);
verifyShowConnectionDialog(connectionProfileWithEmptyUnsavedPassword, ConnectionType.default, uri, true, connectionResult);
verifyShowFirewallRuleDialog(connectionProfile, false);
done();
}).catch(err => {
done(err);
});
});
test('connect when password is empty and saved should not open the dialog', done => {
let uri = undefined;
let expectedConnection: boolean = true;
let options: IConnectionCompletionOptions = {
params: undefined,
saveTheConnection: false,
showDashboard: false,
showConnectionDialogOnError: true,
showFirewallRuleOnError: true
};
let connectionResult: IConnectionResult = {
connected: expectedConnection,
errorMessage: undefined,
errorCode: undefined,
callStack: undefined
};
connect(uri, options, false, connectionProfileWithEmptySavedPassword).then(result => {
assert.equal(result.connected, expectedConnection);
assert.equal(result.errorMessage, connectionResult.errorMessage);
verifyShowConnectionDialog(connectionProfileWithEmptySavedPassword, ConnectionType.default, uri, true, connectionResult, false);
done();
}).catch(err => {
done(err);
});
});
test('connect from editor when empty password when it is required and saved should not open the dialog', done => {
let uri = 'editor 3';
let expectedConnection: boolean = true;
let options: IConnectionCompletionOptions = {
params: {
connectionType: ConnectionType.editor,
input: {
onConnectSuccess: undefined,
onConnectReject: undefined,
onConnectStart: undefined,
onDisconnect: undefined,
onConnectCanceled: undefined,
uri: uri
},
querySelection: undefined,
runQueryOnCompletion: RunQueryOnConnectionMode.none
},
saveTheConnection: true,
showDashboard: false,
showConnectionDialogOnError: true,
showFirewallRuleOnError: true
};
let connectionResult: IConnectionResult = {
connected: expectedConnection,
errorMessage: undefined,
errorCode: undefined,
callStack: undefined
};
connect(uri, options, false, connectionProfileWithEmptySavedPassword).then(result => {
assert.equal(result.connected, expectedConnection);
assert.equal(result.errorMessage, connectionResult.errorMessage);
verifyShowConnectionDialog(connectionProfileWithEmptySavedPassword, ConnectionType.editor, uri, true, connectionResult, false);
done();
}).catch(err => {
done(err);
});
});
test('doChangeLanguageFlavor should throw on unknown provider', done => {
// given a provider that will never exist
let invalidProvider = 'notaprovider';
// when I call doChangeLanguageFlavor
// Then I expect it to throw
assert.throws(() => connectionManagementService.doChangeLanguageFlavor('file://my.sql', 'sql', invalidProvider));
done();
});
test('doChangeLanguageFlavor should send event for known provider', done => {
// given a provider that is registered
let uri = 'file://my.sql';
let language = 'sql';
let flavor = 'MSSQL';
// when I call doChangeLanguageFlavor
try {
let called = false;
connectionManagementService.onLanguageFlavorChanged((changeParams: azdata.DidChangeLanguageFlavorParams) => {
called = true;
assert.equal(changeParams.uri, uri);
assert.equal(changeParams.language, language);
assert.equal(changeParams.flavor, flavor);
});
connectionManagementService.doChangeLanguageFlavor(uri, language, flavor);
assert.ok(called, 'expected onLanguageFlavorChanged event to be sent');
done();
} catch (error) {
done(error);
}
});
test('ensureDefaultLanguageFlavor should not send event if uri is connected', done => {
let uri: string = 'Editor Uri';
let options: IConnectionCompletionOptions = {
params: undefined,
saveTheConnection: false,
showDashboard: false,
showConnectionDialogOnError: false,
showFirewallRuleOnError: true
};
let connectionManagementService = createConnectionManagementService();
let called = false;
connectionManagementService.onLanguageFlavorChanged((changeParams: azdata.DidChangeLanguageFlavorParams) => {
called = true;
});
connect(uri, options).then(() => {
connectionManagementService.ensureDefaultLanguageFlavor(uri);
assert.equal(called, false, 'do not expect flavor change to be called');
done();
}).catch(err => {
done(err);
});
});
test('getConnectionId returns the URI associated with a connection that has had its database filled in', done => {
// Set up the connection management service with a connection corresponding to a default database
let dbName = 'master';
let serverName = 'test_server';
let userName = 'test_user';
let connectionProfileWithoutDb: IConnectionProfile = Object.assign(connectionProfile,
{ serverName: serverName, databaseName: '', userName: userName, getOptionsKey: () => undefined });
let connectionProfileWithDb: IConnectionProfile = Object.assign(connectionProfileWithoutDb, { databaseName: dbName });
// Save the database with a URI that has the database name filled in, to mirror Carbon's behavior
let ownerUri = Utils.generateUri(connectionProfileWithDb);
connect(ownerUri, undefined, false, connectionProfileWithoutDb).then(() => {
try {
// If I get the URI for the connection with or without a database from the connection management service
let actualUriWithDb = connectionManagementService.getConnectionUri(connectionProfileWithDb);
let actualUriWithoutDb = connectionManagementService.getConnectionUri(connectionProfileWithoutDb);
// Then the retrieved URIs should match the one on the connection
let expectedUri = Utils.generateUri(connectionProfileWithoutDb);
assert.equal(actualUriWithDb, expectedUri);
assert.equal(actualUriWithoutDb, expectedUri);
done();
} catch (err) {
done(err);
}
}, err => done(err));
});
test('getTabColorForUri returns undefined when there is no connection for the given URI', () => {
let connectionManagementService = createConnectionManagementService();
let color = connectionManagementService.getTabColorForUri('invalidUri');
assert.equal(color, undefined);
});
test('getTabColorForUri returns the group color corresponding to the connection for a URI', done => {
// Set up the connection store to give back a group for the expected connection profile
configResult['tabColorMode'] = 'border';
let expectedColor = 'red';
connectionStore.setup(x => x.getGroupFromId(connectionProfile.groupId)).returns(() => <IConnectionProfileGroup>{
color: expectedColor
});
let uri = 'testUri';
connect(uri).then(() => {
try {
let tabColor = connectionManagementService.getTabColorForUri(uri);
assert.equal(tabColor, expectedColor);
done();
} catch (e) {
done(e);
}
}, err => done(err));
});
test('getActiveConnectionCredentials returns the credentials dictionary for a connection profile', () => {
let profile = Object.assign({}, connectionProfile);
profile.options = { password: profile.password };
profile.id = 'test_id';
connectionStatusManager.addConnection(profile, 'test_uri');
(connectionManagementService as any)._connectionStatusManager = connectionStatusManager;
let credentials = connectionManagementService.getActiveConnectionCredentials(profile.id);
assert.equal(credentials['password'], profile.options['password']);
});
test('getConnectionUriFromId returns a URI of an active connection with the given id', () => {
let profile = Object.assign({}, connectionProfile);
profile.options = { password: profile.password };
profile.id = 'test_id';
let uri = 'test_initial_uri';
connectionStatusManager.addConnection(profile, uri);
(connectionManagementService as any)._connectionStatusManager = connectionStatusManager;
// If I call getConnectionUriFromId on the given connection
let foundUri = connectionManagementService.getConnectionUriFromId(profile.id);
// Then the returned URI matches the connection's original URI
assert.equal(foundUri, uri);
});
test('getConectionUriFromId returns undefined if the given connection is not active', () => {
let profile = Object.assign({}, connectionProfile);
profile.options = { password: profile.password };
profile.id = 'test_id';
connectionStatusManager.addConnection(profile, Utils.generateUri(profile));
(connectionManagementService as any)._connectionStatusManager = connectionStatusManager;
// If I call getConnectionUriFromId with a different URI than the connection's
let foundUri = connectionManagementService.getConnectionUriFromId('different_id');
// Then undefined is returned
assert.equal(foundUri, undefined);
});
test('addSavedPassword fills in Azure access tokens for Azure accounts', async () => {
// Set up a connection profile that uses Azure
let azureConnectionProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile);
azureConnectionProfile.authenticationType = 'AzureMFA';
let username = 'testuser@microsoft.com';
azureConnectionProfile.userName = username;
let servername = 'test-database.database.windows.net';
azureConnectionProfile.serverName = servername;
// Set up the account management service to return a token for the given user
accountManagementService.setup(x => x.getAccountsForProvider(TypeMoq.It.isAny())).returns(providerId => Promise.resolve<azdata.Account[]>([
{
key: {
accountId: username,
providerId: providerId
},
displayInfo: undefined,
isStale: false,
properties: undefined
}
]));
let testToken = 'testToken';
accountManagementService.setup(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({
azurePublicCloud: {
token: testToken
}
}));
connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is(profile => profile.authenticationType === 'AzureMFA'))).returns(profile => Promise.resolve({
profile: profile,
savedCred: false
}));
// If I call addSavedPassword
let profileWithCredentials = await connectionManagementService.addSavedPassword(azureConnectionProfile);
// Then the returned profile has the account token set
assert.equal(profileWithCredentials.userName, username);
assert.equal(profileWithCredentials.options['azureAccountToken'], testToken);
});
test('addSavedPassword fills in Azure access token for selected tenant', async () => {
// Set up a connection profile that uses Azure
let azureConnectionProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile);
azureConnectionProfile.authenticationType = 'AzureMFA';
let username = 'testuser@microsoft.com';
azureConnectionProfile.userName = username;
let servername = 'test-database.database.windows.net';
azureConnectionProfile.serverName = servername;
let azureTenantId = 'testTenant';
azureConnectionProfile.azureTenantId = azureTenantId;
// Set up the account management service to return a token for the given user
accountManagementService.setup(x => x.getAccountsForProvider(TypeMoq.It.isAny())).returns(providerId => Promise.resolve<azdata.Account[]>([
{
key: {
accountId: username,
providerId: providerId
},
displayInfo: undefined,
isStale: false,
properties: undefined
}
]));
let testToken = 'testToken';
let returnedTokens = {};
returnedTokens['azurePublicCloud'] = { token: 'badToken' };
returnedTokens[azureTenantId] = { token: testToken };
accountManagementService.setup(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(returnedTokens));
connectionStore.setup(x => x.addSavedPassword(TypeMoq.It.is(profile => profile.authenticationType === 'AzureMFA'))).returns(profile => Promise.resolve({
profile: profile,
savedCred: false
}));
// If I call addSavedPassword
let profileWithCredentials = await connectionManagementService.addSavedPassword(azureConnectionProfile);
// Then the returned profile has the account token set corresponding to the requested tenant
assert.equal(profileWithCredentials.userName, username);
assert.equal(profileWithCredentials.options['azureAccountToken'], testToken);
});
test('getConnections test', () => {
const connectionStatusManagerMock = TypeMoq.Mock.ofType(ConnectionStatusManager, TypeMoq.MockBehavior.Loose);
const connectionStoreMock = TypeMoq.Mock.ofType(ConnectionStore, TypeMoq.MockBehavior.Loose, new TestStorageService());
connectionStatusManagerMock.setup(x => x.getActiveConnectionProfiles(undefined)).returns(() => {
return [createConnectionProfile('1'), createConnectionProfile('2')];
});
connectionStoreMock.setup(x => x.getRecentlyUsedConnections(undefined)).returns(() => {
return [createConnectionProfile('1'), createConnectionProfile('3')];
});
const group1 = createConnectionGroup('group1');
const group2 = createConnectionGroup('group2');
group1.connections = [createConnectionProfile('1'), createConnectionProfile('4')];
group1.children = [group2];
group2.connections = [createConnectionProfile('5'), createConnectionProfile('6')];
connectionStoreMock.setup(x => x.getConnectionProfileGroups(TypeMoq.It.isAny(), undefined)).returns(() => {
return [group1];
});
const connectionManagementService = new ConnectionManagementService(connectionStoreMock.object, connectionStatusManagerMock.object, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
// dupe connections have been seeded the numbers below already reflected the de-duped results
const verifyConnections = (actualConnections: ConnectionProfile[], expectedConnectionIds: string[], scenario: string) => {
assert.equal(actualConnections.length, expectedConnectionIds.length, 'incorrect number of connections returned, ' + scenario);
assert.deepEqual(actualConnections.map(conn => conn.id).sort(), expectedConnectionIds.sort(), 'connections do not match expectation, ' + scenario);
};
// no parameter - default to false
let connections = connectionManagementService.getConnections();
verifyConnections(connections, ['1', '2', '3', '4', '5', '6'], 'no parameter provided');
// explicitly set to false
connections = connectionManagementService.getConnections(false);
verifyConnections(connections, ['1', '2', '3', '4', '5', '6'], 'parameter is false');
// active connections only
connections = connectionManagementService.getConnections(true);
verifyConnections(connections, ['1', '2'], 'parameter is true');
});
});
function createConnectionProfile(id: string): ConnectionProfile {
const capabilitiesService = new TestCapabilitiesService();
return new ConnectionProfile(capabilitiesService, {
connectionName: 'newName',
savePassword: false,
groupFullName: 'testGroup',
serverName: 'testServerName',
databaseName: 'testDatabaseName',
authenticationType: Constants.integrated,
password: 'test',
userName: 'testUsername',
groupId: undefined,
providerName: Constants.mssqlProviderName,
options: {},
saveProfile: true,
id: id
});
}
function createConnectionGroup(id: string): ConnectionProfileGroup {
return new ConnectionProfileGroup(id, undefined, id, undefined, undefined);
}

View File

@@ -0,0 +1,222 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { IConnectionProfile, IConnectionProfileStore } from 'sql/platform/connection/common/interfaces';
import * as azdata from 'azdata';
import * as assert from 'assert';
import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/common/connectionProviderExtension';
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
suite('SQL ConnectionProfileInfo tests', () => {
let msSQLCapabilities: ConnectionProviderProperties;
let capabilitiesService: TestCapabilitiesService;
let connectionProfile: IConnectionProfile = {
connectionName: 'new name',
serverName: 'new server',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g2/g2-2',
groupId: 'group id',
getOptionsKey: undefined,
matches: undefined,
providerName: mssqlProviderName,
options: {},
saveProfile: true,
id: undefined
};
let storedProfile: IConnectionProfileStore = {
groupId: 'groupId',
id: 'id',
options: {
connectionName: 'new name',
serverName: 'new server',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: ''
},
providerName: mssqlProviderName,
savePassword: true
};
setup(() => {
let connectionProvider: azdata.ConnectionOption[] = [
{
name: 'connectionName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.connectionName,
valueType: ServiceOptionType.string
},
{
name: 'serverName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.serverName,
valueType: ServiceOptionType.string
},
{
name: 'databaseName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.databaseName,
valueType: ServiceOptionType.string
},
{
name: 'userName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.userName,
valueType: ServiceOptionType.string
},
{
name: 'authenticationType',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.authType,
valueType: ServiceOptionType.string
},
{
name: 'password',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.password,
valueType: ServiceOptionType.string
}
];
msSQLCapabilities = {
providerId: mssqlProviderName,
displayName: 'MSSQL',
connectionOptions: connectionProvider
};
capabilitiesService = new TestCapabilitiesService();
capabilitiesService.capabilities[mssqlProviderName] = { connection: msSQLCapabilities };
});
test('set properties should set the values correctly', () => {
let conn = new ConnectionProfile(capabilitiesService, undefined);
assert.equal(conn.serverName, undefined);
conn.connectionName = connectionProfile.connectionName;
conn.serverName = connectionProfile.serverName;
conn.databaseName = connectionProfile.databaseName;
conn.authenticationType = connectionProfile.authenticationType;
conn.password = connectionProfile.password;
conn.userName = connectionProfile.userName;
conn.groupId = connectionProfile.groupId;
conn.groupFullName = connectionProfile.groupFullName;
conn.savePassword = connectionProfile.savePassword;
assert.equal(conn.connectionName, connectionProfile.connectionName);
assert.equal(conn.serverName, connectionProfile.serverName);
assert.equal(conn.databaseName, connectionProfile.databaseName);
assert.equal(conn.authenticationType, connectionProfile.authenticationType);
assert.equal(conn.password, connectionProfile.password);
assert.equal(conn.userName, connectionProfile.userName);
assert.equal(conn.groupId, connectionProfile.groupId);
assert.equal(conn.groupFullName, connectionProfile.groupFullName);
assert.equal(conn.savePassword, connectionProfile.savePassword);
});
test('constructor should initialize the options given a valid model', () => {
let conn = new ConnectionProfile(capabilitiesService, connectionProfile);
assert.equal(conn.connectionName, connectionProfile.connectionName);
assert.equal(conn.serverName, connectionProfile.serverName);
assert.equal(conn.databaseName, connectionProfile.databaseName);
assert.equal(conn.authenticationType, connectionProfile.authenticationType);
assert.equal(conn.password, connectionProfile.password);
assert.equal(conn.userName, connectionProfile.userName);
assert.equal(conn.groupId, connectionProfile.groupId);
assert.equal(conn.groupFullName, connectionProfile.groupFullName);
assert.equal(conn.savePassword, connectionProfile.savePassword);
});
test('getOptionsKey should create a valid unique id', () => {
let conn = new ConnectionProfile(capabilitiesService, connectionProfile);
let expectedId = 'providerName:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user|databaseDisplayName:database|group:group id';
let id = conn.getOptionsKey();
assert.equal(id, expectedId);
});
test('createFromStoredProfile should create connection profile from stored profile', () => {
let savedProfile = storedProfile;
let connectionProfile = ConnectionProfile.createFromStoredProfile(savedProfile, capabilitiesService);
assert.equal(savedProfile.groupId, connectionProfile.groupId);
assert.deepEqual(savedProfile.providerName, connectionProfile.providerName);
assert.deepEqual(savedProfile.savePassword, connectionProfile.savePassword);
assert.deepEqual(savedProfile.id, connectionProfile.id);
});
test('createFromStoredProfile should set the id to new guid if not set in stored profile', () => {
let savedProfile = Object.assign({}, storedProfile, { id: undefined });
let connectionProfile = ConnectionProfile.createFromStoredProfile(savedProfile, capabilitiesService);
assert.equal(savedProfile.groupId, connectionProfile.groupId);
assert.deepEqual(savedProfile.providerName, connectionProfile.providerName);
assert.equal(savedProfile.savePassword, connectionProfile.savePassword);
assert.notEqual(connectionProfile.id, undefined);
assert.equal(savedProfile.id, undefined);
});
test('withoutPassword should create a new instance without password', () => {
let conn = new ConnectionProfile(capabilitiesService, connectionProfile);
assert.notEqual(conn.password, '');
let withoutPassword = conn.withoutPassword();
assert.equal(withoutPassword.password, '');
});
test('unique id should not include password', () => {
let conn = new ConnectionProfile(capabilitiesService, connectionProfile);
let withoutPassword = conn.withoutPassword();
assert.equal(withoutPassword.getOptionsKey(), conn.getOptionsKey());
});
test('cloneWithDatabase should create new profile with new id', () => {
let conn = new ConnectionProfile(capabilitiesService, connectionProfile);
let newProfile = conn.cloneWithDatabase('new db');
assert.notEqual(newProfile.id, conn.id);
assert.equal(newProfile.databaseName, 'new db');
});
test('an empty connection profile does not cause issues', () => {
assert.doesNotThrow(() => new ConnectionProfile(capabilitiesService, {} as IConnectionProfile));
});
});

View File

@@ -0,0 +1,145 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import * as assert from 'assert';
suite('SQL ConnectionProfileGroup tests', () => {
let root: ConnectionProfileGroup;
let Groups1 = 'G1';
let Groups11 = 'G11';
let Groups2 = 'G2';
let group1Node: ConnectionProfileGroup;
let group11Node: ConnectionProfileGroup;
let group2Node: ConnectionProfileGroup;
setup(() => {
root = new ConnectionProfileGroup(ConnectionProfileGroup.RootGroupName, undefined, ConnectionProfileGroup.RootGroupName, undefined, undefined);
group1Node = new ConnectionProfileGroup(Groups1, root, Groups1, undefined, undefined);
group2Node = new ConnectionProfileGroup(Groups2, root, Groups2, undefined, undefined);
group11Node = new ConnectionProfileGroup(Groups11, root, Groups11, undefined, undefined);
root.addGroups([group1Node]);
group1Node.addGroups([group11Node]);
root.addGroups([group2Node]);
});
test('Root name should be returned as empty string', () => {
assert.equal(root.name, '');
});
test('Fullname should return the group full name correctly', () => {
assert.equal(group1Node.fullName, 'G1');
assert.equal(group2Node.fullName, 'G2');
assert.equal(group11Node.fullName, 'G1/G11');
});
test('getGroupFullNameParts should return a list With ROOT in it given an empty string', () => {
let groupFullName: string = '';
let expected: string[] = [ConnectionProfileGroup.RootGroupName];
let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
assert.deepEqual(actual, expected);
});
test('getGroupFullNameParts should return a list With ROOT in it given null', () => {
let groupFullName: string = undefined;
let expected: string[] = [ConnectionProfileGroup.RootGroupName];
let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
assert.deepEqual(actual, expected);
});
test('getGroupFullNameParts should return a list With ROOT in it given /', () => {
let groupFullName: string = '/';
let expected: string[] = [ConnectionProfileGroup.RootGroupName];
let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
assert.deepEqual(actual, expected);
});
test('getGroupFullNameParts should add ROOT as first item if not added already and string starts with /', () => {
let groupFullName: string = '/Groups/Group1';
let expected: string[] = [ConnectionProfileGroup.RootGroupName, 'Groups', 'Group1'];
let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
assert.deepEqual(actual, expected);
});
test('getGroupFullNameParts should add ROOT as first item if not added already', () => {
let groupFullName: string = 'Groups/Group1';
let expected: string[] = [ConnectionProfileGroup.RootGroupName, 'Groups', 'Group1'];
let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
assert.deepEqual(actual, expected);
});
test('getGroupFullNameParts should not add ROOT if already added and string starts with /', () => {
let groupFullName: string = '/ROOT/Groups/Group1';
let expected: string[] = [ConnectionProfileGroup.RootGroupName, 'Groups', 'Group1'];
let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
assert.deepEqual(actual, expected);
});
test('getGroupFullNameParts should not add ROOT if already added', () => {
let groupFullName: string = 'ROOT/Groups/Group1';
let expected: string[] = [ConnectionProfileGroup.RootGroupName, 'Groups', 'Group1'];
let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
assert.deepEqual(actual, expected);
});
test('getGroupFullNameParts should not add ROOT if already added and it is not uppercase', () => {
let groupFullName: string = 'rOOT/Groups/Group1';
let expected: string[] = [ConnectionProfileGroup.RootGroupName, 'Groups', 'Group1'];
let actual = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
assert.deepEqual(actual, expected);
});
test('isRoot should return true given empty string', () => {
let name: string = '';
let expected: boolean = true;
let actual = ConnectionProfileGroup.isRoot(name);
assert.deepEqual(actual, expected);
});
test('isRoot should return true given null', () => {
let name: string = undefined;
let expected: boolean = true;
let actual = ConnectionProfileGroup.isRoot(name);
assert.deepEqual(actual, expected);
});
test('isRoot should return true given /', () => {
let name: string = '/';
let expected: boolean = true;
let actual = ConnectionProfileGroup.isRoot(name);
assert.deepEqual(actual, expected);
});
test('isRoot should return true given root', () => {
let name: string = 'root';
let expected: boolean = true;
let actual = ConnectionProfileGroup.isRoot(name);
assert.deepEqual(actual, expected);
});
test('sameGroupName should return true given root', () => {
let name1: string = '/';
let name2: string = '';
let expected: boolean = true;
let actual = ConnectionProfileGroup.sameGroupName(name1, name2);
assert.deepEqual(actual, expected);
});
test('sameGroupName should return true given same group names', () => {
let name1: string = '/group1';
let name2: string = '/Group1';
let expected: boolean = true;
let actual = ConnectionProfileGroup.sameGroupName(name1, name2);
assert.deepEqual(actual, expected);
});
test('sameGroupName should return false given two different groups', () => {
let name1: string = '/';
let name2: string = '/Group1';
let expected: boolean = false;
let actual = ConnectionProfileGroup.sameGroupName(name1, name2);
assert.deepEqual(actual, expected);
});
});

View File

@@ -0,0 +1,258 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as azdata from 'azdata';
import { ConnectionStatusManager } from 'sql/platform/connection/common/connectionStatusManager';
import * as Utils from 'sql/platform/connection/common/utils';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
import { NullLogService } from 'vs/platform/log/common/log';
let connections: ConnectionStatusManager;
let capabilitiesService: TestCapabilitiesService;
let connectionProfileObject: ConnectionProfile;
let connectionProfile: IConnectionProfile = {
connectionName: 'new name',
serverName: 'new server',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g2/g2-2',
groupId: 'group id',
getOptionsKey: () => 'connection1',
matches: undefined,
providerName: mssqlProviderName,
options: {},
saveProfile: true,
id: undefined
};
let editorConnectionProfile: IConnectionProfile = {
connectionName: 'new name',
serverName: 'new server',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g2/g2-2',
groupId: 'group id',
getOptionsKey: () => 'connection2',
matches: undefined,
providerName: mssqlProviderName,
options: {},
saveProfile: true,
id: undefined
};
let connectionProfileWithoutDbName: IConnectionProfile = {
connectionName: 'new name',
serverName: 'new server',
databaseName: '',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g2/g2-2',
groupId: 'group id',
getOptionsKey: () => 'connection1',
matches: undefined,
providerName: mssqlProviderName,
options: {},
saveProfile: true,
id: undefined
};
let connection1Id: string;
let connection2Id: string;
let connection3Id: string;
suite('SQL ConnectionStatusManager tests', () => {
setup(() => {
capabilitiesService = new TestCapabilitiesService();
connectionProfileObject = new ConnectionProfile(capabilitiesService, connectionProfile);
connections = new ConnectionStatusManager(capabilitiesService, new NullLogService(), TestEnvironmentService, new TestNotificationService());
connection1Id = Utils.generateUri(connectionProfile);
connection2Id = 'connection2Id';
connection3Id = 'connection3Id';
connections.addConnection(connectionProfile, connection1Id);
connections.addConnection(editorConnectionProfile, connection2Id);
connections.addConnection(connectionProfileWithoutDbName, connection3Id);
});
test('findConnection should return undefined given invalid id', () => {
let id: string = 'invalid id';
let expected = undefined;
let actual = connections.findConnection(id);
assert.equal(actual, expected);
});
test('findConnection should return connection given valid id', () => {
let id: string = connection1Id;
let expected = connectionProfileObject;
let actual = connections.findConnection(id);
assert.equal(connectionProfileObject.matches(actual.connectionProfile), true);
});
test('getConnectionProfile should return undefined given invalid id', () => {
let id: string = 'invalid id';
let expected = undefined;
let actual = connections.getConnectionProfile(id);
assert.equal(actual, expected);
});
test('getConnectionProfile should return connection given valid id', () => {
let id: string = connection1Id;
let expected = connectionProfileObject;
let actual = connections.getConnectionProfile(id);
assert.equal(connectionProfileObject.matches(actual), true);
});
test('hasConnection should return false given invalid id', () => {
let id: string = 'invalid id';
let expected = false;
let actual = connections.hasConnection(id);
assert.equal(actual, expected);
});
test('hasConnection should return true given valid id', () => {
let id: string = connection1Id;
let expected = true;
let actual = connections.hasConnection(id);
assert.equal(actual, expected);
});
test('addConnection should set connecting to true', () => {
let expected = true;
let summary: azdata.ConnectionInfoSummary = {
ownerUri: connection1Id,
connectionId: connection1Id,
messages: undefined,
errorMessage: undefined,
errorNumber: undefined,
serverInfo: undefined,
connectionSummary: undefined
};
connections.onConnectionComplete(summary);
let actual = connections.addConnection(connectionProfile, connection1Id).connecting;
assert.equal(actual, expected);
});
test('onConnectionComplete should set connecting to false', () => {
let expected = false;
let summary: azdata.ConnectionInfoSummary = {
ownerUri: connection1Id,
connectionId: connection1Id,
messages: undefined,
errorMessage: undefined,
errorNumber: undefined,
serverInfo: undefined,
connectionSummary: undefined
};
connections.onConnectionComplete(summary);
let actual = connections.findConnection(connection1Id).connecting;
assert.equal(actual, expected);
actual = connections.isConnecting(connection1Id);
assert.equal(actual, expected);
});
test('updateConnection should update the connection info', () => {
let expected = connectionProfile.groupId + '1';
let expectedConnectionId = 'new id';
connections.addConnection(connectionProfile, connection1Id);
let updatedConnection = Object.assign({}, connectionProfile, { groupId: expected, getOptionsKey: () => connectionProfile.getOptionsKey() + expected, id: expectedConnectionId });
let actualId = connections.updateConnectionProfile(updatedConnection, connection1Id);
let newId = Utils.generateUri(updatedConnection);
let actual = connections.getConnectionProfile(newId).groupId;
let actualConnectionId = connections.getConnectionProfile(newId).id;
assert.equal(actual, expected);
assert.equal(actualId, newId);
assert.equal(actualConnectionId, expectedConnectionId);
});
test('updateDatabaseName should update the database name in connection', () => {
let dbName: string = 'db name';
let summary: azdata.ConnectionInfoSummary = {
connectionSummary: {
databaseName: dbName,
serverName: undefined,
userName: undefined
}
, ownerUri: connection3Id,
connectionId: 'connection id',
errorMessage: undefined,
errorNumber: undefined,
messages: undefined,
serverInfo: undefined
};
//The original connection didn't have database name
let connectionStatus = connections.findConnection(connection3Id);
connectionStatus.connectionProfile.databaseName = '';
//Verify database name changed after connection is complete
connections.updateDatabaseName(summary);
connectionStatus = connections.findConnection(connection3Id);
assert.equal(connectionStatus.connectionProfile.databaseName, dbName);
});
test('getOriginalOwnerUri should return the original uri given uri with db name', () => {
let dbName: string = 'db name';
let summary: azdata.ConnectionInfoSummary = {
connectionSummary: {
databaseName: dbName,
serverName: undefined,
userName: undefined
}
, ownerUri: connection3Id,
connectionId: 'connection id',
errorMessage: undefined,
errorNumber: undefined,
messages: undefined,
serverInfo: undefined
};
//The original connection didn't have database name
let connectionStatus = connections.findConnection(connection3Id);
connectionStatus.connectionProfile.databaseName = '';
//Verify database name changed after connection is complete
connections.updateDatabaseName(summary);
connectionStatus = connections.findConnection(connection3Id);
let ownerUriWithDbName = Utils.generateUriWithPrefix(connectionStatus.connectionProfile, 'connection://');
//The uri assigned to connection without db name should be the original one
let connectionWitDbStatus = connections.getOriginalOwnerUri(ownerUriWithDbName);
assert.equal(connectionWitDbStatus, connection3Id);
});
test('getOriginalOwnerUri should return given uri if the original uri is the same as the given uri', () => {
let connectionStatus = connections.getOriginalOwnerUri(connection2Id);
assert.equal(connectionStatus, connection2Id);
});
test('getActiveConnectionProfiles should return a list of all the unique connections that the status manager knows about', () => {
// Add duplicate connections
let newConnection = Object.assign({}, connectionProfile);
newConnection.id = 'test_id';
newConnection.serverName = 'new_server_name';
newConnection.options['databaseDisplayName'] = newConnection.databaseName;
connections.addConnection(newConnection, 'test_uri_1');
connections.addConnection(newConnection, 'test_uri_2');
// Get the connections and verify that the duplicate is only returned once
let activeConnections = connections.getActiveConnectionProfiles();
assert.equal(activeConnections.length, 4);
assert.equal(activeConnections.filter(connection => connection.matches(newConnection)).length, 1, 'Did not find newConnection in active connections');
});
});

View File

@@ -13,7 +13,7 @@ import { TestConfigurationService } from 'sql/platform/connection/test/common/te
import { TestCredentialsService } from 'sql/platform/credentials/test/common/testCredentialsService';
import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/common/connectionProviderExtension';
import { CapabilitiesTestService } from 'sqltest/stubs/capabilitiesTestService';
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
import { deepClone, deepFreeze } from 'vs/base/common/objects';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
@@ -37,7 +37,7 @@ suite('ConnectionStore', () => {
saveProfile: true,
id: undefined
});
let capabilitiesService: CapabilitiesTestService;
let capabilitiesService: TestCapabilitiesService;
let maxRecent = 5;
let msSQLCapabilities: ConnectionProviderProperties;
let provider2Capabilities: ConnectionProviderProperties;
@@ -46,7 +46,7 @@ suite('ConnectionStore', () => {
setup(() => {
// setup configuration to return maxRecent for the #MRU items
capabilitiesService = new CapabilitiesTestService();
capabilitiesService = new TestCapabilitiesService();
let connectionProvider: azdata.ConnectionOption[] = [
{
name: 'connectionName',

View File

@@ -0,0 +1,263 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ProviderConnectionInfo } from 'sql/platform/connection/common/providerConnectionInfo';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import * as azdata from 'azdata';
import * as assert from 'assert';
import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
suite('SQL ProviderConnectionInfo tests', () => {
let msSQLCapabilities: any;
let capabilitiesService: TestCapabilitiesService;
let connectionProfile: IConnectionProfile = {
connectionName: 'name',
serverName: 'new server',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g2/g2-2',
groupId: undefined,
getOptionsKey: undefined,
matches: undefined,
providerName: mssqlProviderName,
options: undefined,
saveProfile: true,
id: undefined
};
setup(() => {
let capabilities: azdata.DataProtocolServerCapabilities[] = [];
let connectionProvider: azdata.ConnectionOption[] = [
{
name: 'connectionName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.connectionName,
valueType: ServiceOptionType.string
},
{
name: 'serverName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.serverName,
valueType: ServiceOptionType.string
},
{
name: 'databaseName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.databaseName,
valueType: ServiceOptionType.string
},
{
name: 'userName',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.userName,
valueType: ServiceOptionType.string
},
{
name: 'authenticationType',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.authType,
valueType: ServiceOptionType.string
},
{
name: 'password',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: true,
isRequired: true,
specialValueType: ConnectionOptionSpecialType.password,
valueType: ServiceOptionType.string
},
{
name: 'encrypt',
displayName: undefined,
description: undefined,
groupName: undefined,
categoryValues: undefined,
defaultValue: undefined,
isIdentity: false,
isRequired: false,
specialValueType: undefined,
valueType: ServiceOptionType.string
}
];
msSQLCapabilities = {
providerId: mssqlProviderName,
displayName: 'MSSQL',
connectionOptions: connectionProvider,
};
capabilities.push(msSQLCapabilities);
capabilitiesService = new TestCapabilitiesService();
capabilitiesService.capabilities[mssqlProviderName] = { connection: msSQLCapabilities };
});
test('constructor should accept undefined parameters', () => {
let conn = new ProviderConnectionInfo(undefined, undefined);
assert.equal(conn.serverName, undefined);
});
test('set properties should set the values correctly', () => {
let conn = new ProviderConnectionInfo(capabilitiesService, mssqlProviderName);
assert.equal(conn.serverName, undefined);
conn.connectionName = connectionProfile.connectionName;
conn.serverName = connectionProfile.serverName;
conn.databaseName = connectionProfile.databaseName;
conn.authenticationType = connectionProfile.authenticationType;
conn.password = connectionProfile.password;
conn.userName = connectionProfile.userName;
assert.equal(conn.connectionName, connectionProfile.connectionName);
assert.equal(conn.serverName, connectionProfile.serverName);
assert.equal(conn.databaseName, connectionProfile.databaseName);
assert.equal(conn.authenticationType, connectionProfile.authenticationType);
assert.equal(conn.password, connectionProfile.password);
assert.equal(conn.userName, connectionProfile.userName);
});
test('set properties should store the values in the options', () => {
let conn = new ProviderConnectionInfo(capabilitiesService, mssqlProviderName);
assert.equal(conn.serverName, undefined);
conn.serverName = connectionProfile.serverName;
conn.databaseName = connectionProfile.databaseName;
conn.authenticationType = connectionProfile.authenticationType;
conn.password = connectionProfile.password;
conn.userName = connectionProfile.userName;
assert.equal(conn.getOptionValue('serverName'), connectionProfile.serverName);
assert.equal(conn.getOptionValue('databaseName'), connectionProfile.databaseName);
assert.equal(conn.getOptionValue('authenticationType'), connectionProfile.authenticationType);
assert.equal(conn.getOptionValue('password'), connectionProfile.password);
assert.equal(conn.getOptionValue('userName'), connectionProfile.userName);
});
test('constructor should initialize the options given a valid model', () => {
let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
assert.equal(conn.connectionName, connectionProfile.connectionName);
assert.equal(conn.serverName, connectionProfile.serverName);
assert.equal(conn.databaseName, connectionProfile.databaseName);
assert.equal(conn.authenticationType, connectionProfile.authenticationType);
assert.equal(conn.password, connectionProfile.password);
assert.equal(conn.userName, connectionProfile.userName);
});
test('clone should create a new instance that equals the old one', () => {
let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
let conn2 = conn.clone();
assert.equal(conn.connectionName, conn2.connectionName);
assert.equal(conn.serverName, conn2.serverName);
assert.equal(conn.databaseName, conn2.databaseName);
assert.equal(conn.authenticationType, conn2.authenticationType);
assert.equal(conn.password, conn2.password);
assert.equal(conn.userName, conn2.userName);
});
test('Changing the cloned object should not change the original one', () => {
let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
let conn2 = conn.clone();
conn2.serverName = conn.serverName + '1';
assert.notEqual(conn.serverName, conn2.serverName);
});
test('constructor should initialize the options given a valid model with options', () => {
let options = {};
options['encrypt'] = 'test value';
let conn2 = Object.assign({}, connectionProfile, { options: options });
let conn = new ProviderConnectionInfo(capabilitiesService, conn2);
assert.equal(conn.connectionName, conn2.connectionName);
assert.equal(conn.serverName, conn2.serverName);
assert.equal(conn.databaseName, conn2.databaseName);
assert.equal(conn.authenticationType, conn2.authenticationType);
assert.equal(conn.password, conn2.password);
assert.equal(conn.userName, conn2.userName);
assert.equal(conn.options['encrypt'], 'test value');
});
test('getOptionsKey should create a valid unique id', () => {
let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
let expectedId = 'providerName:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user';
let id = conn.getOptionsKey();
assert.equal(id, expectedId);
});
test('getOptionsKey should create different id for different server names', () => {
let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
let conn2 = new ProviderConnectionInfo(capabilitiesService, Object.assign({}, connectionProfile, { serverName: connectionProfile.serverName + '1' }));
assert.notEqual(conn.getOptionsKey(), conn2.getOptionsKey());
});
test('titleParts should return server, database and auth type as first items', () => {
let conn = new ProviderConnectionInfo(capabilitiesService, connectionProfile);
let titleParts = conn.titleParts;
assert.equal(titleParts.length, 4);
assert.equal(titleParts[0], connectionProfile.serverName);
assert.equal(titleParts[1], connectionProfile.databaseName);
assert.equal(titleParts[2], connectionProfile.authenticationType);
assert.equal(titleParts[3], connectionProfile.userName);
});
test('getProviderFromOptionsKey should return the provider name from the options key successfully', () => {
let optionsKey = `providerName:${mssqlProviderName}|authenticationType:|databaseName:database|serverName:new server|userName:user`;
let actual = ProviderConnectionInfo.getProviderFromOptionsKey(optionsKey);
assert.equal(mssqlProviderName, actual);
});
test('getProviderFromOptionsKey should return empty string give null', () => {
let optionsKey = undefined;
let expectedProviderId: string = '';
let actual = ProviderConnectionInfo.getProviderFromOptionsKey(optionsKey);
assert.equal(expectedProviderId, actual);
});
test('getProviderFromOptionsKey should return empty string give key without provider name', () => {
let optionsKey = 'providerName2:MSSQL|authenticationType:|databaseName:database|serverName:new server|userName:user';
let expectedProviderId: string = '';
let actual = ProviderConnectionInfo.getProviderFromOptionsKey(optionsKey);
assert.equal(expectedProviderId, actual);
});
});

View File

@@ -0,0 +1,297 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IConnectionManagementService, IConnectableInput, IConnectionCompletionOptions, IConnectionCallbacks, IConnectionResult, INewConnectionParams }
from 'sql/platform/connection/common/connectionManagement';
import { IConnectionProfileGroup, ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
import * as azdata from 'azdata';
import { Event, Emitter } from 'vs/base/common/event';
import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/common/connectionProviderExtension';
// Test stubs for commonly used objects
export class TestConnectionManagementService implements IConnectionManagementService {
_serviceBrand: any;
onAddConnectionProfile = undefined;
onDeleteConnectionProfile = undefined;
onConnectionChanged = undefined;
onLanguageFlavorChanged = undefined;
public get onConnect(): Event<any> {
let conEvent = new Emitter<any>();
return conEvent.event;
}
public get onDisconnect(): Event<any> {
let conEvent = new Emitter<any>();
return conEvent.event;
}
registerProvider(providerId: string, provider: azdata.ConnectionProvider): void {
}
registerIconProvider(providerId: string, provider: azdata.IconProvider): void {
}
showConnectionDialog(params?: INewConnectionParams, options?: IConnectionCompletionOptions, model?: IConnectionProfile, connectionResult?: IConnectionResult): Promise<void> {
return undefined;
}
showCreateServerGroupDialog(): Promise<void> {
return undefined;
}
showEditServerGroupDialog(group: ConnectionProfileGroup): Promise<void> {
return undefined;
}
onConnectionComplete(handle: number, connectionInfoSummary: azdata.ConnectionInfoSummary): void {
}
onIntelliSenseCacheComplete(handle: number, connectionUri: string): void {
}
public onConnectionChangedNotification(handle: number, changedConnInfo: azdata.ChangedConnectionInfo): void {
}
getCurrentConnectionSummary(): azdata.ConnectionSummary {
return undefined;
}
getConnectionGroups(providers?: string[]): ConnectionProfileGroup[] {
return [];
}
getActiveConnections(providers?: string[]): ConnectionProfile[] {
return [];
}
saveProfileGroup(profile: IConnectionProfileGroup): Promise<string> {
return undefined;
}
getRecentConnections(providers?: string[]): ConnectionProfile[] {
return [];
}
public clearRecentConnectionsList(): void {
return;
}
public clearRecentConnection(connectionProfile: ConnectionProfile): void {
return;
}
getUnsavedConnections(): ConnectionProfile[] {
return [];
}
changeGroupIdForConnectionGroup(source: IConnectionProfileGroup, target: IConnectionProfileGroup): Promise<void> {
return Promise.resolve();
}
changeGroupIdForConnection(source: ConnectionProfile, targetGroupId: string): Promise<void> {
return Promise.resolve();
}
deleteConnection(connection: ConnectionProfile): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
resolve(true);
});
}
deleteConnectionGroup(group: ConnectionProfileGroup): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
resolve(true);
});
}
getAdvancedProperties(): azdata.ConnectionOption[] {
return [];
}
getConnectionUri(connectionProfile: ConnectionProfile): string {
return undefined;
}
getFormattedUri(uri: string, connectionProfile: ConnectionProfile): string {
return undefined;
}
getConnectionUriFromId(connectionId: string): string {
return undefined;
}
isConnected(fileUri: string, connectionProfile?: ConnectionProfile): boolean {
return false;
}
isRecent(connectionProfile: ConnectionProfile): boolean {
return false;
}
isProfileConnected(connectionProfile: IConnectionProfile): boolean {
return false;
}
isProfileConnecting(connectionProfile: IConnectionProfile): boolean {
return false;
}
findExistingConnection(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection'): ConnectionProfile {
return undefined;
}
connect(connection: IConnectionProfile, uri: string, options?: IConnectionCompletionOptions, callbacks?: IConnectionCallbacks): Promise<IConnectionResult> {
return new Promise<IConnectionResult>((resolve, reject) => {
resolve({ connected: true, errorMessage: undefined, errorCode: undefined, callStack: undefined });
});
}
connectAndSaveProfile(connection: IConnectionProfile, uri: string, options?: IConnectionCompletionOptions, callbacks?: IConnectionCallbacks): Promise<IConnectionResult> {
return new Promise<IConnectionResult>(() => true);
}
disconnectEditor(owner: IConnectableInput): Promise<boolean> {
return new Promise<boolean>(() => true);
}
disconnect(connection: IConnectionProfile);
disconnect(uri: string);
disconnect(input: any): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
resolve(true);
});
}
getConnectionProfile(fileUri: string): IConnectionProfile {
return undefined;
}
getConnectionInfo(fileUri: string): ConnectionManagementInfo {
return undefined;
}
addSavedPassword(connectionProfile: IConnectionProfile): Promise<IConnectionProfile> {
return new Promise<IConnectionProfile>(() => connectionProfile);
}
public listDatabases(connectionUri: string): Thenable<azdata.ListDatabasesResult> {
return Promise.resolve(undefined);
}
cancelConnection(connection: IConnectionProfile): Thenable<boolean> {
return undefined;
}
cancelEditorConnection(owner: IConnectableInput): Thenable<boolean> {
return undefined;
}
showDashboard(connection: ConnectionProfile): Promise<boolean> {
return new Promise(() => true);
}
closeDashboard(uri: string): void {
}
changeDatabase(connectionUri: string, databaseName: string): Thenable<boolean> {
return new Promise(() => true);
}
editGroup(group: ConnectionProfileGroup): Promise<void> {
return Promise.resolve();
}
getProviderIdFromUri(ownerUri: string): string {
return undefined;
}
hasRegisteredServers(): boolean {
return true;
}
getCapabilities(providerName: string): azdata.DataProtocolServerCapabilities {
return undefined;
}
canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean {
return true;
}
doChangeLanguageFlavor(uri: string, language: string, flavor: string): void {
}
ensureDefaultLanguageFlavor(uri: string): void {
}
public getProviderNames(): string[] {
return [];
}
connectIfNotConnected(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection', saveConnection: boolean = false): Promise<string> {
return undefined;
}
rebuildIntelliSenseCache(uri: string): Thenable<void> {
return undefined;
}
getTabColorForUri(uri: string): string {
return undefined;
}
removeConnectionProfileCredentials(profile: IConnectionProfile): IConnectionProfile {
return undefined;
}
getActiveConnectionCredentials(profileId: string): { [name: string]: string } {
return undefined;
}
getServerInfo(profileId: string): azdata.ServerInfo {
return undefined;
}
getConnectionString(connectionId: string): Thenable<string> {
return undefined;
}
buildConnectionInfo(connectionString: string, provider?: string): Thenable<azdata.ConnectionInfo> {
return undefined;
}
providerRegistered(providerId: string): boolean {
return undefined;
}
getConnectionProfileById(profileId: string): IConnectionProfile {
return undefined;
}
getProviderProperties(providerName: string): ConnectionProviderProperties {
return undefined;
}
getConnectionIconId(connectionId: string): string {
return undefined;
}
getDefaultProviderId(): string {
return undefined;
}
getConnections(activeConnectionsOnly?: boolean): ConnectionProfile[] {
return [];
}
}

View File

@@ -0,0 +1,51 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
export class TestConnectionProvider implements azdata.ConnectionProvider {
public readonly providerId = mssqlProviderName;
connect(connectionUri: string, connectionInfo: azdata.ConnectionInfo): Thenable<boolean> {
return undefined;
}
disconnect(connectionUri: string): Thenable<boolean> {
return undefined;
}
cancelConnect(connectionUri: string): Thenable<boolean> {
return undefined;
}
listDatabases(connectionUri: string): Thenable<azdata.ListDatabasesResult> {
return undefined;
}
changeDatabase(connectionUri: string, newDatabase: string): Thenable<boolean> {
return undefined;
}
getConnectionString(connectionUri: string): Thenable<string> {
return undefined;
}
rebuildIntelliSenseCache(connectionUri: string): Thenable<void> {
return undefined;
}
registerOnConnectionComplete(handler: (connSummary: azdata.ConnectionInfoSummary) => any) {
return undefined;
}
registerOnIntelliSenseCacheComplete(handler: (connectionUri: string) => any) {
return undefined;
}
registerOnConnectionChanged(handler: (changedConnInfo: azdata.ChangedConnectionInfo) => any) {
return undefined;
}
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { ICredentialsService, CredentialManagementEvents } from 'sql/platform/credentials/common/credentialsService';
import { Credential } from 'azdata';
import { Credential, CredentialProvider } from 'azdata';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Emitter } from 'vs/base/common/event';
@@ -40,3 +40,27 @@ export class TestCredentialsService implements ICredentialsService {
throw new Error('Method not implemented.');
}
}
export class TestCredentialsProvider implements CredentialProvider {
handle: number;
public storedCredentials: { [K: string]: Credential } = {};
saveCredential(credentialId: string, password: string): Thenable<boolean> {
this.storedCredentials[credentialId] = {
credentialId: credentialId,
password: password
};
return Promise.resolve(true);
}
readCredential(credentialId: string): Thenable<Credential> {
return Promise.resolve(this.storedCredentials[credentialId]);
}
deleteCredential(credentialId: string): Thenable<boolean> {
let exists = this.storedCredentials[credentialId] !== undefined;
delete this.storedCredentials[credentialId];
return Promise.resolve(exists);
}
}

View File

@@ -9,10 +9,10 @@ import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
import * as nls from 'vs/nls';
import { IExtensionPointUser, ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { ProviderProperties } from 'sql/workbench/parts/dashboard/widgets/properties/propertiesWidget.component';
import { DATABASE_DASHBOARD_TABS } from 'sql/workbench/parts/dashboard/pages/databaseDashboardPage.contribution';
import { SERVER_DASHBOARD_TABS, SERVER_DASHBOARD_PROPERTIES } from 'sql/workbench/parts/dashboard/pages/serverDashboardPage.contribution';
import { DASHBOARD_CONFIG_ID, DASHBOARD_TABS_KEY_PROPERTY } from 'sql/workbench/parts/dashboard/pages/dashboardPageContribution';
import { ProviderProperties } from 'sql/workbench/parts/dashboard/browser/widgets/properties/propertiesWidget.component';
import { DATABASE_DASHBOARD_TABS } from 'sql/workbench/parts/dashboard/browser/pages/databaseDashboardPage.contribution';
import { SERVER_DASHBOARD_TABS } from 'sql/workbench/parts/dashboard/browser/pages/serverDashboardPage.contribution';
import { DASHBOARD_CONFIG_ID, DASHBOARD_TABS_KEY_PROPERTY } from 'sql/workbench/parts/dashboard/browser/pages/dashboardPageContribution';
export const Extensions = {
DashboardContributions: 'dashboard.contributions'

View File

@@ -3,11 +3,11 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Type } from '@angular/core';
import { IInsightsConfig, IInsightsView } from 'sql/workbench/parts/dashboard/widgets/insights/interfaces';
import * as platform from 'vs/platform/registry/common/platform';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import * as nls from 'vs/nls';
import { IInsightData } from 'sql/workbench/parts/charts/browser/interfaces';
export type InsightIdentifier = string;
@@ -15,6 +15,57 @@ export const Extensions = {
InsightContribution: 'dashboard.contributions.insights'
};
export interface IInsightsConfig {
cacheId?: string;
type: any;
name?: string;
when?: string;
gridItemConfig?: ISize;
query?: string | Array<string>;
queryFile?: string;
details?: IInsightsConfigDetails;
autoRefreshInterval?: number;
}
export interface IInsightsLabel {
column: string;
icon?: string;
state?: Array<IStateCondition>;
}
export interface IStateCondition {
condition: {
if: string,
equals?: string
};
color?: string;
icon?: string;
}
export interface IInsightsConfigDetails {
query?: string | Array<string>;
queryFile?: string;
label?: string | IInsightsLabel;
value?: string;
actions?: {
types: Array<string>;
database?: string;
server?: string;
user?: string;
};
}
export interface ISize {
x: number;
y: number;
}
export interface IInsightsView {
data: IInsightData;
setConfig?: (config: { [key: string]: any }) => void;
init?: () => void;
}
export interface IInsightRegistry {
insightSchema: IJSONSchema;
registerInsight(id: string, description: string, schema: IJSONSchema, ctor: Type<IInsightsView>): InsightIdentifier;

View File

@@ -6,7 +6,7 @@ import { Type } from '@angular/core';
import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
import * as platform from 'vs/platform/registry/common/platform';
import { IComponent } from 'sql/workbench/electron-browser/modelComponents/interfaces';
import { IComponent } from 'sql/workbench/browser/modelComponents/interfaces';
export type ComponentIdentifier = string;

View File

@@ -2,14 +2,11 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IInsightsConfig } from 'sql/workbench/parts/dashboard/widgets/insights/interfaces';
import * as platform from 'vs/platform/registry/common/platform';
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import * as nls from 'vs/nls';
const contributionRegistry = platform.Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
import { IInsightsConfig } from 'sql/platform/dashboard/browser/insightRegistry';
export type WidgetIdentifier = string;

View File

@@ -5,10 +5,6 @@
import { Registry } from 'vs/platform/registry/common/platform';
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
import { Extensions as ConfigurationExtension } from 'vs/platform/configuration/common/configurationRegistry';
import { deepClone } from 'vs/base/common/objects';
import { WidgetConfig } from 'sql/workbench/parts/dashboard/common/dashboardWidget';
export const Extensions = {
dashboardContainerContributions: 'dashboard.contributions.container'

View File

@@ -3,9 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { DialogModal } from 'sql/platform/dialog/dialogModal';
import { WizardModal } from 'sql/platform/dialog/wizardModal';
import { Dialog, Wizard } from 'sql/platform/dialog/dialogTypes';
import { DialogModal } from 'sql/platform/dialog/browser/dialogModal';
import { WizardModal } from 'sql/platform/dialog/browser/wizardModal';
import { Dialog, Wizard } from 'sql/platform/dialog/common/dialogTypes';
import { IModalOptions } from 'sql/workbench/browser/modal/modal';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';

View File

@@ -10,21 +10,22 @@ import { FormsModule } from '@angular/forms';
import { CommonModule, APP_BASE_HREF } from '@angular/common';
import { BrowserModule } from '@angular/platform-browser';
import { DialogContainer } from 'sql/platform/dialog/dialogContainer.component';
import { WizardNavigation } from 'sql/platform/dialog/wizardNavigation.component';
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { ModelViewContent } from 'sql/workbench/electron-browser/modelComponents/modelViewContent.component';
import { ModelComponentWrapper } from 'sql/workbench/electron-browser/modelComponents/modelComponentWrapper.component';
import { ComponentHostDirective } from 'sql/workbench/parts/dashboard/common/componentHost.directive';
import { IBootstrapParams, ISelector, providerIterator } from 'sql/platform/bootstrap/node/bootstrapService';
import { CommonServiceInterface } from 'sql/platform/bootstrap/node/commonServiceInterface.service';
import { EditableDropDown } from 'sql/platform/electron-browser/editableDropdown/editableDropdown.component';
import { Checkbox } from 'sql/base/electron-browser/ui/checkbox/checkbox.component';
import { SelectBox } from 'sql/platform/ui/electron-browser/selectBox/selectBox.component';
import { InputBox } from 'sql/base/electron-browser/ui/inputBox/inputBox.component';
import { DialogContainer } from 'sql/platform/dialog/browser/dialogContainer.component';
import { WizardNavigation } from 'sql/platform/dialog/browser/wizardNavigation.component';
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/browser/modelComponentRegistry';
import { ModelViewContent } from 'sql/workbench/browser/modelComponents/modelViewContent.component';
import { ModelComponentWrapper } from 'sql/workbench/browser/modelComponents/modelComponentWrapper.component';
import { ComponentHostDirective } from 'sql/workbench/parts/dashboard/browser/core/componentHost.directive';
import { providerIterator } from 'sql/platform/bootstrap/browser/bootstrapService';
import { CommonServiceInterface } from 'sql/platform/bootstrap/browser/commonServiceInterface.service';
import { EditableDropDown } from 'sql/platform/browser/editableDropdown/editableDropdown.component';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
import { SelectBox } from 'sql/platform/browser/selectBox/selectBox.component';
import { InputBox } from 'sql/platform/browser/inputbox/inputBox.component';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Registry } from 'vs/platform/registry/common/platform';
import { IBootstrapParams, ISelector } from 'sql/platform/bootstrap/common/bootstrapParams';
export const DialogModule = (params, selector: string, instantiationService: IInstantiationService): any => {

View File

@@ -5,11 +5,11 @@
import 'vs/css!./media/dialogModal';
import { Component, ViewChild, Inject, forwardRef, ElementRef, AfterViewInit } from '@angular/core';
import { ModelViewContent } from 'sql/workbench/electron-browser/modelComponents/modelViewContent.component';
import { IBootstrapParams } from 'sql/platform/bootstrap/node/bootstrapService';
import { DialogPane } from 'sql/platform/dialog/dialogPane';
import { ComponentEventType } from 'sql/workbench/electron-browser/modelComponents/interfaces';
import { ModelViewContent } from 'sql/workbench/browser/modelComponents/modelViewContent.component';
import { DialogPane } from 'sql/platform/dialog/browser/dialogPane';
import { ComponentEventType } from 'sql/workbench/browser/modelComponents/interfaces';
import { Event, Emitter } from 'vs/base/common/event';
import { IBootstrapParams } from 'sql/platform/bootstrap/common/bootstrapParams';
export interface LayoutRequestParams {
modelViewId?: string;

View File

@@ -6,8 +6,8 @@
import 'vs/css!./media/dialogModal';
import { Modal, IModalOptions } from 'sql/workbench/browser/modal/modal';
import { attachModalDialogStyler } from 'sql/platform/theme/common/styler';
import { Dialog, DialogButton } from 'sql/platform/dialog/dialogTypes';
import { DialogPane } from 'sql/platform/dialog/dialogPane';
import { Dialog, DialogButton } from 'sql/platform/dialog/common/dialogTypes';
import { DialogPane } from 'sql/platform/dialog/browser/dialogPane';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
@@ -18,7 +18,7 @@ import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { Emitter } from 'vs/base/common/event';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { DialogMessage } from '../../workbench/api/common/sqlExtHostTypes';
import { DialogMessage } from 'sql/workbench/api/common/sqlExtHostTypes';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { append, $ } from 'vs/base/browser/dom';

View File

@@ -8,11 +8,11 @@ import 'vs/css!./media/dialogModal';
import { NgModuleRef } from '@angular/core';
import { IModalDialogStyles } from 'sql/workbench/browser/modal/modal';
import { DialogTab } from 'sql/platform/dialog/dialogTypes';
import { TabbedPanel, IPanelTab, IPanelView } from 'sql/base/browser/ui/panel/panel';
import { bootstrapAngular } from 'sql/platform/bootstrap/node/bootstrapService';
import { DialogModule } from 'sql/platform/dialog/dialog.module';
import { DialogComponentParams, LayoutRequestParams } from 'sql/platform/dialog/dialogContainer.component';
import { DialogTab } from 'sql/platform/dialog/common/dialogTypes';
import { TabbedPanel } from 'sql/base/browser/ui/panel/panel';
import { bootstrapAngular } from 'sql/platform/bootstrap/browser/bootstrapService';
import { DialogModule } from 'sql/platform/dialog/browser/dialog.module';
import { DialogComponentParams, LayoutRequestParams } from 'sql/platform/dialog/browser/dialogContainer.component';
import * as DOM from 'vs/base/browser/dom';
import { IThemable } from 'vs/platform/theme/common/styler';

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -6,11 +6,11 @@
import 'vs/css!./media/dialogModal';
import { Modal, IModalOptions } from 'sql/workbench/browser/modal/modal';
import { attachModalDialogStyler } from 'sql/platform/theme/common/styler';
import { Wizard, DialogButton, WizardPage } from 'sql/platform/dialog/dialogTypes';
import { DialogPane } from 'sql/platform/dialog/dialogPane';
import { bootstrapAngular } from 'sql/platform/bootstrap/node/bootstrapService';
import { Wizard, DialogButton, WizardPage } from 'sql/platform/dialog/common/dialogTypes';
import { DialogPane } from 'sql/platform/dialog/browser/dialogPane';
import { bootstrapAngular } from 'sql/platform/bootstrap/browser/bootstrapService';
import { DialogMessage } from 'sql/workbench/api/common/sqlExtHostTypes';
import { DialogModule } from 'sql/platform/dialog/dialog.module';
import { DialogModule } from 'sql/platform/dialog/browser/dialog.module';
import { Button } from 'vs/base/browser/ui/button/button';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';

View File

@@ -5,11 +5,11 @@
import 'vs/css!./media/wizardNavigation';
import { Component, Inject, forwardRef, ElementRef, AfterViewInit, ChangeDetectorRef, ViewChild } from '@angular/core';
import { IBootstrapParams } from 'sql/platform/bootstrap/node/bootstrapService';
import { Event, Emitter } from 'vs/base/common/event';
import { Wizard } from './dialogTypes';
import { Wizard } from '../common/dialogTypes';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { IBootstrapParams } from 'sql/platform/bootstrap/common/bootstrapParams';
export class WizardNavigationParams implements IBootstrapParams {
wizard: Wizard;

View File

@@ -0,0 +1,105 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { Dialog, DialogTab } from 'sql/platform/dialog/common/dialogTypes';
import { DialogPane } from 'sql/platform/dialog/browser/dialogPane';
import { DialogComponentParams } from 'sql/platform/dialog/browser/dialogContainer.component';
import { bootstrapAngular } from 'sql/platform/bootstrap/browser/bootstrapService';
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
interface BootstrapAngular {
(collection, moduleType, container, selectorString, params: DialogComponentParams, input, callbackSetModule): void;
}
suite('Dialog Pane Tests', () => {
let dialog: Dialog;
let container: HTMLElement;
let bootstrapSave: BootstrapAngular;
function setupBootstrap(fn: BootstrapAngular): void {
(<any>bootstrapAngular) = fn;
}
setup(() => {
dialog = new Dialog('test_dialog');
container = document.createElement('div');
bootstrapSave = bootstrapAngular;
});
test('Creating a pane from content without tabs initializes the model view content correctly', () => {
// If I fill in a dialog's content with the ID of a specific model view provider and then render the dialog
let modelViewId = 'test_content';
let bootstrapCalls = 0;
setupBootstrap((collection, moduleType, container, selectorString, params: DialogComponentParams, input, callbackSetModule) => {
assert.equal(params.modelViewId, modelViewId);
bootstrapCalls++;
});
dialog.content = modelViewId;
const themeService = new TestThemeService();
let dialogPane = new DialogPane(dialog.title, dialog.content, () => undefined, undefined, themeService, false);
dialogPane.createBody(container);
assert.equal(bootstrapCalls, 1);
});
test('Creating a pane from content with a single tab initializes without showing tabs', () => {
// If I fill in a dialog's content with a single tab and then render the dialog
let modelViewId = 'test_content';
let bootstrapCalls = 0;
setupBootstrap((collection, moduleType, container, selectorString, params: DialogComponentParams, input, callbackSetModule) => {
assert.equal(params.modelViewId, modelViewId);
bootstrapCalls++;
});
dialog.content = [new DialogTab('', modelViewId)];
const themeService = new TestThemeService();
let dialogPane = new DialogPane(dialog.title, dialog.content, () => undefined, undefined, themeService, false);
dialogPane.createBody(container);
assert.equal(bootstrapCalls, 1);
});
test('Dialog validation gets set based on the validity of the model view content', () => {
// Set up the mock bootstrap service to intercept validation callbacks
let validationCallbacks: ((valid: boolean) => void)[] = [];
setupBootstrap((collection, moduleType, container, selectorString, params: DialogComponentParams, input, callbackSetModule) => {
validationCallbacks.push(params.validityChangedCallback);
});
let modelViewId1 = 'test_content_1';
let modelViewId2 = 'test_content_2';
dialog.content = [new DialogTab('tab1', modelViewId1), new DialogTab('tab2', modelViewId2)];
const themeService = new TestThemeService();
let dialogPane = new DialogPane(dialog.title, dialog.content, valid => dialog.notifyValidityChanged(valid), undefined, themeService, false);
dialogPane.createBody(container);
let validityChanges: boolean[] = [];
dialog.onValidityChanged(valid => validityChanges.push(valid));
// If I set tab 2's validation to false
validationCallbacks[1](false);
// Then the whole dialog's validation is false
assert.equal(dialog.valid, false);
assert.equal(validityChanges.length, 1);
assert.equal(validityChanges[0], false);
// If I then set it back to true
validationCallbacks[1](true);
// Then the whole dialog's validation is true
assert.equal(dialog.valid, true);
assert.equal(validityChanges.length, 2);
assert.equal(validityChanges[1], true);
// If I set tab 1's validation to false
validationCallbacks[0](false);
// Then the whole dialog's validation is false
assert.equal(dialog.valid, false);
assert.equal(validityChanges.length, 3);
assert.equal(validityChanges[2], false);
});
teardown(() => {
(<any>bootstrapAngular) = bootstrapSave;
});
});

View File

@@ -0,0 +1,13 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import Severity from 'vs/base/common/severity';
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
export class TestErrorMessageService implements IErrorMessageService {
_serviceBrand: any;
showDialog(severity: Severity, headerTitle: string, message: string): void {
}
}

View File

@@ -8,18 +8,18 @@ import * as nls from 'vs/nls';
import * as azdata from 'azdata';
import { INotificationService } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity';
import { JobHistoryComponent } from 'sql/workbench/parts/jobManagement/electron-browser/jobHistory.component';
import { JobHistoryComponent } from 'sql/workbench/parts/jobManagement/browser/jobHistory.component';
import { IJobManagementService } from '../common/interfaces';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { JobsViewComponent } from 'sql/workbench/parts/jobManagement/electron-browser/jobsView.component';
import { AlertsViewComponent } from 'sql/workbench/parts/jobManagement/electron-browser/alertsView.component';
import { OperatorsViewComponent } from 'sql/workbench/parts/jobManagement/electron-browser/operatorsView.component';
import { ProxiesViewComponent } from 'sql/workbench/parts/jobManagement/electron-browser/proxiesView.component';
import { JobsViewComponent } from 'sql/workbench/parts/jobManagement/browser/jobsView.component';
import { AlertsViewComponent } from 'sql/workbench/parts/jobManagement/browser/alertsView.component';
import { OperatorsViewComponent } from 'sql/workbench/parts/jobManagement/browser/operatorsView.component';
import { ProxiesViewComponent } from 'sql/workbench/parts/jobManagement/browser/proxiesView.component';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
import { JobManagementView } from 'sql/workbench/parts/jobManagement/electron-browser/jobManagementView';
import { JobManagementView } from 'sql/workbench/parts/jobManagement/browser/jobManagementView';
export const successLabel: string = nls.localize('jobaction.successLabel', 'Success');
export const errorLabel: string = nls.localize('jobaction.faillabel', 'Error');

View File

@@ -0,0 +1,17 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { JobManagementService } from 'sql/platform/jobManagement/common/jobManagementService';
// TESTS ///////////////////////////////////////////////////////////////////
suite('Job Management service tests', () => {
setup(() => {
});
test('Construction - Job Service Initialization', () => {
// ... Create instance of the service and reder account picker
let service = new JobManagementService(undefined);
});
});

View File

@@ -0,0 +1,376 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import * as TypeMoq from 'typemoq';
import { JobsRefreshAction, NewJobAction, EditJobAction, RunJobAction, StopJobAction, DeleteJobAction, NewStepAction, DeleteStepAction, NewAlertAction, EditAlertAction, DeleteAlertAction, NewOperatorAction, EditOperatorAction, DeleteOperatorAction, NewProxyAction, EditProxyAction, DeleteProxyAction } from 'sql/platform/jobManagement/browser/jobActions';
import { JobManagementService } from 'sql/platform/jobManagement/common/jobManagementService';
// Mock View Components
let mockJobsViewComponent: TypeMoq.Mock<TestJobManagementView>;
let mockAlertsViewComponent: TypeMoq.Mock<TestJobManagementView>;
let mockOperatorsViewComponent: TypeMoq.Mock<TestJobManagementView>;
let mockProxiesViewComponent: TypeMoq.Mock<TestJobManagementView>;
let mockJobManagementService: TypeMoq.Mock<JobManagementService>;
// Mock Job Actions
let mockRefreshAction: TypeMoq.Mock<JobsRefreshAction>;
let mockNewJobAction: TypeMoq.Mock<NewJobAction>;
let mockEditJobAction: TypeMoq.Mock<EditJobAction>;
let mockRunJobAction: TypeMoq.Mock<RunJobAction>;
let mockStopJobAction: TypeMoq.Mock<StopJobAction>;
let mockDeleteJobAction: TypeMoq.Mock<DeleteJobAction>;
// Mock Step Actions
let mockNewStepAction: TypeMoq.Mock<NewStepAction>;
let mockDeleteStepAction: TypeMoq.Mock<DeleteStepAction>;
// Mock Alert Actions
let mockNewAlertAction: TypeMoq.Mock<NewAlertAction>;
let mockEditAlertAction: TypeMoq.Mock<EditAlertAction>;
let mockDeleteAlertAction: TypeMoq.Mock<DeleteAlertAction>;
// Mock Operator Actions
let mockNewOperatorAction: TypeMoq.Mock<NewOperatorAction>;
let mockEditOperatorAction: TypeMoq.Mock<EditOperatorAction>;
let mockDeleteOperatorAction: TypeMoq.Mock<DeleteOperatorAction>;
// Mock Proxy Actions
let mockNewProxyAction: TypeMoq.Mock<NewProxyAction>;
let mockEditProxyAction: TypeMoq.Mock<EditProxyAction>;
let mockDeleteProxyAction: TypeMoq.Mock<DeleteProxyAction>;
/**
* Class to test Job Management Views
*/
class TestJobManagementView {
refreshJobs() { return undefined; }
openCreateJobDialog() { return undefined; }
openCreateAlertDialog() { return undefined; }
openCreateOperatorDialog() { return undefined; }
openCreateProxyDialog() { return undefined; }
}
// Tests
suite('Job Management Actions', () => {
// Job Actions
setup(() => {
mockJobsViewComponent = TypeMoq.Mock.ofType<TestJobManagementView>(TestJobManagementView);
mockAlertsViewComponent = TypeMoq.Mock.ofType<TestJobManagementView>(TestJobManagementView);
mockOperatorsViewComponent = TypeMoq.Mock.ofType<TestJobManagementView>(TestJobManagementView);
mockProxiesViewComponent = TypeMoq.Mock.ofType<TestJobManagementView>(TestJobManagementView);
mockJobManagementService = TypeMoq.Mock.ofType<JobManagementService>(JobManagementService);
let resultStatus: azdata.ResultStatus = {
success: true,
errorMessage: null
};
mockJobManagementService.setup(s => s.jobAction(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(resultStatus));
});
test('Jobs Refresh Action', (done) => {
mockRefreshAction = TypeMoq.Mock.ofType(JobsRefreshAction, TypeMoq.MockBehavior.Strict, JobsRefreshAction.ID, JobsRefreshAction.LABEL);
mockRefreshAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => mockJobsViewComponent.object.refreshJobs());
mockRefreshAction.setup(s => s.id).returns(() => JobsRefreshAction.ID);
mockRefreshAction.setup(s => s.label).returns(() => JobsRefreshAction.LABEL);
should(mockRefreshAction.object.id).equal(JobsRefreshAction.ID);
should(mockRefreshAction.object.label).equal(JobsRefreshAction.LABEL);
// Job Refresh Action from Jobs View should refresh the component
mockRefreshAction.object.run(null);
mockJobsViewComponent.verify(c => c.refreshJobs(), TypeMoq.Times.once());
done();
});
test('New Job Action', (done) => {
mockNewJobAction = TypeMoq.Mock.ofType(NewJobAction, TypeMoq.MockBehavior.Strict, NewJobAction.ID, NewJobAction.LABEL);
mockNewJobAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => mockJobsViewComponent.object.openCreateJobDialog());
mockNewJobAction.setup(s => s.id).returns(() => NewJobAction.ID);
mockNewJobAction.setup(s => s.label).returns(() => NewJobAction.LABEL);
should(mockNewJobAction.object.id).equal(NewJobAction.ID);
should(mockNewJobAction.object.label).equal(NewJobAction.LABEL);
// New Job Action from Jobs View should open a dialog
mockNewJobAction.object.run(null);
mockJobsViewComponent.verify(c => c.openCreateJobDialog(), TypeMoq.Times.once());
done();
});
test('Edit Job Action', (done) => {
mockEditJobAction = TypeMoq.Mock.ofType(EditJobAction, TypeMoq.MockBehavior.Strict, EditJobAction.ID, EditJobAction.LABEL);
let commandServiceCalled: boolean = false;
mockEditJobAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => {
commandServiceCalled = true;
return Promise.resolve(commandServiceCalled);
});
mockEditJobAction.setup(s => s.id).returns(() => EditJobAction.ID);
mockEditJobAction.setup(s => s.label).returns(() => EditJobAction.LABEL);
should(mockEditJobAction.object.id).equal(EditJobAction.ID);
should(mockEditJobAction.object.label).equal(EditJobAction.LABEL);
// Edit Job Action from Jobs View should open a dialog
mockEditJobAction.object.run(null);
should(commandServiceCalled).equal(true);
done();
});
test('Run Job Action', (done) => {
mockRunJobAction = TypeMoq.Mock.ofType(RunJobAction, TypeMoq.MockBehavior.Strict, RunJobAction.ID, RunJobAction.LABEL, null, null, mockJobManagementService);
mockRunJobAction.setup(s => s.run(TypeMoq.It.isAny())).returns(async () => {
let result = await mockJobManagementService.object.jobAction(null, null, null).then((result) => result.success);
return result;
});
mockRunJobAction.setup(s => s.id).returns(() => RunJobAction.ID);
mockRunJobAction.setup(s => s.label).returns(() => RunJobAction.LABEL);
should(mockRunJobAction.object.id).equal(RunJobAction.ID);
should(mockRunJobAction.object.label).equal(RunJobAction.LABEL);
// Run Job Action should make the Job Management service call job action
mockRunJobAction.object.run(null);
mockJobManagementService.verify(s => s.jobAction(null, null, null), TypeMoq.Times.once());
done();
});
test('Stop Job Action', (done) => {
mockStopJobAction = TypeMoq.Mock.ofType(StopJobAction, TypeMoq.MockBehavior.Strict, StopJobAction.ID, StopJobAction.LABEL, null, null, mockJobManagementService);
mockStopJobAction.setup(s => s.run(TypeMoq.It.isAny())).returns(async () => {
let result = await mockJobManagementService.object.jobAction(null, null, null).then((result) => result.success);
return result;
});
mockStopJobAction.setup(s => s.id).returns(() => RunJobAction.ID);
mockStopJobAction.setup(s => s.label).returns(() => RunJobAction.LABEL);
should(mockStopJobAction.object.id).equal(RunJobAction.ID);
should(mockStopJobAction.object.label).equal(RunJobAction.LABEL);
// Run Job Action should make the Job Management service call job action
mockStopJobAction.object.run(null);
mockJobManagementService.verify(s => s.jobAction(null, null, null), TypeMoq.Times.once());
done();
});
test('Delete Job Action', (done) => {
mockDeleteJobAction = TypeMoq.Mock.ofType(DeleteJobAction, TypeMoq.MockBehavior.Strict, DeleteJobAction.ID, DeleteJobAction.LABEL, null, null, mockJobManagementService);
mockDeleteJobAction.setup(s => s.run(TypeMoq.It.isAny())).returns(async () => {
let result = await mockJobManagementService.object.jobAction(null, null, null).then((result) => result.success);
return result;
});
mockDeleteJobAction.setup(s => s.id).returns(() => DeleteJobAction.ID);
mockDeleteJobAction.setup(s => s.label).returns(() => DeleteJobAction.LABEL);
should(mockDeleteJobAction.object.id).equal(DeleteJobAction.ID);
should(mockDeleteJobAction.object.label).equal(DeleteJobAction.LABEL);
// Run Job Action should make the Job Management service call job action
mockDeleteJobAction.object.run(null);
mockJobManagementService.verify(s => s.jobAction(null, null, null), TypeMoq.Times.once());
done();
});
// Step Actions
test('New Step Action', (done) => {
mockNewStepAction = TypeMoq.Mock.ofType(NewStepAction, TypeMoq.MockBehavior.Strict, NewJobAction.ID, NewJobAction.LABEL);
let commandServiceCalled = false;
mockNewStepAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => {
commandServiceCalled = true;
return Promise.resolve(commandServiceCalled);
});
mockNewStepAction.setup(s => s.id).returns(() => NewJobAction.ID);
mockNewStepAction.setup(s => s.label).returns(() => NewJobAction.LABEL);
should(mockNewStepAction.object.id).equal(NewJobAction.ID);
should(mockNewStepAction.object.label).equal(NewJobAction.LABEL);
// New Step Action should called command service
mockNewStepAction.object.run(null);
should(commandServiceCalled).equal(true);
done();
});
test('Delete Step Action', (done) => {
mockDeleteStepAction = TypeMoq.Mock.ofType(DeleteStepAction, TypeMoq.MockBehavior.Strict, DeleteStepAction.ID, DeleteStepAction.LABEL);
let commandServiceCalled = false;
mockDeleteStepAction.setup(s => s.run(TypeMoq.It.isAny())).returns(async () => {
commandServiceCalled = true;
await mockJobManagementService.object.deleteJobStep(null, null).then((result) => result.success);
return Promise.resolve(commandServiceCalled);
});
mockDeleteStepAction.setup(s => s.id).returns(() => DeleteStepAction.ID);
mockDeleteStepAction.setup(s => s.label).returns(() => DeleteStepAction.LABEL);
should(mockDeleteStepAction.object.id).equal(DeleteStepAction.ID);
should(mockDeleteStepAction.object.label).equal(DeleteStepAction.LABEL);
// Delete Step Action should called command service
mockDeleteStepAction.object.run(null);
should(commandServiceCalled).equal(true);
mockJobManagementService.verify(s => s.deleteJobStep(null, null), TypeMoq.Times.once());
done();
});
// Alert Actions
test('New Alert Action', (done) => {
mockNewAlertAction = TypeMoq.Mock.ofType(NewJobAction, TypeMoq.MockBehavior.Strict, NewJobAction.ID, NewJobAction.LABEL);
mockNewAlertAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => mockAlertsViewComponent.object.openCreateAlertDialog());
mockNewAlertAction.setup(s => s.id).returns(() => NewJobAction.ID);
mockNewAlertAction.setup(s => s.label).returns(() => NewJobAction.LABEL);
should(mockNewAlertAction.object.id).equal(NewJobAction.ID);
should(mockNewAlertAction.object.label).equal(NewJobAction.LABEL);
// New Alert Action from Alerts View should open a dialog
mockNewAlertAction.object.run(null);
mockAlertsViewComponent.verify(c => c.openCreateAlertDialog(), TypeMoq.Times.once());
done();
});
test('Edit Alert Action', (done) => {
mockEditAlertAction = TypeMoq.Mock.ofType(EditAlertAction, TypeMoq.MockBehavior.Strict, EditAlertAction.ID, EditAlertAction.LABEL);
let commandServiceCalled: boolean = false;
mockEditAlertAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => {
commandServiceCalled = true;
return Promise.resolve(commandServiceCalled);
});
mockEditAlertAction.setup(s => s.id).returns(() => EditAlertAction.ID);
mockEditAlertAction.setup(s => s.label).returns(() => EditAlertAction.LABEL);
should(mockEditAlertAction.object.id).equal(EditAlertAction.ID);
should(mockEditAlertAction.object.label).equal(EditAlertAction.LABEL);
// Edit Alert Action from Jobs View should open a dialog
mockEditAlertAction.object.run(null);
should(commandServiceCalled).equal(true);
done();
});
test('Delete Alert Action', (done) => {
mockDeleteAlertAction = TypeMoq.Mock.ofType(DeleteAlertAction, TypeMoq.MockBehavior.Strict, DeleteAlertAction.ID, DeleteAlertAction.LABEL, null, null, mockJobManagementService);
let commandServiceCalled = false;
mockDeleteAlertAction.setup(s => s.run(TypeMoq.It.isAny())).returns(async () => {
commandServiceCalled = true;
await mockJobManagementService.object.deleteAlert(null, null).then((result) => result.success);
return commandServiceCalled;
});
mockDeleteAlertAction.setup(s => s.id).returns(() => DeleteAlertAction.ID);
mockDeleteAlertAction.setup(s => s.label).returns(() => DeleteAlertAction.LABEL);
should(mockDeleteAlertAction.object.id).equal(DeleteAlertAction.ID);
should(mockDeleteAlertAction.object.label).equal(DeleteAlertAction.LABEL);
// Delete Alert Action should call job management service
mockDeleteAlertAction.object.run(null);
should(commandServiceCalled).equal(true);
mockJobManagementService.verify(s => s.deleteAlert(null, null), TypeMoq.Times.once());
done();
});
// Operator Tests
test('New Operator Action', (done) => {
mockNewOperatorAction = TypeMoq.Mock.ofType(NewOperatorAction, TypeMoq.MockBehavior.Strict, NewOperatorAction.ID, NewOperatorAction.LABEL);
mockNewOperatorAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => mockOperatorsViewComponent.object.openCreateOperatorDialog());
mockNewOperatorAction.setup(s => s.id).returns(() => NewOperatorAction.ID);
mockNewOperatorAction.setup(s => s.label).returns(() => NewOperatorAction.LABEL);
should(mockNewOperatorAction.object.id).equal(NewOperatorAction.ID);
should(mockNewOperatorAction.object.label).equal(NewOperatorAction.LABEL);
// New Operator Action from Operators View should open a dialog
mockNewOperatorAction.object.run(null);
mockOperatorsViewComponent.verify(c => c.openCreateOperatorDialog(), TypeMoq.Times.once());
done();
});
test('Edit Operator Action', (done) => {
mockEditOperatorAction = TypeMoq.Mock.ofType(EditOperatorAction, TypeMoq.MockBehavior.Strict, EditOperatorAction.ID, EditOperatorAction.LABEL);
let commandServiceCalled: boolean = false;
mockEditOperatorAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => {
commandServiceCalled = true;
return Promise.resolve(commandServiceCalled);
});
mockEditOperatorAction.setup(s => s.id).returns(() => EditOperatorAction.ID);
mockEditOperatorAction.setup(s => s.label).returns(() => EditOperatorAction.LABEL);
should(mockEditOperatorAction.object.id).equal(EditOperatorAction.ID);
should(mockEditOperatorAction.object.label).equal(EditOperatorAction.LABEL);
// Edit Operator Action from Jobs View should open a dialog
mockEditOperatorAction.object.run(null);
should(commandServiceCalled).equal(true);
done();
});
test('Delete Operator Action', (done) => {
mockDeleteOperatorAction = TypeMoq.Mock.ofType(DeleteOperatorAction, TypeMoq.MockBehavior.Strict, DeleteOperatorAction.ID, DeleteOperatorAction.LABEL, null, null, mockJobManagementService);
let commandServiceCalled = false;
mockDeleteOperatorAction.setup(s => s.run(TypeMoq.It.isAny())).returns(async () => {
commandServiceCalled = true;
await mockJobManagementService.object.deleteOperator(null, null).then((result) => result.success);
return commandServiceCalled;
});
mockDeleteOperatorAction.setup(s => s.id).returns(() => DeleteOperatorAction.ID);
mockDeleteOperatorAction.setup(s => s.label).returns(() => DeleteOperatorAction.LABEL);
should(mockDeleteOperatorAction.object.id).equal(DeleteOperatorAction.ID);
should(mockDeleteOperatorAction.object.label).equal(DeleteOperatorAction.LABEL);
// Delete Operator Action should call job management service
mockDeleteOperatorAction.object.run(null);
should(commandServiceCalled).equal(true);
mockJobManagementService.verify(s => s.deleteOperator(null, null), TypeMoq.Times.once());
done();
});
// Proxy Actions
test('New Proxy Action', (done) => {
mockNewProxyAction = TypeMoq.Mock.ofType(NewProxyAction, TypeMoq.MockBehavior.Strict, NewProxyAction.ID, NewProxyAction.LABEL);
mockNewProxyAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => mockProxiesViewComponent.object.openCreateProxyDialog());
mockNewProxyAction.setup(s => s.id).returns(() => NewProxyAction.ID);
mockNewProxyAction.setup(s => s.label).returns(() => NewProxyAction.LABEL);
should(mockNewProxyAction.object.id).equal(NewProxyAction.ID);
should(mockNewProxyAction.object.label).equal(NewProxyAction.LABEL);
// New Proxy Action from Alerts View should open a dialog
mockNewProxyAction.object.run(null);
mockProxiesViewComponent.verify(c => c.openCreateProxyDialog(), TypeMoq.Times.once());
done();
});
test('Edit Proxy Action', (done) => {
mockEditProxyAction = TypeMoq.Mock.ofType(EditProxyAction, TypeMoq.MockBehavior.Strict, EditProxyAction.ID, EditProxyAction.LABEL);
let commandServiceCalled: boolean = false;
mockEditProxyAction.setup(s => s.run(TypeMoq.It.isAny())).returns(() => {
commandServiceCalled = true;
return Promise.resolve(commandServiceCalled);
});
mockEditProxyAction.setup(s => s.id).returns(() => EditProxyAction.ID);
mockEditProxyAction.setup(s => s.label).returns(() => EditProxyAction.LABEL);
should(mockEditProxyAction.object.id).equal(EditProxyAction.ID);
should(mockEditProxyAction.object.label).equal(EditProxyAction.LABEL);
// Edit Proxy Action from Proxies View should open a dialog
mockEditProxyAction.object.run(null);
should(commandServiceCalled).equal(true);
done();
});
test('Delete Proxy Action', (done) => {
mockDeleteProxyAction = TypeMoq.Mock.ofType(DeleteProxyAction, TypeMoq.MockBehavior.Strict, DeleteProxyAction.ID, DeleteProxyAction.LABEL, null, null, mockJobManagementService);
let commandServiceCalled = false;
mockDeleteProxyAction.setup(s => s.run(TypeMoq.It.isAny())).returns(async () => {
commandServiceCalled = true;
await mockJobManagementService.object.deleteProxy(null, null).then((result) => result.success);
return commandServiceCalled;
});
mockDeleteProxyAction.setup(s => s.id).returns(() => DeleteProxyAction.ID);
mockDeleteProxyAction.setup(s => s.label).returns(() => DeleteProxyAction.LABEL);
should(mockDeleteProxyAction.object.id).equal(DeleteProxyAction.ID);
should(mockDeleteProxyAction.object.label).equal(DeleteProxyAction.LABEL);
// Delete Proxy Action should call job management service
mockDeleteProxyAction.object.run(null);
should(commandServiceCalled).equal(true);
mockJobManagementService.verify(s => s.deleteProxy(null, null), TypeMoq.Times.once());
done();
});
});

View File

@@ -5,7 +5,7 @@
import * as azdata from 'azdata';
import { IItemConfig, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
import { IComponentEventArgs } from 'sql/workbench/electron-browser/modelComponents/interfaces';
import { IComponentEventArgs } from 'sql/workbench/browser/modelComponents/interfaces';
import { Event } from 'vs/base/common/event';
export interface IView {

View File

@@ -8,8 +8,8 @@ import { IConnectionManagementService } from 'sql/platform/connection/common/con
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable } from 'vs/base/common/lifecycle';
import * as azdata from 'azdata';
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
import * as TelemetryUtils from 'sql/platform/telemetry/telemetryUtilities';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import * as TelemetryUtils from 'sql/platform/telemetry/common/telemetryUtilities';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { Event, Emitter } from 'vs/base/common/event';
import { keys } from 'vs/base/common/map';

View File

@@ -9,19 +9,16 @@ import QueryRunner from 'sql/platform/query/common/queryRunner';
import { DataService } from 'sql/workbench/parts/grid/services/dataService';
import { IQueryModelService, IQueryEvent } from 'sql/platform/query/common/queryModel';
import { QueryInput } from 'sql/workbench/parts/query/common/queryInput';
import { SqlFlavorStatusbarItem } from 'sql/workbench/parts/query/browser/flavorStatus';
import * as azdata from 'azdata';
import * as nls from 'vs/nls';
import * as platform from 'vs/platform/registry/common/platform';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Event, Emitter } from 'vs/base/common/event';
import * as strings from 'vs/base/common/strings';
import * as types from 'vs/base/common/types';
import { INotificationService } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity';
import { StatusbarAlignment } from 'vs/platform/statusbar/common/statusbar';
const selectionSnippetMaxLen = 100;

View File

@@ -27,7 +27,7 @@ import { ITextResourcePropertiesService } from 'vs/editor/common/services/resour
import { URI } from 'vs/base/common/uri';
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
import { IGridDataProvider, getResultsString } from 'sql/platform/query/common/gridDataProvider';
import { getErrorMessage } from 'sql/workbench/parts/notebook/notebookUtils';
import { getErrorMessage } from 'vs/base/common/errors';
export interface IEditSessionReadyEvent {
ownerUri: string;

View File

@@ -21,8 +21,8 @@ import * as Utils from 'sql/platform/connection/common/utils';
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService';
import { ITaskService } from 'sql/platform/tasks/common/tasksService';
import { TaskStatus, TaskNode } from 'sql/platform/tasks/common/tasksNode';
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
import * as TelemetryUtils from 'sql/platform/telemetry/telemetryUtilities';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import * as TelemetryUtils from 'sql/platform/telemetry/common/telemetryUtilities';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { invalidProvider } from 'sql/base/common/errors';
import { ILogService } from 'vs/platform/log/common/log';

View File

@@ -0,0 +1,115 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { MenuRegistry, ICommandAction } from 'vs/platform/actions/common/actions';
import { IDisposable } from 'vs/base/common/lifecycle';
import { ITaskRegistry, ITaskHandler, ITask, ITaskHandlerDescription, ITaskOptions } from 'sql/platform/tasks/common/tasks';
import * as types from 'vs/base/common/types';
import { Event, Emitter } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { createCSSRule } from 'vs/base/browser/dom';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IdGenerator } from 'vs/base/common/idGenerator';
const ids = new IdGenerator('task-icon-');
export const TaskRegistry: ITaskRegistry = new class implements ITaskRegistry {
private _tasks = new Array<string>();
private _onTaskRegistered = new Emitter<string>();
public readonly onTaskRegistered: Event<string> = this._onTaskRegistered.event;
private taskIdToIconClassNameMap: Map<string /* task id */, string /* CSS rule */> = new Map<string, string>();
registerTask(idOrTask: string | ITask, handler?: ITaskHandler): IDisposable {
let disposable: IDisposable;
let id: string;
if (types.isString(idOrTask)) {
disposable = CommandsRegistry.registerCommand(idOrTask, handler);
id = idOrTask;
} else {
if (idOrTask.iconClass) {
this.taskIdToIconClassNameMap.set(idOrTask.id, idOrTask.iconClass);
}
disposable = CommandsRegistry.registerCommand(idOrTask);
id = idOrTask.id;
}
this._tasks.push(id);
this._onTaskRegistered.fire(id);
return {
dispose: () => {
let index = this._tasks.indexOf(id);
if (index >= 0) {
this._tasks = this._tasks.splice(index, 1);
}
disposable.dispose();
}
};
}
getOrCreateTaskIconClassName(item: ICommandAction): string {
let iconClass = null;
if (this.taskIdToIconClassNameMap.has(item.id)) {
iconClass = this.taskIdToIconClassNameMap.get(item.id);
} else if (item.iconLocation) {
iconClass = ids.nextId();
createCSSRule(`.icon.${iconClass}`, `background-image: url("${(item.iconLocation.light || item.iconLocation.dark).toString()}")`);
createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: url("${(item.iconLocation.dark).toString()}")`);
this.taskIdToIconClassNameMap.set(item.id, iconClass);
}
return iconClass;
}
getTasks(): string[] {
return this._tasks.slice(0);
}
};
export abstract class Task {
public readonly id: string;
public readonly title: string;
public readonly iconPathDark: string;
public readonly iconPath: { dark: URI; light?: URI; };
private readonly _iconClass: string;
private readonly _description: ITaskHandlerDescription;
constructor(private opts: ITaskOptions) {
this.id = opts.id;
this.title = opts.title;
this.iconPath = {
dark: opts.iconPath ? URI.parse(opts.iconPath.dark) : undefined,
light: opts.iconPath ? URI.parse(opts.iconPath.light) : undefined,
};
this._iconClass = opts.iconClass;
this._description = opts.description;
}
private toITask(): ITask {
return {
id: this.id,
handler: (accessor, profile, args) => this.runTask(accessor, profile, args),
description: this._description,
iconClass: this._iconClass
};
}
private toCommandAction(): ICommandAction {
return {
iconLocation: this.iconPath,
id: this.id,
title: this.title
};
}
public registerTask(): IDisposable {
MenuRegistry.addCommand(this.toCommandAction());
return TaskRegistry.registerTask(this.toITask());
}
public abstract runTask(accessor: ServicesAccessor, profile: IConnectionProfile, args: any): void | Promise<void>;
}

View File

@@ -6,14 +6,11 @@
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import * as types from 'vs/base/common/types';
import { ILocalizedString, MenuRegistry, ICommandAction } from 'vs/platform/actions/common/actions';
import { Event, Emitter } from 'vs/base/common/event';
import { ILocalizedString, ICommandAction } from 'vs/platform/actions/common/actions';
import { Event } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IdGenerator } from 'vs/base/common/idGenerator';
import { createCSSRule } from 'vs/base/browser/dom';
import { URI } from 'vs/base/common/uri';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
export interface ITaskOptions {
@@ -24,50 +21,6 @@ export interface ITaskOptions {
iconClass?: string;
}
export abstract class Task {
public readonly id: string;
public readonly title: string;
public readonly iconPathDark: string;
public readonly iconPath: { dark: URI; light?: URI; };
private readonly _iconClass: string;
private readonly _description: ITaskHandlerDescription;
constructor(private opts: ITaskOptions) {
this.id = opts.id;
this.title = opts.title;
this.iconPath = {
dark: opts.iconPath ? URI.parse(opts.iconPath.dark) : undefined,
light: opts.iconPath ? URI.parse(opts.iconPath.light) : undefined,
};
this._iconClass = opts.iconClass;
this._description = opts.description;
}
private toITask(): ITask {
return {
id: this.id,
handler: (accessor, profile, args) => this.runTask(accessor, profile, args),
description: this._description,
iconClass: this._iconClass
};
}
private toCommandAction(): ICommandAction {
return {
iconLocation: this.iconPath,
id: this.id,
title: this.title
};
}
public registerTask(): IDisposable {
MenuRegistry.addCommand(this.toCommandAction());
return TaskRegistry.registerTask(this.toITask());
}
public abstract runTask(accessor: ServicesAccessor, profile: IConnectionProfile, args: any): void | Promise<void>;
}
export interface ITaskHandlerDescription {
description: string;
args: { name: string; description?: string; constraint?: types.TypeConstraint; }[];
@@ -105,58 +58,3 @@ export interface ITaskRegistry {
getOrCreateTaskIconClassName(item: ICommandAction): string;
onTaskRegistered: Event<string>;
}
const ids = new IdGenerator('task-icon-');
export const TaskRegistry: ITaskRegistry = new class implements ITaskRegistry {
private _tasks = new Array<string>();
private _onTaskRegistered = new Emitter<string>();
public readonly onTaskRegistered: Event<string> = this._onTaskRegistered.event;
private taskIdToIconClassNameMap: Map<string /* task id */, string /* CSS rule */> = new Map<string, string>();
registerTask(idOrTask: string | ITask, handler?: ITaskHandler): IDisposable {
let disposable: IDisposable;
let id: string;
if (types.isString(idOrTask)) {
disposable = CommandsRegistry.registerCommand(idOrTask, handler);
id = idOrTask;
} else {
if (idOrTask.iconClass) {
this.taskIdToIconClassNameMap.set(idOrTask.id, idOrTask.iconClass);
}
disposable = CommandsRegistry.registerCommand(idOrTask);
id = idOrTask.id;
}
this._tasks.push(id);
this._onTaskRegistered.fire(id);
return {
dispose: () => {
let index = this._tasks.indexOf(id);
if (index >= 0) {
this._tasks = this._tasks.splice(index, 1);
}
disposable.dispose();
}
};
}
getOrCreateTaskIconClassName(item: ICommandAction): string {
let iconClass = null;
if (this.taskIdToIconClassNameMap.has(item.id)) {
iconClass = this.taskIdToIconClassNameMap.get(item.id);
} else if (item.iconLocation) {
iconClass = ids.nextId();
createCSSRule(`.icon.${iconClass}`, `background-image: url("${(item.iconLocation.light || item.iconLocation.dark).toString()}")`);
createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: url("${(item.iconLocation.dark).toString()}")`);
this.taskIdToIconClassNameMap.set(item.id, iconClass);
}
return iconClass;
}
getTasks(): string[] {
return this._tasks.slice(0);
}
};

View File

@@ -1,179 +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 { TelemetryView } from 'sql/platform/telemetry/telemetryKeys';
import { IAdsTelemetryService } from 'sql/platform/telemetry/telemetry';
import { Registry } from 'vs/platform/registry/common/platform';
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry, IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { Disposable } from 'vs/base/common/lifecycle';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { ICommandService, ICommandEvent } from 'vs/platform/commands/common/commands';
export class SqlTelemetryContribution extends Disposable implements IWorkbenchContribution {
constructor(
@IAdsTelemetryService private telemetryService: IAdsTelemetryService,
@IStorageService storageService: IStorageService,
@ICommandService commandService: ICommandService
) {
super();
this._register(
commandService.onWillExecuteCommand(
(e: ICommandEvent) => {
// Filter out high-frequency events
if (!['type', 'cursorUp', 'cursorDown', 'cursorRight', 'cursorLeft', 'deleteLeft', 'deleteRight'].find(id => id === e.commandId)) {
telemetryService.sendActionEvent(TelemetryView.Shell, 'adsCommandExecuted', e.commandId);
}
}));
const dailyLastUseDate: number = Date.parse(storageService.get('telemetry.dailyLastUseDate', StorageScope.GLOBAL, '0'));
const weeklyLastUseDate: number = Date.parse(storageService.get('telemetry.weeklyLastUseDate', StorageScope.GLOBAL, '0'));
const monthlyLastUseDate: number = Date.parse(storageService.get('telemetry.monthlyLastUseDate', StorageScope.GLOBAL, '0'));
const firstTimeUser: boolean = dailyLastUseDate === Date.parse('0');
let todayString: string = new Date().toUTCString();
// daily user event
if (this.didDayChange(dailyLastUseDate)) {
// daily first use
telemetryService.sendTelemetryEvent('telemetry.dailyFirstUse', { dailyFirstUse: 'true' });
storageService.store('telemetry.dailyLastUseDate', todayString, StorageScope.GLOBAL);
}
// weekly user event
if (this.didWeekChange(weeklyLastUseDate)) {
// weekly first use
telemetryService.sendTelemetryEvent('telemetry.weeklyFirstUse', { weeklyFirstUse: 'true' });
storageService.store('telemetry.weeklyLastUseDate', todayString, StorageScope.GLOBAL);
}
/* send monthly uses once the user launches on a day that's in a month
after the last time we sent a monthly usage count */
const monthlyUseCount: number = storageService.getNumber('telemetry.monthlyUseCount', StorageScope.GLOBAL, 0);
if (this.didMonthChange(monthlyLastUseDate)) {
telemetryService.sendTelemetryEvent('telemetry.monthlyUse', { monthlyFirstUse: 'true' });
// the month changed, so send the user usage type event based on monthly count for last month
// and reset the count for this month
let lastMonthDate = new Date(monthlyLastUseDate);
this.sendUsageEvent(monthlyUseCount, lastMonthDate);
const wasActiveLastMonth: boolean = storageService.getBoolean('telemetry.wasActiveLastMonth', StorageScope.GLOBAL, false);
if (firstTimeUser) {
// new user
this.sendGrowthTypeEvent(UserGrowthType.NewUser, lastMonthDate);
}
// continuing or returning user
this.sendGrowthTypeEvent(wasActiveLastMonth ? UserGrowthType.ContinuingUser : UserGrowthType.ReturningUser, lastMonthDate);
// set wasActiveUserLastMonth
storageService.store('telemetry.wasActiveLastMonth', true, StorageScope.GLOBAL);
// reset the monthly count for the new month
storageService.store('telemetry.monthlyUseCount', 1, StorageScope.GLOBAL);
storageService.store('telemetry.monthlyLastUseDate', todayString, StorageScope.GLOBAL);
} else {
// if it's the same month, increment the monthly use count
storageService.store('telemetry.monthlyUseCount', monthlyUseCount + 1, StorageScope.GLOBAL);
}
}
private didDayChange(lastUseDateNumber: number): boolean {
let nowDateNumber: number = Date.parse(new Date().toUTCString());
if (this.diffInDays(nowDateNumber, lastUseDateNumber) >= 1) {
return true;
} else {
let nowDate = new Date(nowDateNumber);
let lastUseDate = new Date(lastUseDateNumber);
return nowDate.getUTCDay() !== lastUseDate.getUTCDay();
}
}
private didWeekChange(lastUseDateNumber: number): boolean {
let nowDateNumber: number = Date.parse(new Date().toUTCString());
if (this.diffInDays(nowDateNumber, lastUseDateNumber) >= 7) {
return true;
} else {
let nowDate = new Date(nowDateNumber);
let lastUseDate = new Date(lastUseDateNumber);
return nowDate.getUTCDay() < lastUseDate.getUTCDay();
}
}
private didMonthChange(lastUseDateNumber: number): boolean {
let nowDateNumber: number = Date.parse(new Date().toUTCString());
if (this.diffInDays(nowDateNumber, lastUseDateNumber) >= 30) {
return true;
} else {
let nowDate = new Date(nowDateNumber);
let lastUseDate = new Date(lastUseDateNumber);
return nowDate.getUTCMonth() !== lastUseDate.getUTCMonth();
}
}
private diffInDays(nowDate: number, lastUseDate: number): number {
return (nowDate - lastUseDate) / (3600 * 1000 * 24);
}
// Usage Metrics
private sendUsageEvent(monthlyUseCount: number, lastMonthDate: Date): void {
let userUsageType: UserUsageType | undefined;
if (monthlyUseCount === 1) {
userUsageType = UserUsageType.TireKicker;
} else if (monthlyUseCount >= 2 && monthlyUseCount <= 11) {
userUsageType = UserUsageType.Occasional;
} else if (monthlyUseCount >= 12 && monthlyUseCount <= 20) {
userUsageType = UserUsageType.Engaged;
} else if (monthlyUseCount > 20) {
userUsageType = UserUsageType.Dedicated;
}
if (userUsageType) {
this.telemetryService.sendTelemetryEvent('telemetry.userUsage',
{ userType: userUsageType.toString(), monthlyUseCount: monthlyUseCount.toString(), month: lastMonthDate.getMonth().toString(), year: lastMonthDate.getFullYear().toString() });
}
}
// Growth Metrics
private sendGrowthTypeEvent(growthType: UserGrowthType, lastMonthDate: Date): void {
this.telemetryService.sendTelemetryEvent('telemetry.userGrowthType', {
userGrowthType: growthType.toString(), month: lastMonthDate.getMonth().toString(), year: lastMonthDate.getFullYear().toString()
});
}
}
/**
* Growth Metrics
* Active here means opened app atleast 1 time in a month
*/
export enum UserGrowthType {
// first time opening app
NewUser = 1,
// was active before, wasn't active last month, but is active this month
ReturningUser = 2,
// was active last month and this month
ContinuingUser = 3
}
/**
* Usage Metrics
* TireKicker = 1 day/month
* Occasional = 2-11 days/month
* Engaged = 12-20 days/month
* Dedicated = 20+ days/month
*/
export enum UserUsageType {
/* 1 day per month */
TireKicker = 1,
/* 2-11 days per month */
Occasional = 2,
/* 12-20 days per month */
Engaged = 3,
/* 20+ days per month */
Dedicated = 4
}
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(SqlTelemetryContribution, LifecyclePhase.Starting);

View File

@@ -0,0 +1,107 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as TelemetryUtils from 'sql/platform/telemetry/common/telemetryUtilities';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import * as TypeMoq from 'typemoq';
import * as assert from 'assert';
import { NullLogService } from 'vs/platform/log/common/log';
import { SimpleTelemetryService } from 'vs/workbench/browser/web.simpleservices';
suite('SQL Telemetry Utilities tests', () => {
let telemetryService: TypeMoq.Mock<ITelemetryService>;
let none: void;
let providerName: string = 'provider name';
let telemetryKey: string = 'tel key';
let connectionProfile = {
connectionName: '',
databaseName: '',
serverName: '',
authenticationType: '',
getOptionsKey: () => '',
matches: undefined,
groupFullName: '',
groupId: '',
id: '',
options: {},
password: '',
providerName: providerName,
savePassword: true,
saveProfile: true,
userName: ''
};
setup(() => {
telemetryService = TypeMoq.Mock.ofType(SimpleTelemetryService, TypeMoq.MockBehavior.Strict);
telemetryService.setup(x => x.publicLog(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(x => Promise.resolve(none));
});
test('addTelemetry should add provider id using the connection', (done) => {
let data: TelemetryUtils.IConnectionTelemetryData = {
};
const logService = new NullLogService();
TelemetryUtils.addTelemetry(telemetryService.object, logService, telemetryKey, data, connectionProfile).then(() => {
telemetryService.verify(x => x.publicLog(TypeMoq.It.is(a => a === telemetryKey), TypeMoq.It.is(b => b.provider === providerName)), TypeMoq.Times.once());
done();
}).catch(err => {
assert.fail(err);
done(err);
});
});
test('addTelemetry should pass the telemetry data to telemetry service', (done) => {
let data: TelemetryUtils.IConnectionTelemetryData = {
target: 'target',
from: 'from'
};
data.test1 = '1';
const logService = new NullLogService();
TelemetryUtils.addTelemetry(telemetryService.object, logService, telemetryKey, data, connectionProfile).then(() => {
telemetryService.verify(x => x.publicLog(
TypeMoq.It.is(a => a === telemetryKey),
TypeMoq.It.is(b => b.provider === providerName
&& b.from === data.from
&& b.target === data.target
&& b.test1 === data.test1
&& b.connection === undefined)), TypeMoq.Times.once());
done();
}).catch(err => {
assert.fail(err);
done(err);
});
});
test('addTelemetry should not crash not given data', (done) => {
const logService = new NullLogService();
TelemetryUtils.addTelemetry(telemetryService.object, logService, telemetryKey).then(() => {
telemetryService.verify(x => x.publicLog(
TypeMoq.It.is(a => a === telemetryKey),
TypeMoq.It.is(b => b !== undefined)), TypeMoq.Times.once());
done();
}).catch(err => {
assert.fail(err);
done(err);
});
});
test('addTelemetry should try to get the provider name from data first', (done) => {
let data: TelemetryUtils.IConnectionTelemetryData = {
connection: connectionProfile
};
data.provider = providerName + '1';
const logService = new NullLogService();
TelemetryUtils.addTelemetry(telemetryService.object, logService, telemetryKey, data, connectionProfile).then(() => {
telemetryService.verify(x => x.publicLog(TypeMoq.It.is(a => a === telemetryKey), TypeMoq.It.is(b => b.provider === data.provider)), TypeMoq.Times.once());
done();
}).catch(err => {
assert.fail(err);
done(err);
});
});
});

View File

@@ -10,7 +10,6 @@ import * as cr from 'vs/platform/theme/common/colorRegistry';
import { IThemable, attachStyler } from 'vs/platform/theme/common/styler';
import { IDisposable } from 'vs/base/common/lifecycle';
import { SIDE_BAR_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, SIDE_BAR_DRAG_AND_DROP_BACKGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND } from 'vs/workbench/common/theme';
import { IPanelColors } from 'vs/workbench/browser/parts/views/panelViewlet';
export function attachModalDialogStyler(widget: IThemable, themeService: IThemeService, style?:
{
@@ -272,7 +271,7 @@ export function attachCheckboxStyler(widget: IThemable, themeService: IThemeServ
}
export function attachPanelStyler(widget: IThemable, themeService: IThemeService) {
return attachStyler<IPanelColors>(themeService, {
return attachStyler(themeService, {
headerForeground: SIDE_BAR_SECTION_HEADER_FOREGROUND,
headerBackground: SIDE_BAR_SECTION_HEADER_BACKGROUND,
// headerHighContrastBorder: index === 0 ? null : contrastBorder,