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

@@ -0,0 +1,628 @@
/*---------------------------------------------------------------------------------------------
* 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 AccountStore from 'sql/platform/accounts/common/accountStore';
import { AccountDialogController } from 'sql/platform/accounts/browser/accountDialogController';
import { AccountManagementService } from 'sql/workbench/services/accountManagement/browser/accountManagementService';
import { AccountAdditionResult, AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/platform/accounts/common/eventTypes';
import { IAccountStore } from 'sql/platform/accounts/common/interfaces';
import { AccountProviderStub } from 'sql/platform/accounts/test/common/testAccountManagementService';
import { EventVerifierSingle } from 'sqltest/utils/eventVerifier';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
// SUITE CONSTANTS /////////////////////////////////////////////////////////
const hasAccountProvider: azdata.AccountProviderMetadata = {
id: 'hasAccounts',
displayName: 'Provider with Accounts'
};
const noAccountProvider: azdata.AccountProviderMetadata = {
id: 'noAccounts',
displayName: 'Provider without Accounts'
};
const account: azdata.Account = {
key: {
providerId: hasAccountProvider.id,
accountId: 'testAccount1'
},
displayInfo: {
displayName: 'Test Account 1',
accountType: 'test',
contextualDisplayName: 'Azure Account',
userId: 'user@email.com'
},
isStale: false,
properties: {}
};
const accountList: azdata.Account[] = [account];
suite('Account Management Service Tests:', () => {
test('Constructor', () => {
// If: I construct an account management service
let ams = getTestState().accountManagementService;
// Then:
// ... It should be created successfully
// ... Events should be available to register
assert.ok(ams.addAccountProviderEvent);
assert.ok(ams.removeAccountProviderEvent);
assert.ok(ams.updateAccountListEvent);
});
test('Account Updated - account added', done => {
// Setup:
// ... Create account management service and to mock up the store
let state = getTestState();
state.mockAccountStore.setup(x => x.addOrUpdate(TypeMoq.It.isAny()))
.returns(account => Promise.resolve(<AccountAdditionResult>{
accountModified: false,
accountAdded: true,
changedAccount: account
}));
state.mockAccountStore.setup(x => x.remove(TypeMoq.It.isAny()))
.returns(() => Promise.resolve(true));
// ... Register a account provider with the management service
let mockProvider = TypeMoq.Mock.ofType<azdata.AccountProvider>(AccountProviderStub);
mockProvider.setup(x => x.clear(TypeMoq.It.isAny())).returns(() => Promise.resolve());
state.accountManagementService._providers[hasAccountProvider.id] = {
accounts: [account],
provider: mockProvider.object,
metadata: hasAccountProvider
};
// If: I update an account that doesn't exist
state.accountManagementService.accountUpdated(account)
.then(() => {
// Then: Make sure the mocked methods are called
state.mockAccountStore.verify(x => x.addOrUpdate(TypeMoq.It.isAny()), TypeMoq.Times.once());
state.mockAccountStore.verify(x => x.remove(TypeMoq.It.isAny()), TypeMoq.Times.once());
})
.then(
() => done(),
err => done(err)
);
});
test('Account Updated - account modified', done => {
// Setup:
// ... Create account management service and to mock up the store
let state = getTestState();
state.mockAccountStore.setup(x => x.addOrUpdate(TypeMoq.It.isAny()))
.returns(account => Promise.resolve(<AccountAdditionResult>{
accountModified: true,
accountAdded: false,
changedAccount: account
}));
// ... Register a account provider with the management service
let mockProvider = TypeMoq.Mock.ofType<azdata.AccountProvider>(AccountProviderStub);
mockProvider.setup(x => x.clear(TypeMoq.It.isAny())).returns(() => Promise.resolve());
state.accountManagementService._providers[hasAccountProvider.id] = {
accounts: [account],
provider: mockProvider.object,
metadata: hasAccountProvider
};
// If: I update an account that exists
state.accountManagementService.accountUpdated(account)
.then(() => {
// Then:
// ... The mocked method was called
state.mockAccountStore.verify(x => x.addOrUpdate(TypeMoq.It.isAny()), TypeMoq.Times.once());
// ... The account list was updated
state.eventVerifierUpdate.assertFiredWithVerify((params: UpdateAccountListEventParams) => {
assert.equal(params.providerId, hasAccountProvider.id);
assert.ok(Array.isArray(params.accountList));
assert.equal(params.accountList.length, 1);
});
})
.then(
() => done(),
err => done(err)
);
});
test('Add account - provider exists, account does not exist', done => {
// Setup:
// ... Create account management service with a provider
let state = getTestState();
let mockProvider = getMockAccountProvider();
state.accountManagementService._providers[hasAccountProvider.id] = {
accounts: [],
provider: mockProvider.object,
metadata: hasAccountProvider
};
// ... Add add/update handler to the account store that says account was new
state.mockAccountStore.setup(x => x.addOrUpdate(TypeMoq.It.isValue(account)))
.returns(() => Promise.resolve(<AccountAdditionResult>{
accountModified: false,
accountAdded: true,
changedAccount: account
}));
// If: I ask to add an account
return state.accountManagementService.addAccount(hasAccountProvider.id)
.then(() => {
// Then:
// ... The provider should have been prompted
mockProvider.verify(x => x.prompt(), TypeMoq.Times.once());
// ... The account store should have added/updated
state.mockAccountStore.verify(x => x.addOrUpdate(TypeMoq.It.isValue(account)), TypeMoq.Times.once());
// ... The account list change should have been fired
state.eventVerifierUpdate.assertFiredWithVerify(param => {
assert.equal(param.providerId, hasAccountProvider.id);
assert.ok(Array.isArray(param.accountList));
assert.equal(param.accountList.length, 1);
assert.equal(param.accountList[0], account);
});
})
.then(
() => done(),
err => done(err)
);
});
test('Add account - provider exists, account exists', done => {
// Setup:
// ... Create account management service with a provider
let state = getTestState();
let mockProvider = getMockAccountProvider();
state.accountManagementService._providers[hasAccountProvider.id] = {
accounts: [account],
provider: mockProvider.object,
metadata: hasAccountProvider
};
// ... Add add/update handler to the account store that says account was not new
state.mockAccountStore.setup(x => x.addOrUpdate(TypeMoq.It.isValue(account)))
.returns(() => Promise.resolve(<AccountAdditionResult>{
accountModified: true,
accountAdded: false,
changedAccount: account
}));
// If: I ask to add an account
return state.accountManagementService.addAccount(hasAccountProvider.id)
.then(() => {
// Then:
// ... The provider should have been prompted
mockProvider.verify(x => x.prompt(), TypeMoq.Times.once());
// ... The account store should have added/updated
state.mockAccountStore.verify(x => x.addOrUpdate(TypeMoq.It.isValue(account)), TypeMoq.Times.once());
// ... The account list change should have been fired
state.eventVerifierUpdate.assertFiredWithVerify(param => {
assert.equal(param.providerId, hasAccountProvider.id);
assert.ok(Array.isArray(param.accountList));
assert.equal(param.accountList.length, 1);
assert.equal(param.accountList[0], account);
});
})
.then(
() => done(),
err => done(err)
);
});
test('Add account - provider doesn\'t exist', done => {
// Setup: Create account management service
let ams = getTestState().accountManagementService;
// If: I add an account when the provider doesn't exist
// Then: It should not resolve
Promise.race([
new Promise((resolve, reject) => setTimeout(() => resolve(), 100)),
ams.addAccount('doesNotExist').then((
() => done('Promise resolved when the provider did not exist')
))
]).then(() => done(), err => done(err));
});
test('Add account - provider exists, provider fails', done => {
// Setup: Create account management service with a provider
let state = getTestState();
let mockProvider = getFailingMockAccountProvider(false);
state.accountManagementService.registerProvider(noAccountProvider, mockProvider.object);
// If: I ask to add an account and the user cancels
// Then: Nothing should have happened and the promise should be resolved
return state.accountManagementService.addAccount(noAccountProvider.id)
.then(
() => done('Add account promise resolved when it should have rejected'),
() => done()
);
});
test('Add account - provider exists, user cancelled', done => {
// Setup: Create account management service with a provider
let state = getTestState();
let mockProvider = getFailingMockAccountProvider(true);
state.accountManagementService.registerProvider(noAccountProvider, mockProvider.object);
// If: I ask to add an account and the user cancels
// Then: Nothing should have happened and the promise should be resolved
return state.accountManagementService.addAccount(noAccountProvider.id)
.then(
() => done(),
err => done(err)
);
});
test('Get account provider metadata - providers exist', done => {
// Setup: Create account management service with a provider
let state = getTestState();
state.accountManagementService._providers[noAccountProvider.id] = {
accounts: [],
provider: null, // Doesn't matter
metadata: noAccountProvider
};
// If: I ask for all the account provider metadata
return state.accountManagementService.getAccountProviderMetadata()
.then(result => {
// Then: The list should have the one account provider in it
assert.ok(Array.isArray(result));
assert.equal(result.length, 1);
assert.equal(result[0], noAccountProvider);
})
.then(
() => done(),
err => done(err)
);
});
test('Get account provider metadata - no providers', done => {
// Setup: Create account management service
let ams = getTestState().accountManagementService;
// If: I ask for account provider metadata when there isn't any providers
ams.getAccountProviderMetadata()
.then(result => {
// Then: The results should be an empty array
assert.ok(Array.isArray(result));
assert.equal(result.length, 0);
})
.then(
() => done(),
err => done(err)
);
});
test('Get accounts by provider - provider does not exist', done => {
// Setup: Create account management service
let ams = getTestState().accountManagementService;
// If: I get accounts when the provider doesn't exist
// Then: It should not resolve
Promise.race([
new Promise((resolve, reject) => setTimeout(() => resolve(), 100)),
ams.getAccountsForProvider('doesNotExist').then((
() => done('Promise resolved when the provider did not exist')
))
]).then(() => done(), err => done(err));
});
test('Get accounts by provider - provider exists, no accounts', done => {
// Setup: Create account management service
let ams = getTestState().accountManagementService;
ams._providers[noAccountProvider.id] = {
accounts: [],
provider: null, // Doesn't matter
metadata: noAccountProvider
};
// If: I ask for the accounts for a provider with no accounts
ams.getAccountsForProvider(noAccountProvider.id)
.then(result => {
// Then: I should get back an empty array
assert.ok(Array.isArray(result));
assert.equal(result.length, 0);
})
.then(
() => done(),
err => done(err)
);
});
test('Get accounts by provider - provider exists, has accounts', done => {
// Setup: Create account management service
let ams = getTestState().accountManagementService;
ams._providers[hasAccountProvider.id] = {
accounts: [account],
provider: null, // Doesn't matter
metadata: hasAccountProvider
};
// If: I ask for the accounts for a provider with accounts
ams.getAccountsForProvider(hasAccountProvider.id)
.then(result => {
// Then: I should get back the list of accounts
assert.equal(result, accountList);
})
.then(
() => done(),
err => done(err)
);
});
test('Remove account - account exists', done => {
// Setup:
// ... Create account management service and to fake removing an account that exists
let state = getTestState();
state.mockAccountStore.setup(x => x.remove(TypeMoq.It.isAny()))
.returns(() => Promise.resolve(true));
// ... Register a account provider with the management service
let mockProvider = TypeMoq.Mock.ofType<azdata.AccountProvider>(AccountProviderStub);
mockProvider.setup(x => x.clear(TypeMoq.It.isAny())).returns(() => Promise.resolve());
state.accountManagementService._providers[hasAccountProvider.id] = {
accounts: [account],
provider: mockProvider.object,
metadata: hasAccountProvider
};
// If: I remove an account that exists
state.accountManagementService.removeAccount(account.key)
.then(result => {
// Then:
// ... I should have gotten true back
assert.ok(result);
// ... The account store should have had remove called
state.mockAccountStore.verify(x => x.remove(TypeMoq.It.isValue(account.key)), TypeMoq.Times.once());
// ... The provider should have had clear called
mockProvider.verify(x => x.clear(TypeMoq.It.isValue(account.key)), TypeMoq.Times.once());
// ... The updated account list event should have fired
state.eventVerifierUpdate.assertFiredWithVerify((params: UpdateAccountListEventParams) => {
assert.equal(params.providerId, hasAccountProvider.id);
assert.ok(Array.isArray(params.accountList));
assert.equal(params.accountList.length, 0);
});
})
.then(
() => done(),
err => done(err)
);
});
test('Remove account - account doesn\'t exist', done => {
// Setup:
// ... Create account management service and to fake removing an account that doesn't exist
let state = getTestState();
state.mockAccountStore.setup(x => x.remove(TypeMoq.It.isAny()))
.returns(() => Promise.resolve(false));
// ... Register a account provider with the management service
let mockProvider = getMockAccountProvider();
mockProvider.setup(x => x.clear(TypeMoq.It.isAny())).returns(() => Promise.resolve());
state.accountManagementService._providers[noAccountProvider.id] = {
accounts: [],
provider: mockProvider.object,
metadata: noAccountProvider
};
// If: I remove an account that doesn't exist
let accountKey = { providerId: noAccountProvider.id, accountId: 'foobar' };
state.accountManagementService.removeAccount(accountKey)
.then(result => {
// Then:
// ... I should have gotten false back
assert.ok(!result);
// ... The account store should have had remove called
state.mockAccountStore.verify(x => x.remove(TypeMoq.It.isValue(accountKey)), TypeMoq.Times.once());
// ... The provider should have had clear called
mockProvider.verify(x => x.clear(TypeMoq.It.isValue(accountKey)), TypeMoq.Times.once());
// ... The updated account list event should not have fired
state.eventVerifierUpdate.assertNotFired();
})
.then(
() => done(),
err => done(err)
);
});
test('Open account dialog - first call', done => {
// Setup:
// ... Create account management ervice
let state = getTestState();
// ... Add mocking for instantiating an account dialog controller
let mockDialogController = TypeMoq.Mock.ofType(AccountDialogController);
mockDialogController.setup(x => x.openAccountDialog());
state.instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountDialogController)))
.returns(() => mockDialogController.object);
// If: I open the account dialog when it doesn't exist
state.accountManagementService.openAccountListDialog()
.then(() => {
// Then:
// ... The instantiation service should have been called once
state.instantiationService.verify(x => x.createInstance(TypeMoq.It.isValue(AccountDialogController)), TypeMoq.Times.once());
// ... The dialog should have been opened
mockDialogController.verify(x => x.openAccountDialog(), TypeMoq.Times.once());
})
.then(
() => done(),
err => done(err)
);
});
test('Open account dialog - subsequent calls', done => {
// Setup:
// ... Create account management ervice
let state = getTestState();
// ... Add mocking for instantiating an account dialog controller
let mockDialogController = TypeMoq.Mock.ofType(AccountDialogController);
mockDialogController.setup(x => x.openAccountDialog());
state.instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountDialogController)))
.returns(() => mockDialogController.object);
// If: I open the account dialog for a second time
state.accountManagementService.openAccountListDialog()
.then(() => state.accountManagementService.openAccountListDialog())
.then(() => {
// Then:
// ... The instantiation service should have only been called once
state.instantiationService.verify(x => x.createInstance(TypeMoq.It.isValue(AccountDialogController)), TypeMoq.Times.once());
// ... The dialog should have been opened twice
mockDialogController.verify(x => x.openAccountDialog(), TypeMoq.Times.exactly(2));
})
.then(
() => done(),
err => done(err)
);
});
// test('Perform oauth - success', done => {
// TODO: implement this test properly once we remove direct IPC calls (see https://github.com/Microsoft/carbon/issues/2091)
// });
test('Register provider - success', done => {
// Setup:
// ... Create ams, account store that will accept account add/update
let mocks = getTestState();
mocks.mockAccountStore.setup(x => x.addOrUpdate(TypeMoq.It.isAny()))
.returns(() => Promise.resolve(undefined));
// ... Create mock account provider
let mockProvider = getMockAccountProvider();
// If: I register a new provider
mocks.accountManagementService.registerProvider(noAccountProvider, mockProvider.object)
.then(() => {
// Then:
// ... Account store should have been called to get dehydrated accounts
mocks.mockAccountStore.verify(x => x.getAccountsByProvider(TypeMoq.It.isValue(noAccountProvider.id)), TypeMoq.Times.once());
// ... The provider should have been initialized
mockProvider.verify(x => x.initialize(TypeMoq.It.isAny()), TypeMoq.Times.once());
// ... The provider added event should have fired
mocks.eventVerifierProviderAdded.assertFiredWithVerify((param: AccountProviderAddedEventParams) => {
assert.equal(param.addedProvider, noAccountProvider);
assert.ok(Array.isArray(param.initialAccounts));
assert.equal(param.initialAccounts.length, 0);
});
})
.then(
() => done(),
err => done(err)
);
});
test('Unregister provider - success', done => {
// Setup:
// ... Create ams
let mocks = getTestState();
// ... Register a provider to remove
let mockProvider = getMockAccountProvider();
mocks.accountManagementService.registerProvider(noAccountProvider, mockProvider.object)
.then((success) => {
// If: I remove an account provider
mocks.accountManagementService.unregisterProvider(noAccountProvider);
// Then: The provider removed event should have fired
mocks.eventVerifierProviderRemoved.assertFired(noAccountProvider);
}, error => {
}).then(() => done(), err => done(err));
});
});
function getTestState(): AccountManagementState {
// Create mock account store
let mockAccountStore = TypeMoq.Mock.ofType<IAccountStore>(AccountStore);
mockAccountStore.setup(x => x.getAccountsByProvider(TypeMoq.It.isValue(noAccountProvider.id)))
.returns(() => Promise.resolve([]));
mockAccountStore.setup(x => x.getAccountsByProvider(TypeMoq.It.isValue(hasAccountProvider.id)))
.returns(() => Promise.resolve(accountList));
// Create instantiation service
let mockInstantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Strict);
mockInstantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountStore), TypeMoq.It.isAny()))
.returns(() => mockAccountStore.object);
// Create mock memento
let mockMemento = {};
// Create the account management service
let ams = new AccountManagementService(mockMemento, mockInstantiationService.object, new TestStorageService(), null);
// Wire up event handlers
let evUpdate = new EventVerifierSingle<UpdateAccountListEventParams>();
let evAddProvider = new EventVerifierSingle<AccountProviderAddedEventParams>();
let evRemoveProvider = new EventVerifierSingle<azdata.AccountProviderMetadata>();
ams.updateAccountListEvent(evUpdate.eventHandler);
ams.addAccountProviderEvent(evAddProvider.eventHandler);
ams.removeAccountProviderEvent(evRemoveProvider.eventHandler);
// Create the account management service
return {
accountManagementService: ams,
instantiationService: mockInstantiationService,
mockAccountStore: mockAccountStore,
eventVerifierUpdate: evUpdate,
eventVerifierProviderAdded: evAddProvider,
eventVerifierProviderRemoved: evRemoveProvider
};
}
function getMockAccountProvider(): TypeMoq.Mock<azdata.AccountProvider> {
let mockProvider = TypeMoq.Mock.ofType<azdata.AccountProvider>(AccountProviderStub);
mockProvider.setup(x => x.clear(TypeMoq.It.isAny())).returns(() => Promise.resolve());
mockProvider.setup(x => x.initialize(TypeMoq.It.isAny())).returns(param => Promise.resolve(param));
mockProvider.setup(x => x.prompt()).returns(() => Promise.resolve(account));
return mockProvider;
}
function getFailingMockAccountProvider(cancel: boolean): TypeMoq.Mock<azdata.AccountProvider> {
let mockProvider = TypeMoq.Mock.ofType<azdata.AccountProvider>(AccountProviderStub);
mockProvider.setup(x => x.clear(TypeMoq.It.isAny()))
.returns(() => Promise.resolve());
mockProvider.setup(x => x.initialize(TypeMoq.It.isAny()))
.returns(param => Promise.resolve(param));
mockProvider.setup(x => x.prompt())
.returns(() => {
return cancel
? Promise.resolve(<azdata.PromptFailedResult>{ canceled: true }).then()
: Promise.reject(new Error()).then();
});
mockProvider.setup(x => x.refresh(TypeMoq.It.isAny()))
.returns(() => {
return cancel
? Promise.resolve(<azdata.PromptFailedResult>{ canceled: true }).then()
: Promise.reject(new Error()).then();
});
return mockProvider;
}
interface AccountManagementState {
accountManagementService: AccountManagementService;
instantiationService: TypeMoq.Mock<InstantiationService>;
mockAccountStore: TypeMoq.Mock<IAccountStore>;
eventVerifierUpdate: EventVerifierSingle<UpdateAccountListEventParams>;
eventVerifierProviderAdded: EventVerifierSingle<AccountProviderAddedEventParams>;
eventVerifierProviderRemoved: EventVerifierSingle<azdata.AccountProviderMetadata>;
}

View File

@@ -13,7 +13,7 @@ import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import * as ConnectionUtils from 'sql/platform/connection/common/utils';
import { ProviderConnectionInfo } from 'sql/platform/connection/common/providerConnectionInfo';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { BackupDialog } from 'sql/workbench/parts/backup/electron-browser/backupDialog';
import { BackupDialog } from 'sql/workbench/parts/backup/browser/backupDialog';
import { OptionsDialog } from 'sql/workbench/browser/modal/optionsDialog';
import { IBackupService, TaskExecutionMode } from 'sql/platform/backup/common/backupService';
import { IBackupUiService } from 'sql/workbench/services/backup/common/backupUiService';

View File

@@ -0,0 +1,393 @@
/*---------------------------------------------------------------------------------------------
* 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 * as azdata from 'azdata';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { CommandLineService } from 'sql/workbench/services/commandLine/common/commandLineService';
import * as Constants from 'sql/platform/connection/common/constants';
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IConnectionManagementService, IConnectionCompletionOptions, ConnectionType } from 'sql/platform/connection/common/connectionManagement';
import { TestConnectionManagementService } from 'sql/platform/connection/test/common/testConnectionManagementService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { TestCommandService } from 'vs/editor/test/browser/editorTestServices';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { assertThrowsAsync } from 'sqltest/utils/testUtils';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestEditorService } from 'vs/workbench/test/workbenchTestServices';
import { QueryInput, QueryEditorState } from 'sql/workbench/parts/query/common/queryInput';
import { URI } from 'vs/base/common/uri';
import { ILogService, NullLogService } from 'vs/platform/log/common/log';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
class TestParsedArgs implements ParsedArgs {
[arg: string]: any;
_: string[];
aad?: boolean;
add?: boolean;
database?: string;
command?: string;
debugBrkPluginHost?: string;
debugBrkSearch?: string;
debugId?: string;
debugPluginHost?: string;
debugSearch?: string;
diff?: boolean;
'disable-crash-reporter'?: string;
'disable-extension'?: string | string[];
'disable-extensions'?: boolean;
'disable-restore-windows'?: boolean;
'disable-telemetry'?: boolean;
'disable-updates'?: string;
'driver'?: string;
'enable-proposed-api'?: string | string[];
'export-default-configuration'?: string;
'extensions-dir'?: string;
extensionDevelopmentPath?: string;
extensionTestsPath?: string;
'file-chmod'?: boolean;
'file-write'?: boolean;
'folder-uri'?: string | string[];
goto?: boolean;
help?: boolean;
'install-extension'?: string | string[];
'install-source'?: string;
integrated?: boolean;
'list-extensions'?: boolean;
locale?: string;
log?: string;
logExtensionHostCommunication?: boolean;
'max-memory'?: string;
'new-window'?: boolean;
'open-url'?: boolean;
performance?: boolean;
'prof-append-timers'?: string;
'prof-startup'?: string;
'prof-startup-prefix'?: string;
'reuse-window'?: boolean;
server?: string;
'show-versions'?: boolean;
'skip-add-to-recently-opened'?: boolean;
'skip-getting-started'?: boolean;
'skip-release-notes'?: boolean;
status?: boolean;
'sticky-quickopen'?: boolean;
'uninstall-extension'?: string | string[];
'unity-launch'?: boolean; // Always open a new window, except if opening the first window or opening a file or folder as part of the launch.
'upload-logs'?: string;
user?: string;
'user-data-dir'?: string;
_urls?: string[];
verbose?: boolean;
version?: boolean;
wait?: boolean;
waitMarkerFilePath?: string;
}
suite('commandLineService tests', () => {
let capabilitiesService: TestCapabilitiesService;
setup(() => {
capabilitiesService = new TestCapabilitiesService();
});
function getCommandLineService(connectionManagementService: IConnectionManagementService,
configurationService: IConfigurationService,
capabilitiesService?: ICapabilitiesService,
commandService?: ICommandService,
editorService?: IEditorService,
logService?: ILogService
): CommandLineService {
let service = new CommandLineService(
capabilitiesService,
connectionManagementService,
undefined,
undefined,
undefined,
editorService,
commandService,
configurationService,
undefined,
logService
);
return service;
}
function getConfigurationServiceMock(showConnectDialogOnStartup: boolean): TypeMoq.Mock<IConfigurationService> {
let configurationService = TypeMoq.Mock.ofType<IConfigurationService>(TestConfigurationService);
configurationService.setup((c) => c.getValue(TypeMoq.It.isAnyString())).returns((config: string) => showConnectDialogOnStartup);
return configurationService;
}
test('processCommandLine shows connection dialog by default', done => {
const connectionManagementService: TypeMoq.Mock<IConnectionManagementService>
= TypeMoq.Mock.ofType<IConnectionManagementService>(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
connectionManagementService.setup((c) => c.showConnectionDialog())
.returns(() => new Promise<void>((resolve, reject) => { resolve(); }))
.verifiable();
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => false);
connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.returns(() => new Promise<string>((resolve, reject) => { resolve('unused'); }))
.verifiable(TypeMoq.Times.never());
const configurationService = getConfigurationServiceMock(true);
let service = getCommandLineService(connectionManagementService.object, configurationService.object);
service.processCommandLine(new TestParsedArgs()).then(() => {
connectionManagementService.verifyAll();
done();
}, error => { assert.fail(error, null, 'processCommandLine rejected ' + error); done(); });
});
test('processCommandLine does nothing if no server name and command name is provided and the configuration \'workbench.showConnectDialogOnStartup\' is set to false, even if registered servers exist', async () => {
const connectionManagementService: TypeMoq.Mock<IConnectionManagementService>
= TypeMoq.Mock.ofType<IConnectionManagementService>(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
connectionManagementService.setup((c) => c.showConnectionDialog()).verifiable(TypeMoq.Times.never());
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => false);
connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.verifiable(TypeMoq.Times.never());
const configurationService = getConfigurationServiceMock(false);
let service = getCommandLineService(connectionManagementService.object, configurationService.object);
await service.processCommandLine(new TestParsedArgs());
connectionManagementService.verifyAll();
});
test('processCommandLine does nothing if registered servers exist and no server name is provided', async () => {
const connectionManagementService: TypeMoq.Mock<IConnectionManagementService>
= TypeMoq.Mock.ofType<IConnectionManagementService>(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
connectionManagementService.setup((c) => c.showConnectionDialog()).verifiable(TypeMoq.Times.never());
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true);
connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.returns(() => new Promise<string>((resolve, reject) => { resolve('unused'); }))
.verifiable(TypeMoq.Times.never());
const configurationService = getConfigurationServiceMock(true);
let service = getCommandLineService(connectionManagementService.object, configurationService.object);
try {
await service.processCommandLine(new TestParsedArgs());
connectionManagementService.verifyAll();
} catch (error) {
assert.fail(error, null, 'processCommandLine rejected ' + error);
}
});
test('processCommandLine opens a new connection if a server name is passed', async () => {
const connectionManagementService: TypeMoq.Mock<IConnectionManagementService>
= TypeMoq.Mock.ofType<IConnectionManagementService>(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
const args: TestParsedArgs = new TestParsedArgs();
args.server = 'myserver';
args.database = 'mydatabase';
args.user = 'myuser';
connectionManagementService.setup((c) => c.showConnectionDialog()).verifiable(TypeMoq.Times.never());
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce());
connectionManagementService.setup(c => c.getConnectionGroups(TypeMoq.It.isAny())).returns(() => []);
let originalProfile: IConnectionProfile = undefined;
connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.sqlLogin), 'connection', true))
.returns((conn) => {
originalProfile = conn;
return Promise.resolve('unused');
})
.verifiable(TypeMoq.Times.once());
connectionManagementService.setup(c => c.getConnectionProfileById(TypeMoq.It.isAnyString())).returns(() => originalProfile);
const configurationService = getConfigurationServiceMock(true);
const logService = new NullLogService();
let service = getCommandLineService(connectionManagementService.object, configurationService.object, capabilitiesService, undefined, undefined, logService);
await service.processCommandLine(args);
connectionManagementService.verifyAll();
});
test('processCommandLine invokes a command without a profile parameter when no server is passed', async () => {
const connectionManagementService: TypeMoq.Mock<IConnectionManagementService>
= TypeMoq.Mock.ofType<IConnectionManagementService>(TestConnectionManagementService, TypeMoq.MockBehavior.Loose);
const commandService: TypeMoq.Mock<ICommandService> = TypeMoq.Mock.ofType<ICommandService>(TestCommandService);
const args: TestParsedArgs = new TestParsedArgs();
args.command = 'mycommand';
connectionManagementService.setup((c) => c.showConnectionDialog()).verifiable(TypeMoq.Times.never());
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce());
connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.verifiable(TypeMoq.Times.never());
let capturedArgs: any;
commandService.setup(c => c.executeCommand(TypeMoq.It.isAnyString(), undefined))
.returns((command, args) => {
capturedArgs = args;
return Promise.resolve();
})
.verifiable(TypeMoq.Times.once());
const configurationService = getConfigurationServiceMock(true);
let service = getCommandLineService(connectionManagementService.object, configurationService.object, capabilitiesService, commandService.object);
await service.processCommandLine(args);
connectionManagementService.verifyAll();
commandService.verifyAll();
should(capturedArgs).be.undefined();
});
test('processCommandLine invokes a command with a profile parameter when a server is passed', async () => {
const connectionManagementService: TypeMoq.Mock<IConnectionManagementService>
= TypeMoq.Mock.ofType<IConnectionManagementService>(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
const commandService: TypeMoq.Mock<ICommandService> = TypeMoq.Mock.ofType<ICommandService>(TestCommandService);
const args: TestParsedArgs = new TestParsedArgs();
args.command = 'mycommand';
args.server = 'myserver';
connectionManagementService.setup((c) => c.showConnectionDialog()).verifiable(TypeMoq.Times.never());
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce());
let originalProfile: IConnectionProfile = undefined;
connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver'), 'connection', true))
.returns((conn) => {
originalProfile = conn;
return Promise.resolve('unused');
})
.verifiable(TypeMoq.Times.once());
connectionManagementService.setup(c => c.getConnectionProfileById(TypeMoq.It.isAnyString())).returns(() => originalProfile);
connectionManagementService.setup(c => c.getConnectionGroups(TypeMoq.It.isAny())).returns(() => []);
let actualProfile: azdata.ConnectedContext = undefined;
commandService.setup(c => c.executeCommand('mycommand', TypeMoq.It.isAny()))
.returns((cmdName, profile) => {
actualProfile = profile;
return Promise.resolve();
})
.verifiable(TypeMoq.Times.once());
const configurationService = getConfigurationServiceMock(true);
let service = getCommandLineService(connectionManagementService.object, configurationService.object, capabilitiesService, commandService.object);
await service.processCommandLine(args);
connectionManagementService.verifyAll();
commandService.verifyAll();
should(actualProfile).not.be.undefined();
should(actualProfile.connectionProfile.serverName).equal(args.server);
});
test('processCommandLine rejects unknown commands', async () => {
const connectionManagementService: TypeMoq.Mock<IConnectionManagementService>
= TypeMoq.Mock.ofType<IConnectionManagementService>(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
const commandService: TypeMoq.Mock<ICommandService> = TypeMoq.Mock.ofType<ICommandService>(TestCommandService);
const args: TestParsedArgs = new TestParsedArgs();
args.command = 'mycommand';
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true);
commandService.setup(c => c.executeCommand('mycommand'))
.returns(() => Promise.reject(new Error('myerror')))
.verifiable(TypeMoq.Times.once());
const configurationService = getConfigurationServiceMock(true);
let service = getCommandLineService(connectionManagementService.object, configurationService.object, capabilitiesService, commandService.object);
assertThrowsAsync(async () => await service.processCommandLine(args));
});
test('processCommandLine uses Integrated auth if no user name or auth type is passed', async () => {
const connectionManagementService: TypeMoq.Mock<IConnectionManagementService>
= TypeMoq.Mock.ofType<IConnectionManagementService>(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
const args: TestParsedArgs = new TestParsedArgs();
args.server = 'myserver';
args.database = 'mydatabase';
connectionManagementService.setup((c) => c.showConnectionDialog()).verifiable(TypeMoq.Times.never());
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce());
let originalProfile: IConnectionProfile = undefined;
connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.integrated), 'connection', true))
.returns((conn) => {
originalProfile = conn;
return Promise.resolve('unused');
})
.verifiable(TypeMoq.Times.once());
connectionManagementService.setup(c => c.getConnectionProfileById(TypeMoq.It.isAnyString())).returns(() => originalProfile);
connectionManagementService.setup(c => c.getConnectionGroups(TypeMoq.It.isAny())).returns(() => []);
const configurationService = getConfigurationServiceMock(true);
const logService = new NullLogService();
let service = getCommandLineService(connectionManagementService.object, configurationService.object, capabilitiesService, undefined, undefined, logService);
await service.processCommandLine(args);
connectionManagementService.verifyAll();
});
test('processCommandLine reuses saved connections that match args', async () => {
const connectionManagementService: TypeMoq.Mock<IConnectionManagementService>
= TypeMoq.Mock.ofType<IConnectionManagementService>(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
let connection = new ConnectionProfile(capabilitiesService, {
connectionName: 'Test',
savePassword: false,
groupFullName: 'testGroup',
serverName: 'myserver',
databaseName: 'mydatabase',
authenticationType: Constants.integrated,
password: undefined,
userName: '',
groupId: undefined,
providerName: 'MSSQL',
options: {},
saveProfile: true,
id: 'testID'
});
let conProfGroup = new ConnectionProfileGroup('testGroup', undefined, 'testGroup', undefined, undefined);
conProfGroup.connections = [connection];
const args: TestParsedArgs = new TestParsedArgs();
args.server = 'myserver';
args.database = 'mydatabase';
connectionManagementService.setup((c) => c.showConnectionDialog()).verifiable(TypeMoq.Times.never());
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce());
let originalProfile: IConnectionProfile = undefined;
connectionManagementService.setup(c => c.connectIfNotConnected(
TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.integrated && p.connectionName === 'Test' && p.id === 'testID'), 'connection', true))
.returns((conn) => {
originalProfile = conn;
return Promise.resolve('unused');
})
.verifiable(TypeMoq.Times.once());
connectionManagementService.setup(c => c.getConnectionProfileById('testID')).returns(() => originalProfile).verifiable(TypeMoq.Times.once());
connectionManagementService.setup(x => x.getConnectionGroups(TypeMoq.It.isAny())).returns(() => [conProfGroup]);
const configurationService = getConfigurationServiceMock(true);
const logService = new NullLogService();
let service = getCommandLineService(connectionManagementService.object, configurationService.object, capabilitiesService, undefined, undefined, logService);
await service.processCommandLine(args);
connectionManagementService.verifyAll();
});
test('processCommandLine connects opened query files to given server', async () => {
const connectionManagementService: TypeMoq.Mock<IConnectionManagementService>
= TypeMoq.Mock.ofType<IConnectionManagementService>(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
const args: TestParsedArgs = new TestParsedArgs();
args.server = 'myserver';
args.database = 'mydatabase';
args.user = 'myuser';
args._ = ['c:\\dir\\file.sql'];
connectionManagementService.setup((c) => c.showConnectionDialog()).verifiable(TypeMoq.Times.never());
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true).verifiable(TypeMoq.Times.atMostOnce());
connectionManagementService.setup(c => c.getConnectionGroups(TypeMoq.It.isAny())).returns(() => []);
let originalProfile: IConnectionProfile = undefined;
connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.sqlLogin), 'connection', true))
.returns((conn) => {
originalProfile = conn;
return Promise.resolve('unused');
}).verifiable(TypeMoq.Times.once());
connectionManagementService.setup(c => c.getConnectionProfileById(TypeMoq.It.isAnyString())).returns(() => originalProfile);
const configurationService = getConfigurationServiceMock(true);
const queryInput: TypeMoq.Mock<QueryInput> = TypeMoq.Mock.ofType<QueryInput>(QueryInput);
let uri = URI.file(args._[0]);
const queryState = new QueryEditorState();
queryState.connected = true;
queryInput.setup(q => q.state).returns(() => queryState);
queryInput.setup(q => q.getResource()).returns(() => uri).verifiable(TypeMoq.Times.once());
const editorService: TypeMoq.Mock<IEditorService> = TypeMoq.Mock.ofType<IEditorService>(TestEditorService, TypeMoq.MockBehavior.Strict);
editorService.setup(e => e.editors).returns(() => [queryInput.object]);
connectionManagementService.setup(c =>
c.connect(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.sqlLogin),
uri.toString(),
TypeMoq.It.is<IConnectionCompletionOptions>(i => i.params.input === queryInput.object && i.params.connectionType === ConnectionType.editor))
).verifiable(TypeMoq.Times.once());
let service = getCommandLineService(connectionManagementService.object, configurationService.object, capabilitiesService, undefined, editorService.object);
await service.processCommandLine(args);
queryInput.verifyAll();
connectionManagementService.verifyAll();
});
});

View File

@@ -16,7 +16,7 @@ import { ConnectionProfile } from 'sql/platform/connection/common/connectionProf
import { TabbedPanel, PanelTabIdentifier } from 'sql/base/browser/ui/panel/panel';
import { RecentConnectionTreeController, RecentConnectionActionsProvider } from 'sql/workbench/parts/connection/browser/recentConnectionTreeController';
import { SavedConnectionTreeController } from 'sql/workbench/parts/connection/browser/savedConnectionTreeController';
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import { ClearRecentConnectionsAction } from 'sql/workbench/parts/connection/common/connectionActions';
import * as Constants from 'sql/platform/connection/common/constants';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';

View File

@@ -0,0 +1,89 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ConnectionDialogService } from 'sql/workbench/services/connection/browser/connectionDialogService';
import { ConnectionDialogWidget } from 'sql/workbench/services/connection/browser/connectionDialogWidget';
import { ConnectionManagementService } from 'sql/platform/connection/common/connectionManagementService';
import { ConnectionType, IConnectableInput, IConnectionResult, INewConnectionParams } from 'sql/platform/connection/common/connectionManagement';
import { TestErrorMessageService } from 'sql/platform/errorMessage/test/common/testErrorMessageService';
import * as TypeMoq from 'typemoq';
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
suite('ConnectionDialogService tests', () => {
let connectionDialogService: ConnectionDialogService;
let mockConnectionManagementService: TypeMoq.Mock<ConnectionManagementService>;
let mockConnectionDialog: TypeMoq.Mock<ConnectionDialogWidget>;
setup(() => {
let errorMessageService = getMockErrorMessageService();
connectionDialogService = new ConnectionDialogService(undefined, undefined, undefined, errorMessageService.object,
undefined, undefined, undefined);
mockConnectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Strict, {}, {}, new TestStorageService());
(connectionDialogService as any)._connectionManagementService = mockConnectionManagementService.object;
mockConnectionDialog = TypeMoq.Mock.ofType(ConnectionDialogWidget, TypeMoq.MockBehavior.Strict,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
new MockContextKeyService()
);
mockConnectionDialog.setup(c => c.resetConnection());
(connectionDialogService as any)._connectionDialog = mockConnectionDialog.object;
});
function getMockErrorMessageService(): TypeMoq.Mock<TestErrorMessageService> {
let mockMessageService = TypeMoq.Mock.ofType(TestErrorMessageService);
mockMessageService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()));
return mockMessageService;
}
function testHandleDefaultOnConnectUri(isEditor: boolean): Thenable<void> {
let testUri = 'test_uri';
let connectionParams = <INewConnectionParams>{
connectionType: isEditor ? ConnectionType.editor : ConnectionType.default,
input: <IConnectableInput>{
uri: testUri,
onConnectStart: undefined,
onConnectSuccess: undefined,
onConnectReject: undefined,
onDisconnect: undefined,
onConnectCanceled: undefined
},
runQueryOnCompletion: undefined,
querySelection: undefined
};
mockConnectionManagementService.setup(x => x.connectAndSaveProfile(undefined, TypeMoq.It.is(_ => true), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(
() => Promise.resolve(<IConnectionResult>{ connected: true, errorMessage: undefined, errorCode: undefined }));
// If I call handleDefaultOnConnect with the given parameters
let thenable: Thenable<void> = (connectionDialogService as any).handleDefaultOnConnect(connectionParams, undefined);
return thenable.then(() => {
// Then the Connection Management Service's connect method was called with the expected URI
let expectedUri = isEditor ? testUri : undefined;
mockConnectionManagementService.verify(
x => x.connectAndSaveProfile(undefined, TypeMoq.It.is(uri => uri === expectedUri), TypeMoq.It.isAny(), TypeMoq.It.isAny()),
TypeMoq.Times.once());
});
}
test('handleDefaultOnConnect uses params URI for editor connections', done => {
testHandleDefaultOnConnectUri(true).then(() => done(), err => {
done(err);
});
});
test('handleDefaultOnConnect uses undefined URI for non-editor connections', done => {
testHandleDefaultOnConnectUri(false).then(() => done(), err => {
done(err);
});
});
});

View File

@@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { INewConnectionParams, IConnectionResult, IConnectionManagementService, IConnectionCompletionOptions } from 'sql/platform/connection/common/connectionManagement';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService';
export class TestConnectionDialogService implements IConnectionDialogService {
_serviceBrand: any;
public showDialog(connectionManagementService: IConnectionManagementService,
params: INewConnectionParams, model: IConnectionProfile, connectionResult?: IConnectionResult, connectionOptions?: IConnectionCompletionOptions): Promise<void> {
let none: void;
return Promise.resolve(none);
}
public openDialogAndWait(connectionManagementService: IConnectionManagementService,
params?: INewConnectionParams, model?: IConnectionProfile, connectionResult?: IConnectionResult): Promise<IConnectionProfile> {
return Promise.resolve(undefined);
}
public openDialogAndWaitButDontConnect(connectionManagementService: IConnectionManagementService,
params?: INewConnectionParams, model?: IConnectionProfile, connectionResult?: IConnectionResult): Promise<IConnectionProfile> {
return Promise.resolve(undefined);
}
}

View File

@@ -3,240 +3,11 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!sql/media/icons/common-icons';
import 'vs/css!./media/newDashboardTabDialog';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IDashboardTab } from 'sql/platform/dashboard/browser/dashboardRegistry';
import * as DOM from 'vs/base/browser/dom';
import { List } from 'vs/base/browser/ui/list/listWidget';
import { Event, Emitter } from 'vs/base/common/event';
import { localize } from 'vs/nls';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Button } from 'sql/base/browser/ui/button/button';
import { Modal } from 'sql/workbench/browser/modal/modal';
import { attachModalDialogStyler, attachButtonStyler } from 'sql/platform/theme/common/styler';
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
import { NewDashboardTabViewModel, IDashboardUITab } from 'sql/workbench/services/dashboard/common/newDashboardTabViewModel';
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
import { ILogService } from 'vs/platform/log/common/log';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
class ExtensionListDelegate implements IListVirtualDelegate<IDashboardUITab> {
private static readonly HEIGHT = 101;
public getHeight(element: IDashboardUITab): number {
return ExtensionListDelegate.HEIGHT;
}
public getTemplateId(element: IDashboardUITab): string {
return 'extensionListRenderer';
}
}
interface ExtensionListTemplate {
root: HTMLElement;
icon: HTMLElement;
title: HTMLElement;
description: HTMLElement;
publisher: HTMLElement;
}
class ExtensionListRenderer implements IListRenderer<IDashboardUITab, ExtensionListTemplate> {
public static TEMPLATE_ID = 'extensionListRenderer';
private static readonly OPENED_TAB_CLASS = 'success';
private static readonly ICON_CLASS = 'extension-status-icon icon';
public get templateId(): string {
return ExtensionListRenderer.TEMPLATE_ID;
}
public renderTemplate(container: HTMLElement): ExtensionListTemplate {
const tableTemplate: ExtensionListTemplate = Object.create(null);
tableTemplate.root = DOM.append(container, DOM.$('div.list-row.extensionTab-list'));
tableTemplate.icon = DOM.append(tableTemplate.root, DOM.$('div.icon'));
let titleContainer = DOM.append(tableTemplate.root, DOM.$('div.extension-details'));
tableTemplate.title = DOM.append(titleContainer, DOM.$('div.title'));
tableTemplate.description = DOM.append(titleContainer, DOM.$('div.description'));
tableTemplate.publisher = DOM.append(titleContainer, DOM.$('div.publisher'));
return tableTemplate;
}
public renderElement(dashboardTab: IDashboardUITab, index: number, templateData: ExtensionListTemplate): void {
templateData.icon.className = ExtensionListRenderer.ICON_CLASS;
if (dashboardTab.isOpened) {
templateData.icon.classList.add(ExtensionListRenderer.OPENED_TAB_CLASS);
}
templateData.title.innerText = dashboardTab.tabConfig.title;
templateData.description.innerText = dashboardTab.tabConfig.description;
templateData.publisher.innerText = dashboardTab.tabConfig.publisher;
}
public disposeTemplate(template: ExtensionListTemplate): void {
// noop
}
public disposeElement(element: IDashboardUITab, index: number, templateData: ExtensionListTemplate): void {
// noop
}
}
export class NewDashboardTabDialog extends Modal {
// MEMBER letIABLES ////////////////////////////////////////////////////
private _addNewTabButton: Button;
private _cancelButton: Button;
private _extensionList: List<IDashboardUITab>;
private _extensionViewContainer: HTMLElement;
private _noExtensionViewContainer: HTMLElement;
private _viewModel: NewDashboardTabViewModel;
// EVENTING ////////////////////////////////////////////////////////////
private _onAddTabs: Emitter<Array<IDashboardUITab>>;
public get onAddTabs(): Event<Array<IDashboardUITab>> { return this._onAddTabs.event; }
private _onCancel: Emitter<void>;
public get onCancel(): Event<void> { return this._onCancel.event; }
constructor(
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@IThemeService themeService: IThemeService,
@ITelemetryService telemetryService: ITelemetryService,
@IContextKeyService contextKeyService: IContextKeyService,
@IClipboardService clipboardService: IClipboardService,
@ILogService logService: ILogService
) {
super(
localize('newDashboardTab.openDashboardExtensions', 'Open dashboard extensions'),
TelemetryKeys.AddNewDashboardTab,
telemetryService,
layoutService,
clipboardService,
themeService,
logService,
contextKeyService,
{ hasSpinner: true }
);
// Setup the event emitters
this._onAddTabs = new Emitter<IDashboardUITab[]>();
this._onCancel = new Emitter<void>();
this._viewModel = new NewDashboardTabViewModel();
this._register(this._viewModel.updateTabListEvent(tabs => this.onUpdateTabList(tabs)));
}
// MODAL OVERRIDE METHODS //////////////////////////////////////////////
protected layout(height?: number): void {
this._extensionList.layout(height);
}
public render() {
super.render();
attachModalDialogStyler(this, this._themeService);
this._addNewTabButton = this.addFooterButton(localize('newDashboardTab.ok', 'OK'), () => this.addNewTabs());
this._cancelButton = this.addFooterButton(localize('newDashboardTab.cancel', 'Cancel'), () => this.cancel());
this.registerListeners();
}
protected renderBody(container: HTMLElement) {
this._extensionViewContainer = DOM.$('div.extension-view');
DOM.append(container, this._extensionViewContainer);
this.createExtensionList(this._extensionViewContainer);
this._noExtensionViewContainer = DOM.$('.no-extension-view');
let noExtensionTitle = DOM.append(this._noExtensionViewContainer, DOM.$('.no-extensionTab-label'));
let noExtensionLabel = localize('newdashboardTabDialog.noExtensionLabel', 'No dashboard extensions are installed at this time. Go to Extension Manager to explore recommended extensions.');
noExtensionTitle.textContent = noExtensionLabel;
DOM.append(container, this._noExtensionViewContainer);
}
private createExtensionList(container: HTMLElement) {
// Create a fixed list view for the extensions
let extensionTabViewContainer = DOM.$('.extensionTab-view');
let delegate = new ExtensionListDelegate();
let extensionTabRenderer = new ExtensionListRenderer();
this._extensionList = new List<IDashboardUITab>(extensionTabViewContainer, delegate, [extensionTabRenderer]);
this._extensionList.onMouseDblClick(e => this.onAccept());
this._extensionList.onKeyDown(e => {
let event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Enter)) {
this.onAccept();
} else if (event.equals(KeyCode.Escape)) {
this.onClose();
}
});
DOM.append(container, extensionTabViewContainer);
this._register(attachListStyler(this._extensionList, this._themeService));
}
private registerListeners(): void {
// Theme styler
this._register(attachButtonStyler(this._cancelButton, this._themeService));
this._register(attachButtonStyler(this._addNewTabButton, this._themeService));
}
/* Overwrite escape key behavior */
protected onClose() {
this.cancel();
}
/* Overwrite enter key behavior */
protected onAccept() {
this.addNewTabs();
}
public close() {
this.hide();
}
private addNewTabs() {
if (this._addNewTabButton.enabled) {
let selectedTabs = this._extensionList.getSelectedElements();
this._onAddTabs.fire(selectedTabs);
}
}
public cancel() {
this.hide();
}
public open(dashboardTabs: Array<IDashboardTab>, openedTabs: Array<IDashboardTab>) {
this.show();
this._viewModel.updateDashboardTabs(dashboardTabs, openedTabs);
}
private onUpdateTabList(tabs: IDashboardUITab[]) {
this._extensionList.splice(0, this._extensionList.length, tabs);
this.layout();
if (this._extensionList.length > 0) {
this._extensionViewContainer.hidden = false;
this._noExtensionViewContainer.hidden = true;
this._extensionList.setSelection([0]);
this._extensionList.domFocus();
this._addNewTabButton.enabled = true;
} else {
this._extensionViewContainer.hidden = true;
this._noExtensionViewContainer.hidden = false;
this._addNewTabButton.enabled = false;
this._cancelButton.focus();
}
}
public dispose(): void {
super.dispose();
}
export const INewDashboardTabDialogService = createDecorator<INewDashboardTabDialogService>('addNewDashboardTabService');
export interface INewDashboardTabDialogService {
_serviceBrand: any;
showDialog(dashboardTabs: Array<IDashboardTab>, openedTabs: Array<IDashboardTab>, uri: string): void;
}

