mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-02 09:35:40 -05:00
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:
@@ -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>;
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
@@ -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[][];
|
||||
@@ -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 = /\${(.*?)\}/;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = /\${(.*?)\}/;
|
||||
@@ -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'],
|
||||
@@ -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', () => {
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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 };
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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());
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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';
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user