move code from parts to contrib (#8319)

This commit is contained in:
Anthony Dresser
2019-11-14 12:23:11 -08:00
committed by GitHub
parent 6438967202
commit 7a2c30e159
619 changed files with 848 additions and 848 deletions

View File

@@ -0,0 +1,11 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { CommandLineWorkbenchContribution } from 'sql/workbench/contrib/commandLine/electron-browser/commandLine';
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(CommandLineWorkbenchContribution, LifecyclePhase.Restored);

View File

@@ -0,0 +1,261 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as querystring from 'querystring';
import * as azdata from 'azdata';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { equalsIgnoreCase } from 'vs/base/common/strings';
import { IConnectionManagementService, IConnectionCompletionOptions, ConnectionType, RunQueryOnConnectionMode } from 'sql/platform/connection/common/connectionManagement';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import * as Constants from 'sql/platform/connection/common/constants';
import * as platform from 'vs/platform/registry/common/platform';
import { IConnectionProviderRegistry, Extensions as ConnectionProviderExtensions } from 'sql/workbench/contrib/connection/common/connectionProviderExtension';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ipcRenderer as ipc } from 'electron';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { localize } from 'vs/nls';
import { QueryInput } from 'sql/workbench/contrib/query/common/queryInput';
import { URI } from 'vs/base/common/uri';
import { ILogService } from 'vs/platform/log/common/log';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { openNewQuery } from 'sql/workbench/contrib/query/browser/queryActions';
import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
import { getErrorMessage } from 'vs/base/common/errors';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { find } from 'vs/base/common/arrays';
const connectAuthority = 'connect';
interface SqlArgs {
_?: string[];
aad?: boolean;
database?: string;
integrated?: boolean;
server?: string;
user?: string;
command?: string;
provider?: string;
}
export class CommandLineWorkbenchContribution implements IWorkbenchContribution, IURLHandler {
constructor(
@ICapabilitiesService private readonly _capabilitiesService: ICapabilitiesService,
@IConnectionManagementService private readonly _connectionManagementService: IConnectionManagementService,
@IEnvironmentService environmentService: IEnvironmentService,
@IEditorService private readonly _editorService: IEditorService,
@ICommandService private readonly _commandService: ICommandService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@INotificationService private readonly _notificationService: INotificationService,
@ILogService private readonly logService: ILogService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IURLService urlService: IURLService,
@IDialogService private readonly dialogService: IDialogService
) {
if (ipc) {
ipc.on('ads:processCommandLine', (event: any, args: ParsedArgs) => this.onLaunched(args));
}
// we only get the ipc from main during window reuse
if (environmentService) {
this.onLaunched(environmentService.args);
}
if (urlService) {
urlService.registerHandler(this);
}
}
private onLaunched(args: ParsedArgs) {
const registry = platform.Registry.as<IConnectionProviderRegistry>(ConnectionProviderExtensions.ConnectionProviderContributions);
let sqlProvider = registry.getProperties(Constants.mssqlProviderName);
// We can't connect to object explorer until the MSSQL connection provider is registered
if (sqlProvider) {
this.processCommandLine(args).catch(reason => { this.logService.warn('processCommandLine failed: ' + reason); });
} else {
registry.onNewProvider(e => {
if (e.id === Constants.mssqlProviderName) {
this.processCommandLine(args).catch(reason => { this.logService.warn('processCommandLine failed: ' + reason); });
}
});
}
}
// We base our logic on the combination of (server, command) values.
// (serverName, commandName) => Connect object explorer and execute the command, passing the connection profile to the command. Do not load query editor.
// (null, commandName) => Launch the command with a null connection. If the command implementation needs a connection, it will need to create it.
// (serverName, null) => Connect object explorer and open a new query editor if no file names are passed. If file names are passed, connect their editors to the server.
// (null, null) => Prompt for a connection unless there are registered servers
public async processCommandLine(args: SqlArgs): Promise<void> {
let profile: IConnectionProfile = undefined;
let commandName = undefined;
if (args) {
if (this._commandService) {
commandName = args.command;
}
if (args.server) {
profile = this.readProfileFromArgs(args);
}
}
let showConnectDialogOnStartup: boolean = this._configurationService.getValue('workbench.showConnectDialogOnStartup');
if (showConnectDialogOnStartup && !commandName && !profile && !this._connectionManagementService.hasRegisteredServers()) {
// prompt the user for a new connection on startup if no profiles are registered
await this._connectionManagementService.showConnectionDialog();
return;
}
let connectedContext: azdata.ConnectedContext = undefined;
if (profile) {
if (this._notificationService) {
this._notificationService.status(localize('connectingLabel', "Connecting: {0}", profile.serverName), { hideAfter: 2500 });
}
try {
await this._connectionManagementService.connectIfNotConnected(profile, 'connection', true);
// Before sending to extensions, we should a) serialize to IConnectionProfile or things will fail,
// and b) use the latest version of the profile from the service so most fields are filled in.
let updatedProfile = this._connectionManagementService.getConnectionProfileById(profile.id);
connectedContext = { connectionProfile: new ConnectionProfile(this._capabilitiesService, updatedProfile).toIConnectionProfile() };
} catch (err) {
this.logService.warn('Failed to connect due to error' + getErrorMessage(err));
}
}
if (commandName) {
if (this._notificationService) {
this._notificationService.status(localize('runningCommandLabel', "Running command: {0}", commandName), { hideAfter: 2500 });
}
await this._commandService.executeCommand(commandName, connectedContext);
} else if (profile) {
// If we were given a file and it was opened with the sql editor,
// we want to connect the given profile to to it.
// If more than one file was passed, only show the connection dialog error on one of them.
if (args._ && args._.length > 0) {
await Promise.all(args._.map((f, i) => this.processFile(URI.file(f).toString(), profile, i === 0)));
}
else {
// Default to showing new query
if (this._notificationService) {
this._notificationService.status(localize('openingNewQueryLabel', "Opening new query: {0}", profile.serverName), { hideAfter: 2500 });
}
try {
await this.instantiationService.invokeFunction(openNewQuery, profile);
} catch (error) {
this.logService.warn('unable to open query editor ' + error);
// Note: we are intentionally swallowing this error.
// In part this is to accommodate unit testing where we don't want to set up the query stack
}
}
}
}
public async handleURL(uri: URI): Promise<boolean> {
// Catch file URLs
let authority = uri.authority.toLowerCase();
if (authority === connectAuthority) {
try {
let args = this.parseProtocolArgs(uri);
if (!args.server) {
this._notificationService.warn(localize('warnServerRequired', "Cannot connect as no server information was provided"));
return true;
}
let isOpenOk = await this.confirmConnect(args);
if (isOpenOk) {
await this.processCommandLine(args);
}
} catch (err) {
this._notificationService.error(localize('errConnectUrl', "Could not open URL due to error {0}", getErrorMessage(err)));
}
// Handled either way
return true;
}
return false;
}
private async confirmConnect(args: SqlArgs): Promise<boolean> {
let detail = args && args.server ? localize('connectServerDetail', "This will connect to server {0}", args.server) : '';
const result = await this.dialogService.confirm({
message: localize('confirmConnect', "Are you sure you want to connect?"),
detail: detail,
primaryButton: localize('open', "&&Open"),
type: 'question'
});
if (result.confirmed) {
return true;
}
return false;
}
private parseProtocolArgs(uri: URI): SqlArgs {
let args: SqlArgs = querystring.parse(uri.query);
// Clear out command, not supporting arbitrary command via this path
args.command = undefined;
return args;
}
// If an open and connectable query editor exists for the given URI, attach it to the connection profile
private async processFile(uriString: string, profile: IConnectionProfile, warnOnConnectFailure: boolean): Promise<void> {
let activeEditor = this._editorService.editors.filter(v => v.getResource().toString() === uriString).pop();
if (activeEditor) {
let queryInput = activeEditor as QueryInput;
if (queryInput && queryInput.state.connected) {
let options: IConnectionCompletionOptions = {
params: { connectionType: ConnectionType.editor, runQueryOnCompletion: RunQueryOnConnectionMode.none, input: queryInput },
saveTheConnection: false,
showDashboard: false,
showConnectionDialogOnError: warnOnConnectFailure,
showFirewallRuleOnError: warnOnConnectFailure
};
if (this._notificationService) {
this._notificationService.status(localize('connectingQueryLabel', "Connecting query file"), { hideAfter: 2500 });
}
await this._connectionManagementService.connect(profile, uriString, options);
}
}
}
private readProfileFromArgs(args: SqlArgs) {
let profile = new ConnectionProfile(this._capabilitiesService, null);
// We want connection store to use any matching password it finds
profile.savePassword = true;
profile.providerName = args.provider ? args.provider : Constants.mssqlProviderName;
profile.serverName = args.server;
profile.databaseName = args.database ? args.database : '';
profile.userName = args.user ? args.user : '';
profile.authenticationType = args.integrated ? Constants.integrated : args.aad ? Constants.azureMFA : (profile.userName.length > 0) ? Constants.sqlLogin : Constants.integrated;
profile.connectionName = '';
profile.setOptionValue('applicationName', Constants.applicationName);
profile.setOptionValue('databaseDisplayName', profile.databaseName);
profile.setOptionValue('groupId', profile.groupId);
return this._connectionManagementService ? this.tryMatchSavedProfile(profile) : profile;
}
private tryMatchSavedProfile(profile: ConnectionProfile) {
let match: ConnectionProfile = undefined;
// If we can find a saved mssql provider connection that matches the args, use it
let groups = this._connectionManagementService.getConnectionGroups([Constants.mssqlProviderName]);
if (groups && groups.length > 0) {
let rootGroup = groups[0];
let connections = ConnectionProfileGroup.getConnectionsInGroup(rootGroup);
match = find(connections, (c) => this.matchProfile(profile, c));
}
return match ? match : profile;
}
// determines if the 2 profiles are a functional match
// profile1 is the profile generated from command line parameters
private matchProfile(profile1: ConnectionProfile, profile2: ConnectionProfile): boolean {
return equalsIgnoreCase(profile1.serverName, profile2.serverName)
&& equalsIgnoreCase(profile1.providerName, profile2.providerName)
// case sensitive servers can have 2 databases whose name differs only in case
&& profile1.databaseName === profile2.databaseName
&& equalsIgnoreCase(profile1.userName, profile2.userName)
&& profile1.authenticationType === profile2.authenticationType;
}
}