View File

@@ -0,0 +1,242 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!sql/media/icons/common-icons';
import 'vs/css!./media/newDashboardTabDialog';
import * as DOM from 'vs/base/browser/dom';
import { List } from 'vs/base/browser/ui/list/listWidget';
import { Event, Emitter } from 'vs/base/common/event';
import { localize } from 'vs/nls';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IListVirtualDelegate, IListRenderer } from 'vs/base/browser/ui/list/list';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Button } from 'sql/base/browser/ui/button/button';
import { Modal } from 'sql/workbench/browser/modal/modal';
import { attachModalDialogStyler, attachButtonStyler } from 'sql/platform/theme/common/styler';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import { NewDashboardTabViewModel, IDashboardUITab } from 'sql/workbench/services/dashboard/browser/newDashboardTabViewModel';
import { IDashboardTab } from 'sql/platform/dashboard/browser/dashboardRegistry';
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
import { ILogService } from 'vs/platform/log/common/log';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
class ExtensionListDelegate implements IListVirtualDelegate<IDashboardUITab> {
private static readonly HEIGHT = 101;
public getHeight(element: IDashboardUITab): number {
return ExtensionListDelegate.HEIGHT;
}
public getTemplateId(element: IDashboardUITab): string {
return 'extensionListRenderer';
}
}
interface ExtensionListTemplate {
root: HTMLElement;
icon: HTMLElement;
title: HTMLElement;
description: HTMLElement;
publisher: HTMLElement;
}
class ExtensionListRenderer implements IListRenderer<IDashboardUITab, ExtensionListTemplate> {
public static TEMPLATE_ID = 'extensionListRenderer';
private static readonly OPENED_TAB_CLASS = 'success';
private static readonly ICON_CLASS = 'extension-status-icon icon';
public get templateId(): string {
return ExtensionListRenderer.TEMPLATE_ID;
}
public renderTemplate(container: HTMLElement): ExtensionListTemplate {
const tableTemplate: ExtensionListTemplate = Object.create(null);
tableTemplate.root = DOM.append(container, DOM.$('div.list-row.extensionTab-list'));
tableTemplate.icon = DOM.append(tableTemplate.root, DOM.$('div.icon'));
let titleContainer = DOM.append(tableTemplate.root, DOM.$('div.extension-details'));
tableTemplate.title = DOM.append(titleContainer, DOM.$('div.title'));
tableTemplate.description = DOM.append(titleContainer, DOM.$('div.description'));
tableTemplate.publisher = DOM.append(titleContainer, DOM.$('div.publisher'));
return tableTemplate;
}
public renderElement(dashboardTab: IDashboardUITab, index: number, templateData: ExtensionListTemplate): void {
templateData.icon.className = ExtensionListRenderer.ICON_CLASS;
if (dashboardTab.isOpened) {
templateData.icon.classList.add(ExtensionListRenderer.OPENED_TAB_CLASS);
}
templateData.title.innerText = dashboardTab.tabConfig.title;
templateData.description.innerText = dashboardTab.tabConfig.description;
templateData.publisher.innerText = dashboardTab.tabConfig.publisher;
}
public disposeTemplate(template: ExtensionListTemplate): void {
// noop
}
public disposeElement(element: IDashboardUITab, index: number, templateData: ExtensionListTemplate): void {
// noop
}
}
export class NewDashboardTabDialog extends Modal {
// MEMBER letIABLES ////////////////////////////////////////////////////
private _addNewTabButton: Button;
private _cancelButton: Button;
private _extensionList: List<IDashboardUITab>;
private _extensionViewContainer: HTMLElement;
private _noExtensionViewContainer: HTMLElement;
private _viewModel: NewDashboardTabViewModel;
// EVENTING ////////////////////////////////////////////////////////////
private _onAddTabs: Emitter<Array<IDashboardUITab>>;
public get onAddTabs(): Event<Array<IDashboardUITab>> { return this._onAddTabs.event; }
private _onCancel: Emitter<void>;
public get onCancel(): Event<void> { return this._onCancel.event; }
constructor(
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@IThemeService themeService: IThemeService,
@ITelemetryService telemetryService: ITelemetryService,
@IContextKeyService contextKeyService: IContextKeyService,
@IClipboardService clipboardService: IClipboardService,
@ILogService logService: ILogService
) {
super(
localize('newDashboardTab.openDashboardExtensions', 'Open dashboard extensions'),
TelemetryKeys.AddNewDashboardTab,
telemetryService,
layoutService,
clipboardService,
themeService,
logService,
contextKeyService,
{ hasSpinner: true }
);
// Setup the event emitters
this._onAddTabs = new Emitter<IDashboardUITab[]>();
this._onCancel = new Emitter<void>();
this._viewModel = new NewDashboardTabViewModel();
this._register(this._viewModel.updateTabListEvent(tabs => this.onUpdateTabList(tabs)));
}
// MODAL OVERRIDE METHODS //////////////////////////////////////////////
protected layout(height?: number): void {
this._extensionList.layout(height);
}
public render() {
super.render();
attachModalDialogStyler(this, this._themeService);
this._addNewTabButton = this.addFooterButton(localize('newDashboardTab.ok', 'OK'), () => this.addNewTabs());
this._cancelButton = this.addFooterButton(localize('newDashboardTab.cancel', 'Cancel'), () => this.cancel());
this.registerListeners();
}
protected renderBody(container: HTMLElement) {
this._extensionViewContainer = DOM.$('div.extension-view');
DOM.append(container, this._extensionViewContainer);
this.createExtensionList(this._extensionViewContainer);
this._noExtensionViewContainer = DOM.$('.no-extension-view');
let noExtensionTitle = DOM.append(this._noExtensionViewContainer, DOM.$('.no-extensionTab-label'));
let noExtensionLabel = localize('newdashboardTabDialog.noExtensionLabel', 'No dashboard extensions are installed at this time. Go to Extension Manager to explore recommended extensions.');
noExtensionTitle.textContent = noExtensionLabel;
DOM.append(container, this._noExtensionViewContainer);
}
private createExtensionList(container: HTMLElement) {
// Create a fixed list view for the extensions
let extensionTabViewContainer = DOM.$('.extensionTab-view');
let delegate = new ExtensionListDelegate();
let extensionTabRenderer = new ExtensionListRenderer();
this._extensionList = new List<IDashboardUITab>(extensionTabViewContainer, delegate, [extensionTabRenderer]);
this._extensionList.onMouseDblClick(e => this.onAccept());
this._extensionList.onKeyDown(e => {
let event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Enter)) {
this.onAccept();
} else if (event.equals(KeyCode.Escape)) {
this.onClose();
}
});
DOM.append(container, extensionTabViewContainer);
this._register(attachListStyler(this._extensionList, this._themeService));
}
private registerListeners(): void {
// Theme styler
this._register(attachButtonStyler(this._cancelButton, this._themeService));
this._register(attachButtonStyler(this._addNewTabButton, this._themeService));
}
/* Overwrite escape key behavior */
protected onClose() {
this.cancel();
}
/* Overwrite enter key behavior */
protected onAccept() {
this.addNewTabs();
}
public close() {
this.hide();
}
private addNewTabs() {
if (this._addNewTabButton.enabled) {
let selectedTabs = this._extensionList.getSelectedElements();
this._onAddTabs.fire(selectedTabs);
}
}
public cancel() {
this.hide();
}
public open(dashboardTabs: Array<IDashboardTab>, openedTabs: Array<IDashboardTab>) {
this.show();
this._viewModel.updateDashboardTabs(dashboardTabs, openedTabs);
}
private onUpdateTabList(tabs: IDashboardUITab[]) {
this._extensionList.splice(0, this._extensionList.length, tabs);
this.layout();
if (this._extensionList.length > 0) {
this._extensionViewContainer.hidden = false;
this._noExtensionViewContainer.hidden = true;
this._extensionList.setSelection([0]);
this._extensionList.domFocus();
this._addNewTabButton.enabled = true;
} else {
this._extensionViewContainer.hidden = true;
this._noExtensionViewContainer.hidden = false;
this._addNewTabButton.enabled = false;
this._cancelButton.focus();
}
}
public dispose(): void {
super.dispose();
}
}

View File

@@ -3,11 +3,11 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { INewDashboardTabDialogService } from 'sql/workbench/services/dashboard/common/newDashboardTabDialog';
import { NewDashboardTabDialog } from 'sql/workbench/services/dashboard/browser/newDashboardTabDialog';
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
import { INewDashboardTabDialogService } from 'sql/workbench/services/dashboard/browser/newDashboardTabDialog';
import { NewDashboardTabDialog } from 'sql/workbench/services/dashboard/browser/newDashboardTabDialogImpl';
import { IDashboardTab } from 'sql/platform/dashboard/browser/dashboardRegistry';
import { IAngularEventingService, AngularEventType } from 'sql/platform/angularEventing/common/angularEventingService';
import { IDashboardUITab } from 'sql/workbench/services/dashboard/common/newDashboardTabViewModel';
import { IDashboardUITab } from 'sql/workbench/services/dashboard/browser/newDashboardTabViewModel';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';

View File

@@ -5,7 +5,7 @@
import { Event, Emitter } from 'vs/base/common/event';
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
import { IDashboardTab } from 'sql/platform/dashboard/browser/dashboardRegistry';
export interface IDashboardUITab {
@@ -41,4 +41,4 @@ export class NewDashboardTabViewModel {
});
this._updateTabListEmitter.fire(tabList);
}
}
}