View File

@@ -0,0 +1,558 @@
/*---------------------------------------------------------------------------------------------
* 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 { CommandLineWorkbenchContribution } from 'sql/workbench/contrib/commandLine/electron-browser/commandLine';
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 'sql/base/test/common/async';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestEditorService, TestDialogService } from 'vs/workbench/test/workbenchTestServices';
import { QueryInput, QueryEditorState } from 'sql/workbench/contrib/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';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { isUndefinedOrNull } from 'vs/base/common/types';
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'?: boolean;
'disable-extension'?: string[]; // undefined or array of 1 or more
'disable-extensions'?: boolean;
'disable-restore-windows'?: boolean;
'disable-telemetry'?: boolean;
'disable-updates'?: boolean;
'driver'?: string;
'enable-proposed-api'?: string[];
'export-default-configuration'?: string;
'extensions-dir'?: string;
extensionDevelopmentPath?: string[];
extensionTestsPath?: string;
'file-chmod'?: boolean;
'file-write'?: boolean;
'folder-uri'?: string[];
goto?: boolean;
help?: boolean;
'install-extension'?: 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'?: boolean;
'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[];
'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 getCommandLineContribution(
connectionManagementService: IConnectionManagementService,
configurationService: IConfigurationService,
capabilitiesService?: ICapabilitiesService,
commandService?: ICommandService,
editorService?: IEditorService,
logService?: ILogService,
dialogService?: IDialogService,
notificationService?: INotificationService
): CommandLineWorkbenchContribution {
return new CommandLineWorkbenchContribution(
capabilitiesService,
connectionManagementService,
undefined,
editorService,
commandService,
configurationService,
notificationService,
logService,
undefined,
undefined,
dialogService
);
}
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 contribution = getCommandLineContribution(connectionManagementService.object, configurationService.object);
contribution.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 contribution = getCommandLineContribution(connectionManagementService.object, configurationService.object);
await contribution.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 contribution = getCommandLineContribution(connectionManagementService.object, configurationService.object);
try {
await contribution.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 contribution = getCommandLineContribution(connectionManagementService.object, configurationService.object, capabilitiesService, undefined, undefined, logService);
await contribution.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 contribution = getCommandLineContribution(connectionManagementService.object, configurationService.object, capabilitiesService, commandService.object);
await contribution.processCommandLine(args);
connectionManagementService.verifyAll();
commandService.verifyAll();
assert(isUndefinedOrNull(capturedArgs));
});
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 contribution = getCommandLineContribution(connectionManagementService.object, configurationService.object, capabilitiesService, commandService.object);
await contribution.processCommandLine(args);
connectionManagementService.verifyAll();
commandService.verifyAll();
assert(!isUndefinedOrNull(actualProfile));
assert.equal(actualProfile.connectionProfile.serverName, 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 contribution = getCommandLineContribution(connectionManagementService.object, configurationService.object, capabilitiesService, commandService.object);
assertThrowsAsync(async () => await contribution.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 contribution = getCommandLineContribution(connectionManagementService.object, configurationService.object, capabilitiesService, undefined, undefined, logService);
await contribution.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 contribution = getCommandLineContribution(connectionManagementService.object, configurationService.object, capabilitiesService, undefined, undefined, logService);
await contribution.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 contribution = getCommandLineContribution(connectionManagementService.object, configurationService.object, capabilitiesService, undefined, editorService.object);
await contribution.processCommandLine(args);
queryInput.verifyAll();
connectionManagementService.verifyAll();
});
suite('URL Handler', () => {
let dialogService: TypeMoq.Mock<TestDialogService>;
setup(() => {
dialogService = TypeMoq.Mock.ofType(TestDialogService);
});
test('handleUrl ignores non-connect URLs', async () => {
// Given a URI pointing to a server
let uri: URI = URI.parse('azuredatastudio://file?server=myserver&database=mydatabase&user=myuser');
const connectionManagementService: TypeMoq.Mock<IConnectionManagementService>
= TypeMoq.Mock.ofType<IConnectionManagementService>(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
const configurationService = getConfigurationServiceMock(true);
const logService = new NullLogService();
let contribution = getCommandLineContribution(connectionManagementService.object, configurationService.object, capabilitiesService, undefined, undefined, logService, dialogService.object);
// When I call the URL handler and user confirms they should connect
dialogService.setup(d => d.confirm(TypeMoq.It.isAny())).returns(() => Promise.resolve({ confirmed: true }));
let result = await contribution.handleURL(uri);
// Then I expect connection management service to have been called
assert.equal(result, false, 'Expected URL to be ignored');
});
test('handleUrl opens a new connection if a server name is passed', async () => {
// Given a URI pointing to a server
let uri: URI = URI.parse('azuredatastudio://connect?server=myserver&database=mydatabase&user=myuser');
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).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 contribution = getCommandLineContribution(connectionManagementService.object, configurationService.object, capabilitiesService, undefined, undefined, logService, dialogService.object);
// When I call the URL handler and user confirms they should connect
dialogService.setup(d => d.confirm(TypeMoq.It.isAny())).returns(() => Promise.resolve({ confirmed: true }));
let result = await contribution.handleURL(uri);
// Then I expect connection management service to have been called
assert.equal(result, true, 'Expected URL to be handled');
connectionManagementService.verifyAll();
});
test('handleUrl does nothing if a user does not confirm', async () => {
// Given a URI pointing to a server
let uri: URI = URI.parse('azuredatastudio://connect?server=myserver&database=mydatabase&user=myuser');
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).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');
})
// Note: should not run since we expect to cancel before this
.verifiable(TypeMoq.Times.never());
connectionManagementService.setup(c => c.getConnectionProfileById(TypeMoq.It.isAnyString())).returns(() => originalProfile);
const configurationService = getConfigurationServiceMock(true);
const logService = new NullLogService();
let contribution = getCommandLineContribution(connectionManagementService.object, configurationService.object, capabilitiesService, undefined, undefined, logService, dialogService.object);
// When I call the URL handler and user says no on confirmation dialog
dialogService.setup(d => d.confirm(TypeMoq.It.isAny())).returns(() => Promise.resolve({ confirmed: false }));
let result = await contribution.handleURL(uri);
// Then I expect no connection, but the URL should still be handled
assert.equal(result, true, 'Expected URL to be handled');
connectionManagementService.verifyAll();
});
test('handleUrl ignores commands', async () => {
// Given I pass a command
let uri: URI = URI.parse('azuredatastudio://connect?command=mycommand');
const connectionManagementService: TypeMoq.Mock<IConnectionManagementService>
= TypeMoq.Mock.ofType<IConnectionManagementService>(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
const commandService: TypeMoq.Mock<ICommandService> = TypeMoq.Mock.ofType<ICommandService>(TestCommandService);
connectionManagementService.setup(c => c.hasRegisteredServers()).returns(() => true);
commandService.setup(c => c.executeCommand('mycommand'))
.returns(() => Promise.resolve())
.verifiable(TypeMoq.Times.never());
const configurationService = getConfigurationServiceMock(true);
const notificationService = TypeMoq.Mock.ofType(TestNotificationService);
notificationService.setup(n => n.warn(TypeMoq.It.isAny())).returns(() => undefined)
.verifiable(TypeMoq.Times.once());
let contribution = getCommandLineContribution(connectionManagementService.object, configurationService.object, capabilitiesService, commandService.object, undefined, new NullLogService(), dialogService.object, notificationService.object);
// When I handle the command URL
let result = await contribution.handleURL(uri);
// Then command service should not have been called, and instead connection should be handled
assert.equal(result, true);
commandService.verifyAll();
notificationService.verifyAll();
});
test('handleUrl ignores commands and connects', async () => {
// Given I pass a command
let uri: URI = URI.parse('azuredatastudio://connect?command=mycommand&server=myserver&database=mydatabase&user=myuser');
const connectionManagementService: TypeMoq.Mock<IConnectionManagementService>
= TypeMoq.Mock.ofType<IConnectionManagementService>(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
const commandService: TypeMoq.Mock<ICommandService> = TypeMoq.Mock.ofType<ICommandService>(TestCommandService);
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(() => []);
connectionManagementService.setup(c => c.connectIfNotConnected(TypeMoq.It.is<ConnectionProfile>(p => p.serverName === 'myserver' && p.authenticationType === Constants.sqlLogin), 'connection', true))
.returns((conn) => {
return Promise.resolve('unused');
})
.verifiable(TypeMoq.Times.once());
commandService.setup(c => c.executeCommand('mycommand'))
.returns(() => Promise.resolve())
.verifiable(TypeMoq.Times.never());
const configurationService = getConfigurationServiceMock(true);
const notificationService = TypeMoq.Mock.ofType(TestNotificationService);
notificationService.setup(n => n.warn(TypeMoq.It.isAny())).returns(() => undefined)
.verifiable(TypeMoq.Times.never());
let contribution = getCommandLineContribution(connectionManagementService.object, configurationService.object, capabilitiesService, commandService.object, undefined, new NullLogService(), dialogService.object, notificationService.object);
// When I handle the command URL
dialogService.setup(d => d.confirm(TypeMoq.It.isAny())).returns(() => Promise.resolve({ confirmed: true }));
let result = await contribution.handleURL(uri);
// Then command service should not have been called, and instead connection should be handled
assert.equal(result, true);
commandService.verifyAll();
notificationService.verifyAll();
connectionManagementService.verifyAll();
});
});
});