View File

@@ -1,13 +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 { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
export const INewDashboardTabDialogService = createDecorator<INewDashboardTabDialogService>('addNewDashboardTabService');
export interface INewDashboardTabDialogService {
_serviceBrand: any;
showDialog(dashboardTabs: Array<IDashboardTab>, openedTabs: Array<IDashboardTab>, uri: string): void;
}

View File

@@ -7,7 +7,7 @@ import 'vs/css!sql/media/icons/common-icons';
import 'vs/css!./media/errorMessageDialog';
import { Button } from 'sql/base/browser/ui/button/button';
import { Modal } from 'sql/workbench/browser/modal/modal';
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import { attachButtonStyler, attachModalDialogStyler } from 'sql/platform/theme/common/styler';
import Severity from 'vs/base/common/severity';

View File

@@ -11,7 +11,7 @@ import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import * as DialogHelper from 'sql/workbench/browser/modal/dialogHelper';
import { Modal } from 'sql/workbench/browser/modal/modal';
import { attachModalDialogStyler, attachButtonStyler } from 'sql/platform/theme/common/styler';
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import { FileNode } from 'sql/workbench/services/fileBrowser/common/fileNode';
import { FileBrowserTreeView } from 'sql/workbench/services/fileBrowser/browser/fileBrowserTreeView';
import { FileBrowserViewModel } from 'sql/workbench/services/fileBrowser/common/fileBrowserViewModel';

View File

@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IInsightDialogActionContext } from 'sql/workbench/services/insights/common/insightsDialogService';
import { IInsightDialogActionContext } from 'sql/workbench/services/insights/browser/insightsDialogService';
import { Action } from 'vs/base/common/actions';
import * as nls from 'vs/nls';

View File

@@ -3,11 +3,20 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IInsightsDialogModel, ListResource } from 'sql/workbench/services/insights/common/insightsDialogService';
import { IInsightsConfigDetails, IInsightsLabel } from 'sql/workbench/parts/dashboard/widgets/insights/interfaces';
import { Conditional } from 'sql/workbench/parts/dashboard/common/interfaces';
import { IInsightsDialogModel, ListResource } from 'sql/workbench/services/insights/browser/insightsDialogService';
import { Event, Emitter } from 'vs/base/common/event';
import { IInsightsConfigDetails, IInsightsLabel } from 'sql/platform/dashboard/browser/insightRegistry';
export enum Conditional {
'equals',
'notEquals',
'greaterThanOrEquals',
'greaterThan',
'lessThanOrEquals',
'lessThan',
'always'
}
export class InsightsDialogModel implements IInsightsDialogModel {
private _rows: string[][];

View File

@@ -2,41 +2,42 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { InsightsDialogController } from 'sql/workbench/services/insights/common/insightsDialogController';
import { InsightsDialogView } from 'sql/workbench/services/insights/browser/insightsDialogView';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IInsightsConfig } from 'sql/workbench/parts/dashboard/widgets/insights/interfaces';
import { IInsightsDialogModel, IInsightsDialogService } from 'sql/workbench/services/insights/common/insightsDialogService';
import { InsightsDialogModel } from 'sql/workbench/services/insights/common/insightsDialogModel';
import { BaseActionContext } from 'sql/workbench/common/actions';
import { IInsightsConfigDetails, IInsightsConfig } from 'sql/platform/dashboard/browser/insightRegistry';
export class InsightsDialogService implements IInsightsDialogService {
_serviceBrand: any;
private _insightsDialogController: InsightsDialogController;
private _insightsDialogView: InsightsDialogView;
private _insightsDialogModel: IInsightsDialogModel;
constructor(@IInstantiationService private _instantiationService: IInstantiationService) { }
// query string
public show(input: IInsightsConfig, connectionProfile: IConnectionProfile): void {
if (!this._insightsDialogView) {
this._insightsDialogModel = new InsightsDialogModel();
this._insightsDialogController = this._instantiationService.createInstance(InsightsDialogController, this._insightsDialogModel);
this._insightsDialogView = this._instantiationService.createInstance(InsightsDialogView, this._insightsDialogModel);
this._insightsDialogView.render();
} else {
this._insightsDialogModel.reset();
this._insightsDialogView.reset();
}
this._insightsDialogModel.insight = input.details;
this._insightsDialogController.update(input.details, connectionProfile);
this._insightsDialogView.open(input.details, connectionProfile);
}
public close(): void {
this._insightsDialogView.close();
}
export interface IInsightsDialogModel {
rows: string[][];
columns: string[];
getListResources(labelIndex: number, valueIndex: number): ListResource[];
reset(): void;
onDataChange: Event<void>;
insight: IInsightsConfigDetails;
}
export interface ListResource {
value: string;
label: string;
icon?: string;
data?: string[];
stateColor?: string;
stateIcon?: string;
}
export const IInsightsDialogService = createDecorator<IInsightsDialogService>('insightsDialogService');
export interface IInsightsDialogService {
_serviceBrand: any;
show(input: IInsightsConfig, connectionProfile: IConnectionProfile): void;
close();
}
export interface IInsightDialogActionContext extends BaseActionContext {
cellData: string;
}
/* Regex that matches the form `${value}` */
export const insertValueRegex: RegExp = /\${(.*?)\}/;

View File

@@ -0,0 +1,42 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { InsightsDialogView } from 'sql/workbench/services/insights/browser/insightsDialogView';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IInsightsDialogModel, IInsightsDialogService } from 'sql/workbench/services/insights/browser/insightsDialogService';
import { InsightsDialogModel } from 'sql/workbench/services/insights/browser/insightsDialogModel';
import { IInsightsConfig } from 'sql/platform/dashboard/browser/insightRegistry';
import { InsightsDialogController } from 'sql/workbench/services/insights/common/insightsDialogController';
export class InsightsDialogService implements IInsightsDialogService {
_serviceBrand: any;
private _insightsDialogController: InsightsDialogController;
private _insightsDialogView: InsightsDialogView;
private _insightsDialogModel: IInsightsDialogModel;
constructor(@IInstantiationService private _instantiationService: IInstantiationService) { }
// query string
public show(input: IInsightsConfig, connectionProfile: IConnectionProfile): void {
if (!this._insightsDialogView) {
this._insightsDialogModel = new InsightsDialogModel();
this._insightsDialogController = this._instantiationService.createInstance(InsightsDialogController, this._insightsDialogModel);
this._insightsDialogView = this._instantiationService.createInstance(InsightsDialogView, this._insightsDialogModel);
this._insightsDialogView.render();
} else {
this._insightsDialogModel.reset();
this._insightsDialogView.reset();
}
this._insightsDialogModel.insight = input.details;
this._insightsDialogController.update(input.details, connectionProfile);
this._insightsDialogView.open(input.details, connectionProfile);
}
public close(): void {
this._insightsDialogView.close();
}
}

View File

@@ -7,16 +7,14 @@ import 'vs/css!./media/insightsDialog';
import { Button } from 'sql/base/browser/ui/button/button';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { Modal } from 'sql/workbench/browser/modal/modal';
import { IInsightsConfigDetails } from 'sql/workbench/parts/dashboard/widgets/insights/interfaces';
import { attachButtonStyler, attachModalDialogStyler, attachTableStyler, attachPanelStyler } from 'sql/platform/theme/common/styler';
import { TaskRegistry } from 'sql/platform/tasks/common/tasks';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import * as TelemetryKeys from 'sql/platform/telemetry/telemetryKeys';
import { IInsightsDialogModel, ListResource, IInsightDialogActionContext, insertValueRegex } from 'sql/workbench/services/insights/common/insightsDialogService';
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
import { IInsightsDialogModel, ListResource, IInsightDialogActionContext, insertValueRegex } from 'sql/workbench/services/insights/browser/insightsDialogService';
import { TableDataView } from 'sql/base/browser/ui/table/tableDataView';
import { RowSelectionModel } from 'sql/base/browser/ui/table/plugins/rowSelectionModel.plugin';
import { Table } from 'sql/base/browser/ui/table/table';
import { CopyInsightDialogSelectionAction } from 'sql/workbench/services/insights/common/insightDialogActions';
import { CopyInsightDialogSelectionAction } from 'sql/workbench/services/insights/browser/insightDialogActions';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
import { IDisposableDataProvider } from 'sql/base/browser/ui/table/interfaces';
@@ -41,6 +39,8 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService } from 'vs/platform/log/common/log';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IInsightsConfigDetails } from 'sql/platform/dashboard/browser/insightRegistry';
import { TaskRegistry } from 'sql/platform/tasks/browser/tasksRegistry';
const labelDisplay = nls.localize("insights.item", "Item");
const valueDisplay = nls.localize("insights.value", "Value");

View File

@@ -5,10 +5,8 @@
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IInsightsConfigDetails } from 'sql/workbench/parts/dashboard/widgets/insights/interfaces';
import QueryRunner from 'sql/platform/query/common/queryRunner';
import * as Utils from 'sql/platform/connection/common/utils';
import { IInsightsDialogModel } from 'sql/workbench/services/insights/common/insightsDialogService';
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
import { resolveQueryFilePath } from './insightsUtils';
@@ -22,6 +20,8 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { ILogService } from 'vs/platform/log/common/log';
import { IFileService } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { IInsightsDialogModel } from 'sql/workbench/services/insights/browser/insightsDialogService';
import { IInsightsConfigDetails } from 'sql/platform/dashboard/browser/insightRegistry';
export class InsightsDialogController {
private _queryRunner: QueryRunner;

View File

@@ -1,43 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IInsightsConfigDetails, IInsightsConfig } from 'sql/workbench/parts/dashboard/widgets/insights/interfaces';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { BaseActionContext } from 'sql/workbench/common/actions';
export interface IInsightsDialogModel {
rows: string[][];
columns: string[];
getListResources(labelIndex: number, valueIndex: number): ListResource[];
reset(): void;
onDataChange: Event<void>;
insight: IInsightsConfigDetails;
}
export interface ListResource {
value: string;
label: string;
icon?: string;
data?: string[];
stateColor?: string;
stateIcon?: string;
}
export const IInsightsDialogService = createDecorator<IInsightsDialogService>('insightsDialogService');
export interface IInsightsDialogService {
_serviceBrand: any;
show(input: IInsightsConfig, connectionProfile: IConnectionProfile): void;
close();
}
export interface IInsightDialogActionContext extends BaseActionContext {
cellData: string;
}
/* Regex that matches the form `${value}` */
export const insertValueRegex: RegExp = /\${(.*?)\}/;

View File

@@ -4,10 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { InsightsDialogController } from 'sql/workbench/services/insights/common/insightsDialogController';
import { InsightsDialogModel } from 'sql/workbench/services/insights/common/insightsDialogModel';
import QueryRunner from 'sql/platform/query/common/queryRunner';
import { ConnectionManagementService } from 'sql/platform/connection/common/connectionManagementService';
import { IInsightsConfigDetails } from 'sql/workbench/parts/dashboard/widgets/insights/interfaces';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
@@ -17,6 +15,8 @@ import { equal } from 'assert';
import { Mock, MockBehavior, It } from 'typemoq';
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
import { Emitter } from 'vs/base/common/event';
import { InsightsDialogModel } from 'sql/workbench/services/insights/browser/insightsDialogModel';
import { IInsightsConfigDetails } from 'sql/platform/dashboard/browser/insightRegistry';
const testData: string[][] = [
['1', '2', '3', '4'],

View File

@@ -2,11 +2,12 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IInsightsLabel, IInsightsConfigDetails } from 'sql/workbench/parts/dashboard/widgets/insights/interfaces';
import { InsightsDialogModel } from 'sql/workbench/services/insights/common/insightsDialogModel';
import { InsightsDialogModel } from 'sql/workbench/services/insights/browser/insightsDialogModel';
import { isUndefinedOrNull } from 'vs/base/common/types';
import * as assert from 'assert';
import { IInsightsLabel, IInsightsConfigDetails } from 'sql/platform/dashboard/browser/insightRegistry';
suite('Insights Dialog Model Tests', () => {
test('does parse condition right', () => {

View File

@@ -9,9 +9,7 @@ import * as os from 'os';
import { resolveQueryFilePath } from 'sql/workbench/services/insights/common/insightsUtils';
import * as path from 'vs/base/common/path';
import * as pfs from 'vs/base/node/pfs';
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { Workspace, toWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/configurationResolverService';
import { TestContextService, TestFileService } from 'vs/workbench/test/workbenchTestServices';
@@ -22,6 +20,8 @@ import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { IFileService } from 'vs/platform/files/common/files';
import * as pfs from 'vs/base/node/pfs';
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
class TestEnvironmentService implements IWorkbenchEnvironmentService {
webviewCspSource: string;
@@ -318,9 +318,4 @@ suite('Insights Utils tests', function () {
}
});
suiteTeardown(() => {
// Clean up our test files
return pfs.rimraf(testRootPath, pfs.RimRafMode.MOVE);
});
});

View File

@@ -8,14 +8,14 @@ import * as azdata from 'azdata';
import { Event } from 'vs/base/common/event';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
import { IBootstrapParams } from 'sql/platform/bootstrap/node/bootstrapService';
import { RenderMimeRegistry } from 'sql/workbench/parts/notebook/outputs/registry';
import { ModelFactory } from 'sql/workbench/parts/notebook/models/modelFactory';
import { RenderMimeRegistry } from 'sql/workbench/parts/notebook/electron-browser/outputs/registry';
import { ModelFactory } from 'sql/workbench/parts/notebook/node/models/modelFactory';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { NotebookInput } from 'sql/workbench/parts/notebook/notebookInput';
import { NotebookInput } from 'sql/workbench/parts/notebook/node/notebookInput';
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
import { ICellModel, INotebookModel, ILanguageMagic } from 'sql/workbench/parts/notebook/models/modelInterfaces';
import { NotebookChangeType } from 'sql/workbench/parts/notebook/models/contracts';
import { ICellModel, INotebookModel } from 'sql/workbench/parts/notebook/node/models/modelInterfaces';
import { NotebookChangeType } from 'sql/workbench/parts/notebook/common/models/contracts';
import { IBootstrapParams } from 'sql/platform/bootstrap/common/bootstrapParams';
export const SERVICE_ID = 'notebookService';
export const INotebookService = createDecorator<INotebookService>(SERVICE_ID);
@@ -25,6 +25,13 @@ export const DEFAULT_NOTEBOOK_FILETYPE = 'IPYNB';
export const SQL_NOTEBOOK_PROVIDER = 'sql';
export const OVERRIDE_EDITOR_THEMING_SETTING = 'notebook.overrideEditorTheming';
export interface ILanguageMagic {
magic: string;
language: string;
kernels?: string[];
executionTarget?: string;
}
export interface INotebookService {
_serviceBrand: any;

View File

@@ -10,10 +10,10 @@ import { Registry } from 'vs/platform/registry/common/platform';
import {
INotebookService, INotebookManager, INotebookProvider,
DEFAULT_NOTEBOOK_FILETYPE, INotebookEditor, SQL_NOTEBOOK_PROVIDER, OVERRIDE_EDITOR_THEMING_SETTING, INavigationProvider
DEFAULT_NOTEBOOK_FILETYPE, INotebookEditor, SQL_NOTEBOOK_PROVIDER, OVERRIDE_EDITOR_THEMING_SETTING, INavigationProvider, ILanguageMagic
} from 'sql/workbench/services/notebook/common/notebookService';
import { RenderMimeRegistry } from 'sql/workbench/parts/notebook/outputs/registry';
import { standardRendererFactories } from 'sql/workbench/parts/notebook/outputs/factories';
import { RenderMimeRegistry } from 'sql/workbench/parts/notebook/electron-browser/outputs/registry';
import { standardRendererFactories } from 'sql/workbench/parts/notebook/electron-browser/outputs/factories';
import { Extensions, INotebookProviderRegistry, NotebookProviderRegistration } from 'sql/workbench/services/notebook/common/notebookRegistry';
import { Emitter, Event } from 'vs/base/common/event';
import { Memento } from 'vs/workbench/common/memento';
@@ -26,11 +26,11 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { NotebookEditorVisibleContext } from 'sql/workbench/services/notebook/common/notebookContext';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { NotebookEditor } from 'sql/workbench/parts/notebook/notebookEditor';
import { NotebookEditor } from 'sql/workbench/parts/notebook/electron-browser/notebookEditor';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { registerNotebookThemes } from 'sql/workbench/parts/notebook/notebookStyles';
import { registerNotebookThemes } from 'sql/workbench/parts/notebook/browser/notebookStyles';
import { IQueryManagementService } from 'sql/platform/query/common/queryManagement';
import { ILanguageMagic, notebookConstants } from 'sql/workbench/parts/notebook/models/modelInterfaces';
import { notebookConstants } from 'sql/workbench/parts/notebook/node/models/modelInterfaces';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { SqlNotebookProvider } from 'sql/workbench/services/notebook/sql/sqlNotebookProvider';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
@@ -40,7 +40,7 @@ import { RunOnceScheduler } from 'vs/base/common/async';
import { Schemas } from 'vs/base/common/network';
import { ILogService } from 'vs/platform/log/common/log';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { NotebookChangeType } from 'sql/workbench/parts/notebook/models/contracts';
import { NotebookChangeType } from 'sql/workbench/parts/notebook/common/models/contracts';
import product from 'vs/platform/product/node/product';
export interface NotebookProviderProperties {

View File

@@ -5,7 +5,7 @@
import { nb } from 'azdata';
import { localize } from 'vs/nls';
import { FutureInternal } from 'sql/workbench/parts/notebook/models/modelInterfaces';
import { FutureInternal } from 'sql/workbench/parts/notebook/node/models/modelInterfaces';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
export const noKernel: string = localize('noKernel', 'No Kernel');

View File

@@ -12,10 +12,10 @@ import * as pfs from 'vs/base/node/pfs';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { JSONObject } from 'sql/workbench/parts/notebook/models/jsonext';
import { OutputTypes } from 'sql/workbench/parts/notebook/models/contracts';
import { nbversion } from 'sql/workbench/parts/notebook/notebookConstants';
import { nbformat } from 'sql/workbench/parts/notebook/models/nbformat';
import { JSONObject } from 'sql/workbench/parts/notebook/common/models/jsonext';
import { OutputTypes } from 'sql/workbench/parts/notebook/common/models/contracts';
import { nbversion } from 'sql/workbench/parts/notebook/common/models/notebookConstants';
import { nbformat } from 'sql/workbench/parts/notebook/common/models/nbformat';
type MimeBundle = { [key: string]: string | string[] | undefined };

View File

@@ -6,8 +6,7 @@
import * as os from 'os';
import { nb, QueryExecuteSubsetResult, IDbColumn, BatchSummary, IResultMessage, ResultSetSummary } from 'azdata';
import { localize } from 'vs/nls';
import * as strings from 'vs/base/common/strings';
import { FutureInternal, ILanguageMagic, notebookConstants } from 'sql/workbench/parts/notebook/models/modelInterfaces';
import { FutureInternal, notebookConstants } from 'sql/workbench/parts/notebook/node/models/modelInterfaces';
import QueryRunner from 'sql/platform/query/common/queryRunner';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -18,12 +17,12 @@ import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMess
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { escape } from 'sql/base/common/strings';
import { elapsedTimeLabel } from 'sql/workbench/parts/query/common/localizedConstants';
import * as notebookUtils from 'sql/workbench/parts/notebook/notebookUtils';
import * as notebookUtils from 'sql/workbench/parts/notebook/node/models/notebookUtils';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { ILogService } from 'vs/platform/log/common/log';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { ILanguageMagic } from 'sql/workbench/services/notebook/common/notebookService';
export const sqlKernelError: string = localize("sqlKernelError", "SQL kernel error");
export const MAX_ROWS = 5000;

View File

@@ -13,8 +13,8 @@ import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { Event, Emitter } from 'vs/base/common/event';
import * as azdata from 'azdata';
import * as nls from 'vs/nls';
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 { ServerTreeView } from 'sql/workbench/parts/objectExplorer/browser/serverTreeView';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';

View File

@@ -0,0 +1,799 @@
/*---------------------------------------------------------------------------------------------
* 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 { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { ObjectExplorerService, NodeExpandInfoWithProviderId } from 'sql/workbench/services/objectExplorer/common/objectExplorerService';
import { NodeType } from 'sql/workbench/parts/objectExplorer/common/nodeType';
import { TreeNode, TreeItemCollapsibleState } from 'sql/workbench/parts/objectExplorer/common/treeNode';
import * as azdata from 'azdata';
import * as TypeMoq from 'typemoq';
import * as assert from 'assert';
import { ServerTreeView } from 'sql/workbench/parts/objectExplorer/browser/serverTreeView';
import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
import { Event, Emitter } from 'vs/base/common/event';
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
import { NullLogService } from 'vs/platform/log/common/log';
import { TestObjectExplorerProvider } from 'sql/workbench/services/objectExplorer/test/common/testObjectExplorerProvider';
import { TestConnectionManagementService } from 'sql/platform/connection/test/common/testConnectionManagementService';
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
suite('SQL Object Explorer Service tests', () => {
let sqlOEProvider: TypeMoq.Mock<TestObjectExplorerProvider>;
let connectionManagementService: TypeMoq.Mock<TestConnectionManagementService>;
let connection: ConnectionProfile;
let connectionToFail: ConnectionProfile;
let conProfGroup: ConnectionProfileGroup;
let objectExplorerService: ObjectExplorerService;
let objectExplorerSession: azdata.ObjectExplorerSession;
let objectExplorerFailedSession: azdata.ObjectExplorerSession;
let objectExplorerCloseSessionResponse: azdata.ObjectExplorerCloseSessionResponse;
let objectExplorerExpandInfo: NodeExpandInfoWithProviderId;
let objectExplorerExpandInfoRefresh: NodeExpandInfoWithProviderId;
let sessionId = '1234';
let failedSessionId = '12345';
let numberOfSuccessfulSessions: number = 0;
let serverTreeView: TypeMoq.Mock<ServerTreeView>;
setup(() => {
let NodeInfoTable1 = {
nodePath: 'testServerName/tables/dbo.Table1',
nodeType: NodeType.Table,
label: 'dbo.Table1',
isLeaf: false,
metadata: null,
nodeSubType: '',
nodeStatus: '',
errorMessage: ''
};
let NodeInfoTable2 = {
nodePath: 'testServerName/tables/dbo.Table2',
nodeType: NodeType.Table,
label: 'dbo.Table2',
isLeaf: false,
metadata: null,
nodeSubType: '',
nodeStatus: '',
errorMessage: ''
};
let NodeInfoTable3 = {
nodePath: 'testServerName/tables/dbo.Table3',
nodeType: NodeType.Table,
label: 'dbo.Table3',
isLeaf: false,
metadata: null,
nodeSubType: '',
nodeStatus: '',
errorMessage: ''
};
objectExplorerSession = {
success: true,
sessionId: sessionId,
rootNode: {
nodePath: 'testServerName/tables',
nodeType: NodeType.Folder,
label: 'Tables',
isLeaf: false,
metadata: null,
nodeSubType: '',
nodeStatus: '',
errorMessage: ''
},
errorMessage: ''
};
objectExplorerFailedSession = {
success: false,
sessionId: failedSessionId,
rootNode: undefined,
errorMessage: 'Connection Failed'
};
objectExplorerCloseSessionResponse = {
success: true,
sessionId: sessionId,
};
objectExplorerExpandInfo = {
sessionId: sessionId,
nodes: [NodeInfoTable1, NodeInfoTable2],
errorMessage: '',
nodePath: objectExplorerSession.rootNode.nodePath,
providerId: mssqlProviderName
};
objectExplorerExpandInfoRefresh = {
sessionId: sessionId,
nodes: [NodeInfoTable1, NodeInfoTable3],
errorMessage: '',
nodePath: objectExplorerSession.rootNode.nodePath,
providerId: mssqlProviderName
};
let response: azdata.ObjectExplorerSessionResponse = {
sessionId: objectExplorerSession.sessionId
};
let failedResponse: azdata.ObjectExplorerSessionResponse = {
sessionId: failedSessionId
};
sqlOEProvider = TypeMoq.Mock.ofType(TestObjectExplorerProvider, TypeMoq.MockBehavior.Loose);
sqlOEProvider.callBase = true;
let onCapabilitiesRegistered = new Emitter<string>();
let sqlProvider = {
providerId: mssqlProviderName,
displayName: 'MSSQL',
connectionOptions: [
{
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
}
]
};
let capabilitiesService = new TestCapabilitiesService();
capabilitiesService.capabilities[mssqlProviderName] = { connection: sqlProvider };
connection = new ConnectionProfile(capabilitiesService, {
connectionName: 'newName',
savePassword: false,
groupFullName: 'testGroup',
serverName: 'testServerName',
databaseName: 'testDatabaseName',
authenticationType: 'inetgrated',
password: 'test',
userName: 'testUsername',
groupId: undefined,
providerName: mssqlProviderName,
options: {},
saveProfile: true,
id: 'testID'
});
conProfGroup = new ConnectionProfileGroup('testGroup', undefined, 'testGroup', undefined, undefined);
connectionToFail = new ConnectionProfile(capabilitiesService, {
connectionName: 'newName2',
savePassword: false,
groupFullName: 'testGroup',
serverName: 'testServerName2',
databaseName: 'testDatabaseName2',
authenticationType: 'inetgrated',
password: 'test',
userName: 'testUsername',
groupId: undefined,
providerName: mssqlProviderName,
options: {},
saveProfile: true,
id: 'testID2'
});
conProfGroup = new ConnectionProfileGroup('testGroup', undefined, 'testGroup', undefined, undefined);
conProfGroup.connections = [connection];
connectionManagementService = TypeMoq.Mock.ofType(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
connectionManagementService.setup(x => x.getConnectionGroups()).returns(() => [conProfGroup]);
connectionManagementService.setup(x => x.getActiveConnections()).returns(() => [connection]);
connectionManagementService.setup(x => x.addSavedPassword(TypeMoq.It.isAny())).returns(() => new Promise<ConnectionProfile>((resolve) => {
resolve(connection);
}));
connectionManagementService.setup(x => x.getCapabilities(mssqlProviderName)).returns(() => undefined);
let extensionManagementServiceMock = {
getInstalled: () => {
return Promise.resolve([]);
}
};
const logService = new NullLogService();
objectExplorerService = new ObjectExplorerService(connectionManagementService.object, undefined, capabilitiesService, logService);
objectExplorerService.registerProvider(mssqlProviderName, sqlOEProvider.object);
sqlOEProvider.setup(x => x.createNewSession(TypeMoq.It.is<azdata.ConnectionInfo>(x => x.options['serverName'] === connection.serverName))).returns(() => new Promise<any>((resolve) => {
resolve(response);
}));
sqlOEProvider.setup(x => x.createNewSession(TypeMoq.It.is<azdata.ConnectionInfo>(x => x.options['serverName'] === connectionToFail.serverName))).returns(() => new Promise<any>((resolve) => {
resolve(failedResponse);
}));
sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.isAny())).callback(() => {
objectExplorerService.onNodeExpanded(objectExplorerExpandInfo);
}).returns(() => Promise.resolve(true));
sqlOEProvider.setup(x => x.refreshNode(TypeMoq.It.isAny())).callback(() => {
objectExplorerService.onNodeExpanded(objectExplorerExpandInfoRefresh);
}).returns(() => Promise.resolve(true));
sqlOEProvider.setup(x => x.closeSession(TypeMoq.It.isAny())).returns(() => Promise.resolve(objectExplorerCloseSessionResponse));
objectExplorerService.onUpdateObjectExplorerNodes(args => {
if (args && args.errorMessage === undefined) {
numberOfSuccessfulSessions++;
}
});
serverTreeView = TypeMoq.Mock.ofInstance({
setExpandedState: (element, expandedState) => Promise.resolve() as Thenable<void>,
reveal: element => Promise.resolve() as Thenable<void>,
setSelected: (element, selected, clearOtherSelections) => undefined,
isExpanded: element => undefined,
onSelectionOrFocusChange: Event.None,
refreshElement: (element) => Promise.resolve() as Thenable<void>
} as ServerTreeView);
});
test('create new session should create session successfully', (done) => {
objectExplorerService.createNewSession(mssqlProviderName, connection).then(session => {
assert.equal(session !== null || session !== undefined, true);
assert.equal(session.sessionId, '1234');
objectExplorerService.onSessionCreated(1, objectExplorerSession);
let node = objectExplorerService.getObjectExplorerNode(connection);
assert.notEqual(node, undefined);
assert.equal(node.session.success, true);
done();
}, err => {
// Must call done here so test indicates it's finished if errors occur
done(err);
});
});
test('create new session should raise failed event for failed session', (done) => {
objectExplorerService.createNewSession(mssqlProviderName, connectionToFail).then(session => {
assert.equal(session !== null || session !== undefined, true);
assert.equal(session.sessionId, failedSessionId);
let currentNumberOfSuccessfulSessions = numberOfSuccessfulSessions;
objectExplorerService.onSessionCreated(1, objectExplorerFailedSession);
let node = objectExplorerService.getObjectExplorerNode(connection);
assert.equal(node, undefined);
assert.equal(currentNumberOfSuccessfulSessions, numberOfSuccessfulSessions);
done();
}, err => {
// Must call done here so test indicates it's finished if errors occur
done(err);
});
});
test('close session should close session successfully', (done) => {
objectExplorerService.closeSession(mssqlProviderName, objectExplorerSession).then(session => {
assert.equal(session !== null || session !== undefined, true);
assert.equal(session.success, true);
assert.equal(session.sessionId, '1234');
done();
}, err => {
// Must call done here so test indicates it's finished if errors occur
done(err);
});
});
test('expand node should expand node correctly', (done) => {
objectExplorerService.createNewSession(mssqlProviderName, connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.expandNode(mssqlProviderName, objectExplorerSession, 'testServerName/tables').then(expandInfo => {
assert.equal(expandInfo !== null || expandInfo !== undefined, true);
assert.equal(expandInfo.sessionId, '1234');
assert.equal(expandInfo.nodes.length, 2);
let children = expandInfo.nodes;
assert.equal(children[0].label, 'dbo.Table1');
assert.equal(children[1].label, 'dbo.Table2');
done();
}, err => {
// Must call done here so test indicates it's finished if errors occur
done(err);
});
});
});
test('refresh node should refresh node correctly', (done) => {
objectExplorerService.createNewSession(mssqlProviderName, connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.refreshNode(mssqlProviderName, objectExplorerSession, 'testServerName/tables').then(expandInfo => {
assert.equal(expandInfo !== null || expandInfo !== undefined, true);
assert.equal(expandInfo.sessionId, '1234');
assert.equal(expandInfo.nodes.length, 2);
let children = expandInfo.nodes;
assert.equal(children[0].label, 'dbo.Table1');
assert.equal(children[1].label, 'dbo.Table3');
done();
}, err => {
// Must call done here so test indicates it's finished if errors occur
done(err);
});
});
});
test('expand tree node should get correct children', (done) => {
let tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName/tables', '', '', null, null, undefined, undefined);
tablesNode.connection = connection;
objectExplorerService.createNewSession(mssqlProviderName, connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, tablesNode).then(children => {
assert.equal(children !== null || children !== undefined, true);
assert.equal(children[0].label, 'dbo.Table1');
assert.equal(children[0].parent, tablesNode);
assert.equal(children[0].nodePath, 'testServerName/tables/dbo.Table1');
assert.equal(children[1].label, 'dbo.Table2');
assert.equal(children[1].parent, tablesNode);
assert.equal(children[1].nodePath, 'testServerName/tables/dbo.Table2');
done();
}, err => {
// Must call done here so test indicates it's finished if errors occur
done(err);
});
});
});
test('refresh tree node should children correctly', (done) => {
let tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName/tables', '', '', null, null, undefined, undefined);
tablesNode.connection = connection;
objectExplorerService.createNewSession(mssqlProviderName, connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.refreshTreeNode(objectExplorerSession, tablesNode).then(children => {
assert.equal(children !== null || children !== undefined, true);
assert.equal(children[0].label, 'dbo.Table1');
assert.equal(children[0].parent, tablesNode);
assert.equal(children[0].nodePath, 'testServerName/tables/dbo.Table1');
assert.equal(children[1].label, 'dbo.Table3');
assert.equal(children[1].parent, tablesNode);
assert.equal(children[1].nodePath, 'testServerName/tables/dbo.Table3');
done();
}, err => {
// Must call done here so test indicates it's finished if errors occur
done(err);
});
});
});
test('update object explorer nodes should get active connection, create session, add to the active OE nodes successfully', (done) => {
objectExplorerService.createNewSession(mssqlProviderName, connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.updateObjectExplorerNodes(connection).then(() => {
let treeNode = objectExplorerService.getObjectExplorerNode(connection);
assert.equal(treeNode !== null || treeNode !== undefined, true);
assert.equal(treeNode.getSession(), objectExplorerSession);
assert.equal(treeNode.getConnectionProfile(), connection);
assert.equal(treeNode.label, 'Tables');
assert.equal(treeNode.nodePath, 'testServerName/tables');
done();
}, err => {
// Must call done here so test indicates it's finished if errors occur
done(err);
});
});
});
test('delete object explorerNode nodes should delete session, delete the root node to the active OE node', (done) => {
objectExplorerService.createNewSession(mssqlProviderName, connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.updateObjectExplorerNodes(connection).then(() => {
let treeNode = objectExplorerService.getObjectExplorerNode(connection);
assert.equal(treeNode !== null && treeNode !== undefined, true);
objectExplorerService.deleteObjectExplorerNode(connection).then(() => {
treeNode = objectExplorerService.getObjectExplorerNode(connection);
assert.equal(treeNode === null || treeNode === undefined, true);
done();
});
}, err => {
// Must call done here so test indicates it's finished if errors occur
done(err);
});
});
});
test('children tree nodes should return correct object explorer session, connection profile and database name', () => {
let databaseMetaData = {
metadataType: 0,
metadataTypeName: 'Database',
urn: '//server/db1/',
name: 'Db1',
schema: null
};
let databaseNode = new TreeNode(NodeType.Database, 'Db1', false, 'testServerName\\Db1', '', '', null, databaseMetaData, undefined, undefined);
databaseNode.connection = connection;
databaseNode.session = objectExplorerSession;
let tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\\Db1\\tables', '', '', databaseNode, null, undefined, undefined);
databaseNode.children = [tablesNode];
let table1Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\\Db1\\tables\\dbo.Table1', '', '', tablesNode, null, undefined, undefined);
let table2Node = new TreeNode(NodeType.Table, 'dbo.Table2', false, 'testServerName\\Db1\\tables\\dbo.Table2', '', '', tablesNode, null, undefined, undefined);
tablesNode.children = [table1Node, table2Node];
assert.equal(table1Node.getSession(), objectExplorerSession);
assert.equal(table1Node.getConnectionProfile(), connection);
assert.equal(table1Node.getDatabaseName(), 'Db1');
});
test('getSelectedProfileAndDatabase returns the profile if it is selected', () => {
let serverTreeView = TypeMoq.Mock.ofInstance({ getSelection: () => undefined, onSelectionOrFocusChange: Event.None } as ServerTreeView);
serverTreeView.setup(x => x.getSelection()).returns(() => [connection]);
objectExplorerService.registerServerTreeView(serverTreeView.object);
let selectedProfileAndDatabase = objectExplorerService.getSelectedProfileAndDatabase();
assert.equal(selectedProfileAndDatabase.profile, connection);
assert.equal(selectedProfileAndDatabase.databaseName, undefined);
});
test('getSelectedProfileAndDatabase returns the profile but no database if children of a server are selected', () => {
let serverTreeView = TypeMoq.Mock.ofInstance({ getSelection: () => undefined, onSelectionOrFocusChange: Event.None } as ServerTreeView);
let databaseNode = new TreeNode(NodeType.Folder, 'Folder1', false, 'testServerName\\Folder1', '', '', undefined, undefined, undefined, undefined);
databaseNode.connection = connection;
serverTreeView.setup(x => x.getSelection()).returns(() => [databaseNode]);
objectExplorerService.registerServerTreeView(serverTreeView.object);
let selectedProfileAndDatabase = objectExplorerService.getSelectedProfileAndDatabase();
assert.equal(selectedProfileAndDatabase.profile, connection);
assert.equal(selectedProfileAndDatabase.databaseName, undefined);
});
test('getSelectedProfileAndDatabase returns the profile and database if children of a database node are selected', () => {
let serverTreeView = TypeMoq.Mock.ofInstance({ getSelection: () => undefined, onSelectionOrFocusChange: Event.None } as ServerTreeView);
let databaseMetadata = {
metadataType: 0,
metadataTypeName: 'Database',
urn: '//server/db1/',
name: 'Db1',
schema: undefined
};
let databaseName = 'Db1';
let databaseNode = new TreeNode(NodeType.Database, databaseName, false, 'testServerName\\Db1', '', '', undefined, databaseMetadata, undefined, undefined);
let tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\\Db1\\tables', '', '', databaseNode, undefined, undefined, undefined);
databaseNode.connection = connection;
databaseNode.children = [tablesNode];
serverTreeView.setup(x => x.getSelection()).returns(() => [tablesNode]);
objectExplorerService.registerServerTreeView(serverTreeView.object);
let selectedProfileAndDatabase = objectExplorerService.getSelectedProfileAndDatabase();
assert.equal(selectedProfileAndDatabase.profile, connection);
assert.equal(selectedProfileAndDatabase.databaseName, databaseName);
});
test('getSelectedProfileAndDatabase returns undefined when there is no selection', () => {
let serverTreeView = TypeMoq.Mock.ofInstance({ getSelection: () => undefined, onSelectionOrFocusChange: Event.None } as ServerTreeView);
serverTreeView.setup(x => x.getSelection()).returns(() => []);
objectExplorerService.registerServerTreeView(serverTreeView.object);
let selectedProfileAndDatabase = objectExplorerService.getSelectedProfileAndDatabase();
assert.equal(selectedProfileAndDatabase, undefined);
});
test('isExpanded returns true when the node and its parents are expanded', (done) => {
let table1NodePath = objectExplorerExpandInfo.nodes[0].nodePath;
let tableExpandInfo = {
sessionId: sessionId,
nodes: [],
errorMessage: '',
nodePath: table1NodePath,
providerId: mssqlProviderName
};
serverTreeView.setup(x => x.isExpanded(TypeMoq.It.isAny())).returns(treeNode => {
return treeNode === connection || treeNode.nodePath === table1NodePath;
});
objectExplorerService.registerServerTreeView(serverTreeView.object);
objectExplorerService.createNewSession(mssqlProviderName, connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, objectExplorerService.getObjectExplorerNode(connection)).then(childNodes => {
sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.isAny())).callback(() => {
objectExplorerService.onNodeExpanded(tableExpandInfo);
}).returns(() => Promise.resolve(true));
let tableNode = childNodes.find(node => node.nodePath === table1NodePath);
objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, tableNode).then(() => {
// If I check whether the table is expanded, the answer should be yes
tableNode.isExpanded().then(isExpanded => {
try {
assert.equal(isExpanded, true);
done();
} catch (err) {
done(err);
}
}, err => done(err));
}, err => done(err));
}, err => done(err));
}, err => done(err));
});
test('isExpanded returns false when the node is not expanded', (done) => {
let table1NodePath = objectExplorerExpandInfo.nodes[0].nodePath;
serverTreeView.setup(x => x.isExpanded(TypeMoq.It.isAny())).returns(treeNode => {
return treeNode === connection;
});
objectExplorerService.registerServerTreeView(serverTreeView.object);
objectExplorerService.createNewSession(mssqlProviderName, connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, objectExplorerService.getObjectExplorerNode(connection)).then(childNodes => {
// If I check whether the table is expanded, the answer should be no because only its parent node is expanded
let tableNode = childNodes.find(node => node.nodePath === table1NodePath);
tableNode.isExpanded().then(isExpanded => {
try {
assert.equal(isExpanded, false);
done();
} catch (err) {
done(err);
}
}, err => done(err));
}, err => done(err));
}, err => done(err));
});
test('isExpanded returns false when the parent of the requested node is not expanded', (done) => {
let table1NodePath = objectExplorerExpandInfo.nodes[0].nodePath;
let tableExpandInfo = {
sessionId: sessionId,
nodes: [],
errorMessage: '',
nodePath: table1NodePath,
providerId: mssqlProviderName
};
serverTreeView.setup(x => x.isExpanded(TypeMoq.It.isAny())).returns(treeNode => {
return treeNode.nodePath === table1NodePath;
});
objectExplorerService.registerServerTreeView(serverTreeView.object);
objectExplorerService.createNewSession(mssqlProviderName, connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, objectExplorerService.getObjectExplorerNode(connection)).then(childNodes => {
sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.isAny())).callback(() => {
objectExplorerService.onNodeExpanded(tableExpandInfo);
}).returns(() => Promise.resolve(true));
objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, childNodes.find(node => node.nodePath === table1NodePath)).then(() => {
// If I check whether the table is expanded, the answer should be yes
let tableNode = childNodes.find(node => node.nodePath === table1NodePath);
tableNode.isExpanded().then(isExpanded => {
try {
assert.equal(isExpanded, false);
done();
} catch (err) {
done(err);
}
}, err => done(err));
}, err => done(err));
}, err => done(err));
}, err => done(err));
});
test('setting a node to expanded calls expand on the requested tree node', (done) => {
let table1NodePath = objectExplorerExpandInfo.nodes[0].nodePath;
let tableExpandInfo = {
sessionId: sessionId,
nodes: [],
errorMessage: '',
nodePath: table1NodePath,
providerId: mssqlProviderName
};
// Set up the OE provider so that the second expand call expands the table
sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.is(nodeInfo => nodeInfo.nodePath === table1NodePath))).callback(() => {
objectExplorerService.onNodeExpanded(tableExpandInfo);
}).returns(() => Promise.resolve(true));
serverTreeView.setup(x => x.setExpandedState(TypeMoq.It.isAny(), TypeMoq.It.is(state => state === TreeItemCollapsibleState.Expanded))).returns(treeNode => {
if (treeNode instanceof ConnectionProfile) {
treeNode = objectExplorerService.getObjectExplorerNode(treeNode);
}
return objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, treeNode).then(() => undefined);
});
serverTreeView.setup(x => x.reveal(TypeMoq.It.isAny())).returns(() => Promise.resolve());
objectExplorerService.registerServerTreeView(serverTreeView.object);
objectExplorerService.createNewSession(mssqlProviderName, connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
// If I expand the node, then it should get revealed and expanded
objectExplorerService.getTreeNode(connection.id, table1NodePath).then(tableNode => {
tableNode.setExpandedState(TreeItemCollapsibleState.Expanded).then(() => {
try {
serverTreeView.verify(x => x.setExpandedState(TypeMoq.It.isValue(tableNode), TypeMoq.It.is(state => state === TreeItemCollapsibleState.Expanded)), TypeMoq.Times.once());
serverTreeView.verify(x => x.reveal(TypeMoq.It.isValue(tableNode)), TypeMoq.Times.once());
done();
} catch (err) {
done(err);
}
}, err => done(err));
}, err => done(err));
});
});
test('setting a node to collapsed calls collapse on the requested tree node', (done) => {
serverTreeView.setup(x => x.isExpanded(TypeMoq.It.isAny())).returns(treeNode => {
return treeNode === connection;
});
serverTreeView.setup(x => x.setExpandedState(TypeMoq.It.is(treeNode => treeNode === connection), TypeMoq.It.is(state => state === TreeItemCollapsibleState.Collapsed))).returns(() => Promise.resolve());
objectExplorerService.registerServerTreeView(serverTreeView.object);
objectExplorerService.createNewSession(mssqlProviderName, connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, objectExplorerService.getObjectExplorerNode(connection)).then(childNodes => {
// If I collapse the connection node, then the tree's collapse method should get called
objectExplorerService.getTreeNode(connection.id, undefined).then(treeNode => treeNode.setExpandedState(TreeItemCollapsibleState.Collapsed).then(() => {
try {
serverTreeView.verify(x => x.setExpandedState(TypeMoq.It.is(treeNode => treeNode === connection), TypeMoq.It.is(state => state === TreeItemCollapsibleState.Collapsed)), TypeMoq.Times.once());
done();
} catch (err) {
done(err);
}
}, err => done(err)));
}, err => done(err));
}, err => done(err));
});
test('setNodeSelected sets the tree selection to the requested tree node', (done) => {
let table1NodePath = objectExplorerExpandInfo.nodes[0].nodePath;
serverTreeView.setup(x => x.setSelected(TypeMoq.It.is((treeNode: TreeNode) => treeNode.nodePath === table1NodePath), TypeMoq.It.isAny(), undefined)).returns(() => Promise.resolve());
serverTreeView.setup(x => x.reveal(TypeMoq.It.isAny())).returns(() => Promise.resolve());
objectExplorerService.registerServerTreeView(serverTreeView.object);
objectExplorerService.createNewSession(mssqlProviderName, connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
// If I select the table node, then it should be selected and revealed
objectExplorerService.getTreeNode(connection.id, table1NodePath).then(tableNode => {
tableNode.setSelected(true).then(() => {
try {
serverTreeView.verify(x => x.setSelected(TypeMoq.It.isValue(tableNode), TypeMoq.It.isValue(true), undefined), TypeMoq.Times.once());
serverTreeView.verify(x => x.reveal(TypeMoq.It.isValue(tableNode)), TypeMoq.Times.once());
done();
} catch (err) {
done(err);
}
}, err => done(err));
}, err => done(err));
}, err => done(err));
});
test('findTreeNode returns the tree node for the relevant node', (done) => {
let table1NodePath = objectExplorerExpandInfo.nodes[0].nodePath;
objectExplorerService.createNewSession(mssqlProviderName, connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.getTreeNode(connection.id, table1NodePath).then(treeNode => {
try {
assert.equal(treeNode.nodePath, objectExplorerExpandInfo.nodes[0].nodePath);
assert.equal(treeNode.nodeTypeId, objectExplorerExpandInfo.nodes[0].nodeType);
assert.equal(treeNode.label, objectExplorerExpandInfo.nodes[0].label);
done();
} catch (err) {
done(err);
}
}, err => done(err));
});
});
test('findTreeNode returns undefined if the requested node does not exist', (done) => {
let invalidNodePath = objectExplorerSession.rootNode.nodePath + '/invalidNode';
objectExplorerService.createNewSession(mssqlProviderName, connection).then(result => {
objectExplorerService.onSessionCreated(1, objectExplorerSession);
objectExplorerService.getTreeNode(connection.id, invalidNodePath).then(nodeInfo => {
try {
assert.equal(nodeInfo, undefined);
done();
} catch (err) {
done(err);
}
}, err => done(err));
});
});
test('refreshInView refreshes the node, expands it, and returns the refreshed node', async () => {
// Set up the session and tree view
await objectExplorerService.createNewSession(mssqlProviderName, connection);
objectExplorerService.onSessionCreated(1, objectExplorerSession);
serverTreeView.setup(x => x.refreshElement(TypeMoq.It.isAny())).returns(() => Promise.resolve());
objectExplorerService.registerServerTreeView(serverTreeView.object);
// Refresh the node
let nodePath = objectExplorerSession.rootNode.nodePath;
let refreshedNode = await objectExplorerService.refreshNodeInView(connection.id, nodePath);
// Verify that it was refreshed, expanded, and the refreshed detailed were returned
sqlOEProvider.verify(x => x.refreshNode(TypeMoq.It.is(refreshNode => refreshNode.nodePath === nodePath)), TypeMoq.Times.once());
refreshedNode.children.forEach((childNode, index) => {
assert.equal(childNode.nodePath, objectExplorerExpandInfoRefresh.nodes[index].nodePath);
});
});
test('Session can be closed even if expand requests are pending', async () => {
// Set up the session
await objectExplorerService.createNewSession(mssqlProviderName, connection);
objectExplorerService.onSessionCreated(1, objectExplorerSession);
// Set up the provider to not respond to the second expand request, simulating a request that takes a long time to complete
const nodePath = objectExplorerSession.rootNode.nodePath;
sqlOEProvider.setup(x => x.expandNode(TypeMoq.It.is(x => x.nodePath === nodePath))).callback(() => { }).returns(() => Promise.resolve(true));
// If I queue a second expand request (the first completes normally because of the original mock) and then close the session
await objectExplorerService.expandNode(mssqlProviderName, objectExplorerSession, objectExplorerSession.rootNode.nodePath);
let expandPromise = objectExplorerService.expandNode(mssqlProviderName, objectExplorerSession, objectExplorerSession.rootNode.nodePath);
let closeSessionResult = await objectExplorerService.closeSession(mssqlProviderName, objectExplorerSession);
// Then the expand request has completed and the session is closed
let expandResult = await expandPromise;
assert.equal(expandResult.nodes.length, 0);
assert.equal(closeSessionResult.success, true);
});
test('resolveTreeNodeChildren refreshes a node if it currently has an error', async () => {
await objectExplorerService.createNewSession(mssqlProviderName, connection);
objectExplorerService.onSessionCreated(1, objectExplorerSession);
// If I call resolveTreeNodeChildren once, set an error on the node, and then call it again
let tablesNodePath = 'testServerName/tables';
let tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, tablesNodePath, '', '', null, null, undefined, undefined);
tablesNode.connection = connection;
await objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, tablesNode);
sqlOEProvider.verify(x => x.refreshNode(TypeMoq.It.is(x => x.nodePath === tablesNodePath)), TypeMoq.Times.never());
tablesNode.errorStateMessage = 'test error message';
await objectExplorerService.resolveTreeNodeChildren(objectExplorerSession, tablesNode);
// Then refresh gets called on the node
sqlOEProvider.verify(x => x.refreshNode(TypeMoq.It.is(x => x.nodePath === tablesNodePath)), TypeMoq.Times.once());
});
});

View File

@@ -0,0 +1,45 @@
/*---------------------------------------------------------------------------------------------
* 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';
// Test stubs for commonly used objects
export class TestObjectExplorerProvider implements azdata.ObjectExplorerProvider {
public readonly providerId = mssqlProviderName;
public createNewSession(connInfo: azdata.ConnectionInfo): Thenable<azdata.ObjectExplorerCloseSessionResponse> {
return Promise.resolve(undefined);
}
public expandNode(nodeInfo: azdata.ExpandNodeInfo): Thenable<boolean> {
return Promise.resolve(undefined);
}
public refreshNode(nodeInfo: azdata.ExpandNodeInfo): Thenable<boolean> {
return Promise.resolve(undefined);
}
public closeSession(closeSessionInfo: azdata.ObjectExplorerCloseSessionInfo): Thenable<azdata.ObjectExplorerCloseSessionResponse> {
return Promise.resolve(undefined);
}
public registerOnSessionCreated(handler: (response: azdata.ObjectExplorerSession) => any): void {
}
public registerOnSessionDisconnected(handler: (response: azdata.ObjectExplorerSession) => any): void {
}
public registerOnExpandCompleted(handler: (response: azdata.ObjectExplorerExpandInfo) => any): void {
}
public findNodes(findNodesInfo: azdata.FindNodesInfo): Thenable<azdata.ObjectExplorerFindNodesResponse> {
return undefined;
}
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IConnectionManagementService, IConnectionCompletionOptions, ConnectionType, RunQueryOnConnectionMode } from 'sql/platform/connection/common/connectionManagement';
import { ProfilerSessionID, IProfilerSession, IProfilerService, IProfilerViewTemplate, IProfilerSessionTemplate, PROFILER_SETTINGS, IProfilerSettings, EngineType, ProfilerFilter, PROFILER_FILTER_SETTINGS } from './interfaces';
import { ProfilerSessionID, IProfilerSession, IProfilerService, IProfilerViewTemplate, IProfilerSessionTemplate, PROFILER_SETTINGS, IProfilerSettings, EngineType, ProfilerFilter, PROFILER_FILTER_SETTINGS } from '../common/interfaces';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { ProfilerInput } from 'sql/workbench/parts/profiler/browser/profilerInput';
import { ProfilerColumnEditorDialog } from 'sql/workbench/parts/profiler/browser/profilerColumnEditorDialog';

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 assert from 'assert';
import { FilterData } from 'sql/workbench/services/profiler/common/profilerFilter';
import { ProfilerFilterClauseOperator, ProfilerFilter } from 'sql/workbench/services/profiler/common/interfaces';
const property1 = 'property1';
const property2 = 'property2';
suite('Profiler filter data tests', () => {
test('number type filter data test', () => {
let filter: ProfilerFilter = { clauses: [] };
let entry1: TestData = { property1: '-1', property2: '0' };
let entry2: TestData = { property1: '0', property2: '10' };
let entry3: TestData = { property1: '10.0', property2: '-1' };
let data: TestData[] = [entry1, entry2, entry3];
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.Equals, value: '10' }];
filterAndVerify(filter, data, [entry3], 'Equals operator');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.NotEquals, value: '-1' }];
filterAndVerify(filter, data, [entry2, entry3], 'NotEquals operator');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.GreaterThan, value: '2' }];
filterAndVerify(filter, data, [entry3], 'GreaterThan operator');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.GreaterThanOrEquals, value: '0' }];
filterAndVerify(filter, data, [entry2, entry3], 'GreaterThanOrEquals operator');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.LessThan, value: '0' }];
filterAndVerify(filter, data, [entry1], 'LessThan operator');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.LessThanOrEquals, value: '0' }];
filterAndVerify(filter, data, [entry1, entry2], 'LessThanOrEquals operator');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.LessThanOrEquals, value: '-2' }];
filterAndVerify(filter, data, [], 'Empty result set');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.LessThanOrEquals, value: '10' }];
filterAndVerify(filter, data, data, 'All matches');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.LessThanOrEquals, value: '0' },
{ field: property2, operator: ProfilerFilterClauseOperator.LessThan, value: '10' }];
filterAndVerify(filter, data, [entry1], 'Multiple clauses');
});
test('date type filter data test', () => {
let filter: ProfilerFilter = { clauses: [] };
let entry1: TestData = { property1: '2019-01-02T19:00:00.000Z', property2: '' };
let entry2: TestData = { property1: '2019-01-03T10:00:00.000Z', property2: '' };
let entry3: TestData = { property1: '2019-01-04T10:00:00.000Z', property2: '' };
let data: TestData[] = [entry1, entry2, entry3];
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.Equals, value: '2019-01-02T19:00:00Z' }];
filterAndVerify(filter, data, [entry1], 'Equals operator');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.NotEquals, value: '2019-01-03T10:00:00Z' }];
filterAndVerify(filter, data, [entry1, entry3], 'NotEquals operator');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.GreaterThan, value: '2019-01-01T00:00:00Z' }];
filterAndVerify(filter, data, [entry1, entry2, entry3], 'GreaterThan operator');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.GreaterThanOrEquals, value: '2019-01-03T10:00:00.000Z' }];
filterAndVerify(filter, data, [entry2, entry3], 'GreaterThanOrEquals operator');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.LessThan, value: '2019-01-03T10:00:00.000Z' }];
filterAndVerify(filter, data, [entry1], 'LessThan operator');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.LessThanOrEquals, value: '2019-01-03T10:00:00Z' }];
filterAndVerify(filter, data, [entry1, entry2], 'LessThanOrEquals operator');
});
test('string type filter data test', () => {
let filter: ProfilerFilter = { clauses: [] };
let entry1: TestData = { property1: '', property2: '' };
let entry2: TestData = { property1: 'test string', property2: '' };
let entry3: TestData = { property1: 'new string', property2: '' };
let data: TestData[] = [entry1, entry2, entry3];
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.IsNull, value: '' }];
filterAndVerify(filter, data, [entry1], 'IsNull operator');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.IsNotNull, value: '' }];
filterAndVerify(filter, data, [entry2, entry3], 'IsNotNull operator');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.Contains, value: 'sTRing' }];
filterAndVerify(filter, data, [entry2, entry3], 'Contains operator');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.NotContains, value: 'string' }];
filterAndVerify(filter, data, [entry1], 'NotContains operator');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.StartsWith, value: 'tEst' }];
filterAndVerify(filter, data, [entry2], 'StartsWith operator');
filter.clauses = [{ field: property1, operator: ProfilerFilterClauseOperator.NotStartsWith, value: 'Test' }];
filterAndVerify(filter, data, [entry1, entry3], 'NotStartsWith operator');
});
});
function filterAndVerify(filter: ProfilerFilter, data: TestData[], expectedResult: TestData[], stepName: string) {
let actualResult = FilterData(filter, data);
assert.equal(actualResult.length, expectedResult.length, `length check for ${stepName}`);
for (let i = 0; i < actualResult.length; i++) {
let actual = actualResult[i];
let expected = expectedResult[i];
assert(actual.property1 === expected.property1 && actual.property2 === expected.property2, `array content check for ${stepName}`);
}
}
interface TestData {
property1: string;
property2: string;
}

View File

@@ -8,8 +8,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IResourceProviderService, IHandleFirewallRuleResult } from 'sql/workbench/services/resourceProvider/common/resourceProviderService';
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 { FirewallRuleDialogController } from 'sql/platform/accounts/browser/firewallRuleDialogController';
import * as azdata from 'azdata';

View File

@@ -0,0 +1,32 @@
/*---------------------------------------------------------------------------------------------
* 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 { IHandleFirewallRuleResult, IResourceProviderService } from 'sql/workbench/services/resourceProvider/common/resourceProviderService';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
export class TestResourceProvider implements IResourceProviderService {
_serviceBrand: any;
registerProvider(providerId: string, provider: azdata.ResourceProvider) {
}
unregisterProvider(ProviderId: string) {
}
createFirewallRule(selectedAccount: azdata.Account, firewallruleInfo: azdata.FirewallRuleInfo, resourceProviderId: string): Promise<azdata.CreateFirewallRuleResponse> {
return undefined;
}
handleFirewallRule(errorCode: number, errorMessage: string, connectionTypeId: string): Promise<IHandleFirewallRuleResult> {
return undefined;
}
showFirewallRuleDialog(connection: IConnectionProfile, ipAddress: string, resourceProviderId: string): Promise<boolean> {
return undefined;
}
}