Initial Code Layering (#3788)

* working on formatting

* fixed basic lint errors; starting moving things to their appropriate location

* formatting

* update tslint to match the version of vscode we have

* remove unused code

* work in progress fixing layering

* formatting

* moved connection management service to platform

* formatting

* add missing file

* moving more servies

* formatting

* moving more services

* formatting

* wip

* moving more services

* formatting

* revert back tslint rules

* move css file

* add missing svgs
This commit is contained in:
Anthony Dresser
2019-01-25 14:52:35 -08:00
committed by GitHub
parent c8986464ec
commit ea67859de7
338 changed files with 2036 additions and 7386 deletions

View File

@@ -33,7 +33,7 @@ import { attachModalDialogStyler, attachButtonStyler, attachPanelStyler } from '
import { AccountViewModel } from 'sql/parts/accountManagement/accountDialog/accountViewModel';
import { AddAccountAction } from 'sql/parts/accountManagement/common/accountActions';
import { AccountListRenderer, AccountListDelegate } from 'sql/parts/accountManagement/common/accountListRenderer';
import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/services/accountManagement/eventTypes';
import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/platform/accountManagement/common/eventTypes';
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
import * as TelemetryKeys from 'sql/common/telemetryKeys';

View File

@@ -7,9 +7,9 @@
import Severity from 'vs/base/common/severity';
import { AccountDialog } from 'sql/parts/accountManagement/accountDialog/accountDialog';
import { IErrorMessageService } from 'sql/parts/connection/common/connectionManagement';
import { localize } from 'vs/nls';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
export class AccountDialogController {

View File

@@ -7,8 +7,8 @@
import * as sqlops from 'sqlops';
import { Event, Emitter } from 'vs/base/common/event';
import { IAccountManagementService } from 'sql/services/accountManagement/interfaces';
import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/services/accountManagement/eventTypes';
import { IAccountManagementService } from 'sql/platform/accountManagement/common/interfaces';
import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/platform/accountManagement/common/eventTypes';
/**
* View model for account dialog

View File

@@ -17,7 +17,7 @@ import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { Themable, STATUS_BAR_FOREGROUND } from 'vs/workbench/common/theme';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IAccountManagementService } from 'sql/services/accountManagement/interfaces';
import { IAccountManagementService } from 'sql/platform/accountManagement/common/interfaces';
export class AccountListStatusbarItem extends Themable implements IStatusbarItem {
private _manageLinkedAccountAction: IAction;

View File

@@ -1,238 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!./media/accountPicker';
import { Builder } from 'vs/base/browser/builder';
import * as DOM from 'vs/base/browser/dom';
import { Event, Emitter } from 'vs/base/common/event';
import { List } from 'vs/base/browser/ui/list/listWidget';
import { IDropdownOptions } from 'vs/base/browser/ui/dropdown/dropdown';
import { IListEvent } from 'vs/base/browser/ui/list/list';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { buttonBackground } from 'vs/platform/theme/common/colorRegistry';
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { attachListStyler } from 'vs/platform/theme/common/styler';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import * as sqlops from 'sqlops';
import { DropdownList } from 'sql/base/browser/ui/dropdownList/dropdownList';
import { attachDropdownStyler } from 'sql/common/theme/styler';
import { AddAccountAction, RefreshAccountAction } from 'sql/parts/accountManagement/common/accountActions';
import { AccountPickerListRenderer, AccountListDelegate } from 'sql/parts/accountManagement/common/accountListRenderer';
import { AccountPickerViewModel } from 'sql/parts/accountManagement/accountPicker/accountPickerViewModel';
export class AccountPicker extends Disposable {
public static ACCOUNTPICKERLIST_HEIGHT = 47;
public viewModel: AccountPickerViewModel;
private _accountList: List<sqlops.Account>;
private _rootElement: HTMLElement;
private _refreshContainer: HTMLElement;
private _listContainer: HTMLElement;
private _dropdown: DropdownList;
private _refreshAccountAction: RefreshAccountAction;
// EVENTING ////////////////////////////////////////////////////////////
private _addAccountCompleteEmitter: Emitter<void>;
public get addAccountCompleteEvent(): Event<void> { return this._addAccountCompleteEmitter.event; }
private _addAccountErrorEmitter: Emitter<string>;
public get addAccountErrorEvent(): Event<string> { return this._addAccountErrorEmitter.event; }
private _addAccountStartEmitter: Emitter<void>;
public get addAccountStartEvent(): Event<void> { return this._addAccountStartEmitter.event; }
private _onAccountSelectionChangeEvent: Emitter<sqlops.Account>;
public get onAccountSelectionChangeEvent(): Event<sqlops.Account> { return this._onAccountSelectionChangeEvent.event; }
constructor(
private _providerId: string,
@IWorkbenchThemeService private _themeService: IWorkbenchThemeService,
@IInstantiationService private _instantiationService: IInstantiationService,
@IContextViewService private _contextViewService: IContextViewService
) {
super();
// Create event emitters
this._addAccountCompleteEmitter = new Emitter<void>();
this._addAccountErrorEmitter = new Emitter<string>();
this._addAccountStartEmitter = new Emitter<void>();
this._onAccountSelectionChangeEvent = new Emitter<sqlops.Account>();
// Create the view model, wire up the events, and initialize with baseline data
this.viewModel = this._instantiationService.createInstance(AccountPickerViewModel, this._providerId);
this.viewModel.updateAccountListEvent(arg => {
if (arg.providerId === this._providerId) {
this.updateAccountList(arg.accountList);
}
});
}
// PUBLIC METHODS //////////////////////////////////////////////////////
/**
* Render account picker
*/
public render(container: HTMLElement): void {
DOM.append(container, this._rootElement);
}
// PUBLIC METHODS //////////////////////////////////////////////////////
/**
* Create account picker component
*/
public createAccountPickerComponent() {
// Create an account list
let delegate = new AccountListDelegate(AccountPicker.ACCOUNTPICKERLIST_HEIGHT);
let accountRenderer = new AccountPickerListRenderer();
this._listContainer = DOM.$('div.account-list-container');
this._accountList = new List<sqlops.Account>(this._listContainer, delegate, [accountRenderer]);
this._register(attachListStyler(this._accountList, this._themeService));
this._rootElement = DOM.$('div.account-picker-container');
// Create a dropdown for account picker
let option: IDropdownOptions = {
contextViewProvider: this._contextViewService,
labelRenderer: (container) => this.renderLabel(container)
};
// Create the add account action
let addAccountAction = this._instantiationService.createInstance(AddAccountAction, this._providerId);
addAccountAction.addAccountCompleteEvent(() => this._addAccountCompleteEmitter.fire());
addAccountAction.addAccountErrorEvent((msg) => this._addAccountErrorEmitter.fire(msg));
addAccountAction.addAccountStartEvent(() => this._addAccountStartEmitter.fire());
this._dropdown = this._register(new DropdownList(this._rootElement, option, this._listContainer, this._accountList, this._themeService, addAccountAction));
this._register(attachDropdownStyler(this._dropdown, this._themeService));
this._register(this._accountList.onSelectionChange((e: IListEvent<sqlops.Account>) => {
if (e.elements.length === 1) {
this._dropdown.renderLabel();
this.onAccountSelectionChange(e.elements[0]);
}
}));
// Create refresh account action
this._refreshContainer = DOM.append(this._rootElement, DOM.$('div.refresh-container'));
DOM.append(this._refreshContainer, DOM.$('div.sql icon warning'));
let actionBar = new ActionBar(this._refreshContainer, { animated: false });
this._refreshAccountAction = this._instantiationService.createInstance(RefreshAccountAction);
actionBar.push(this._refreshAccountAction, { icon: false, label: true });
if (this._accountList.length > 0) {
this._accountList.setSelection([0]);
this.onAccountSelectionChange(this._accountList.getSelectedElements()[0]);
} else {
new Builder(this._refreshContainer).hide();
}
this._register(this._themeService.onDidColorThemeChange(e => this.updateTheme(e)));
this.updateTheme(this._themeService.getColorTheme());
// Load the initial contents of the view model
this.viewModel.initialize()
.then((accounts: sqlops.Account[]) => {
this.updateAccountList(accounts);
});
}
public dispose() {
super.dispose();
if (this._accountList) {
this._accountList.dispose();
}
}
// PRIVATE HELPERS /////////////////////////////////////////////////////
private onAccountSelectionChange(account: sqlops.Account) {
this.viewModel.selectedAccount = account;
if (account && account.isStale) {
this._refreshAccountAction.account = account;
new Builder(this._refreshContainer).show();
} else {
new Builder(this._refreshContainer).hide();
}
this._onAccountSelectionChangeEvent.fire(account);
}
private renderLabel(container: HTMLElement): IDisposable {
if (container.hasChildNodes()) {
for (let i = 0; i < container.childNodes.length; i++) {
container.removeChild(container.childNodes.item(i));
}
}
let selectedAccounts = this._accountList.getSelectedElements();
let account = selectedAccounts ? selectedAccounts[0] : null;
if (account) {
const badge = DOM.$('div.badge');
const row = DOM.append(container, DOM.$('div.selected-account-container'));
const icon = DOM.append(row, DOM.$('div.icon'));
DOM.append(icon, badge);
const badgeContent = DOM.append(badge, DOM.$('div.badge-content'));
const label = DOM.append(row, DOM.$('div.label'));
// Set the account icon
icon.classList.add('icon', account.displayInfo.accountType);
// TODO: Pick between the light and dark logo
label.innerText = account.displayInfo.displayName + ' (' + account.displayInfo.contextualDisplayName + ')';
if (account.isStale) {
badgeContent.className = 'badge-content icon warning-badge';
} else {
badgeContent.className = 'badge-content';
}
} else {
const row = DOM.append(container, DOM.$('div.no-account-container'));
row.innerText = AddAccountAction.LABEL + '...';
}
return null;
}
private updateAccountList(accounts: sqlops.Account[]): void {
// keep the selection to the current one
let selectedElements = this._accountList.getSelectedElements();
// find selected index
let selectedIndex: number;
if (selectedElements.length > 0 && accounts.length > 0) {
selectedIndex = accounts.findIndex((account) => {
return (account.key.accountId === selectedElements[0].key.accountId);
});
}
// Replace the existing list with the new one
this._accountList.splice(0, this._accountList.length, accounts);
if (this._accountList.length > 0) {
if (selectedIndex && selectedIndex !== -1) {
this._accountList.setSelection([selectedIndex]);
} else {
this._accountList.setSelection([0]);
}
} else {
// if the account is empty, re-render dropdown label
this.onAccountSelectionChange(undefined);
this._dropdown.renderLabel();
}
this._accountList.layout(this._accountList.contentHeight);
}
/**
* Update theming that is specific to account picker
*/
private updateTheme(theme: IColorTheme): void {
let linkColor = theme.getColor(buttonBackground);
let link = linkColor ? linkColor.toString() : null;
this._refreshContainer.style.color = link;
if (this._refreshContainer) {
this._refreshContainer.style.color = link;
}
}
}

View File

@@ -1,66 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Event, Emitter } from 'vs/base/common/event';
import * as sqlops from 'sqlops';
import { IAccountPickerService } from 'sql/parts/accountManagement/common/interfaces';
import { AccountPicker } from 'sql/parts/accountManagement/accountPicker/accountPicker';
export class AccountPickerService implements IAccountPickerService {
_serviceBrand: any;
private _accountPicker: AccountPicker;
// EVENTING ////////////////////////////////////////////////////////////
private _addAccountCompleteEmitter: Emitter<void>;
public get addAccountCompleteEvent(): Event<void> { return this._addAccountCompleteEmitter.event; }
private _addAccountErrorEmitter: Emitter<string>;
public get addAccountErrorEvent(): Event<string> { return this._addAccountErrorEmitter.event; }
private _addAccountStartEmitter: Emitter<void>;
public get addAccountStartEvent(): Event<void> { return this._addAccountStartEmitter.event; }
private _onAccountSelectionChangeEvent: Emitter<sqlops.Account>;
public get onAccountSelectionChangeEvent(): Event<sqlops.Account> { return this._onAccountSelectionChangeEvent.event; }
constructor(
@IInstantiationService private _instantiationService: IInstantiationService
) {
// Create event emitters
this._addAccountCompleteEmitter = new Emitter<void>();
this._addAccountErrorEmitter = new Emitter<string>();
this._addAccountStartEmitter = new Emitter<void>();
this._onAccountSelectionChangeEvent = new Emitter<sqlops.Account>();
}
/**
* Get selected account
*/
public get selectedAccount(): sqlops.Account {
return this._accountPicker.viewModel.selectedAccount;
}
/**
* Render account picker
*/
public renderAccountPicker(container: HTMLElement): void {
if (!this._accountPicker) {
// TODO: expand support to multiple providers
const providerId: string = 'azurePublicCloud';
this._accountPicker = this._instantiationService.createInstance(AccountPicker, providerId);
this._accountPicker.createAccountPickerComponent();
}
this._accountPicker.addAccountCompleteEvent(() => this._addAccountCompleteEmitter.fire());
this._accountPicker.addAccountErrorEvent((msg) => this._addAccountErrorEmitter.fire(msg));
this._accountPicker.addAccountStartEvent(() => this._addAccountStartEmitter.fire());
this._accountPicker.onAccountSelectionChangeEvent((account) => this._onAccountSelectionChangeEvent.fire(account));
this._accountPicker.render(container);
}
}

View File

@@ -1,50 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import { Event, Emitter } from 'vs/base/common/event';
import { IAccountManagementService } from 'sql/services/accountManagement/interfaces';
import { UpdateAccountListEventParams } from 'sql/services/accountManagement/eventTypes';
/**
* View model for account picker
*/
export class AccountPickerViewModel {
// EVENTING ////////////////////////////////////////////////////////////
private _updateAccountListEmitter: Emitter<UpdateAccountListEventParams>;
public get updateAccountListEvent(): Event<UpdateAccountListEventParams> { return this._updateAccountListEmitter.event; }
public selectedAccount: sqlops.Account;
constructor(
private _providerId: string,
@IAccountManagementService private _accountManagementService: IAccountManagementService
) {
let self = this;
// Create our event emitters
this._updateAccountListEmitter = new Emitter<UpdateAccountListEventParams>();
// Register handlers for any changes to the accounts
this._accountManagementService.updateAccountListEvent(arg => self._updateAccountListEmitter.fire(arg));
}
// PUBLIC METHODS //////////////////////////////////////////////////////
/**
* Loads an initial list of accounts from the account management service
* @return {Thenable<Account[]>} Promise to return the list of accounts
*/
public initialize(): Thenable<sqlops.Account[]> {
// Load a baseline of the accounts for the provider
return this._accountManagementService.getAccountsForProvider(this._providerId)
.then(null, () => {
// In the event we failed to lookup accounts for the provider, just send
// back an empty collection
return [];
});
}
}

View File

@@ -1,99 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/* Selected account */
.selected-account-container {
padding: 6px;
display: flex;
align-items: flex-start;
}
.selected-account-container .icon {
flex: 0 0 25px;
height: 25px;
width: 25px;
}
.selected-account-container .label {
flex: 1 1 auto;
padding-left: 10px;
align-self: center;
}
.selected-account-container .icon {
background-size: 25px;
}
.selected-account-container .icon .badge {
position: relative;
top: 15px;
left: 15px;
overflow: hidden;
width: 12px;
height: 12px;
}
.selected-account-container .icon .badge .badge-content {
width: 12px;
height: 12px;
background-size: 12px;
}
/* A container when the account list is empty */
.no-account-container {
padding: 6px;
opacity: 0.7;
font-style: italic;
}
/* Account list */
.account-list-container .list-row {
padding: 6px;
}
.account-list-container .list-row .icon {
flex: 0 0 35px;
height: 35px;
width: 35px;
background-size: 35px;
}
.account-list-container .list-row .icon .badge {
position: relative;
top: 22px;
left: 22px;
overflow: hidden;
width: 15px;
height: 15px;
}
.account-list-container .list-row .icon .badge .badge-content {
width: 15px;
height: 15px;
background-size: 15px;
}
/* Refresh link */
.refresh-container {
padding-left: 15px;
padding-top: 6px;
display: flex;
align-items: flex-start;
}
.refresh-container .monaco-action-bar .actions-container {
justify-content: flex-start;
}
.refresh-container .icon {
flex: 0 0 16px;
height: 16px;
width: 16px;
}
.refresh-container .monaco-action-bar {
flex: 1 1 auto;
margin-left: 10px;
}

View File

@@ -8,9 +8,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import Severity from 'vs/base/common/severity';
import { localize } from 'vs/nls';
import { IErrorMessageService } from 'sql/parts/connection/common/connectionManagement';
import { AutoOAuthDialog } from 'sql/parts/accountManagement/autoOAuthDialog/autoOAuthDialog';
import { IAccountManagementService } from 'sql/services/accountManagement/interfaces';
import { IAccountManagementService } from 'sql/platform/accountManagement/common/interfaces';
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
export class AutoOAuthDialogController {
// MEMBER VARIABLES ////////////////////////////////////////////////////

View File

@@ -12,8 +12,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
import { error } from 'sql/base/common/log';
import { IAccountManagementService } from 'sql/services/accountManagement/interfaces';
import { IErrorMessageService } from 'sql/parts/connection/common/connectionManagement';
import { IAccountManagementService } from 'sql/platform/accountManagement/common/interfaces';
import { IDialogService, IConfirmation } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity';
@@ -83,7 +82,6 @@ export class RemoveAccountAction extends Action {
private _account: sqlops.Account,
@IDialogService private _dialogService: IDialogService,
@INotificationService private _notificationService: INotificationService,
@IErrorMessageService private _errorMessageService: IErrorMessageService,
@IAccountManagementService private _accountManagementService: IAccountManagementService
) {
super(RemoveAccountAction.ID, RemoveAccountAction.LABEL, 'remove-account-action icon remove');
@@ -107,15 +105,15 @@ export class RemoveAccountAction extends Action {
return new TPromise((resolve, reject) => {
self._accountManagementService.removeAccount(self._account.key)
.then(
(result) => { resolve(result); },
(err) => {
// Must handle here as this is an independent action
self._notificationService.notify({
severity: Severity.Error,
message: localize('removeAccountFailed', 'Failed to remove account')
});
resolve(false);
}
(result) => { resolve(result); },
(err) => {
// Must handle here as this is an independent action
self._notificationService.notify({
severity: Severity.Error,
message: localize('removeAccountFailed', 'Failed to remove account')
});
resolve(false);
}
);
});
}

View File

@@ -1,61 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import * as sqlops from 'sqlops';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
export const SERVICE_ID = 'resourceProviderService';
export const IResourceProviderService = createDecorator<IResourceProviderService>(SERVICE_ID);
export interface IHandleFirewallRuleResult {
canHandleFirewallRule: boolean;
ipAddress: string;
resourceProviderId: string;
}
export interface IResourceProviderService {
_serviceBrand: any;
/**
* Register a resource provider
*/
registerProvider(providerId: string, provider: sqlops.ResourceProvider): void;
/**
* Unregister a resource provider
*/
unregisterProvider(ProviderId: string): void;
/**
* Create a firewall rule
*/
createFirewallRule(selectedAccount: sqlops.Account, firewallruleInfo: sqlops.FirewallRuleInfo, resourceProviderId: string): Promise<sqlops.CreateFirewallRuleResponse>;
/**
* handle a firewall rule
*/
handleFirewallRule(errorCode: number, errorMessage: string, connectionTypeId: string): Promise<IHandleFirewallRuleResult>;
/**
* Show firewall rule dialog
*/
showFirewallRuleDialog(connection: IConnectionProfile, ipAddress: string, resourceProviderId: string): Promise<boolean>;
}
export const IAccountPickerService = createDecorator<IAccountPickerService>('AccountPickerService');
export interface IAccountPickerService {
_serviceBrand: any;
renderAccountPicker(container: HTMLElement): void;
addAccountCompleteEvent: Event<void>;
addAccountErrorEvent: Event<string>;
addAccountStartEvent: Event<void>;
onAccountSelectionChangeEvent: Event<sqlops.Account>;
selectedAccount: sqlops.Account;
}

View File

@@ -1,106 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IResourceProviderService, IHandleFirewallRuleResult } from 'sql/parts/accountManagement/common/interfaces';
import * as Constants from 'sql/common/constants';
import * as TelemetryKeys from 'sql/common/telemetryKeys';
import * as TelemetryUtils from 'sql/common/telemetryUtilities';
import { FirewallRuleDialogController } from 'sql/parts/accountManagement/firewallRuleDialog/firewallRuleDialogController';
import * as sqlops from 'sqlops';
export class ResourceProviderService implements IResourceProviderService {
public _serviceBrand: any;
private _providers: { [handle: string]: sqlops.ResourceProvider; } = Object.create(null);
private _firewallRuleDialogController: FirewallRuleDialogController;
constructor(
@ITelemetryService private _telemetryService: ITelemetryService,
@IInstantiationService private _instantiationService: IInstantiationService,
) {
}
/**
* Opens the firewall rule dialog
*/
public showFirewallRuleDialog(connection: IConnectionProfile, ipAddress: string, resourceProviderId: string): Promise<boolean> {
let self = this;
// If the firewall rule dialog hasn't been defined, create a new one
if (!self._firewallRuleDialogController) {
self._firewallRuleDialogController = self._instantiationService.createInstance(FirewallRuleDialogController);
}
return self._firewallRuleDialogController.openFirewallRuleDialog(connection, ipAddress, resourceProviderId);
}
/**
* Create a firewall rule
*/
public createFirewallRule(selectedAccount: sqlops.Account, firewallruleInfo: sqlops.FirewallRuleInfo, resourceProviderId: string): Promise<sqlops.CreateFirewallRuleResponse> {
return new Promise<sqlops.CreateFirewallRuleResponse>((resolve, reject) => {
let provider = this._providers[resourceProviderId];
if (provider) {
TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.FirewallRuleRequested, { provider: resourceProviderId });
provider.createFirewallRule(selectedAccount, firewallruleInfo).then(result => {
resolve(result);
}, error => {
reject(error);
});
} else {
reject(Constants.InvalidProvider);
}
});
}
/**
* Handle a firewall rule
*/
public handleFirewallRule(errorCode: number, errorMessage: string, connectionTypeId: string): Promise<IHandleFirewallRuleResult> {
let self = this;
return new Promise<IHandleFirewallRuleResult>((resolve, reject) => {
let handleFirewallRuleResult: IHandleFirewallRuleResult;
let promises = [];
if (self._providers) {
for (let key in self._providers) {
let provider = self._providers[key];
promises.push(provider.handleFirewallRule(errorCode, errorMessage, connectionTypeId)
.then(response => {
if (response.result) {
handleFirewallRuleResult = { canHandleFirewallRule: response.result, ipAddress: response.ipAddress, resourceProviderId: key };
}
},
() => { /* Swallow failures at getting accounts, we'll just hide that provider */
}));
}
}
Promise.all(promises).then(() => {
if (handleFirewallRuleResult) {
resolve(handleFirewallRuleResult);
} else {
handleFirewallRuleResult = { canHandleFirewallRule: false, ipAddress: undefined, resourceProviderId: undefined };
resolve(handleFirewallRuleResult);
}
});
});
}
/**
* Register a resource provider
*/
public registerProvider(providerId: string, provider: sqlops.ResourceProvider): void {
this._providers[providerId] = provider;
}
public unregisterProvider(providerId: string): void {
delete this._providers[providerId];
}
}

View File

@@ -13,13 +13,14 @@ import { Event, Emitter } from 'vs/base/common/event';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { localize } from 'vs/nls';
import { buttonBackground } from 'vs/platform/theme/common/colorRegistry';
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import * as sqlops from 'sqlops';
import { Button } from 'sql/base/browser/ui/button/button';
@@ -27,9 +28,8 @@ import { Modal } from 'sql/base/browser/ui/modal/modal';
import { FirewallRuleViewModel } from 'sql/parts/accountManagement/firewallRuleDialog/firewallRuleViewModel';
import { attachModalDialogStyler, attachButtonStyler } from 'sql/common/theme/styler';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
import { IAccountPickerService } from 'sql/parts/accountManagement/common/interfaces';
import { IAccountPickerService } from 'sql/platform/accountManagement/common/accountPicker';
import * as TelemetryKeys from 'sql/common/telemetryKeys';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
// TODO: Make the help link 1) extensible (01/08/2018, https://github.com/Microsoft/azuredatastudio/issues/450)
// in case that other non-Azure sign in is to be used
@@ -65,7 +65,7 @@ export class FirewallRuleDialog extends Modal {
constructor(
@IAccountPickerService private _accountPickerService: IAccountPickerService,
@IPartService partService: IPartService,
@IWorkbenchThemeService private _workbenchThemeService: IWorkbenchThemeService,
@IThemeService themeService: IThemeService,
@IInstantiationService private _instantiationService: IInstantiationService,
@IContextViewService private _contextViewService: IContextViewService,
@ITelemetryService telemetryService: ITelemetryService,
@@ -79,7 +79,7 @@ export class FirewallRuleDialog extends Modal {
partService,
telemetryService,
clipboardService,
_workbenchThemeService,
themeService,
contextKeyService,
{
isFlyout: true,
@@ -206,8 +206,8 @@ export class FirewallRuleDialog extends Modal {
builder.append(firewallRuleSection);
});
this._register(this._workbenchThemeService.onDidColorThemeChange(e => this.updateTheme(e)));
this.updateTheme(this._workbenchThemeService.getColorTheme());
this._register(this._themeService.onThemeChange(e => this.updateTheme(e)));
this.updateTheme(this._themeService.getTheme());
$(this._IPAddressInput).on(DOM.EventType.CLICK, () => {
this.onFirewallRuleOptionSelected(true);
@@ -243,7 +243,7 @@ export class FirewallRuleDialog extends Modal {
}
// Update theming that is specific to firewall rule flyout body
private updateTheme(theme: IColorTheme): void {
private updateTheme(theme: ITheme): void {
let linkColor = theme.getColor(buttonBackground);
let link = linkColor ? linkColor.toString() : null;
if (this._helpLink) {

View File

@@ -9,12 +9,12 @@ import Severity from 'vs/base/common/severity';
import { localize } from 'vs/nls';
import * as sqlops from 'sqlops';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IErrorMessageService } from 'sql/parts/connection/common/connectionManagement';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { FirewallRuleDialog } from 'sql/parts/accountManagement/firewallRuleDialog/firewallRuleDialog';
import { IAccountManagementService, AzureResource } from 'sql/services/accountManagement/interfaces';
import { IResourceProviderService } from 'sql/parts/accountManagement/common/interfaces';
import { IAccountManagementService, AzureResource } from 'sql/platform/accountManagement/common/interfaces';
import { IResourceProviderService } from 'sql/workbench/services/resourceProvider/common/resourceProviderService';
import { Deferred } from 'sql/base/common/promise';
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
export class FirewallRuleDialogController {

View File

@@ -1,131 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { localize } from 'vs/nls';
export const SERVICE_ID = 'adminService';
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { CreateLoginInput } from 'sql/parts/admin/security/createLoginInput';
import { TaskDialogInput } from 'sql/parts/tasks/dialog/taskDialogInput';
import { TPromise } from 'vs/base/common/winjs.base';
import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import * as sqlops from 'sqlops';
export const IAdminService = createDecorator<IAdminService>(SERVICE_ID);
export interface IAdminService {
_serviceBrand: any;
registerProvider(providerId: string, provider: sqlops.AdminServicesProvider): void;
showCreateDatabaseWizard(uri: string, connection: IConnectionProfile): Promise<any>;
showCreateLoginWizard(uri: string, connection: IConnectionProfile): Promise<any>;
createDatabase(connectionUri: string, database: sqlops.DatabaseInfo): Thenable<sqlops.CreateDatabaseResponse>;
getDefaultDatabaseInfo(connectionUri: string): Thenable<sqlops.DatabaseInfo>;
getDatabaseInfo(connectionUri: string): Thenable<sqlops.DatabaseInfo>;
}
export class AdminService implements IAdminService {
_serviceBrand: any;
private _providers: { [handle: string]: sqlops.AdminServicesProvider; } = Object.create(null);
constructor(
@IInstantiationService private _instantiationService: IInstantiationService,
@IEditorService private _editorService: IEditorService,
@IConnectionManagementService private _connectionService: IConnectionManagementService,
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService
) {
}
private _runAction<T>(uri: string, action: (handler: sqlops.AdminServicesProvider) => Thenable<T>): Thenable<T> {
let providerId: string = this._connectionService.getProviderIdFromUri(uri);
if (!providerId) {
return TPromise.wrapError(new Error(localize('adminService.providerIdNotValidError', 'Connection is required in order to interact with adminservice')));
}
let handler = this._providers[providerId];
if (handler) {
return action(handler);
} else {
return TPromise.wrapError(new Error(localize('adminService.noHandlerRegistered', 'No Handler Registered')));
}
}
public showCreateDatabaseWizard(uri: string, connection: IConnectionProfile): Promise<any> {
const self = this;
return new Promise<boolean>((resolve, reject) => {
let input: TaskDialogInput = self._instantiationService ? self._instantiationService.createInstance(TaskDialogInput, uri, connection) : undefined;
self._editorService.openEditor(input, { pinned: true }, ACTIVE_GROUP);
resolve(true);
});
}
public createDatabase(connectionUri: string, database: sqlops.DatabaseInfo): Thenable<sqlops.CreateDatabaseResponse> {
let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri);
if (providerId) {
let provider = this._providers[providerId];
if (provider) {
return provider.createDatabase(connectionUri, database);
}
}
return Promise.resolve(undefined);
}
public showCreateLoginWizard(uri: string, connection: IConnectionProfile): Promise<any> {
const self = this;
self.createLogin(uri, { name: 'TEST: login name' });
return new Promise<boolean>((resolve, reject) => {
let loginInput: CreateLoginInput = self._instantiationService ? self._instantiationService.createInstance(CreateLoginInput, uri, connection) : undefined;
self._editorService.openEditor(loginInput, { pinned: true }, ACTIVE_GROUP);
resolve(true);
});
}
public createLogin(connectionUri: string, login: sqlops.LoginInfo): Thenable<sqlops.CreateLoginResponse> {
let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri);
if (providerId) {
let provider = this._providers[providerId];
if (provider) {
return provider.createLogin(connectionUri, login);
}
}
return Promise.resolve(undefined);
}
public getDefaultDatabaseInfo(connectionUri: string): Thenable<sqlops.DatabaseInfo> {
let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri);
if (providerId) {
let provider = this._providers[providerId];
if (provider) {
return provider.getDefaultDatabaseInfo(connectionUri);
}
}
return Promise.resolve(undefined);
}
public getDatabaseInfo(connectionUri: string): Thenable<sqlops.DatabaseInfo> {
return this._runAction(connectionUri, (runner) => {
return runner.getDatabaseInfo(connectionUri);
});
}
public registerProvider(providerId: string, provider: sqlops.AdminServicesProvider): void {
this._providers[providerId] = provider;
}
}

View File

@@ -7,8 +7,8 @@ import { ChangeDetectorRef, ElementRef, Component, forwardRef, Inject } from '@a
import { NgForm } from '@angular/forms';
import { ITaskDialogComponentParams } from 'sql/services/bootstrap/bootstrapParams';
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
import { IAdminService } from 'sql/parts/admin/common/adminService';
import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
import { IAdminService } from 'sql/workbench/services/admin/common/adminService';
import { ITaskDialogComponent } from 'sql/parts/tasks/common/tasks';
import * as sqlops from 'sqlops';

View File

@@ -6,7 +6,7 @@
import { ElementRef, Component, Inject, forwardRef } from '@angular/core';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { IDashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams';
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
export const CREATELOGIN_SELECTOR: string = 'createlogin-component';
@@ -21,8 +21,8 @@ export class CreateLoginComponent {
public connection: ConnectionManagementInfo;
constructor(
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
@Inject(IBootstrapParams) private _params: IDashboardComponentParams
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
@Inject(IBootstrapParams) private _params: IDashboardComponentParams
) {
}
}

View File

@@ -11,11 +11,11 @@ import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { CreateLoginInput } from './createLoginInput';
import { CreateLoginModule } from './createLogin.module';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { IMetadataService } from 'sql/services/metadata/metadataService';
import { IScriptingService } from 'sql/services/scripting/scriptingService';
import { CreateLoginInput } from 'sql/parts/admin/security/createLoginInput';
import { CreateLoginModule } from 'sql/parts/admin/security/createLogin.module';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { IMetadataService } from 'sql/platform/metadata/common/metadataService';
import { IScriptingService } from 'sql/platform/scripting/common/scriptingService';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { bootstrapAngular, IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { CREATELOGIN_SELECTOR } from 'sql/parts/admin/security/createLogin.component';

View File

@@ -6,7 +6,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { EditorInput, EditorModel } from 'vs/workbench/common/editor';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
export class CreateLoginInput extends EditorInput {

View File

@@ -1,17 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export interface ICommandLineProcessing {
_serviceBrand: any;
/**
* Interprets the various Azure Data Studio-specific command line switches and
* performs the requisite tasks such as connecting to a server
*/
processCommandLine() : Promise<void>;
}
export const ICommandLineProcessing = createDecorator<ICommandLineProcessing>('commandLineService');

View File

@@ -1,127 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import { ICommandLineProcessing } from 'sql/parts/commandLine/common/commandLine';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import * as Constants from 'sql/parts/connection/common/constants';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import * as platform from 'vs/platform/registry/common/platform';
import { ConnectionProviderProperties, IConnectionProviderRegistry, Extensions as ConnectionProviderExtensions } from 'sql/workbench/parts/connection/common/connectionProviderExtension';
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
import { IObjectExplorerService } from 'sql/parts/objectExplorer/common/objectExplorerService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { warn } from 'sql/base/common/log';
export class CommandLineService implements ICommandLineProcessing {
private _connectionProfile: ConnectionProfile;
private _showConnectionDialog: boolean;
private _commandName: string;
constructor(
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
@IEnvironmentService private _environmentService: IEnvironmentService,
@IQueryEditorService private _queryEditorService: IQueryEditorService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
@IEditorService private _editorService: IEditorService,
@ICommandService private _commandService: ICommandService,
@IWorkspaceConfigurationService private _configurationService: IWorkspaceConfigurationService
) {
let profile = null;
if (this._environmentService) {
if (this._commandService) {
this._commandName = this._environmentService.args.command;
}
if (this._environmentService.args.server) {
profile = new ConnectionProfile(_capabilitiesService, null);
// We want connection store to use any matching password it finds
profile.savePassword = true;
profile.providerName = Constants.mssqlProviderName;
profile.serverName = _environmentService.args.server;
profile.databaseName = _environmentService.args.database ? _environmentService.args.database : '';
profile.userName = _environmentService.args.user ? _environmentService.args.user : '';
profile.authenticationType = _environmentService.args.integrated ? 'Integrated' : 'SqlLogin';
profile.connectionName = '';
profile.setOptionValue('applicationName', Constants.applicationName);
profile.setOptionValue('databaseDisplayName', profile.databaseName);
profile.setOptionValue('groupId', profile.groupId);
}
}
this._connectionProfile = profile;
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().catch(reason => { warn('processCommandLine failed: ' + reason); });
} else {
registry.onNewProvider(e => {
if (e.id === Constants.mssqlProviderName) {
this.processCommandLine().catch(reason => { warn('processCommandLine failed: ' + reason); });
}
});
}
}
public _serviceBrand: any;
// 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
// (null, null) => Prompt for a connection unless there are registered servers
public processCommandLine(): Promise<void> {
let self = this;
return new Promise<void>((resolve, reject) => {
let showConnectDialogOnStartup: boolean = this._configurationService.getValue('workbench.showConnectDialogOnStartup');
if (showConnectDialogOnStartup && !self._commandName && !self._connectionProfile && !self._connectionManagementService.hasRegisteredServers()) {
// prompt the user for a new connection on startup if no profiles are registered
self._connectionManagementService.showConnectionDialog()
.then(() => {
resolve();
},
error => {
reject(error);
});
} else if (self._connectionProfile) {
if (!self._commandName) {
self._connectionManagementService.connectIfNotConnected(self._connectionProfile, 'connection', true)
.then(() => {
TaskUtilities.newQuery(self._connectionProfile,
self._connectionManagementService,
self._queryEditorService,
self._objectExplorerService,
self._editorService)
.then(() => {
resolve();
}, error => {
// ignore query editor failing to open.
// the tests don't mock this out
warn('unable to open query editor ' + error);
resolve();
});
}, error => {
reject(error);
});
} else {
self._connectionManagementService.connectIfNotConnected(self._connectionProfile, 'connection', true)
.then(() => {
self._commandService.executeCommand(self._commandName, self._connectionProfile).then(() => resolve(), error => reject(error));
}, error => {
reject(error);
});
}
} else if (self._commandName) {
self._commandService.executeCommand(self._commandName).then(() => resolve(), error => reject(error));
}
else {
resolve();
}
});
}
}

View File

@@ -3,8 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import { EditorInput, IEditorInput } from 'vs/workbench/common/editor';
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
@@ -16,7 +14,7 @@ import { QueryInput } from 'sql/parts/query/common/queryInput';
import { IQueryEditorOptions } from 'sql/parts/query/common/queryEditorService';
import { QueryPlanInput } from 'sql/parts/queryPlan/queryPlanInput';
import { NotebookInput, NotebookInputModel, NotebookInputValidator } from 'sql/parts/notebook/notebookInput';
import { DEFAULT_NOTEBOOK_PROVIDER, INotebookService } from 'sql/services/notebook/notebookService';
import { DEFAULT_NOTEBOOK_PROVIDER, INotebookService } from 'sql/workbench/services/notebook/common/notebookService';
import { getProvidersForFileName } from 'sql/parts/notebook/notebookUtils';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';

View File

@@ -1,16 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Subscription } from 'rxjs/Subscription';
import { IDisposable } from 'vs/base/common/lifecycle';
export function toDisposableSubscription(sub: Subscription): IDisposable {
return {
dispose: () => {
sub.unsubscribe();
}
};
}

View File

@@ -8,8 +8,8 @@ import { Action } from 'vs/base/common/actions';
import { TPromise } from 'vs/base/common/winjs.base';
import { Event, Emitter } from 'vs/base/common/event';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { INotificationService, INotificationActions } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity';
import { IDialogService, IConfirmation, IConfirmationResult } from 'vs/platform/dialogs/common/dialogs';
@@ -159,7 +159,7 @@ export class GetCurrentConnectionStringAction extends Action {
return new TPromise<void>((resolve, reject) => {
let activeInput = this._editorService.activeEditor;
if (activeInput && (activeInput instanceof QueryInput || activeInput instanceof EditDataInput || activeInput instanceof DashboardInput)
&& this._connectionManagementService.isConnected(activeInput.uri)) {
&& this._connectionManagementService.isConnected(activeInput.uri)) {
let includePassword = false;
let connectionProfile = this._connectionManagementService.getConnectionProfile(activeInput.uri);
this._connectionManagementService.getConnectionString(connectionProfile.id, includePassword).then(result => {

View File

@@ -1,481 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as Constants from './constants';
import * as Utils from './utils';
import { IConnectionProfile, IConnectionProfileStore } from './interfaces';
import { IConnectionConfig } from './iconnectionConfig';
import { ConnectionProfileGroup, IConnectionProfileGroup } from './connectionProfileGroup';
import { ConfigurationEditingService, IConfigurationValue } from 'vs/workbench/services/configuration/node/configurationEditingService';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { ConnectionProfile } from './connectionProfile';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import * as sqlops from 'sqlops';
import * as nls from 'vs/nls';
import { generateUuid } from 'vs/base/common/uuid';
export interface ISaveGroupResult {
groups: IConnectionProfileGroup[];
newGroupId: string;
}
/**
* Implements connection profile file storage.
*/
export class ConnectionConfig implements IConnectionConfig {
/**
* Constructor.
*/
public constructor(
private _configurationEditService: ConfigurationEditingService,
private _workspaceConfigurationService: IWorkspaceConfigurationService,
private _capabilitiesService: ICapabilitiesService
) { }
/**
* Returns connection groups from user and workspace settings.
*/
public getAllGroups(): IConnectionProfileGroup[] {
let allGroups: IConnectionProfileGroup[] = [];
let userGroups = this.getConfiguration(Constants.connectionGroupsArrayName).user as IConnectionProfileGroup[];
let workspaceGroups = this.getConfiguration(Constants.connectionGroupsArrayName).workspace as IConnectionProfileGroup[];
if (userGroups) {
if (workspaceGroups) {
userGroups = userGroups.filter(x => workspaceGroups.find(f => this.isSameGroupName(f, x)) === undefined);
allGroups = allGroups.concat(workspaceGroups);
}
allGroups = allGroups.concat(userGroups);
}
allGroups = allGroups.map(g => {
if (g.parentId === '' || !g.parentId) {
g.parentId = undefined;
}
return g;
});
return allGroups;
}
/**
* Add a new connection to the connection config.
*/
public addConnection(profile: IConnectionProfile): Promise<IConnectionProfile> {
return new Promise<IConnectionProfile>((resolve, reject) => {
if (profile.saveProfile) {
this.addGroupFromProfile(profile).then(groupId => {
let profiles = this._workspaceConfigurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
if (!profiles) {
profiles = [];
}
let connectionProfile = this.getConnectionProfileInstance(profile, groupId);
let newProfile = ConnectionProfile.convertToProfileStore(this._capabilitiesService, connectionProfile);
// Remove the profile if already set
var sameProfileInList = profiles.find(value => {
let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
return providerConnectionProfile.matches(connectionProfile);
});
if (sameProfileInList) {
let profileIndex = profiles.findIndex(value => value === sameProfileInList);
newProfile.id = sameProfileInList.id;
connectionProfile.id = sameProfileInList.id;
profiles[profileIndex] = newProfile;
} else {
profiles.push(newProfile);
}
this.writeConfiguration(Constants.connectionsArrayName, profiles).then(() => {
resolve(connectionProfile);
}).catch(err => {
reject(err);
});
});
}
});
}
private getConnectionProfileInstance(profile: IConnectionProfile, groupId: string): ConnectionProfile {
let connectionProfile = profile as ConnectionProfile;
if (connectionProfile === undefined) {
connectionProfile = new ConnectionProfile(this._capabilitiesService, profile);
}
connectionProfile.groupId = groupId;
return connectionProfile;
}
/**
*Returns group id
* @param groupName
*/
public addGroupFromProfile(profile: IConnectionProfile): Promise<string> {
return new Promise<string>((resolve, reject) => {
if (profile.groupId && profile.groupId !== Utils.defaultGroupId) {
resolve(profile.groupId);
} else {
let groups = this._workspaceConfigurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
let result = this.saveGroup(groups, profile.groupFullName, undefined, undefined);
groups = result.groups;
this.writeConfiguration(Constants.connectionGroupsArrayName, groups).then(() => {
resolve(result.newGroupId);
}).catch(err => {
reject(err);
});
}
});
}
/**
*Returns group id
* @param groupName
*/
public addGroup(profileGroup: IConnectionProfileGroup): Promise<string> {
return new Promise<string>((resolve, reject) => {
if (profileGroup.id) {
resolve(profileGroup.id);
} else {
let groups = this._workspaceConfigurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
let sameNameGroup = groups ? groups.find(group => group.name === profileGroup.name) : undefined;
if (sameNameGroup) {
let errMessage: string = nls.localize('invalidServerName', "A server group with the same name already exists.");
reject(errMessage);
} else {
let result = this.saveGroup(groups, profileGroup.name, profileGroup.color, profileGroup.description);
groups = result.groups;
this.writeConfiguration(Constants.connectionGroupsArrayName, groups).then(() => {
resolve(result.newGroupId);
}).catch(err => {
reject(err);
});
}
}
});
}
private getConnectionProfilesForTarget(configTarget: ConfigurationTarget): IConnectionProfileStore[] {
let configs = this.getConfiguration(Constants.connectionsArrayName);
let profiles: IConnectionProfileStore[];
if (configs) {
if (configTarget === ConfigurationTarget.USER) {
profiles = <IConnectionProfileStore[]>configs.user;
} else if (configTarget === ConfigurationTarget.WORKSPACE) {
profiles = <IConnectionProfileStore[]>configs.workspace;
}
if (profiles) {
if (this.fixConnectionIds(profiles)) {
this.writeConfiguration(Constants.connectionsArrayName, profiles, configTarget);
}
} else {
profiles = [];
}
}
return profiles;
}
/**
* Replace duplicate ids with new ones. Sets id for the profiles without id
* @param profiles
*/
public fixConnectionIds(profiles: IConnectionProfileStore[]): boolean {
let idsCache: { [label: string]: boolean } = {};
let changed: boolean = false;
for (var index = 0; index < profiles.length; index++) {
var profile = profiles[index];
if (!profile.id) {
profile.id = generateUuid();
changed = true;
}
if (profile.id in idsCache) {
profile.id = generateUuid();
changed = true;
}
idsCache[profile.id] = true;
}
return changed;
}
/**
* Get a list of all connections in the connection config. Connections returned
* are sorted first by whether they were found in the user/workspace settings,
* and next alphabetically by profile/server name.
*/
public getConnections(getWorkspaceConnections: boolean): ConnectionProfile[] {
let profiles: IConnectionProfileStore[] = [];
//TODO: have to figure out how to sort connections for all provider
// Read from user settings
let userProfiles: IConnectionProfileStore[] = this.getConnectionProfilesForTarget(ConfigurationTarget.USER);
if (userProfiles !== undefined) {
profiles = profiles.concat(userProfiles);
}
if (getWorkspaceConnections) {
// Read from workspace settings
let workspaceProfiles: IConnectionProfileStore[] = this.getConnectionProfilesForTarget(ConfigurationTarget.WORKSPACE);
if (workspaceProfiles !== undefined) {
profiles = profiles.concat(workspaceProfiles);
}
}
let connectionProfiles = profiles.map(p => {
return ConnectionProfile.createFromStoredProfile(p, this._capabilitiesService);
});
return connectionProfiles;
}
/**
* Delete a connection profile from settings.
*/
public deleteConnection(profile: ConnectionProfile): Promise<void> {
// Get all connections in the settings
let profiles = this._workspaceConfigurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
// Remove the profile from the connections
profiles = profiles.filter(value => {
let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
return providerConnectionProfile.getOptionsKey() !== profile.getOptionsKey();
});
// Write connections back to settings
return this.writeConfiguration(Constants.connectionsArrayName, profiles);
}
/**
* Delete a group and all its child connections and groups from settings.
* Fails if writing to settings fails.
*/
public deleteGroup(group: ConnectionProfileGroup): Promise<void> {
let connections = ConnectionProfileGroup.getConnectionsInGroup(group);
let subgroups = ConnectionProfileGroup.getSubgroups(group);
// Add selected group to subgroups list
subgroups.push(group);
// Get all connections in the settings
let profiles = this._workspaceConfigurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user;
// Remove the profiles from the connections
profiles = profiles.filter(value => {
let providerConnectionProfile = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
return !connections.some((val) => val.getOptionsKey() === providerConnectionProfile.getOptionsKey());
});
// Get all groups in the settings
let groups = this._workspaceConfigurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
// Remove subgroups in the settings
groups = groups.filter((grp) => {
return !subgroups.some((item) => item.id === grp.id);
});
return new Promise<void>((resolve, reject) => {
this.writeConfiguration(Constants.connectionsArrayName, profiles).then(() => {
this.writeConfiguration(Constants.connectionGroupsArrayName, groups).then(() => {
resolve();
}).catch(() => reject());
}).catch(() => reject());
});
}
/**
* Moves the source group under the target group.
*/
public changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise<void> {
let groups = this._workspaceConfigurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
groups = groups.map(g => {
if (g.id === source.id) {
g.parentId = target.id;
}
return g;
});
return this.writeConfiguration(Constants.connectionGroupsArrayName, groups);
}
/**
* Returns true if connection can be moved to another group
*/
public canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean {
let profiles = this.getConnections(true);
let existingProfile = profiles.find(p => p.getConnectionInfoId() === profile.getConnectionInfoId()
&& p.groupId === newGroupID);
return existingProfile === undefined;
}
/**
* Moves the connection under the target group with the new ID.
*/
private changeGroupIdForConnectionInSettings(profile: ConnectionProfile, newGroupID: string, target: ConfigurationTarget = ConfigurationTarget.USER): Promise<void> {
return new Promise<void>((resolve, reject) => {
let profiles = target === ConfigurationTarget.USER ? this._workspaceConfigurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).user :
this._workspaceConfigurationService.inspect<IConnectionProfileStore[]>(Constants.connectionsArrayName).workspace;
if (profiles) {
if (profile.parent && profile.parent.id === Constants.unsavedGroupId) {
profile.groupId = newGroupID;
profiles.push(ConnectionProfile.convertToProfileStore(this._capabilitiesService, profile));
} else {
profiles.forEach((value) => {
let configProf = ConnectionProfile.createFromStoredProfile(value, this._capabilitiesService);
if (configProf.getOptionsKey() === profile.getOptionsKey()) {
value.groupId = newGroupID;
}
});
}
this.writeConfiguration(Constants.connectionsArrayName, profiles, target).then(result => {
resolve();
}).catch(error => {
reject(error);
});
} else {
resolve();
}
});
}
/**
* Moves the connection under the target group with the new ID.
*/
public changeGroupIdForConnection(profile: ConnectionProfile, newGroupID: string): Promise<void> {
return new Promise<void>((resolve, reject) => {
if (!this.canChangeConnectionConfig(profile, newGroupID)) {
// Same connection already exists in this group
reject('Same connection already exists in the group');
} else {
this.changeGroupIdForConnectionInSettings(profile, newGroupID, ConfigurationTarget.USER).then(result1 => {
this.changeGroupIdForConnectionInSettings(profile, newGroupID, ConfigurationTarget.WORKSPACE).then(result2 => {
resolve();
}).catch(error2 => {
reject(error2);
});
}).catch(error1 => {
reject(error1);
});
}
});
}
public saveGroup(groups: IConnectionProfileGroup[], groupFullName: string, color: string, description: string): ISaveGroupResult {
let result: ISaveGroupResult;
let groupNames = ConnectionProfileGroup.getGroupFullNameParts(groupFullName);
result = this.saveGroupInTree(groups, undefined, groupNames, color, description, 0);
return result;
}
public editGroup(source: ConnectionProfileGroup): Promise<void> {
let groups = this._workspaceConfigurationService.inspect<IConnectionProfileGroup[]>(Constants.connectionGroupsArrayName).user;
let sameNameGroup = groups ? groups.find(group => group.name === source.name && group.id !== source.id) : undefined;
if (sameNameGroup) {
let errMessage: string = nls.localize('invalidServerName', "A server group with the same name already exists.");
return Promise.reject(errMessage);
}
groups = groups.map(g => {
if (g.id === source.id) {
g.name = source.name;
g.description = source.description;
g.color = source.color;
source.isRenamed = false;
}
return g;
});
return this.writeConfiguration(Constants.connectionGroupsArrayName, groups);
}
private isSameGroupName(group1: IConnectionProfileGroup, group2: IConnectionProfileGroup): boolean {
let sameGroupName: boolean = false;
if (group1 && group2) {
sameGroupName = ((!group1.name && !group2.name) || group1.name.toUpperCase() === group2.name.toUpperCase()) &&
(group1.parentId === group2.parentId || (!group1.parentId && !group2.parentId));
}
return sameGroupName;
}
private saveGroupInTree(groupTree: IConnectionProfileGroup[], parentId: string, groupNames: string[], color: string, description: string, index: number): ISaveGroupResult {
if (!groupTree) {
groupTree = [];
}
let newGroupId: string;
if (index < groupNames.length) {
let groupName: string = groupNames[index];
let newGroup: IConnectionProfileGroup = {
name: groupName,
id: undefined,
parentId: parentId,
color: color,
description: description
};
let found = groupTree.find(group => this.isSameGroupName(group, newGroup));
if (found) {
if (index === groupNames.length - 1) {
newGroupId = found.id;
//Found the group full name
} else {
let result = this.saveGroupInTree(groupTree, found.id, groupNames, color, description, index + 1);
groupTree = result.groups;
newGroupId = result.newGroupId;
}
} else {
if (ConnectionProfileGroup.isRoot(newGroup.name)) {
newGroup.id = Utils.defaultGroupId;
} else {
newGroup.id = generateUuid();
}
let result = this.saveGroupInTree(groupTree, newGroup.id, groupNames, color, description, index + 1);
newGroupId = result.newGroupId;
groupTree = result.groups;
groupTree.push(newGroup);
if (index === groupNames.length - 1) {
newGroupId = newGroup.id;
}
}
}
let groupResult: ISaveGroupResult = {
groups: groupTree,
newGroupId: newGroupId
};
return groupResult;
}
/**
* Get all profiles from the parsed settings file.
* This is public for testing only.
* @param parsedSettingsFile an object representing the parsed contents of the settings file.
* @returns the set of connection profiles found in the parsed settings file.
*/
private getConfiguration(key: string): any {
let configs: any;
configs = this._workspaceConfigurationService.inspect<IConnectionProfileStore[] | IConnectionProfileGroup[] | sqlops.DataProtocolServerCapabilities[]>(key);
return configs;
}
/**
* Replace existing profiles in the settings file with a new set of profiles.
* @param parsedSettingsFile an object representing the parsed contents of the settings file.
* @param profiles the set of profiles to insert into the settings file.
*/
private writeConfiguration(
key: string,
profiles: IConnectionProfileStore[] | IConnectionProfileGroup[] | sqlops.DataProtocolServerCapabilities[],
target: ConfigurationTarget = ConfigurationTarget.USER): Promise<void> {
return new Promise<void>((resolve, reject) => {
let configValue: IConfigurationValue = {
key: key,
value: profiles
};
this._configurationEditService.writeConfiguration(target, configValue).then(result => {
this._workspaceConfigurationService.reloadConfiguration().then(() => {
resolve();
});
}, (error => {
reject(error);
}));
});
}
}

View File

@@ -1,36 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import Interfaces = require('./interfaces');
/**
* Sets sensible defaults for key connection properties, especially
* if connection to Azure
*
* @export connectionInfo/fixupConnectionCredentials
* @param {Interfaces.IConnectionCredentials} connCreds connection to be fixed up
* @returns {Interfaces.IConnectionCredentials} the updated connection
*/
export function fixupConnectionCredentials(connCreds: Interfaces.IConnectionProfile): Interfaces.IConnectionProfile {
if (!connCreds.serverName) {
connCreds.serverName = '';
}
if (!connCreds.databaseName) {
connCreds.databaseName = '';
}
if (!connCreds.userName) {
connCreds.userName = '';
}
if (!connCreds.password) {
connCreds.password = '';
}
return connCreds;
}

View File

@@ -1,368 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IViewlet } from 'vs/workbench/common/viewlet';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { TPromise } from 'vs/base/common/winjs.base';
import { Event } from 'vs/base/common/event';
import { IAction } from 'vs/base/common/actions';
import Severity from 'vs/base/common/severity';
import * as sqlops from 'sqlops';
import { IConnectionProfileGroup, ConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { ConnectionManagementInfo } from './connectionManagementInfo';
export const VIEWLET_ID = 'workbench.view.connections';
export interface IConnectionsViewlet extends IViewlet {
search(text: string): void;
}
/**
* Options for the actions that could happen after connecting is complete
*/
export interface IConnectionCompletionOptions {
/**
* save the connection to MRU and settings (only save to setting if profile.saveProfile is set to true)
*/
saveTheConnection: boolean;
/**
* open the dashboard after connection is complete
*/
showDashboard: boolean;
/**
* Parameters to be used if connecting from an editor
*/
params: INewConnectionParams;
/**
* Open the connection dialog if connection fails
*/
showConnectionDialogOnError: boolean;
/**
* Open the connection firewall rule dialog if connection fails
*/
showFirewallRuleOnError: boolean;
}
export interface IConnectionResult {
connected: boolean;
errorMessage: string;
errorCode: number;
callStack: string;
errorHandled?: boolean;
connectionProfile?: IConnectionProfile;
}
export interface IConnectionCallbacks {
onConnectStart(): void;
onConnectReject(error?: string): void;
onConnectSuccess(params?: INewConnectionParams): void;
onDisconnect(): void;
onConnectCanceled(): void;
}
export const SERVICE_ID = 'connectionManagementService';
export const IConnectionManagementService = createDecorator<IConnectionManagementService>(SERVICE_ID);
export interface IConnectionManagementService {
_serviceBrand: any;
// Event Emitters
onAddConnectionProfile: Event<IConnectionProfile>;
onDeleteConnectionProfile: Event<void>;
onConnect: Event<IConnectionParams>;
onDisconnect: Event<IConnectionParams>;
onConnectionChanged: Event<IConnectionParams>;
onLanguageFlavorChanged: Event<sqlops.DidChangeLanguageFlavorParams>;
/**
* Opens the connection dialog to create new connection
*/
showConnectionDialog(params?: INewConnectionParams, model?: IConnectionProfile, connectionResult?: IConnectionResult): Promise<void>;
/**
* Opens the add server group dialog
*/
showCreateServerGroupDialog(callbacks?: IServerGroupDialogCallbacks): Promise<void>;
/**
* Opens the edit server group dialog
*/
showEditServerGroupDialog(group: ConnectionProfileGroup): Promise<void>;
/**
* Load the password and opens a new connection
*/
connect(connection: IConnectionProfile, uri: string, options?: IConnectionCompletionOptions, callbacks?: IConnectionCallbacks): Promise<IConnectionResult>;
/**
* Opens a new connection and save the profile in settings
*/
connectAndSaveProfile(connection: IConnectionProfile, uri: string, options?: IConnectionCompletionOptions, callbacks?: IConnectionCallbacks): Promise<IConnectionResult>;
/**
* Finds existing connection for given profile and purpose is any exists.
* The purpose is connection by default
*/
findExistingConnection(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection'): ConnectionProfile;
/**
* If there's already a connection for given profile and purpose, returns the ownerUri for the connection
* otherwise tries to make a connection and returns the owner uri when connection is complete
* The purpose is connection by default
*/
connectIfNotConnected(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection', saveConnection?: boolean): Promise<string>;
/**
* Adds the successful connection to MRU and send the connection error back to the connection handler for failed connections
*/
onConnectionComplete(handle: number, connectionInfoSummary: sqlops.ConnectionInfoSummary): void;
onIntelliSenseCacheComplete(handle: number, connectionUri: string): void;
onConnectionChangedNotification(handle: number, changedConnInfo: sqlops.ChangedConnectionInfo);
getConnectionGroups(providers?: string[]): ConnectionProfileGroup[];
getRecentConnections(providers?: string[]): ConnectionProfile[];
clearRecentConnectionsList(): void;
clearRecentConnection(connectionProfile: IConnectionProfile): void;
getActiveConnections(providers?: string[]): ConnectionProfile[];
saveProfileGroup(profile: IConnectionProfileGroup): Promise<string>;
changeGroupIdForConnectionGroup(source: IConnectionProfileGroup, target: IConnectionProfileGroup): Promise<void>;
changeGroupIdForConnection(source: ConnectionProfile, targetGroupName: string): Promise<void>;
deleteConnection(connection: ConnectionProfile): Promise<boolean>;
deleteConnectionGroup(group: ConnectionProfileGroup): Promise<boolean>;
getAdvancedProperties(): sqlops.ConnectionOption[];
getConnectionUri(connectionProfile: IConnectionProfile): string;
getFormattedUri(uri: string, connectionProfile: IConnectionProfile): string;
getConnectionUriFromId(connectionId: string): string;
isConnected(fileUri: string): boolean;
/**
* Returns true if the connection profile is connected
*/
isProfileConnected(connectionProfile: IConnectionProfile): boolean;
/**
* Returns true if the connection profile is connecting
*/
isProfileConnecting(connectionProfile: IConnectionProfile): boolean;
isRecent(connectionProfile: ConnectionProfile): boolean;
isConnected(fileUri: string, connectionProfile?: ConnectionProfile): boolean;
disconnectEditor(owner: IConnectableInput, force?: boolean): Promise<boolean>;
disconnect(connection: IConnectionProfile): Promise<void>;
disconnect(ownerUri: string): Promise<void>;
addSavedPassword(connectionProfile: IConnectionProfile): Promise<IConnectionProfile>;
listDatabases(connectionUri: string): Thenable<sqlops.ListDatabasesResult>;
/**
* Register a connection provider
*/
registerProvider(providerId: string, provider: sqlops.ConnectionProvider): void;
editGroup(group: ConnectionProfileGroup): Promise<void>;
getConnectionProfile(fileUri: string): IConnectionProfile;
getConnectionInfo(fileUri: string): ConnectionManagementInfo;
/**
* Cancels the connection
*/
cancelConnection(connection: IConnectionProfile): Thenable<boolean>;
/**
* Changes the database for an active connection
*/
changeDatabase(connectionUri: string, databaseName: string): Thenable<boolean>;
/**
* Cancels the connection for the editor
*/
cancelEditorConnection(owner: IConnectableInput): Thenable<boolean>;
showDashboard(connection: IConnectionProfile): Thenable<boolean>;
closeDashboard(uri: string): void;
getProviderIdFromUri(ownerUri: string): string;
hasRegisteredServers(): boolean;
canChangeConnectionConfig(profile: IConnectionProfile, newGroupID: string): boolean;
getTabColorForUri(uri: string): string;
/**
* Sends a notification that the language flavor for a given URI has changed.
* For SQL, this would be the specific SQL implementation being used.
*
* @param {string} uri the URI of the resource whose language has changed
* @param {string} language the base language
* @param {string} flavor the specific language flavor that's been set
*
* @memberof IConnectionManagementService
*/
doChangeLanguageFlavor(uri: string, language: string, flavor: string): void;
/**
* Ensures that a default language flavor is set for a URI, if none has already been defined.
* @param {string} uri document identifier
* @memberof ConnectionManagementService
*/
ensureDefaultLanguageFlavor(uri: string): void;
/**
* Refresh the IntelliSense cache for the connection with the given URI
*/
rebuildIntelliSenseCache(uri: string): Thenable<void>;
/**
* Get a copy of the connection profile with its passwords removed
* @param {IConnectionProfile} profile The connection profile to remove passwords from
* @returns {IConnectionProfile} A copy of the connection profile with passwords removed
*/
removeConnectionProfileCredentials(profile: IConnectionProfile): IConnectionProfile;
/**
* Get the credentials for a connected connection profile, as they would appear in the options dictionary
* @param {string} profileId The id of the connection profile to get the password for
* @returns {{ [name: string]: string }} A dictionary containing the credentials as they would be included
* in the connection profile's options dictionary, or undefined if the profile is not connected
*/
getActiveConnectionCredentials(profileId: string): { [name: string]: string };
/**
* Get the connection string for the provided connection ID
*/
getConnectionString(connectionId: string, includePassword: boolean): Thenable<string>;
/**
* Serialize connection string with optional provider
*/
buildConnectionInfo(connectionString: string, provider?: string): Thenable<sqlops.ConnectionInfo>;
}
export const IConnectionDialogService = createDecorator<IConnectionDialogService>('connectionDialogService');
export interface IConnectionDialogService {
_serviceBrand: any;
/**
* Opens the connection dialog and returns the promise for successfully opening the dialog
* @param connectionManagementService
* @param params
* @param model
* @param connectionResult
*/
showDialog(connectionManagementService: IConnectionManagementService, params: INewConnectionParams, model: IConnectionProfile, connectionResult?: IConnectionResult): Thenable<void>;
/**
* Opens the connection dialog and returns the promise when connection is made
* or dialog is closed
* @param connectionManagementService
* @param params
* @param model
* @param connectionResult
*/
openDialogAndWait(connectionManagementService: IConnectionManagementService, params?: INewConnectionParams, model?: IConnectionProfile, connectionResult?: IConnectionResult): Thenable<IConnectionProfile>;
}
export interface IServerGroupDialogCallbacks {
onAddGroup(groupName: string): void;
onClose(): void;
}
export const IServerGroupController = createDecorator<IServerGroupController>('serverGroupController');
export interface IServerGroupController {
_serviceBrand: any;
showCreateGroupDialog(connectionManagementService: IConnectionManagementService, callbacks?: IServerGroupDialogCallbacks): TPromise<void>;
showEditGroupDialog(connectionManagementService: IConnectionManagementService, group: ConnectionProfileGroup): TPromise<void>;
}
export const IErrorMessageService = createDecorator<IErrorMessageService>('errorMessageService');
export interface IErrorMessageService {
_serviceBrand: any;
showDialog(severity: Severity, headerTitle: string, message: string, messageDetails?: string, actions?: IAction[]): void;
}
export enum RunQueryOnConnectionMode {
none = 0,
executeQuery = 1,
executeCurrentQuery = 2,
estimatedQueryPlan = 3,
actualQueryPlan = 4
}
export interface INewConnectionParams {
connectionType: ConnectionType;
input?: IConnectableInput;
runQueryOnCompletion?: RunQueryOnConnectionMode;
querySelection?: sqlops.ISelectionData;
showDashboard?: boolean;
providers?: string[];
}
export interface IConnectableInput {
uri: string;
onConnectStart(): void;
onConnectReject(error?: string): void;
onConnectSuccess(params?: INewConnectionParams): void;
onDisconnect(): void;
onConnectCanceled(): void;
}
export enum ConnectionType {
default = 0,
editor = 1
}
export enum MetadataType {
Table = 0,
View = 1,
SProc = 2,
Function = 3
}
export enum TaskStatus {
NotStarted = 0,
InProgress = 1,
Succeeded = 2,
SucceededWithWarning = 3,
Failed = 4,
Canceled = 5,
Canceling = 6
}
export interface IConnectionParams {
connectionUri: string;
connectionProfile: IConnectionProfile;
}

View File

@@ -1,73 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import * as sqlops from 'sqlops';
import { StopWatch } from 'vs/base/common/stopwatch';
/**
* Information for a document's connection. Exported for testing purposes.
*/
export class ConnectionManagementInfo {
/**
* Connection GUID returned from the service host
*/
public connectionId: string;
public providerId: string;
/**
* Credentials used to connect
*/
public connectionProfile: ConnectionProfile;
/**
* Callback for when a connection notification is received.
*/
public connectHandler: (result: boolean, errorMessage?: string, errorCode?: number, callStack?: string) => void;
/**
* Information about the SQL Server instance.
*/
//public serverInfo: ConnectionContracts.ServerInfo;
/**
* Timer for tracking extension connection time.
*/
public extensionTimer: StopWatch;
/**
* Timer for tracking service connection time.
*/
public serviceTimer: StopWatch;
/**
* Timer for tracking intelliSense activation time.
*/
public intelliSenseTimer: StopWatch;
/**
* Whether the connection is in the process of connecting.
*/
public connecting: boolean;
/**
* Whether the connection should be deleted after connection is complete.
*/
public deleted: boolean;
/**
* Information about the connected server.
*/
serverInfo: sqlops.ServerInfo;
/**
* Owner uri assigned to the connection
*/
public ownerUri: string;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,232 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ConnectionProfileGroup } from './connectionProfileGroup';
import * as sqlops from 'sqlops';
import { ProviderConnectionInfo } from 'sql/parts/connection/common/providerConnectionInfo';
import * as interfaces from 'sql/parts/connection/common/interfaces';
import { equalsIgnoreCase } from 'vs/base/common/strings';
import { generateUuid } from 'vs/base/common/uuid';
import * as objects from 'sql/base/common/objects';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { isString } from 'vs/base/common/types';
// Concrete implementation of the IConnectionProfile interface
/**
* A concrete implementation of an IConnectionProfile with support for profile creation and validation
*/
export class ConnectionProfile extends ProviderConnectionInfo implements interfaces.IConnectionProfile {
public parent: ConnectionProfileGroup = null;
private _id: string;
public savePassword: boolean;
private _groupName: string;
public groupId: string;
public saveProfile: boolean;
public isDisconnecting: boolean = false;
public constructor(
capabilitiesService: ICapabilitiesService,
model: string | sqlops.IConnectionProfile
) {
super(capabilitiesService, model);
if (model && !isString(model)) {
this.groupId = model.groupId;
this.groupFullName = model.groupFullName;
this.savePassword = model.savePassword;
this.saveProfile = model.saveProfile;
this._id = model.id;
this.azureTenantId = model.azureTenantId;
} else {
//Default for a new connection
this.savePassword = false;
this.saveProfile = true;
this._groupName = ConnectionProfile.RootGroupName;
this._id = generateUuid();
}
this.options['groupId'] = this.groupId;
this.options['databaseDisplayName'] = this.databaseName;
}
public matches(other: interfaces.IConnectionProfile): boolean {
return other
&& this.providerName === other.providerName
&& equalsIgnoreCase(this.serverName, other.serverName)
&& equalsIgnoreCase(this.databaseName, other.databaseName)
&& equalsIgnoreCase(this.userName, other.userName)
&& equalsIgnoreCase(this.options['databaseDisplayName'], other.options['databaseDisplayName'])
&& this.authenticationType === other.authenticationType
&& this.groupId === other.groupId;
}
public generateNewId() {
this._id = generateUuid();
}
public getParent(): ConnectionProfileGroup {
return this.parent;
}
public get id(): string {
if (!this._id) {
this._id = generateUuid();
}
return this._id;
}
public set id(value: string) {
this._id = value;
}
public get azureTenantId(): string {
return this.options['azureTenantId'];
}
public set azureTenantId(value: string) {
this.options['azureTenantId'] = value;
}
public get groupFullName(): string {
return this._groupName;
}
public set groupFullName(value: string) {
this._groupName = value;
}
public get isAddedToRootGroup(): boolean {
return (this._groupName === ConnectionProfile.RootGroupName);
}
public clone(): ConnectionProfile {
let instance = new ConnectionProfile(this.capabilitiesService, this);
return instance;
}
public cloneWithNewId(): ConnectionProfile {
let instance = this.clone();
instance.generateNewId();
return instance;
}
public cloneWithDatabase(databaseName: string): ConnectionProfile {
let instance = this.cloneWithNewId();
instance.databaseName = databaseName;
return instance;
}
public static readonly RootGroupName: string = '/';
public withoutPassword(): ConnectionProfile {
let clone = this.clone();
clone.password = '';
return clone;
}
/**
* Returns a key derived the connections options (providerName, authenticationType, serverName, databaseName, userName, groupid)
* This key uniquely identifies a connection in a group
* Example: "providerName:MSSQL|authenticationType:|databaseName:database|serverName:server3|userName:user|group:testid"
*/
public getOptionsKey(): string {
let id = super.getOptionsKey();
let databaseDisplayName: string = this.options['databaseDisplayName'];
if (databaseDisplayName) {
id += ProviderConnectionInfo.idSeparator + 'databaseDisplayName' + ProviderConnectionInfo.nameValueSeparator + databaseDisplayName;
}
return id + ProviderConnectionInfo.idSeparator + 'group' + ProviderConnectionInfo.nameValueSeparator + this.groupId;
}
/**
* Returns the unique id for the connection that doesn't include the group name
*/
public getConnectionInfoId(): string {
return super.getOptionsKey();
}
public toIConnectionProfile(): interfaces.IConnectionProfile {
let result: interfaces.IConnectionProfile = {
connectionName: this.connectionName,
serverName: this.serverName,
databaseName: this.databaseName,
authenticationType: this.authenticationType,
getOptionsKey: undefined,
matches: undefined,
groupId: this.groupId,
groupFullName: this.groupFullName,
password: this.password,
providerName: this.providerName,
savePassword: this.savePassword,
userName: this.userName,
options: this.options,
saveProfile: this.saveProfile,
id: this.id,
azureTenantId: this.azureTenantId
};
return result;
}
public toConnectionInfo(): sqlops.ConnectionInfo {
return {
options: this.options
};
}
public static fromIConnectionProfile(capabilitiesService: ICapabilitiesService, profile: sqlops.IConnectionProfile) {
if (profile) {
if (profile instanceof ConnectionProfile) {
return profile;
} else {
return new ConnectionProfile(capabilitiesService, profile);
}
}
return undefined;
}
public static createFromStoredProfile(profile: interfaces.IConnectionProfileStore, capabilitiesService: ICapabilitiesService): ConnectionProfile {
let connectionInfo = new ConnectionProfile(capabilitiesService, profile.providerName);
connectionInfo.options = profile.options;
// append group ID and original display name to build unique OE session ID
connectionInfo.options = objects.clone(profile.options);
connectionInfo.options['groupId'] = connectionInfo.groupId;
connectionInfo.options['databaseDisplayName'] = connectionInfo.databaseName;
connectionInfo.groupId = profile.groupId;
connectionInfo.providerName = profile.providerName;
connectionInfo.saveProfile = true;
connectionInfo.savePassword = profile.savePassword;
connectionInfo.id = profile.id || generateUuid();
return connectionInfo;
}
public static convertToProfileStore(
capabilitiesService: ICapabilitiesService,
connectionProfile: interfaces.IConnectionProfile): interfaces.IConnectionProfileStore {
if (connectionProfile) {
let connectionInfo = ConnectionProfile.fromIConnectionProfile(capabilitiesService, connectionProfile);
let profile: interfaces.IConnectionProfileStore = {
options: {},
groupId: connectionProfile.groupId,
providerName: connectionInfo.providerName,
savePassword: connectionInfo.savePassword,
id: connectionInfo.id
};
profile.options = connectionInfo.options;
return profile;
} else {
return undefined;
}
}
}

View File

@@ -1,219 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ConnectionProfile } from './connectionProfile';
export interface IConnectionProfileGroup {
id: string;
parentId: string;
name: string;
color: string;
description: string;
}
export class ConnectionProfileGroup implements IConnectionProfileGroup {
public children: ConnectionProfileGroup[];
public connections: ConnectionProfile[];
public parentId: string;
private _isRenamed: boolean;
public constructor(
public name: string,
public parent: ConnectionProfileGroup,
public id: string,
public color: string,
public description: string
) {
this.parentId = parent ? parent.id : undefined;
if (this.name === ConnectionProfileGroup.RootGroupName) {
this.name = '';
}
}
public static GroupNameSeparator: string = '/';
public static RootGroupName: string = 'ROOT';
public toObject(): IConnectionProfileGroup {
let subgroups = undefined;
if (this.children) {
subgroups = [];
this.children.forEach((group) => {
subgroups.push(group.toObject());
});
}
return Object.assign({}, { name: this.name, id: this.id, parentId: this.parentId, children: subgroups, color: this.color, description: this.description });
}
public get groupName(): string {
return this.name;
}
public get fullName(): string {
let fullName: string = (this.id === 'root') ? undefined : this.name;
if (this.parent) {
let parentFullName = this.parent.fullName;
if (parentFullName) {
fullName = parentFullName + ConnectionProfileGroup.GroupNameSeparator + this.name;
}
}
return fullName;
}
public get isRenamed(): boolean {
return this._isRenamed;
}
public set isRenamed(val: boolean) {
this._isRenamed = val;
}
public hasChildren(): boolean {
if ((this.children && this.children.length > 0) || (this.connections && this.connections.length > 0)) {
return true;
}
return false;
}
/**
* Returns true if all connections in the tree have valid options using the correct capabilities
*/
public get hasValidConnections(): boolean {
if (this.connections) {
let invalidConnections = this.connections.find(c => !c.isConnectionOptionsValid);
if (invalidConnections !== undefined) {
return false;
} else {
let childrenAreValid: boolean = true;
this.children.forEach(element => {
let isChildValid = element.hasValidConnections;
if (!isChildValid) {
childrenAreValid = false;
}
});
return childrenAreValid;
}
} else {
return true;
}
}
public getChildren(): any {
let allChildren = [];
if (this.connections) {
this.connections.forEach((conn) => {
allChildren.push(conn);
});
}
if (this.children) {
this.children.forEach((group) => {
allChildren.push(group);
});
}
return allChildren;
}
public equals(other: any): boolean {
if (!(other instanceof ConnectionProfileGroup)) {
return false;
}
return other.id === this.id;
}
public addConnections(connections: ConnectionProfile[]): void {
if (!this.connections) {
this.connections = [];
}
connections.forEach((conn) => {
this.connections = this.connections.filter((curConn) => { return curConn.id !== conn.id; });
conn.parent = this;
this.connections.push(conn);
});
}
public addGroups(groups: ConnectionProfileGroup[]): void {
if (!this.children) {
this.children = [];
}
groups.forEach((group) => {
this.children = this.children.filter((grp) => { return group.id !== grp.id; });
group.parent = this;
this.children.push(group);
});
}
public getParent(): ConnectionProfileGroup {
return this.parent;
}
public isAncestorOf(node: ConnectionProfileGroup | ConnectionProfile): boolean {
let isAncestor = false;
let currentNode = node;
while (currentNode) {
if (currentNode.parent && currentNode.parent.id === this.id) {
isAncestor = true;
break;
}
currentNode = currentNode.parent;
}
return isAncestor;
}
public static getGroupFullNameParts(groupFullName: string): string[] {
groupFullName = groupFullName ? groupFullName : '';
let groupNames: string[] = groupFullName.split(ConnectionProfileGroup.GroupNameSeparator);
groupNames = groupNames.filter(g => !!g);
if (groupNames.length === 0) {
groupNames.push('ROOT');
} else if (groupNames[0].toUpperCase() !== 'ROOT') {
groupNames.unshift('ROOT');
}
groupNames[0] = 'ROOT';
return groupNames;
}
public static isRoot(name: string): boolean {
return (!name || name.toUpperCase() === ConnectionProfileGroup.RootGroupName ||
name === ConnectionProfileGroup.GroupNameSeparator);
}
public static sameGroupName(name1: string, name2: string): boolean {
let sameGroupName: boolean =
(!name1 && !name2) ||
name1.toUpperCase() === name2.toUpperCase() ||
(ConnectionProfileGroup.isRoot(name1) && ConnectionProfileGroup.isRoot(name2));
return sameGroupName;
}
public static getConnectionsInGroup(group: ConnectionProfileGroup): ConnectionProfile[] {
let connections = [];
if (group.connections) {
group.connections.forEach((con) => connections.push(con));
}
if (group.children) {
group.children.forEach((subgroup) => {
connections = connections.concat(this.getConnectionsInGroup(subgroup));
});
}
return connections;
}
public static getSubgroups(group: ConnectionProfileGroup): ConnectionProfileGroup[] {
let subgroups = [];
if (group && group.children) {
group.children.forEach((grp) => subgroups.push(grp));
group.children.forEach((subgroup) => {
subgroups = subgroups.concat(this.getSubgroups(subgroup));
});
}
return subgroups;
}
}

View File

@@ -7,9 +7,9 @@ import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IObjectExplorerService } from 'sql/parts/objectExplorer/common/objectExplorerService';
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor';

View File

@@ -1,210 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ConnectionManagementInfo } from './connectionManagementInfo';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import { IConnectionProfile } from './interfaces';
import * as Utils from './utils';
import * as sqlops from 'sqlops';
import { StopWatch } from 'vs/base/common/stopwatch';
export class ConnectionStatusManager {
private _connections: { [id: string]: ConnectionManagementInfo };
private _providerCapabilitiesMap: { [providerName: string]: sqlops.DataProtocolServerCapabilities };
constructor( @ICapabilitiesService private _capabilitiesService: ICapabilitiesService) {
this._connections = {};
this._providerCapabilitiesMap = {};
}
public findConnection(uri: string): ConnectionManagementInfo {
if (uri in this._connections) {
return this._connections[uri];
} else {
return undefined;
}
}
public findConnectionByProfileId(profileId: string): ConnectionManagementInfo {
return Object.values(this._connections).find((connection: ConnectionManagementInfo) => connection.connectionProfile.id === profileId);
}
public findConnectionProfile(connectionProfile: IConnectionProfile): ConnectionManagementInfo {
let id = Utils.generateUri(connectionProfile);
return this.findConnection(id);
}
public hasConnection(id: string): Boolean {
return !!this.findConnection(id);
}
public deleteConnection(id: string): void {
let info = this.findConnection(id);
if (info) {
for (let key in this._connections) {
if (this._connections[key].connectionId === info.connectionId) {
if (this._connections[key].connecting) {
this._connections[key].deleted = true;
} else {
delete this._connections[key];
}
}
}
}
}
public getConnectionProfile(id: string): ConnectionProfile {
let connectionInfoForId = this.findConnection(id);
return connectionInfoForId ? connectionInfoForId.connectionProfile : undefined;
}
public addConnection(connection: IConnectionProfile, id: string): ConnectionManagementInfo {
// Always create a copy and save that in the list
let connectionProfile = new ConnectionProfile(this._capabilitiesService, connection);
let connectionInfo: ConnectionManagementInfo = new ConnectionManagementInfo();
connectionInfo.providerId = connection.providerName;
connectionInfo.extensionTimer = StopWatch.create();
connectionInfo.intelliSenseTimer = StopWatch.create();
connectionInfo.connectionProfile = connectionProfile;
connectionInfo.connecting = true;
this._connections[id] = connectionInfo;
connectionInfo.serviceTimer = StopWatch.create();
connectionInfo.ownerUri = id;
return connectionInfo;
}
/**
*
* @param uri Remove connection from list of active connections
*/
public removeConnection(uri: string) {
delete this._connections[uri];
}
/**
* Call after a connection is saved to settings. It's only for default url connections
* which their id is generated from connection options. The group id is used in the generated id.
* when the connection is stored, the group id get assigned to the profile and it can change the id
* So for those kind of connections, we need to add the new id and the connection
*/
public updateConnectionProfile(connection: IConnectionProfile, id: string): string {
let newId: string = id;
let connectionInfo: ConnectionManagementInfo = this._connections[id];
if (connectionInfo && connection) {
if (this.isDefaultTypeUri(id)) {
connectionInfo.connectionProfile.groupId = connection.groupId;
newId = Utils.generateUri(connection);
if (newId !== id) {
this.deleteConnection(id);
this._connections[newId] = connectionInfo;
}
}
connectionInfo.connectionProfile.id = connection.id;
}
return newId;
}
public onConnectionComplete(summary: sqlops.ConnectionInfoSummary): ConnectionManagementInfo {
let connection = this._connections[summary.ownerUri];
connection.serviceTimer.stop();
connection.connecting = false;
connection.connectionId = summary.connectionId;
connection.serverInfo = summary.serverInfo;
return connection;
}
/**
* Updates database name after connection is complete
* @param summary connection summary
*/
public updateDatabaseName(summary: sqlops.ConnectionInfoSummary): void {
let connection = this._connections[summary.ownerUri];
//Check if the existing connection database name is different the one in the summary
if (connection.connectionProfile.databaseName !== summary.connectionSummary.databaseName) {
//Add the ownerUri with database name to the map if not already exists
connection.connectionProfile.databaseName = summary.connectionSummary.databaseName;
let prefix = Utils.getUriPrefix(summary.ownerUri);
let ownerUriWithDbName = Utils.generateUriWithPrefix(connection.connectionProfile, prefix);
if (!(ownerUriWithDbName in this._connections)) {
this._connections[ownerUriWithDbName] = connection;
}
}
}
/**
* Tries to find an existing connection that's mapped with the given ownerUri
* The purpose for this method is to find the connection given the ownerUri and find the original uri assigned to it. most of the times should be the same.
* Only if the db name in the original uri is different when connection is complete, we need to use the original uri
* Returns the generated ownerUri for the connection profile if not existing connection found
* @param ownerUri connection owner uri to find an existing connection
* @param purpose purpose for the connection
*/
public getOriginalOwnerUri(ownerUri: string): string {
let ownerUriToReturn: string = ownerUri;
let connectionStatusInfo = this.findConnection(ownerUriToReturn);
if (connectionStatusInfo && connectionStatusInfo.ownerUri) {
//The ownerUri in the connection status is the one service knows about so use that
//To call the service for any operation
ownerUriToReturn = connectionStatusInfo.ownerUri;
}
return ownerUriToReturn;
}
public onConnectionChanged(changedConnInfo: sqlops.ChangedConnectionInfo): IConnectionProfile {
let connection = this._connections[changedConnInfo.connectionUri];
if (connection && connection.connectionProfile) {
connection.connectionProfile.serverName = changedConnInfo.connection.serverName;
connection.connectionProfile.databaseName = changedConnInfo.connection.databaseName;
connection.connectionProfile.userName = changedConnInfo.connection.userName;
return connection.connectionProfile;
}
return undefined;
}
public isConnected(id: string): boolean {
return (id in this._connections && this._connections[id].connectionId && !!this._connections[id].connectionId);
}
public isConnecting(id: string): boolean {
return (id in this._connections && this._connections[id].connecting);
}
public isDefaultTypeUri(uri: string): boolean {
return uri && uri.startsWith(Utils.uriPrefixes.default);
}
public getProviderIdFromUri(ownerUri: string): string {
let providerId: string = '';
let connection = this.findConnection(ownerUri);
if (connection) {
providerId = connection.connectionProfile.providerName;
}
if (!providerId && this.isDefaultTypeUri(ownerUri)) {
let optionsKey = ownerUri.replace(Utils.uriPrefixes.default, '');
providerId = ConnectionProfile.getProviderFromOptionsKey(optionsKey);
}
return providerId;
}
/**
* Get a list of the active connection profiles managed by the status manager
*/
public getActiveConnectionProfiles(providers?: string[]): ConnectionProfile[] {
let profiles = Object.values(this._connections).map((connectionInfo: ConnectionManagementInfo) => connectionInfo.connectionProfile);
// Remove duplicate profiles that may be listed multiple times under different URIs by filtering for profiles that don't have the same ID as an earlier profile in the list
profiles = profiles.filter((profile, index) => profiles.findIndex(otherProfile => otherProfile.id === profile.id) === index);
if (providers) {
profiles = profiles.filter(f => providers.includes(f.providerName));
}
return profiles;
}
}

View File

@@ -1,564 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as Constants from './constants';
import * as ConnInfo from './connectionInfo';
import { ConnectionProfile } from '../common/connectionProfile';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { ICredentialsService } from 'sql/services/credentials/credentialsService';
import { IConnectionConfig } from './iconnectionConfig';
import { ConnectionConfig } from './connectionConfig';
import { Memento, Scope as MementoScope } from 'vs/workbench/common/memento';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ConnectionProfileGroup, IConnectionProfileGroup } from './connectionProfileGroup';
import { ConfigurationEditingService } from 'vs/workbench/services/configuration/node/configurationEditingService';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { ConnectionOptionSpecialType } from 'sql/workbench/api/common/sqlExtHostTypes';
import * as sqlops from 'sqlops';
const MAX_CONNECTIONS_DEFAULT = 25;
/**
* Manages the connections list including saved profiles and the most recently used connections
*
* @export
* @class ConnectionStore
*/
export class ConnectionStore {
private _memento: any;
private _groupIdToFullNameMap: { [groupId: string]: string };
private _groupFullNameToIdMap: { [groupId: string]: string };
constructor(
private _storageService: IStorageService,
private _context: Memento,
private _configurationEditService: ConfigurationEditingService,
private _workspaceConfigurationService: IWorkspaceConfigurationService,
private _credentialService: ICredentialsService,
private _capabilitiesService: ICapabilitiesService,
private _connectionConfig?: IConnectionConfig
) {
if (_context) {
this._memento = this._context.getMemento(this._storageService, MementoScope.GLOBAL);
}
this._groupIdToFullNameMap = {};
this._groupFullNameToIdMap = {};
if (!this._connectionConfig) {
this._connectionConfig = new ConnectionConfig(this._configurationEditService,
this._workspaceConfigurationService, this._capabilitiesService);
}
}
public static get CRED_PREFIX(): string { return 'Microsoft.SqlTools'; }
public static get CRED_SEPARATOR(): string { return '|'; }
public static get CRED_ID_PREFIX(): string { return 'id:'; }
public static get CRED_ITEMTYPE_PREFIX(): string { return 'itemtype:'; }
public static get CRED_PROFILE_USER(): string { return 'Profile'; }
public formatCredentialIdForCred(connectionProfile: IConnectionProfile): string {
if (!connectionProfile) {
throw new Error('Missing Connection which is required');
}
let itemTypeString: string = ConnectionStore.CRED_PROFILE_USER;
return this.formatCredentialId(connectionProfile, itemTypeString);
}
/**
* Creates a formatted credential usable for uniquely identifying a SQL Connection.
* This string can be decoded but is not optimized for this.
* @static
* @param {IConnectionProfile} connectionProfile connection profile - require
* @param {string} itemType type of the item (MRU or Profile) - optional
* @returns {string} formatted string with server, DB and username
*/
public formatCredentialId(connectionProfile: IConnectionProfile, itemType?: string): string {
let connectionProfileInstance: ConnectionProfile = ConnectionProfile.fromIConnectionProfile(
this._capabilitiesService, connectionProfile);
if (!connectionProfileInstance.getConnectionInfoId()) {
throw new Error('Missing Id, which is required');
}
let cred: string[] = [ConnectionStore.CRED_PREFIX];
if (!itemType) {
itemType = ConnectionStore.CRED_PROFILE_USER;
}
ConnectionStore.pushIfNonEmpty(itemType, ConnectionStore.CRED_ITEMTYPE_PREFIX, cred);
ConnectionStore.pushIfNonEmpty(connectionProfileInstance.getConnectionInfoId(), ConnectionStore.CRED_ID_PREFIX, cred);
return cred.join(ConnectionStore.CRED_SEPARATOR);
}
private static pushIfNonEmpty(value: string, prefix: string, arr: string[]): void {
if (value) {
arr.push(prefix.concat(value));
}
}
/**
* Returns true if the password is required
* @param connection profile
*/
public isPasswordRequired(connection: IConnectionProfile): boolean {
if (connection) {
let connectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, connection);
return connectionProfile.isPasswordRequired();
} else {
return false;
}
}
public addSavedPassword(credentialsItem: IConnectionProfile): Promise<{ profile: IConnectionProfile, savedCred: boolean }> {
let self = this;
return new Promise<{ profile: IConnectionProfile, savedCred: boolean }>((resolve, reject) => {
if (credentialsItem.savePassword && this.isPasswordRequired(credentialsItem)
&& !credentialsItem.password) {
let credentialId = this.formatCredentialIdForCred(credentialsItem);
self._credentialService.readCredential(credentialId)
.then(savedCred => {
if (savedCred) {
credentialsItem.password = savedCred.password;
credentialsItem.options['password'] = savedCred.password;
}
resolve({ profile: credentialsItem, savedCred: !!savedCred });
},
reason => {
reject(reason);
});
} else {
// No need to look up the password
resolve({ profile: credentialsItem, savedCred: credentialsItem.savePassword });
}
});
}
/**
* Saves a connection profile to the user settings.
* Password values are stored to a separate credential store if the "savePassword" option is true
*
* @param {IConnectionProfile} profile the profile to save
* @param {forceWritePlaintextPassword} whether the plaintext password should be written to the settings file
* @returns {Promise<IConnectionProfile>} a Promise that returns the original profile, for help in chaining calls
*/
public saveProfile(profile: IConnectionProfile, forceWritePlaintextPassword?: boolean): Promise<IConnectionProfile> {
const self = this;
return new Promise<IConnectionProfile>((resolve, reject) => {
// Add the profile to the saved list, taking care to clear out the password field if necessary
let savedProfile: IConnectionProfile;
if (forceWritePlaintextPassword) {
savedProfile = profile;
} else {
savedProfile = this.getProfileWithoutPassword(profile);
}
self.saveProfileToConfig(savedProfile)
.then(savedConnectionProfile => {
profile.groupId = savedConnectionProfile.groupId;
profile.id = savedConnectionProfile.id;
// Only save if we successfully added the profile
return self.saveProfilePasswordIfNeeded(profile);
// And resolve / reject at the end of the process
}, err => {
reject(err);
}).then(resolved => {
// Add necessary default properties before returning
// this is needed to support immediate connections
ConnInfo.fixupConnectionCredentials(profile);
resolve(profile);
}, err => {
reject(err);
});
});
}
/**
* Saves a connection profile group to the user settings.
*
* @param {IConnectionProfileGroup} profile the profile group to save
* @returns {Promise<string>} a Promise that returns the id of connection group
*/
public saveProfileGroup(profile: IConnectionProfileGroup): Promise<string> {
const self = this;
return new Promise<string>((resolve, reject) => {
self._connectionConfig.addGroup(profile).then(groupId => {
resolve(groupId);
}).catch(error => {
reject(error);
});
});
}
private saveProfileToConfig(profile: IConnectionProfile): Promise<IConnectionProfile> {
const self = this;
return new Promise<IConnectionProfile>((resolve, reject) => {
if (profile.saveProfile) {
self._connectionConfig.addConnection(profile).then(savedProfile => {
resolve(savedProfile);
}).catch(error => {
reject(error);
});
} else {
resolve(profile);
}
});
}
/**
* Gets the list of recently used connections. These will not include the password - a separate call to
* {addSavedPassword} is needed to fill that before connecting
*
* @returns {sqlops.ConnectionInfo} the array of connections, empty if none are found
*/
public getRecentlyUsedConnections(providers?: string[]): ConnectionProfile[] {
let configValues: IConnectionProfile[] = this._memento[Constants.recentConnections];
if (!configValues) {
configValues = [];
}
configValues = configValues.filter(c => !!(c));
if (providers && providers.length > 0) {
configValues = configValues.filter(c => providers.includes(c.providerName));
}
return this.convertConfigValuesToConnectionProfiles(configValues);
}
private convertConfigValuesToConnectionProfiles(configValues: IConnectionProfile[]): ConnectionProfile[] {
return configValues.map(c => {
if (c) {
let connectionProfile = new ConnectionProfile(this._capabilitiesService, c);
if (connectionProfile.saveProfile) {
if (!connectionProfile.groupFullName && connectionProfile.groupId) {
connectionProfile.groupFullName = this.getGroupFullName(connectionProfile.groupId);
}
if (!connectionProfile.groupId && connectionProfile.groupFullName) {
connectionProfile.groupId = this.getGroupId(connectionProfile.groupFullName);
} else if (!connectionProfile.groupId && !connectionProfile.groupFullName) {
connectionProfile.groupId = this.getGroupId('');
}
}
return connectionProfile;
} else {
return undefined;
}
});
}
/**
* Gets the list of active connections. These will not include the password - a separate call to
* {addSavedPassword} is needed to fill that before connecting
*
* @returns {sqlops.ConnectionInfo} the array of connections, empty if none are found
*/
public getActiveConnections(): ConnectionProfile[] {
let configValues: IConnectionProfile[] = this._memento[Constants.activeConnections];
if (!configValues) {
configValues = [];
}
return this.convertConfigValuesToConnectionProfiles(configValues);
}
public getProfileWithoutPassword(conn: IConnectionProfile): ConnectionProfile {
if (conn) {
let savedConn: ConnectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, conn);
savedConn = savedConn.withoutPassword();
return savedConn;
} else {
return undefined;
}
}
/**
* Adds a connection to the active connections list.
* Connection is only added if there are no other connections with the same connection ID in the list.
* Password values are stored to a separate credential store if the "savePassword" option is true
*
* @param {IConnectionCredentials} conn the connection to add
* @returns {Promise<void>} a Promise that returns when the connection was saved
*/
public addActiveConnection(conn: IConnectionProfile, isConnectionToDefaultDb: boolean = false): Promise<void> {
if (this.getActiveConnections().some(existingConn => existingConn.id === conn.id)) {
return Promise.resolve(undefined);
} else {
return this.addConnectionToMemento(conn, Constants.activeConnections, undefined, conn.savePassword).then(() => {
let maxConnections = this.getMaxRecentConnectionsCount();
if (isConnectionToDefaultDb) {
conn.databaseName = '';
}
return this.addConnectionToMemento(conn, Constants.recentConnections, maxConnections);
});
}
}
public addConnectionToMemento(conn: IConnectionProfile, mementoKey: string, maxConnections?: number, savePassword?: boolean): Promise<void> {
const self = this;
return new Promise<void>((resolve, reject) => {
// Get all profiles
let configValues = self.getConnectionsFromMemento(mementoKey);
let configToSave = this.addToConnectionList(conn, configValues);
if (maxConnections) {
// Remove last element if needed
if (configToSave.length > maxConnections) {
configToSave = configToSave.slice(0, maxConnections);
}
}
self._memento[mementoKey] = configToSave;
if (savePassword) {
self.doSavePassword(conn).then(result => {
resolve(undefined);
});
} else {
resolve(undefined);
}
});
}
public removeConnectionToMemento(conn: IConnectionProfile, mementoKey: string): Promise<void> {
const self = this;
return new Promise<void>((resolve, reject) => {
// Get all profiles
let configValues = self.getConnectionsFromMemento(mementoKey);
let configToSave = this.removeFromConnectionList(conn, configValues);
self._memento[mementoKey] = configToSave;
resolve(undefined);
});
}
public getConnectionsFromMemento(mementoKey: string): ConnectionProfile[] {
let configValues: IConnectionProfile[] = this._memento[mementoKey];
if (!configValues) {
configValues = [];
}
return this.convertConfigValuesToConnectionProfiles(configValues);
}
private addToConnectionList(conn: IConnectionProfile, list: ConnectionProfile[]): IConnectionProfile[] {
let savedProfile: ConnectionProfile = this.getProfileWithoutPassword(conn);
// Remove the connection from the list if it already exists
list = list.filter(value => {
let equal = value && value.getConnectionInfoId() === savedProfile.getConnectionInfoId();
if (equal && savedProfile.saveProfile) {
equal = value.groupId === savedProfile.groupId ||
ConnectionProfileGroup.sameGroupName(value.groupFullName, savedProfile.groupFullName);
}
return !equal;
});
list.unshift(savedProfile);
let newList = list.map(c => {
let connectionProfile = c ? c.toIConnectionProfile() : undefined;
return connectionProfile;
});
return newList.filter(n => n !== undefined);
}
private removeFromConnectionList(conn: IConnectionProfile, list: ConnectionProfile[]): IConnectionProfile[] {
let savedProfile: ConnectionProfile = this.getProfileWithoutPassword(conn);
// Remove the connection from the list if it already exists
list = list.filter(value => {
let equal = value && value.getConnectionInfoId() === savedProfile.getConnectionInfoId();
if (equal && savedProfile.saveProfile) {
equal = value.groupId === savedProfile.groupId ||
ConnectionProfileGroup.sameGroupName(value.groupFullName, savedProfile.groupFullName);
}
return !equal;
});
let newList = list.map(c => {
let connectionProfile = c ? c.toIConnectionProfile() : undefined;
return connectionProfile;
});
return newList.filter(n => n !== undefined);
}
/**
* Clear all recently used connections from the MRU list.
*/
public clearRecentlyUsed(): void {
this._memento[Constants.recentConnections] = [];
}
public clearFromMemento(name: string): void {
this._memento[name] = [];
}
/**
* Clear all active connections from the MRU list.
*/
public clearActiveConnections(): void {
this._memento[Constants.activeConnections] = [];
}
/**
* Remove a connection profile from the active connections list.
*/
public removeActiveConnection(conn: IConnectionProfile): Promise<void> {
return this.removeConnectionToMemento(conn, Constants.activeConnections);
}
private saveProfilePasswordIfNeeded(profile: IConnectionProfile): Promise<boolean> {
if (!profile.savePassword) {
return Promise.resolve(true);
}
return this.doSavePassword(profile);
}
private doSavePassword(conn: IConnectionProfile): Promise<boolean> {
let self = this;
return new Promise<boolean>((resolve, reject) => {
if (conn.password) {
let credentialId = this.formatCredentialId(conn);
self._credentialService.saveCredential(credentialId, conn.password)
.then((result) => {
resolve(result);
}, reason => {
// Bubble up error if there was a problem executing the set command
reject(reason);
});
} else {
resolve(true);
}
});
}
public getConnectionProfileGroups(withoutConnections?: boolean, providers?: string[]): ConnectionProfileGroup[] {
let profilesInConfiguration: ConnectionProfile[];
if (!withoutConnections) {
profilesInConfiguration = this._connectionConfig.getConnections(true);
if (providers && providers.length > 0) {
profilesInConfiguration = profilesInConfiguration.filter(x => providers.includes(x.providerName));
}
}
let groups = this._connectionConfig.getAllGroups();
let connectionProfileGroups = this.convertToConnectionGroup(groups, profilesInConfiguration, undefined);
return connectionProfileGroups;
}
private convertToConnectionGroup(groups: IConnectionProfileGroup[], connections: ConnectionProfile[], parent: ConnectionProfileGroup = undefined): ConnectionProfileGroup[] {
let result: ConnectionProfileGroup[] = [];
let children = groups.filter(g => g.parentId === (parent ? parent.id : undefined));
if (children) {
children.map(group => {
let connectionGroup = new ConnectionProfileGroup(group.name, parent, group.id, group.color, group.description);
this.addGroupFullNameToMap(group.id, connectionGroup.fullName);
if (connections) {
let connectionsForGroup = connections.filter(conn => conn.groupId === connectionGroup.id);
var conns = [];
connectionsForGroup.forEach((conn) => {
conn.groupFullName = connectionGroup.fullName;
conns.push(conn);
});
connectionGroup.addConnections(conns);
}
let childrenGroups = this.convertToConnectionGroup(groups, connections, connectionGroup);
connectionGroup.addGroups(childrenGroups);
result.push(connectionGroup);
});
if (parent) {
parent.addGroups(result);
}
}
return result;
}
public getGroupFromId(groupId: string): IConnectionProfileGroup {
let groups = this._connectionConfig.getAllGroups();
return groups.find(group => group.id === groupId);
}
private getMaxRecentConnectionsCount(): number {
let config = this._workspaceConfigurationService.getValue(Constants.sqlConfigSectionName);
let maxConnections: number = config[Constants.configMaxRecentConnections];
if (typeof (maxConnections) !== 'number' || maxConnections <= 0) {
maxConnections = MAX_CONNECTIONS_DEFAULT;
}
return maxConnections;
}
public editGroup(group: ConnectionProfileGroup): Promise<any> {
const self = this;
return new Promise<string>((resolve, reject) => {
self._connectionConfig.editGroup(group).then(() => {
resolve(null);
}).catch(error => {
reject(error);
});
});
}
public deleteConnectionFromConfiguration(connection: ConnectionProfile): Promise<void> {
return this._connectionConfig.deleteConnection(connection);
}
public deleteGroupFromConfiguration(group: ConnectionProfileGroup): Promise<void> {
return this._connectionConfig.deleteGroup(group);
}
public changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise<void> {
return this._connectionConfig.changeGroupIdForConnectionGroup(source, target);
}
public canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean {
return this._connectionConfig.canChangeConnectionConfig(profile, newGroupID);
}
public changeGroupIdForConnection(source: ConnectionProfile, targetGroupId: string): Promise<void> {
return new Promise<void>((resolve, reject) => {
this._connectionConfig.changeGroupIdForConnection(source, targetGroupId).then(() => {
resolve();
}, (error => {
reject(error);
}));
});
}
private addGroupFullNameToMap(groupId: string, groupFullName: string): void {
if (groupId) {
this._groupIdToFullNameMap[groupId] = groupFullName;
}
if (groupFullName !== undefined) {
this._groupFullNameToIdMap[groupFullName.toUpperCase()] = groupId;
}
}
private getGroupFullName(groupId: string): string {
if (groupId in this._groupIdToFullNameMap) {
return this._groupIdToFullNameMap[groupId];
} else {
// Load the cache
this.getConnectionProfileGroups(true);
}
return this._groupIdToFullNameMap[groupId];
}
private getGroupId(groupFullName: string): string {
if (groupFullName === ConnectionProfileGroup.GroupNameSeparator) {
groupFullName = '';
}
let key = groupFullName.toUpperCase();
let result: string = '';
if (key in this._groupFullNameToIdMap) {
result = this._groupFullNameToIdMap[key];
} else {
// Load the cache
this.getConnectionProfileGroups(true);
result = this._groupFullNameToIdMap[key];
}
return result;
}
}

View File

@@ -1,37 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// constants
export const sqlConfigSectionName = 'sql';
export const outputChannelName = 'MSSQL';
export const connectionsArrayName = 'datasource.connections';
export const connectionGroupsArrayName = 'datasource.connectionGroups';
/**Unsaved connections Id */
export const unsavedGroupId = 'unsaved';
/* Memento constants */
export const activeConnections = 'ACTIVE_CONNECTIONS';
export const recentConnections = 'RECENT_CONNECTIONS';
export const capabilitiesOptions = 'OPTIONS_METADATA';
export const configMaxRecentConnections = 'maxRecentConnections';
export const mssqlProviderName = 'MSSQL';
export const anyProviderName = '*';
export const connectionProviderContextKey = 'connectionProvider';
export const applicationName = 'sqlops';
export const defaultEngine = 'defaultEngine';
export const passwordChars = '***************';
/* authentication types */
export const sqlLogin = 'SqlLogin';
export const integrated = 'Integrated';
export const azureMFA = 'AzureMFA';

View File

@@ -1,30 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IConnectionProfile } from './interfaces';
import { IConnectionProfileGroup, ConnectionProfileGroup } from './connectionProfileGroup';
import { ConnectionProfile } from './connectionProfile';
import * as sqlops from 'sqlops';
/**
* Interface for a configuration file that stores connection profiles.
*
* @export
* @interface IConnectionConfig
*/
export interface IConnectionConfig {
addConnection(profile: IConnectionProfile): Promise<IConnectionProfile>;
addGroup(profileGroup: IConnectionProfileGroup): Promise<string>;
getConnections(getWorkspaceConnections: boolean): ConnectionProfile[];
getAllGroups(): IConnectionProfileGroup[];
changeGroupIdForConnectionGroup(source: ConnectionProfileGroup, target: ConnectionProfileGroup): Promise<void>;
changeGroupIdForConnection(source: ConnectionProfile, targetGroupId: string): Promise<void>;
editGroup(group: ConnectionProfileGroup): Promise<void>;
deleteConnection(profile: ConnectionProfile): Promise<void>;
deleteGroup(group: ConnectionProfileGroup): Promise<void>;
canChangeConnectionConfig(profile: ConnectionProfile, newGroupID: string): boolean;
}

View File

@@ -1,21 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
export interface IConnectionProfile extends sqlops.IConnectionProfile {
getOptionsKey(): string;
matches(profile: sqlops.IConnectionProfile): boolean;
}
export interface IConnectionProfileStore {
options: {};
groupId: string;
providerName: string;
savePassword: boolean;
id: string;
}

View File

@@ -1,306 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Disposable } from 'vs/base/common/lifecycle';
import { isString } from 'vs/base/common/types';
import * as sqlops from 'sqlops';
import * as interfaces from 'sql/parts/connection/common/interfaces';
import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
import * as Constants from 'sql/parts/connection/common/constants';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/common/connectionProviderExtension';
export class ProviderConnectionInfo extends Disposable implements sqlops.ConnectionInfo {
options: { [name: string]: any } = {};
private _providerName: string;
protected _serverCapabilities: ConnectionProviderProperties;
private static readonly SqlAuthentication = 'SqlLogin';
public static readonly ProviderPropertyName = 'providerName';
public constructor(
protected capabilitiesService: ICapabilitiesService,
model: string | sqlops.IConnectionProfile
) {
super();
// we can't really do a whole lot if we don't have a provider
if (isString(model) || (model && model.providerName)) {
this.providerName = isString(model) ? model : model.providerName;
if (!isString(model)) {
if (model.options && this._serverCapabilities) {
this._serverCapabilities.connectionOptions.forEach(option => {
let value = model.options[option.name];
this.options[option.name] = value;
});
}
this.serverName = model.serverName;
this.authenticationType = model.authenticationType;
this.databaseName = model.databaseName;
this.password = model.password;
this.userName = model.userName;
this.connectionName = model.connectionName;
}
}
}
public get providerName(): string {
return this._providerName;
}
public set providerName(name: string) {
this._providerName = name;
if (!this._serverCapabilities) {
let capabilities = this.capabilitiesService.getCapabilities(this.providerName);
if (capabilities) {
this._serverCapabilities = capabilities.connection;
}
this._register(this.capabilitiesService.onCapabilitiesRegistered(e => {
if (e.connection.providerId === this.providerName) {
this._serverCapabilities = e.connection;
}
}));
}
}
public clone(): ProviderConnectionInfo {
let instance = new ProviderConnectionInfo(this.capabilitiesService, this.providerName);
instance.options = Object.assign({}, this.options);
return instance;
}
public get serverCapabilities(): ConnectionProviderProperties {
return this._serverCapabilities;
}
public get connectionName(): string {
return this.getSpecialTypeOptionValue(ConnectionOptionSpecialType.connectionName);
}
public get serverName(): string {
return this.getSpecialTypeOptionValue(ConnectionOptionSpecialType.serverName);
}
public get databaseName(): string {
return this.getSpecialTypeOptionValue(ConnectionOptionSpecialType.databaseName);
}
public get userName(): string {
return this.getSpecialTypeOptionValue(ConnectionOptionSpecialType.userName);
}
public get password(): string {
return this.getSpecialTypeOptionValue(ConnectionOptionSpecialType.password);
}
public get authenticationType(): string {
return this.getSpecialTypeOptionValue(ConnectionOptionSpecialType.authType);
}
public set connectionName(value: string) {
this.setSpecialTypeOptionName(ConnectionOptionSpecialType.connectionName, value);
}
public set serverName(value: string) {
this.setSpecialTypeOptionName(ConnectionOptionSpecialType.serverName, value);
}
public set databaseName(value: string) {
this.setSpecialTypeOptionName(ConnectionOptionSpecialType.databaseName, value);
}
public set userName(value: string) {
this.setSpecialTypeOptionName(ConnectionOptionSpecialType.userName, value);
}
public set password(value: string) {
this.setSpecialTypeOptionName(ConnectionOptionSpecialType.password, value);
}
public set authenticationType(value: string) {
this.setSpecialTypeOptionName(ConnectionOptionSpecialType.authType, value);
}
public getOptionValue(name: string): any {
return this.options[name];
}
public setOptionValue(name: string, value: any): void {
//TODO: validate
this.options[name] = value;
}
private getServerInfo() {
let databaseName = this.databaseName ? this.databaseName : '<default>';
let userName = this.userName ? this.userName : 'Windows Authentication';
return this.serverName + ', ' + databaseName + ' (' + userName + ')';
}
/**
* Returns the title of the connection
*/
public get title(): string {
let label = '';
if (this.connectionName) {
label = this.connectionName;
} else {
label = this.getServerInfo();
}
return label;
}
public get serverInfo(): string {
return this.getServerInfo();
}
/**
* Returns true if the capabilities and options are loaded correctly
*/
public get isConnectionOptionsValid(): boolean {
return this.serverCapabilities && this.title.indexOf('undefined') < 0;
}
public isPasswordRequired(): boolean {
let optionMetadata = this._serverCapabilities.connectionOptions.find(
option => option.specialValueType === ConnectionOptionSpecialType.password);
let isPasswordRequired: boolean = optionMetadata.isRequired;
if (this.providerName === Constants.mssqlProviderName) {
isPasswordRequired = this.authenticationType === ProviderConnectionInfo.SqlAuthentication && optionMetadata.isRequired;
}
return isPasswordRequired;
}
private getSpecialTypeOptionValue(type: string): string {
let name = this.getSpecialTypeOptionName(type);
if (name) {
return this.options[name];
}
return undefined;
}
/**
* Returns a key derived the connections options (providerName, authenticationType, serverName, databaseName, userName, groupid)
* This key uniquely identifies a connection in a group
* Example: "providerName:MSSQL|authenticationType:|databaseName:database|serverName:server3|userName:user|group:testid"
*/
public getOptionsKey(): string {
let idNames = [];
if (this._serverCapabilities) {
idNames = this._serverCapabilities.connectionOptions.map(o => {
if ((o.specialValueType || o.isIdentity)
&& o.specialValueType !== ConnectionOptionSpecialType.password
&& o.specialValueType !== ConnectionOptionSpecialType.connectionName) {
return o.name;
} else {
return undefined;
}
});
} else {
// This should never happen but just incase the serverCapabilities was not ready at this time
idNames = ['authenticationType', 'database', 'server', 'user'];
}
idNames = idNames.filter(x => x !== undefined);
//Sort to make sure using names in the same order every time otherwise the ids would be different
idNames.sort();
let idValues: string[] = [];
for (var index = 0; index < idNames.length; index++) {
let value = this.options[idNames[index]];
value = value ? value : '';
idValues.push(`${idNames[index]}${ProviderConnectionInfo.nameValueSeparator}${value}`);
}
return ProviderConnectionInfo.ProviderPropertyName + ProviderConnectionInfo.nameValueSeparator +
this.providerName + ProviderConnectionInfo.idSeparator + idValues.join(ProviderConnectionInfo.idSeparator);
}
public static getProviderFromOptionsKey(optionsKey: string) {
let providerId: string = '';
if (optionsKey) {
let ids: string[] = optionsKey.split(ProviderConnectionInfo.idSeparator);
ids.forEach(id => {
let idParts = id.split(ProviderConnectionInfo.nameValueSeparator);
if (idParts.length >= 2 && idParts[0] === ProviderConnectionInfo.ProviderPropertyName) {
providerId = idParts[1];
}
});
}
return providerId;
}
public getSpecialTypeOptionName(type: string): string {
if (this._serverCapabilities) {
let optionMetadata = this._serverCapabilities.connectionOptions.find(o => o.specialValueType === type);
return !!optionMetadata ? optionMetadata.name : undefined;
} else {
return type.toString();
}
}
public setSpecialTypeOptionName(type: string, value: string): void {
let name = this.getSpecialTypeOptionName(type);
if (!!name) {
this.options[name] = value;
}
}
public get authenticationTypeDisplayName(): string {
let optionMetadata = this._serverCapabilities.connectionOptions.find(o => o.specialValueType === ConnectionOptionSpecialType.authType);
let authType = this.authenticationType;
let displayName: string = authType;
if (optionMetadata && optionMetadata.categoryValues) {
optionMetadata.categoryValues.forEach(element => {
if (element.name === authType) {
displayName = element.displayName;
}
});
}
return displayName;
}
public getProviderOptions(): sqlops.ConnectionOption[] {
return this._serverCapabilities.connectionOptions;
}
public static get idSeparator(): string {
return '|';
}
public static get nameValueSeparator(): string {
return ':';
}
public get titleParts(): string[] {
let parts: string[] = [];
// Always put these three on top. TODO: maybe only for MSSQL?
parts.push(this.serverName);
parts.push(this.databaseName);
parts.push(this.authenticationTypeDisplayName);
this._serverCapabilities.connectionOptions.forEach(element => {
if (element.specialValueType !== ConnectionOptionSpecialType.serverName &&
element.specialValueType !== ConnectionOptionSpecialType.databaseName &&
element.specialValueType !== ConnectionOptionSpecialType.authType &&
element.specialValueType !== ConnectionOptionSpecialType.password &&
element.specialValueType !== ConnectionOptionSpecialType.connectionName &&
element.isIdentity && element.valueType === ServiceOptionType.string) {
let value = this.getOptionValue(element.name);
if (value) {
parts.push(value);
}
}
});
return parts;
}
}

View File

@@ -1,139 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IConnectionProfile } from './interfaces';
import { ConnectionProfile } from './connectionProfile';
import { ConnectionProfileGroup } from './connectionProfileGroup';
// CONSTANTS //////////////////////////////////////////////////////////////////////////////////////
const msInH = 3.6e6;
const msInM = 60000;
const msInS = 1000;
export const uriPrefixes = {
default: 'connection://',
connection: 'connection://',
dashboard: 'dashboard://',
insights: 'insights://'
};
// FUNCTIONS //////////////////////////////////////////////////////////////////////////////////////
export const defaultGroupId = 'C777F06B-202E-4480-B475-FA416154D458';
export const ConnectionUriBackupIdAttributeName = 'backupId';
export const ConnectionUriRestoreIdAttributeName = 'restoreId';
/**
* Takes a string in the format of HH:MM:SS.MS and returns a number representing the time in
* miliseconds
* @param value The string to convert to milliseconds
* @return False is returned if the string is an invalid format,
* the number of milliseconds in the time string is returned otherwise.
*/
export function parseTimeString(value: string): number | boolean {
if (!value) {
return false;
}
let tempVal = value.split('.');
if (tempVal.length === 1) {
// Ideally would handle more cleanly than this but for now handle case where ms not set
tempVal = [tempVal[0], '0'];
} else if (tempVal.length !== 2) {
return false;
}
let msString = tempVal[1];
let msStringEnd = msString.length < 3 ? msString.length : 3;
let ms = parseInt(tempVal[1].substring(0, msStringEnd), 10);
tempVal = tempVal[0].split(':');
if (tempVal.length !== 3) {
return false;
}
let h = parseInt(tempVal[0], 10);
let m = parseInt(tempVal[1], 10);
let s = parseInt(tempVal[2], 10);
return ms + (h * msInH) + (m * msInM) + (s * msInS);
}
/**
* Takes a number of milliseconds and converts it to a string like HH:MM:SS.fff
* @param value The number of milliseconds to convert to a timespan string
* @returns A properly formatted timespan string.
*/
export function parseNumAsTimeString(value: number, includeFraction: boolean = true): string {
let tempVal = value;
let h = Math.floor(tempVal / msInH);
tempVal %= msInH;
let m = Math.floor(tempVal / msInM);
tempVal %= msInM;
let s = Math.floor(tempVal / msInS);
tempVal %= msInS;
let hs = h < 10 ? '0' + h : '' + h;
let ms = m < 10 ? '0' + m : '' + m;
let ss = s < 10 ? '0' + s : '' + s;
let mss = tempVal < 10 ? '00' + tempVal : tempVal < 100 ? '0' + tempVal : '' + tempVal;
let rs = hs + ':' + ms + ':' + ss;
return tempVal > 0 && includeFraction ? rs + '.' + mss : rs;
}
export function generateUri(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection' | 'notebook'): string {
let prefix = purpose ? uriPrefixes[purpose] : uriPrefixes.default;
let uri = generateUriWithPrefix(connection, prefix);
return uri;
}
export function getUriPrefix(ownerUri: string): string {
let prefix: string = '';
if (ownerUri) {
let index = ownerUri.indexOf('://');
if (index > 0) {
prefix = ownerUri.substring(0, index + 3);
} else {
return uriPrefixes.default;
}
}
return prefix;
}
export function generateUriWithPrefix(connection: IConnectionProfile, prefix: string): string {
let id = connection.getOptionsKey();
let uri = prefix + (id ? id : connection.serverName + ':' + connection.databaseName);
return uri;
}
export function findProfileInGroup(og: IConnectionProfile, groups: ConnectionProfileGroup[]): ConnectionProfile {
for (let group of groups) {
for (let conn of group.connections) {
if (conn.id === og.id) {
return conn;
}
}
if (group.hasChildren()) {
let potentialReturn = findProfileInGroup(og, group.children);
if (potentialReturn) {
return potentialReturn;
}
}
}
return undefined;
}
export function isMaster(profile: IConnectionProfile): boolean {
return profile.providerName.toLowerCase() === 'mssql' && profile.databaseName.toLowerCase() === 'master';
}

View File

@@ -5,15 +5,15 @@
'use strict';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { IConnectionComponentCallbacks, IConnectionComponentController, IConnectionValidateResult } from 'sql/parts/connection/connectionDialog/connectionDialogService';
import { ConnectionWidget } from 'sql/parts/connection/connectionDialog/connectionWidget';
import { AdvancedPropertiesController } from 'sql/parts/connection/connectionDialog/advancedPropertiesController';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup';
import * as Constants from 'sql/parts/connection/common/constants';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import * as Constants from 'sql/platform/connection/common/constants';
import * as sqlops from 'sqlops';
import * as Utils from 'sql/parts/connection/common/utils';
import * as Utils from 'sql/platform/connection/common/utils';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ConnectionOptionSpecialType } from 'sql/workbench/api/common/sqlExtHostTypes';
import { ConnectionProviderProperties } from 'sql/workbench/parts/connection/common/connectionProviderExtension';

View File

@@ -6,20 +6,19 @@
'use strict';
import {
IConnectionDialogService, IConnectionManagementService, IErrorMessageService,
IConnectionDialogService, IConnectionManagementService,
ConnectionType, INewConnectionParams, IConnectionCompletionOptions, IConnectionResult
} from 'sql/parts/connection/common/connectionManagement';
} from 'sql/platform/connection/common/connectionManagement';
import { ConnectionDialogWidget, OnShowUIResponse } from 'sql/parts/connection/connectionDialog/connectionDialogWidget';
import { ConnectionController } from 'sql/parts/connection/connectionDialog/connectionController';
import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
import * as Constants from 'sql/parts/connection/common/constants';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import { localize } from 'vs/nls';
import * as Constants from 'sql/platform/connection/common/constants';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { entries } from 'sql/base/common/objects';
import * as sqlops from 'sqlops';
import { Deferred } from 'sql/base/common/promise';
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { TPromise } from 'vs/base/common/winjs.base';
@@ -28,12 +27,11 @@ import * as platform from 'vs/base/common/platform';
import Severity from 'vs/base/common/severity';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { Action, IAction } from 'vs/base/common/actions';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import * as types from 'vs/base/common/types';
import { trim } from 'vs/base/common/strings';
import { Deferred } from 'sql/base/common/promise';
import { localize } from 'vs/nls';
export interface IConnectionValidateResult {
isValid: boolean;
@@ -87,14 +85,14 @@ export class ConnectionDialogService implements IConnectionDialogService {
@IClipboardService private _clipboardService: IClipboardService,
@ICommandService private _commandService: ICommandService
) { }
/**
* Gets the default provider with the following actions
* 1. Checks if master provider(map) has data
* 2. If so, filters provider paramter against master map
* 3. Fetches the result array and extracts the first element
* 4. If none of the above data exists, returns 'MSSQL'
* @returns: Default provider as string
*/
/**
* Gets the default provider with the following actions
* 1. Checks if master provider(map) has data
* 2. If so, filters provider paramter against master map
* 3. Fetches the result array and extracts the first element
* 4. If none of the above data exists, returns 'MSSQL'
* @returns: Default provider as string
*/
private getDefaultProviderName(): string {
let defaultProvider: string;
if (this._providerNameToDisplayNameMap) {

View File

@@ -7,13 +7,13 @@ import 'vs/css!./media/connectionDialog';
import { Button } from 'sql/base/browser/ui/button/button';
import { attachModalDialogStyler, attachButtonStyler } from 'sql/common/theme/styler';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { Modal } from 'sql/base/browser/ui/modal/modal';
import { IConnectionManagementService, INewConnectionParams } from 'sql/parts/connection/common/connectionManagement';
import { IConnectionManagementService, INewConnectionParams } from 'sql/platform/connection/common/connectionManagement';
import * as DialogHelper from 'sql/base/browser/ui/modal/dialogHelper';
import { TreeCreationUtils } from 'sql/parts/objectExplorer/viewlet/treeCreationUtils';
import { TreeUpdateUtils } from 'sql/parts/objectExplorer/viewlet/treeUpdateUtils';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { TabbedPanel, PanelTabIdentifier } from 'sql/base/browser/ui/panel/panel';
import { RecentConnectionTreeController, RecentConnectionActionsProvider } from 'sql/parts/connection/connectionDialog/recentConnectionTreeController';
import { SavedConnectionTreeController } from 'sql/parts/connection/connectionDialog/savedConnectionTreeController';

View File

@@ -13,16 +13,16 @@ import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox';
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
import * as DialogHelper from 'sql/base/browser/ui/modal/dialogHelper';
import { IConnectionComponentCallbacks } from 'sql/parts/connection/connectionDialog/connectionDialogService';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { ConnectionOptionSpecialType } from 'sql/workbench/api/common/sqlExtHostTypes';
import * as Constants from 'sql/parts/connection/common/constants';
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/parts/connection/common/connectionProfileGroup';
import * as Constants from 'sql/platform/connection/common/constants';
import { ConnectionProfileGroup, IConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { Dropdown } from 'sql/base/browser/ui/editableDropdown/dropdown';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { ConnectionProfile } from '../common/connectionProfile';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import * as styler from 'sql/common/theme/styler';
import { IAccountManagementService } from 'sql/services/accountManagement/interfaces';
import { IAccountManagementService } from 'sql/platform/accountManagement/common/interfaces';
import * as sqlops from 'sqlops';

View File

@@ -12,12 +12,12 @@ import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ClearSingleRecentConnectionAction } from 'sql/parts/connection/common/connectionActions';
import { ContributableActionProvider } from 'vs/workbench/browser/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { IAction } from 'vs/base/common/actions';
import { Event, Emitter } from 'vs/base/common/event';
import mouse = require('vs/base/browser/mouseEvent');
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
export class RecentConnectionActionsProvider extends ContributableActionProvider {
private _onRecentConnectionRemoved = new Emitter<void>();
@@ -33,7 +33,7 @@ export class RecentConnectionActionsProvider extends ContributableActionProvider
private getRecentConnectionActions(tree: ITree, element: any): IAction[] {
let actions: IAction[] = [];
let clearSingleConnectionAction = this._instantiationService.createInstance(ClearSingleRecentConnectionAction, ClearSingleRecentConnectionAction.ID,
ClearSingleRecentConnectionAction.LABEL,<IConnectionProfile>element);
ClearSingleRecentConnectionAction.LABEL, <IConnectionProfile>element);
clearSingleConnectionAction.onRecentConnectionRemoved(() => this._onRecentConnectionRemoved.fire());
actions.push(clearSingleConnectionAction);
return actions;

View File

@@ -8,12 +8,12 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
import { IAngularEventingService, AngularEventType, IAngularEvent } from 'sql/services/angularEventing/angularEventingService';
import { IAngularEventingService, AngularEventType, IAngularEvent } from 'sql/platform/angularEventing/common/angularEventingService';
import { INewDashboardTabDialogService } from 'sql/parts/dashboard/newDashboardTabDialog/interface';
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
import { toDisposableSubscription } from 'sql/base/node/rxjsUtils';
export class EditDashboardAction extends Action {
private static readonly ID = 'editDashboard';

View File

@@ -11,7 +11,7 @@ import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/commo
import { error } from 'sql/base/common/log';
import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { WIDGETS_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.contribution';
import { GRID_CONTAINER } from 'sql/parts/dashboard/containers/dashboardGridContainer.contribution';
@@ -21,7 +21,7 @@ import { CONTROLHOST_CONTAINER } from 'sql/parts/dashboard/containers/dashboardC
import { NAV_SECTION } from 'sql/parts/dashboard/containers/dashboardNavSection.contribution';
import { IDashboardContainerRegistry, Extensions as DashboardContainerExtensions } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
import { SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
import * as Constants from 'sql/parts/connection/common/constants';
import * as Constants from 'sql/platform/connection/common/constants';
const dashboardcontainerRegistry = Registry.as<IDashboardContainerRegistry>(DashboardContainerExtensions.dashboardContainerContributions);
const containerTypes = [

View File

@@ -6,7 +6,7 @@
import 'vs/css!sql/parts/dashboard/common/dashboardPage';
import 'sql/parts/dashboard/common/dashboardPanelStyles';
import { Component, Inject, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef } from '@angular/core';
import { Component, Inject, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, ChangeDetectorRef } from '@angular/core';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { CommonServiceInterface, SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
@@ -16,13 +16,13 @@ import { PanelComponent } from 'sql/base/browser/ui/panel/panel.component';
import { IDashboardRegistry, Extensions as DashboardExtensions, IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
import { PinUnpinTabAction, AddFeatureTabAction } from './actions';
import { TabComponent, TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { AngularEventType, IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
import { AngularEventType, IAngularEventingService } from 'sql/platform/angularEventing/common/angularEventingService';
import { DashboardTab, IConfigModifierCollection } from 'sql/parts/dashboard/common/interfaces';
import * as dashboardHelper from 'sql/parts/dashboard/common/dashboardHelper';
import { WIDGETS_CONTAINER } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.contribution';
import { GRID_CONTAINER } from 'sql/parts/dashboard/containers/dashboardGridContainer.contribution';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import * as Constants from 'sql/parts/connection/common/constants';
import { AngularDisposable } from 'sql/base/node/lifecycle';
import * as Constants from 'sql/platform/connection/common/constants';
import { Registry } from 'vs/platform/registry/common/platform';
import * as types from 'vs/base/common/types';

View File

@@ -7,7 +7,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { localize } from 'vs/nls';
import * as types from 'vs/base/common/types';
import * as Constants from 'sql/parts/connection/common/constants';
import * as Constants from 'sql/platform/connection/common/constants';
import { registerTab } from 'sql/platform/dashboard/common/dashboardRegistry';
import { generateContainerTypeSchemaProperties } from 'sql/platform/dashboard/common/dashboardContainerRegistry';
import { NAV_SECTION, validateNavSectionContributionAndRegisterIcon } from 'sql/parts/dashboard/containers/dashboardNavSection.contribution';

View File

@@ -6,10 +6,8 @@
import { OnDestroy } from '@angular/core';
import { Event } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';

View File

@@ -5,22 +5,15 @@
import 'vs/css!./dashboardGridContainer';
import { Component, Inject, Input, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef, EventEmitter } from '@angular/core';
import { NgGridConfig, NgGrid, NgGridItem } from 'angular2-grid';
import { concat } from 'rxjs/operator/concat';
import { Component, Inject, Input, forwardRef, ElementRef, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef } from '@angular/core';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { TabConfig, WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
import { subscriptionToDisposable } from 'sql/base/common/lifecycle';
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
import { WebviewContent } from 'sql/parts/dashboard/contents/webviewContent.component';
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import * as objects from 'vs/base/common/objects';
import { Event, Emitter } from 'vs/base/common/event';
export interface GridCellConfig {

View File

@@ -8,11 +8,10 @@ import 'vs/css!./dashboardHomeContainer';
import { Component, forwardRef, Input, ChangeDetectorRef, Inject, ViewChild, ContentChild } from '@angular/core';
import { DashboardWidgetContainer } from 'sql/parts/dashboard/containers/dashboardWidgetContainer.component';
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { AngularEventType, IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
import { AngularEventType, IAngularEventingService } from 'sql/platform/angularEventing/common/angularEventingService';
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
import { ScrollableDirective } from 'sql/base/browser/ui/scrollable/scrollable.directive';
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';

View File

@@ -5,20 +5,13 @@
import 'vs/css!./dashboardWidgetContainer';
import { Component, Inject, Input, forwardRef, ViewChild, ViewChildren, QueryList, OnDestroy, ChangeDetectorRef, EventEmitter, OnChanges, AfterContentInit } from '@angular/core';
import { NgGridConfig, NgGrid, NgGridItem } from 'angular2-grid';
import { Component, Inject, Input, forwardRef, ViewChild, OnDestroy, ChangeDetectorRef, AfterContentInit } from '@angular/core';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { TabConfig, WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
import { subscriptionToDisposable } from 'sql/base/common/lifecycle';
import { DashboardTab } from 'sql/parts/dashboard/common/interfaces';
import { WidgetContent } from 'sql/parts/dashboard/contents/widgetContent.component';
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import * as objects from 'vs/base/common/objects';
import { Event, Emitter } from 'vs/base/common/event';
@Component({

View File

@@ -6,8 +6,8 @@ import 'vs/css!sql/media/icons/common-icons';
import 'vs/css!./dashboardWidgetWrapper';
import {
Component, Input, Inject, forwardRef, ComponentFactoryResolver, AfterContentInit, ViewChild,
ElementRef, OnInit, ChangeDetectorRef, OnDestroy, ReflectiveInjector, Injector, Type, ComponentRef
Component, Input, Inject, forwardRef, ComponentFactoryResolver, ViewChild,
ElementRef, OnInit, ChangeDetectorRef, ReflectiveInjector, Injector, Type, ComponentRef
} from '@angular/core';
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
@@ -15,7 +15,7 @@ import { WidgetConfig, WIDGET_CONFIG, IDashboardWidget } from 'sql/parts/dashboa
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { error } from 'sql/base/common/log';
import { RefreshWidgetAction, ToggleMoreWidgetAction, DeleteWidgetAction, CollapseWidgetAction } from 'sql/parts/dashboard/common/actions';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import { AngularDisposable } from 'sql/base/node/lifecycle';
/* Widgets */
import { PropertiesWidgetComponent } from 'sql/parts/dashboard/widgets/properties/propertiesWidget.component';
@@ -24,10 +24,8 @@ import { TasksWidget } from 'sql/parts/dashboard/widgets/tasks/tasksWidget.compo
import { InsightsWidget } from 'sql/parts/dashboard/widgets/insights/insightsWidget.component';
import { WebviewWidget } from 'sql/parts/dashboard/widgets/webview/webviewWidget.component';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import * as colors from 'vs/platform/theme/common/colorRegistry';
import * as themeColors from 'vs/workbench/common/theme';
@@ -36,7 +34,6 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { memoize } from 'vs/base/common/decorators';
import { generateUuid } from 'vs/base/common/uuid';
import { Emitter } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
const componentMap: { [x: string]: Type<IDashboardWidget> } = {

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./webviewContent';
import { Component, forwardRef, Input, OnInit, Inject, ChangeDetectorRef, ElementRef } from '@angular/core';
import { Component, forwardRef, Input, OnInit, Inject, ElementRef } from '@angular/core';
import { Event, Emitter } from 'vs/base/common/event';
import { Parts, IPartService } from 'vs/workbench/services/part/common/partService';
@@ -17,8 +17,8 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { WebviewElement } from 'vs/workbench/parts/webview/electron-browser/webviewElement';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { IDashboardWebview, IDashboardViewService } from 'sql/services/dashboard/common/dashboardViewService';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import { IDashboardWebview, IDashboardViewService } from 'sql/platform/dashboard/common/dashboardViewService';
import { AngularDisposable } from 'sql/base/node/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import * as sqlops from 'sqlops';

View File

@@ -12,9 +12,9 @@ import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboar
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardWidgetWrapper } from 'sql/parts/dashboard/contents/dashboardWidgetWrapper.component';
import { subscriptionToDisposable, AngularDisposable } from 'sql/base/common/lifecycle';
import { subscriptionToDisposable, AngularDisposable } from 'sql/base/node/lifecycle';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable } from 'vs/base/common/lifecycle';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import * as objects from 'vs/base/common/objects';
import { Event, Emitter } from 'vs/base/common/event';

View File

@@ -5,16 +5,15 @@
import 'vs/css!./dashboard';
import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild } from '@angular/core';
import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild } from '@angular/core';
import { Router } from '@angular/router';
import { DashboardServiceInterface } from './services/dashboardServiceInterface.service';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import * as Utils from 'sql/parts/connection/common/utils';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import * as Utils from 'sql/platform/connection/common/utils';
import { RefreshWidgetAction, EditDashboardAction } from 'sql/parts/dashboard/common/actions';
import { DashboardPage } from 'sql/parts/dashboard/common/dashboardPage.component';
import { AngularDisposable } from 'sql/base/common/lifecycle';
import { AngularDisposable } from 'sql/base/node/lifecycle';
import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IDisposable } from 'vs/base/common/lifecycle';

View File

@@ -11,7 +11,7 @@ import { FormsModule } from '@angular/forms';
import { NgGridModule } from 'angular2-grid';
import { ChartsModule } from 'ng2-charts/ng2-charts';
import CustomUrlSerializer from 'sql/common/urlSerializer';
import CustomUrlSerializer from 'sql/base/node/urlSerializer';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { Extensions as ComponentExtensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/bootstrap/bootstrapService';

View File

@@ -19,10 +19,10 @@ import { bootstrapAngular } from 'sql/services/bootstrap/bootstrapService';
import { IDashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams';
import { DASHBOARD_SELECTOR } from 'sql/parts/dashboard/dashboard.component';
import { ConnectionContextKey } from 'sql/parts/connection/common/connectionContextKey';
import { IDashboardService } from 'sql/services/dashboard/common/dashboardService';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { CancellationToken } from 'vs/base/common/cancellation';
export class DashboardEditor extends BaseEditor {

View File

@@ -10,8 +10,8 @@ import URI from 'vs/base/common/uri';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
export class DashboardInput extends EditorInput {

View File

@@ -7,7 +7,7 @@
import { INewDashboardTabDialogService } from 'sql/parts/dashboard/newDashboardTabDialog/interface';
import { NewDashboardTabDialog } from 'sql/parts/dashboard/newDashboardTabDialog/newDashboardTabDialog';
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
import { IAngularEventingService, AngularEventType } from 'sql/services/angularEventing/angularEventingService';
import { IAngularEventingService, AngularEventType } from 'sql/platform/angularEventing/common/angularEventingService';
import { IDashboardUITab } from 'sql/parts/dashboard/newDashboardTabDialog/newDashboardTabViewModel';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';

View File

@@ -11,13 +11,12 @@ import { IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
import { IAngularEventingService } from 'sql/platform/angularEventing/common/angularEventingService';
import * as colors from 'vs/platform/theme/common/colorRegistry';
import * as nls from 'vs/nls';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
export class DatabaseDashboardPage extends DashboardPage implements OnInit {

View File

@@ -11,13 +11,12 @@ import { IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces';
import { WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
import { IAngularEventingService } from 'sql/platform/angularEventing/common/angularEventingService';
import * as colors from 'vs/platform/theme/common/colorRegistry';
import * as nls from 'vs/nls';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
export class ServerDashboardPage extends DashboardPage implements OnInit {

View File

@@ -3,15 +3,14 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Injectable, forwardRef, Inject, OnDestroy } from '@angular/core';
import { Injectable, forwardRef, Inject } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { DashboardServiceInterface } from './dashboardServiceInterface.service';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { MenuItem, IBreadcrumbService } from 'sql/base/browser/ui/breadcrumb/interfaces';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as nls from 'vs/nls';
export enum BreadcrumbClass {
@@ -24,11 +23,9 @@ export class BreadcrumbService implements IBreadcrumbService {
public breadcrumbItem: Subject<MenuItem[]>;
private itemBreadcrums: MenuItem[];
private _currentPage: BreadcrumbClass;
private _bootstrap: DashboardServiceInterface;
constructor( @Inject(forwardRef(() => CommonServiceInterface)) private commonService: CommonServiceInterface) {
this._bootstrap = commonService as DashboardServiceInterface;
this._bootstrap.onUpdatePage(() => {
constructor( @Inject(forwardRef(() => CommonServiceInterface)) private commonService: DashboardServiceInterface) {
this.commonService.onUpdatePage(() => {
this.setBreadcrumbs(this._currentPage);
});
this.breadcrumbItem = new Subject<MenuItem[]>();
@@ -43,7 +40,7 @@ export class BreadcrumbService implements IBreadcrumbService {
private getBreadcrumbsLink(page: BreadcrumbClass): MenuItem[] {
this.itemBreadcrums = [];
let profile = this._bootstrap.connectionManagementService.connectionInfo.connectionProfile;
let profile = this.commonService.connectionManagementService.connectionInfo.connectionProfile;
this.itemBreadcrums.push({ label: nls.localize('homeCrumb', 'Home') });
switch (page) {
case BreadcrumbClass.DatabasePage:

View File

@@ -10,12 +10,12 @@ import { Router } from '@angular/router';
/* SQL imports */
import { IDashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams';
import { IBootstrapParams } from 'sql/services/bootstrap/bootstrapService';
import { IMetadataService } from 'sql/services/metadata/metadataService';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { IAdminService } from 'sql/parts/admin/common/adminService';
import { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
import { AngularEventType, IAngularEvent, IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
import { IMetadataService } from 'sql/platform/metadata/common/metadataService';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { IAdminService } from 'sql/workbench/services/admin/common/adminService';
import { IQueryManagementService } from 'sql/platform/query/common/queryManagement';
import { toDisposableSubscription } from 'sql/base/node/rxjsUtils';
import { AngularEventType, IAngularEvent, IAngularEventingService } from 'sql/platform/angularEventing/common/angularEventingService';
import { IDashboardTab } from 'sql/platform/dashboard/common/dashboardRegistry';
import { TabSettingConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';

View File

@@ -5,18 +5,20 @@
import { Router } from '@angular/router';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { MetadataType, IConnectionManagementService, IErrorMessageService } from 'sql/parts/connection/common/connectionManagement';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { MetadataType, IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { SingleConnectionManagementService } from 'sql/services/common/commonServiceInterface.service';
import {
NewQueryAction, ScriptSelectAction, EditDataAction, ScriptCreateAction, ScriptExecuteAction, ScriptAlterAction,
BackupAction, ManageActionContext, BaseActionContext, ManageAction, RestoreAction
} from 'sql/workbench/common/actions';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
import * as Constants from 'sql/parts/connection/common/constants';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
import * as Constants from 'sql/platform/connection/common/constants';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { IScriptingService } from 'sql/services/scripting/scriptingService';
import { IScriptingService } from 'sql/platform/scripting/common/scriptingService';
import { IAngularEventingService } from 'sql/platform/angularEventing/common/angularEventingService';
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
import { ObjectMetadata } from 'sqlops';
@@ -32,7 +34,6 @@ import { $ } from 'vs/base/browser/dom';
import { ExecuteCommandAction } from 'vs/platform/actions/common/actions';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { IAngularEventingService } from 'sql/services/angularEventing/angularEventingService';
export class ObjectMetadataWrapper implements ObjectMetadata {
public metadataType: MetadataType;

View File

@@ -11,12 +11,11 @@ import { Component, Inject, forwardRef, ChangeDetectorRef, OnInit, ViewChild, El
import { Router } from '@angular/router';
import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
import { toDisposableSubscription } from 'sql/base/node/rxjsUtils';
import { ExplorerFilter, ExplorerRenderer, ExplorerDataSource, ExplorerController, ObjectMetadataWrapper, ExplorerModel } from './explorerTree';
import { ConnectionProfile } from 'sql/parts/connection/common/connectionProfile';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { InputBox, IInputOptions } from 'vs/base/browser/ui/inputbox/inputBox';
import { attachInputBoxStyler, attachListStyler } from 'vs/platform/theme/common/styler';
@@ -28,7 +27,6 @@ import { IContextViewService, IContextMenuService } from 'vs/platform/contextvie
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IProgressService } from 'vs/platform/progress/common/progress';
import * as types from 'vs/base/common/types';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
@Component({

View File

@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
import { RunQueryOnConnectionMode, IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { RunQueryOnConnectionMode, IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { InsightActionContext } from 'sql/workbench/common/actions';
import { IObjectExplorerService } from 'sql/parts/objectExplorer/common/objectExplorerService';

View File

@@ -9,11 +9,10 @@ import {
import { Observable } from 'rxjs/Observable';
import { DashboardWidget, IDashboardWidget, WIDGET_CONFIG, WidgetConfig } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
import { InsightAction, InsightActionContext } from 'sql/workbench/common/actions';
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
import { toDisposableSubscription } from 'sql/base/node/rxjsUtils';
import { IInsightsConfig, IInsightsView } from './interfaces';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { insertValueRegex } from 'sql/parts/insights/common/interfaces';

View File

@@ -6,10 +6,9 @@
import { Component, Inject, forwardRef, ChangeDetectorRef, OnInit, ElementRef, ViewChild } from '@angular/core';
import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { ConnectionManagementInfo } from 'sql/parts/connection/common/connectionManagementInfo';
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
import { ConnectionManagementInfo } from 'sql/platform/connection/common/connectionManagementInfo';
import { toDisposableSubscription } from 'sql/base/node/rxjsUtils';
import { error } from 'sql/base/common/log';
import { IDashboardRegistry, Extensions as DashboardExtensions } from 'sql/platform/dashboard/common/dashboardRegistry';

View File

@@ -14,7 +14,7 @@ import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from '
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { TaskRegistry } from 'sql/platform/tasks/common/tasks';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { BaseActionContext } from 'sql/workbench/common/actions';
/* VS imports */

View File

@@ -13,7 +13,7 @@ import { memoize } from 'vs/base/common/decorators';
import { DashboardWidget, IDashboardWidget, WidgetConfig, WIDGET_CONFIG } from 'sql/parts/dashboard/common/dashboardWidget';
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
import { IDashboardWebview, IDashboardViewService } from 'sql/services/dashboard/common/dashboardViewService';
import { IDashboardWebview, IDashboardViewService } from 'sql/platform/dashboard/common/dashboardViewService';
import * as sqlops from 'sqlops';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';

View File

@@ -14,12 +14,12 @@ import { ModalFooterStyle } from 'sql/base/browser/ui/modal/modal';
import { CategoryView } from 'sql/base/browser/ui/modal/optionsDialog';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { attachButtonStyler, attachListBoxStyler, attachInputBoxStyler, attachSelectBoxStyler, attachCheckboxStyler } from 'sql/common/theme/styler';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import * as BackupConstants from 'sql/parts/disasterRecovery/backup/constants';
import { IBackupService, IBackupUiService, TaskExecutionMode } from 'sql/parts/disasterRecovery/backup/common/backupService';
import { IBackupService, IBackupUiService, TaskExecutionMode } from 'sql/platform/backup/common/backupService';
import FileValidationConstants = require('sql/parts/fileBrowser/common/fileValidationServiceConstants');
import { IFileBrowserDialogController } from 'sql/parts/fileBrowser/common/interfaces';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { IFileBrowserDialogController } from 'sql/platform/fileBrowser/common/interfaces';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { ScrollableSplitView } from 'sql/base/browser/ui/scrollableSplitview/scrollableSplitview';
import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox';

View File

@@ -4,11 +4,11 @@
*--------------------------------------------------------------------------------------------*/
import { Modal } from 'sql/base/browser/ui/modal/modal';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { BackupModule } from 'sql/parts/disasterRecovery/backup/backup.module';
import { BACKUP_SELECTOR } from 'sql/parts/disasterRecovery/backup/backup.component';
import { attachModalDialogStyler } from 'sql/common/theme/styler';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import * as TelemetryKeys from 'sql/common/telemetryKeys';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';

View File

@@ -1,67 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event } from 'vs/base/common/event';
import * as sqlops from 'sqlops';
import { IDashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
export enum TaskExecutionMode {
execute = 0,
script = 1,
executeAndScript = 2,
}
export const SERVICE_ID = 'backupService';
export const UI_SERVICE_ID = 'backupUiService';
export const IBackupUiService = createDecorator<IBackupUiService>(UI_SERVICE_ID);
export interface IBackupUiService {
_serviceBrand: any;
/**
* Show backup wizard
*/
showBackup(connection: IConnectionProfile): Promise<any>;
/**
* On show backup event
*/
onShowBackupEvent: Event<{ connection: IConnectionProfile, ownerUri: string }>;
/**
* Close backup wizard
*/
closeBackup();
/**
* After the backup dialog is rendered, run Modal methods to set focusable elements, etc.
*/
onShowBackupDialog();
}
export const IBackupService = createDecorator<IBackupService>(SERVICE_ID);
export interface IBackupService {
_serviceBrand: any;
getBackupConfigInfo(connectionUri: string): Thenable<sqlops.BackupConfigInfo>;
/**
* Backup a data source using the provided connection
*/
backup(connectionUri: string, backupInfo: { [key: string]: any }, taskExecutionMode: sqlops.TaskExecutionMode): Thenable<sqlops.BackupResponse>;
/**
* Register a disaster recovery provider
*/
registerProvider(providerId: string, provider: sqlops.BackupProvider): void;
}

View File

@@ -1,194 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import * as sqlops from 'sqlops';
import * as Constants from 'sql/common/constants';
import * as TelemetryKeys from 'sql/common/telemetryKeys';
import * as TelemetryUtils from 'sql/common/telemetryUtilities';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IBackupService, TaskExecutionMode, IBackupUiService } from 'sql/parts/disasterRecovery/backup/common/backupService';
import { BackupDialog } from 'sql/parts/disasterRecovery/backup/backupDialog';
import { OptionsDialog } from 'sql/base/browser/ui/modal/optionsDialog';
import { Event, Emitter } from 'vs/base/common/event';
import { IDashboardComponentParams } from 'sql/services/bootstrap/bootstrapParams';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { TPromise } from 'vs/base/common/winjs.base';
import * as ConnectionUtils from 'sql/parts/connection/common/utils';
import { ProviderConnectionInfo } from 'sql/parts/connection/common/providerConnectionInfo';
export class BackupService implements IBackupService {
public _serviceBrand: any;
private _providers: { [handle: string]: sqlops.BackupProvider; } = Object.create(null);
constructor(
@IConnectionManagementService private _connectionService: IConnectionManagementService,
@ITelemetryService private _telemetryService: ITelemetryService
) {
}
/**
* Get database metadata needed to populate backup UI
*/
public getBackupConfigInfo(connectionUri: string): Thenable<sqlops.BackupConfigInfo> {
let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri);
if (providerId) {
let provider = this._providers[providerId];
if (provider) {
return provider.getBackupConfigInfo(connectionUri);
}
}
return Promise.resolve(undefined);
}
/**
* Backup a data source using the provided connection
*/
public backup(connectionUri: string, backupInfo: { [key: string]: any }, taskExecutionMode: TaskExecutionMode): Thenable<sqlops.BackupResponse> {
return new Promise<sqlops.BackupResponse>((resolve, reject) => {
let providerResult = this.getProvider(connectionUri);
if (providerResult) {
TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.BackupCreated, { provider: providerResult.providerName });
providerResult.provider.backup(connectionUri, backupInfo, taskExecutionMode).then(result => {
resolve(result);
}, error => {
reject(error);
});
} else {
reject(Constants.InvalidProvider);
}
});
}
private getProvider(connectionUri: string): { provider: sqlops.BackupProvider, providerName: string } {
let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri);
if (providerId) {
return { provider: this._providers[providerId], providerName: providerId };
} else {
return undefined;
}
}
/**
* Register a disaster recovery provider
*/
public registerProvider(providerId: string, provider: sqlops.BackupProvider): void {
this._providers[providerId] = provider;
}
}
export class BackupUiService implements IBackupUiService {
public _serviceBrand: any;
private _backupDialogs: { [providerName: string]: BackupDialog | OptionsDialog } = {};
private _currentProvider: string;
private _optionValues: { [optionName: string]: any } = {};
private _connectionUri: string;
private static _connectionUniqueId: number = 0;
private _onShowBackupEvent: Emitter<{ connection: IConnectionProfile, ownerUri: string }>;
public get onShowBackupEvent(): Event<{ connection: IConnectionProfile, ownerUri: string }> { return this._onShowBackupEvent.event; }
constructor(
@IInstantiationService private _instantiationService: IInstantiationService,
@IPartService private _partService: IPartService,
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
@IBackupService private _disasterRecoveryService: IBackupService,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService
) {
this._onShowBackupEvent = new Emitter<{ connection: IConnectionProfile, ownerUri: string }>();
}
public showBackup(connection: IConnectionProfile): Promise<any> {
let self = this;
return new Promise<void>((resolve, reject) => {
self.showBackupDialog(connection).then(() => {
resolve(void 0);
}, error => {
reject();
});
});
}
private getOptions(provider: string): sqlops.ServiceOption[] {
let feature = this._capabilitiesService.getLegacyCapabilities(this._currentProvider).features.find(f => f.featureName === 'backup');
if (feature) {
return feature.optionsMetadata;
} else {
return undefined;
}
}
public showBackupDialog(connection: IConnectionProfile): TPromise<void> {
let self = this;
self._connectionUri = ConnectionUtils.generateUri(connection);
self._currentProvider = connection.providerName;
let backupDialog = self._backupDialogs[self._currentProvider];
if (!backupDialog) {
let backupOptions = this.getOptions(this._currentProvider);
if (backupOptions) {
backupDialog = self._instantiationService ? self._instantiationService.createInstance(
OptionsDialog, 'Backup database - ' + connection.serverName + ':' + connection.databaseName, 'BackupOptions', undefined) : undefined;
backupDialog.onOk(() => this.handleOptionDialogClosed());
}
else {
backupDialog = self._instantiationService ? self._instantiationService.createInstance(BackupDialog) : undefined;
}
backupDialog.render();
self._backupDialogs[self._currentProvider] = backupDialog;
}
let backupOptions = this.getOptions(this._currentProvider);
return new TPromise<void>((resolve) => {
let uri = this._connectionManagementService.getConnectionUri(connection)
+ ProviderConnectionInfo.idSeparator
+ ConnectionUtils.ConnectionUriBackupIdAttributeName
+ ProviderConnectionInfo.nameValueSeparator
+ BackupUiService._connectionUniqueId;
this._connectionUri = uri;
BackupUiService._connectionUniqueId++;
// Create connection if needed
if (!this._connectionManagementService.isConnected(uri)) {
this._connectionManagementService.connect(connection, uri).then(() => {
this._onShowBackupEvent.fire({ connection: connection, ownerUri: uri });
});
}
if (backupOptions) {
(backupDialog as OptionsDialog).open(backupOptions, self._optionValues);
} else {
(backupDialog as BackupDialog).open(connection);
}
resolve(void 0);
});
}
public onShowBackupDialog() {
let backupDialog = this._backupDialogs[this._currentProvider];
if (backupDialog) {
backupDialog.setFocusableElements();
}
}
public closeBackup() {
let self = this;
let backupDialog = self._backupDialogs[self._currentProvider];
if (backupDialog) {
backupDialog.close();
}
}
private handleOptionDialogClosed() {
this._disasterRecoveryService.backup(this._connectionUri, this._optionValues, TaskExecutionMode.executeAndScript);
}
}

View File

@@ -42,7 +42,7 @@ export const tripleDES = 'Triple DES';
export const serverCertificate = localize('backup.serverCertificate', "Server Certificate");
export const asymmetricKey = localize('backup.asymmetricKey', "Asymmetric Key");
export const fileFiltersSet: {label: string, filters: string[]}[] = [
export const fileFiltersSet: { label: string, filters: string[] }[] = [
{ label: localize('backup.filterBackupFiles', "Backup Files"), filters: ['*.bak', '*.trn', '*.log'] },
{ label: localize('backup.allFiles', "All Files"), filters: ['*'] }
];
];

View File

@@ -1,51 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { TPromise } from 'vs/base/common/winjs.base';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import * as sqlops from 'sqlops';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
export const SERVICE_ID = 'restoreService';
export const IRestoreService = createDecorator<IRestoreService>(SERVICE_ID);
export { TaskExecutionMode } from 'sql/parts/disasterRecovery/backup/common/backupService';
export interface IRestoreService {
_serviceBrand: any;
/**
* Register a disaster recovery provider
*/
registerProvider(providerId: string, provider: sqlops.RestoreProvider): void;
/**
* Restore a data source using a backup file or database
*/
restore(connectionUri: string, restoreInfo: sqlops.RestoreInfo): Thenable<sqlops.RestoreResponse>;
/**
* Gets restore plan to do the restore operation on a database
*/
getRestorePlan(connectionUri: string, restoreInfo: sqlops.RestoreInfo): Thenable<sqlops.RestorePlanResponse>;
/**
* Gets restore config Info
*/
getRestoreConfigInfo(connectionUri: string): Thenable<sqlops.RestoreConfigInfo>;
/**
* Cancel restore plan
*/
cancelRestorePlan(connectionUri: string, restoreInfo: sqlops.RestoreInfo): Thenable<boolean>;
}
export const IRestoreDialogController = createDecorator<IRestoreDialogController>('restoreDialogService');
export interface IRestoreDialogController {
_serviceBrand: any;
showDialog(connection: IConnectionProfile): TPromise<void>;
}

View File

@@ -1,358 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import * as sqlops from 'sqlops';
import { TPromise } from 'vs/base/common/winjs.base';
import * as Constants from 'sql/common/constants';
import * as TelemetryKeys from 'sql/common/telemetryKeys';
import * as TelemetryUtils from 'sql/common/telemetryUtilities';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import * as types from 'vs/base/common/types';
import { ICapabilitiesService } from 'sql/services/capabilities/capabilitiesService';
import { IRestoreService, IRestoreDialogController, TaskExecutionMode } from 'sql/parts/disasterRecovery/restore/common/restoreService';
import { OptionsDialog } from 'sql/base/browser/ui/modal/optionsDialog';
import { RestoreDialog } from 'sql/parts/disasterRecovery/restore/restoreDialog';
import * as ConnectionConstants from 'sql/parts/connection/common/constants';
import { MssqlRestoreInfo } from 'sql/parts/disasterRecovery/restore/mssqlRestoreInfo';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { ProviderConnectionInfo } from 'sql/parts/connection/common/providerConnectionInfo';
import * as Utils from 'sql/parts/connection/common/utils';
import { IObjectExplorerService } from 'sql/parts/objectExplorer/common/objectExplorerService';
import { ITaskService } from 'sql/parts/taskHistory/common/taskService';
import { TaskStatus, TaskNode } from 'sql/parts/taskHistory/common/taskNode';
export class RestoreService implements IRestoreService {
public _serviceBrand: any;
private _providers: { [handle: string]: sqlops.RestoreProvider; } = Object.create(null);
constructor(
@IConnectionManagementService private _connectionService: IConnectionManagementService,
@ITelemetryService private _telemetryService: ITelemetryService
) {
}
/**
* Gets restore config Info
*/
getRestoreConfigInfo(connectionUri: string): Thenable<sqlops.RestoreConfigInfo> {
return new Promise<sqlops.RestoreConfigInfo>((resolve, reject) => {
let providerResult = this.getProvider(connectionUri);
if (providerResult) {
providerResult.provider.getRestoreConfigInfo(connectionUri).then(result => {
resolve(result);
}, error => {
reject(error);
});
} else {
reject(Constants.InvalidProvider);
}
});
}
/**
* Restore a data source using a backup file or database
*/
restore(connectionUri: string, restoreInfo: sqlops.RestoreInfo): Thenable<sqlops.RestoreResponse> {
return new Promise<sqlops.RestoreResponse>((resolve, reject) => {
let providerResult = this.getProvider(connectionUri);
if (providerResult) {
TelemetryUtils.addTelemetry(this._telemetryService, TelemetryKeys.RestoreRequested, { provider: providerResult.providerName });
providerResult.provider.restore(connectionUri, restoreInfo).then(result => {
resolve(result);
}, error => {
reject(error);
});
} else {
reject(Constants.InvalidProvider);
}
});
}
private getProvider(connectionUri: string): { provider: sqlops.RestoreProvider, providerName: string } {
let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri);
if (providerId) {
return { provider: this._providers[providerId], providerName: providerId };
} else {
return undefined;
}
}
/**
* Gets restore plan to do the restore operation on a database
*/
getRestorePlan(connectionUri: string, restoreInfo: sqlops.RestoreInfo): Thenable<sqlops.RestorePlanResponse> {
return new Promise<sqlops.RestorePlanResponse>((resolve, reject) => {
let providerResult = this.getProvider(connectionUri);
if (providerResult) {
providerResult.provider.getRestorePlan(connectionUri, restoreInfo).then(result => {
resolve(result);
}, error => {
reject(error);
});
} else {
reject(Constants.InvalidProvider);
}
});
}
/**
* Cancels a restore plan
*/
cancelRestorePlan(connectionUri: string, restoreInfo: sqlops.RestoreInfo): Thenable<boolean> {
return new Promise<boolean>((resolve, reject) => {
let providerResult = this.getProvider(connectionUri);
if (providerResult) {
providerResult.provider.cancelRestorePlan(connectionUri, restoreInfo).then(result => {
resolve(result);
}, error => {
reject(error);
});
} else {
reject(Constants.InvalidProvider);
}
});
}
/**
* Register a disaster recovery provider
*/
public registerProvider(providerId: string, provider: sqlops.RestoreProvider): void {
this._providers[providerId] = provider;
}
}
export class RestoreDialogController implements IRestoreDialogController {
_serviceBrand: any;
private _restoreDialogs: { [provider: string]: RestoreDialog | OptionsDialog } = {};
private _currentProvider: string;
private _ownerUri: string;
private _sessionId: string;
private readonly _restoreFeature = 'Restore';
private readonly _restoreTaskName: string = 'Restore Database';
private readonly _restoreCompleted: string = 'Completed';
private _optionValues: { [optionName: string]: any } = {};
constructor(
@IInstantiationService private _instantiationService: IInstantiationService,
@IRestoreService private _restoreService: IRestoreService,
@IConnectionManagementService private _connectionService: IConnectionManagementService,
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
@ITaskService private _taskService: ITaskService
) {
}
private handleOnRestore(isScriptOnly: boolean = false): void {
let restoreOption = this.setRestoreOption();
if (isScriptOnly) {
restoreOption.taskExecutionMode = TaskExecutionMode.script;
} else {
restoreOption.taskExecutionMode = TaskExecutionMode.executeAndScript;
}
this._restoreService.restore(this._ownerUri, restoreOption).then(result => {
const self = this;
let connectionProfile = self._connectionService.getConnectionProfile(self._ownerUri);
let activeNode = self._objectExplorerService.getObjectExplorerNode(connectionProfile);
this._taskService.onTaskComplete(response => {
if (result.taskId === response.id && this.isSuccessfulRestore(response) && activeNode) {
self._objectExplorerService.refreshTreeNode(activeNode.getSession(), activeNode).then(result => {
self._objectExplorerService.getServerTreeView().refreshTree();
});
}
});
let restoreDialog = this._restoreDialogs[this._currentProvider];
restoreDialog.close();
});
}
private isSuccessfulRestore(response: TaskNode): boolean {
return (response.taskName === this._restoreTaskName &&
response.message === this._restoreCompleted &&
(response.status === TaskStatus.Succeeded ||
response.status === TaskStatus.SucceededWithWarning) &&
(response.taskExecutionMode === TaskExecutionMode.execute ||
response.taskExecutionMode === TaskExecutionMode.executeAndScript));
}
private handleMssqlOnValidateFile(overwriteTargetDatabase: boolean = false): void {
let restoreDialog = this._restoreDialogs[this._currentProvider] as RestoreDialog;
this._restoreService.getRestorePlan(this._ownerUri, this.setRestoreOption(overwriteTargetDatabase)).then(restorePlanResponse => {
this._sessionId = restorePlanResponse.sessionId;
if (restorePlanResponse.errorMessage) {
restoreDialog.onValidateResponseFail(restorePlanResponse.errorMessage);
} else {
restoreDialog.removeErrorMessage();
restoreDialog.viewModel.onRestorePlanResponse(restorePlanResponse);
}
if (restorePlanResponse.canRestore && !this.isEmptyBackupset()) {
restoreDialog.enableRestoreButton(true);
} else {
restoreDialog.enableRestoreButton(false);
}
}, error => {
restoreDialog.showError(error);
});
}
/**
* Temporary fix for bug #2506: Restore button not disabled when there's not backup set to restore
* Will remove this function once there is a fix in the service (bug #2572)
*/
private isEmptyBackupset(): boolean {
let restoreDialog = this._restoreDialogs[this._currentProvider] as RestoreDialog;
if (!types.isUndefinedOrNull(restoreDialog.viewModel.selectedBackupSets) && restoreDialog.viewModel.selectedBackupSets.length === 0) {
return true;
}
return false;
}
private getMssqlRestoreConfigInfo(): Promise<void> {
return new Promise<void>((resolve, reject) => {
let restoreDialog = this._restoreDialogs[this._currentProvider] as RestoreDialog;
this._restoreService.getRestoreConfigInfo(this._ownerUri).then(restoreConfigInfo => {
restoreDialog.viewModel.updateOptionWithConfigInfo(restoreConfigInfo.configInfo);
resolve();
}, error => {
restoreDialog.showError(error);
reject(error);
});
});
}
private setRestoreOption(overwriteTargetDatabase: boolean = false): sqlops.RestoreInfo {
let restoreInfo = undefined;
let providerId: string = this.getCurrentProviderId();
if (providerId === ConnectionConstants.mssqlProviderName) {
restoreInfo = new MssqlRestoreInfo();
if (this._sessionId) {
restoreInfo.sessionId = this._sessionId;
}
let restoreDialog = this._restoreDialogs[providerId] as RestoreDialog;
restoreInfo.backupFilePaths = restoreDialog.viewModel.filePath;
restoreInfo.readHeaderFromMedia = restoreDialog.viewModel.readHeaderFromMedia;
restoreInfo.selectedBackupSets = restoreDialog.viewModel.selectedBackupSets;
restoreInfo.sourceDatabaseName = restoreDialog.viewModel.sourceDatabaseName;
if (restoreDialog.viewModel.targetDatabaseName) {
restoreInfo.targetDatabaseName = restoreDialog.viewModel.targetDatabaseName;
}
restoreInfo.overwriteTargetDatabase = overwriteTargetDatabase;
// Set other restore options
restoreDialog.viewModel.getRestoreAdvancedOptions(restoreInfo.options);
} else {
restoreInfo = { options: this._optionValues };
}
return restoreInfo;
}
private getRestoreOption(): sqlops.ServiceOption[] {
let options: sqlops.ServiceOption[] = [];
let providerId: string = this.getCurrentProviderId();
let providerCapabilities = this._capabilitiesService.getLegacyCapabilities(providerId);
if (providerCapabilities) {
let restoreMetadataProvider = providerCapabilities.features.find(f => f.featureName === this._restoreFeature);
if (restoreMetadataProvider) {
options = restoreMetadataProvider.optionsMetadata;
}
}
return options;
}
private handleOnClose(): void {
this._connectionService.disconnect(this._ownerUri);
}
private handleOnCancel(): void {
let restoreInfo = new MssqlRestoreInfo();
restoreInfo.sessionId = this._sessionId;
this._restoreService.cancelRestorePlan(this._ownerUri, restoreInfo).then(() => {
this._connectionService.disconnect(this._ownerUri);
});
}
public showDialog(connection: IConnectionProfile): TPromise<void> {
return new TPromise<void>((resolve, reject) => {
let result: void;
this._ownerUri = this._connectionService.getConnectionUri(connection)
+ ProviderConnectionInfo.idSeparator
+ Utils.ConnectionUriRestoreIdAttributeName
+ ProviderConnectionInfo.nameValueSeparator
+ '0';
if (!this._connectionService.isConnected(this._ownerUri)) {
this._connectionService.connect(connection, this._ownerUri).then(connectionResult => {
this._sessionId = null;
this._currentProvider = this.getCurrentProviderId();
if (!this._restoreDialogs[this._currentProvider]) {
let newRestoreDialog: RestoreDialog | OptionsDialog = undefined;
if (this._currentProvider === ConnectionConstants.mssqlProviderName) {
let provider = this._currentProvider;
newRestoreDialog = this._instantiationService.createInstance(RestoreDialog, this.getRestoreOption());
newRestoreDialog.onCancel(() => this.handleOnCancel());
newRestoreDialog.onRestore((isScriptOnly) => this.handleOnRestore(isScriptOnly));
newRestoreDialog.onValidate((overwriteTargetDatabase) => this.handleMssqlOnValidateFile(overwriteTargetDatabase));
newRestoreDialog.onDatabaseListFocused(() => this.fetchDatabases(provider));
} else {
newRestoreDialog = this._instantiationService.createInstance(
OptionsDialog, 'Restore database - ' + connection.serverName + ':' + connection.databaseName, 'RestoreOptions', undefined);
newRestoreDialog.onOk(() => this.handleOnRestore());
}
newRestoreDialog.onCloseEvent(() => this.handleOnClose());
newRestoreDialog.render();
this._restoreDialogs[this._currentProvider] = newRestoreDialog;
}
if (this._currentProvider === ConnectionConstants.mssqlProviderName) {
let restoreDialog = this._restoreDialogs[this._currentProvider] as RestoreDialog;
restoreDialog.viewModel.resetRestoreOptions(connection.databaseName);
this.getMssqlRestoreConfigInfo().then(() => {
restoreDialog.open(connection.serverName, this._ownerUri);
restoreDialog.validateRestore();
}, restoreConfigError => {
reject(restoreConfigError);
});
} else {
let restoreDialog = this._restoreDialogs[this._currentProvider] as OptionsDialog;
restoreDialog.open(this.getRestoreOption(), this._optionValues);
}
resolve(result);
}, error => {
reject(error);
});
}
});
}
private getCurrentProviderId(): string {
return this._connectionService.getProviderIdFromUri(this._ownerUri);
}
private fetchDatabases(provider: string): void {
this._connectionService.listDatabases(this._ownerUri).then(result => {
if (result && result.databaseNames) {
(<RestoreDialog>this._restoreDialogs[provider]).databaseListOptions = result.databaseNames;
}
});
}
}

View File

@@ -1,98 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
export class MssqlRestoreInfo implements sqlops.RestoreInfo {
options: { [name: string]: any };
taskExecutionMode: sqlops.TaskExecutionMode;
public constructor() {
this.options = {};
}
public get sessionId(): string {
return this.options['sessionId'];
}
public set sessionId(value: string) {
this.options['sessionId'] = value;
}
public get backupFilePaths(): string {
return this.options['backupFilePaths'];
}
public set backupFilePaths(value: string) {
this.options['backupFilePaths'] = value;
}
public get targetDatabaseName(): string {
return this.options['targetDatabaseName'];
}
public set targetDatabaseName(value: string) {
this.options['targetDatabaseName'] = value;
}
public get sourceDatabaseName(): string {
return this.options['sourceDatabaseName'];
}
public set sourceDatabaseName(value: string) {
this.options['sourceDatabaseName'] = value;
}
public get relocateDbFiles(): boolean {
return this.options['relocateDbFiles'];
}
public set relocateDbFiles(value: boolean) {
this.options['relocateDbFiles'] = value;
}
public get dataFileFolder(): string {
return this.options['dataFileFolder'];
}
public set dataFileFolder(value: string) {
this.options['dataFileFolder'] = value;
}
public get logFileFolder(): string {
return this.options['logFileFolder'];
}
public set logFileFolder(value: string) {
this.options['logFileFolder'] = value;
}
public get selectedBackupSets(): string[] {
return this.options['selectedBackupSets'];
}
public set selectedBackupSets(value: string[]) {
this.options['selectedBackupSets'] = value;
}
public get readHeaderFromMedia(): boolean {
return this.options['readHeaderFromMedia'];
}
public set readHeaderFromMedia(value: boolean) {
this.options['readHeaderFromMedia'] = value;
}
public get overwriteTargetDatabase(): boolean {
return this.options['overwriteTargetDatabase'];
}
public set overwriteTargetDatabase(value: boolean) {
this.options['overwriteTargetDatabase'] = value;
}
}

View File

@@ -41,7 +41,7 @@ import * as FileValidationConstants from 'sql/parts/fileBrowser/common/fileValid
import { Dropdown } from 'sql/base/browser/ui/editableDropdown/dropdown';
import { TabbedPanel, PanelTabIdentifier } from 'sql/base/browser/ui/panel/panel';
import { ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes';
import { IFileBrowserDialogController } from 'sql/parts/fileBrowser/common/interfaces';
import { IFileBrowserDialogController } from 'sql/platform/fileBrowser/common/interfaces';
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
interface FileListElement {

View File

@@ -5,8 +5,8 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { EditorInput, EditorModel, ConfirmResult, EncodingMode } from 'vs/workbench/common/editor';
import { IConnectionManagementService, IConnectableInput, INewConnectionParams } from 'sql/parts/connection/common/connectionManagement';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { IConnectionManagementService, IConnectableInput, INewConnectionParams } from 'sql/platform/connection/common/connectionManagement';
import { IQueryModelService } from 'sql/platform/query/common/queryModel';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { EditSessionReadyParams, ISelectionData } from 'sqlops';

View File

@@ -25,7 +25,7 @@ import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { Action } from 'vs/base/common/actions';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { IQueryModelService } from 'sql/platform/query/common/queryModel';
import { IEditorDescriptorService } from 'sql/parts/query/editor/editorDescriptorService';
import {
RefreshTableAction, StopRefreshTableAction, ChangeMaxRowsAction, ChangeMaxRowsActionItem, ShowQueryPaneAction

View File

@@ -16,7 +16,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import * as types from 'vs/base/common/types';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { IQueryModelService } from 'sql/platform/query/common/queryModel';
import { bootstrapAngular } from 'sql/services/bootstrap/bootstrapService';
import { BareResultsGridInfo } from 'sql/parts/query/editor/queryResultsEditor';
import { IEditDataComponentParams } from 'sql/services/bootstrap/bootstrapParams';

View File

@@ -6,10 +6,10 @@
import { Action, IActionItem, IActionRunner } from 'vs/base/common/actions';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { IQueryModelService } from 'sql/platform/query/common/queryModel';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { EventEmitter } from 'sql/base/common/eventEmitter';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { EditDataEditor } from 'sql/parts/editData/editor/editDataEditor';
import nls = require('vs/nls');
import * as dom from 'vs/base/browser/dom';

View File

@@ -1,234 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import { IConnectionManagementService, IErrorMessageService } from 'sql/parts/connection/common/connectionManagement';
import { FileBrowserTree } from 'sql/parts/fileBrowser/common/fileBrowserTree';
import { FileNode } from 'sql/parts/fileBrowser/common/fileNode';
import { IFileBrowserService } from 'sql/parts/fileBrowser/common/interfaces';
import * as Constants from 'sql/common/constants';
import { Event, Emitter } from 'vs/base/common/event';
import Severity from 'vs/base/common/severity';
import { localize } from 'vs/nls';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import * as strings from 'vs/base/common/strings';
export class FileBrowserService implements IFileBrowserService {
public _serviceBrand: any;
private _providers: { [handle: string]: sqlops.FileBrowserProvider; } = Object.create(null);
private _onAddFileTree = new Emitter<FileBrowserTree>();
private _onExpandFolder = new Emitter<FileNode>();
private _onPathValidate = new Emitter<sqlops.FileBrowserValidatedParams>();
private _pathToFileNodeMap: { [path: string]: FileNode } = {};
private _expandResolveMap: { [key: string]: any } = {};
static fileNodeId: number = 0;
constructor( @IConnectionManagementService private _connectionService: IConnectionManagementService,
@IInstantiationService private _instantiationService: IInstantiationService,
@IErrorMessageService private _errorMessageService: IErrorMessageService) {
}
public registerProvider(providerId: string, provider: sqlops.FileBrowserProvider): void {
this._providers[providerId] = provider;
}
public get onAddFileTree(): Event<FileBrowserTree> {
return this._onAddFileTree.event;
}
public get onExpandFolder(): Event<FileNode> {
return this._onExpandFolder.event;
}
public get onPathValidate(): Event<sqlops.FileBrowserValidatedParams> {
return this._onPathValidate.event;
}
public openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Thenable<boolean> {
return new Promise<boolean>((resolve, reject) => {
let provider = this.getProvider(ownerUri);
if (provider) {
provider.openFileBrowser(ownerUri, expandPath, fileFilters, changeFilter).then(result => {
resolve(result);
}, error => {
reject(error);
});
} else {
reject(Constants.InvalidProvider);
}
});
}
public onFileBrowserOpened(handle: number, fileBrowserOpenedParams: sqlops.FileBrowserOpenedParams) {
if (fileBrowserOpenedParams.succeeded === true
&& fileBrowserOpenedParams.fileTree
&& fileBrowserOpenedParams.fileTree.rootNode
&& fileBrowserOpenedParams.fileTree.selectedNode
) {
var fileTree = this.convertFileTree(null, fileBrowserOpenedParams.fileTree.rootNode, fileBrowserOpenedParams.fileTree.selectedNode.fullPath, fileBrowserOpenedParams.ownerUri);
this._onAddFileTree.fire({ rootNode: fileTree.rootNode, selectedNode: fileTree.selectedNode, expandedNodes: fileTree.expandedNodes });
} else {
let genericErrorMessage = localize('fileBrowserErrorMessage', 'An error occured while loading the file browser.');
let errorDialogTitle = localize('fileBrowserErrorDialogTitle', 'File browser error');
let errorMessage = strings.isFalsyOrWhitespace(fileBrowserOpenedParams.message) ? genericErrorMessage : fileBrowserOpenedParams.message;
this._errorMessageService.showDialog(Severity.Error, errorDialogTitle, errorMessage);
}
}
public expandFolderNode(fileNode: FileNode): Thenable<FileNode[]> {
this._pathToFileNodeMap[fileNode.fullPath] = fileNode;
let self = this;
return new Promise<FileNode[]>((resolve, reject) => {
let provider = this.getProvider(fileNode.ownerUri);
if (provider) {
provider.expandFolderNode(fileNode.ownerUri, fileNode.fullPath).then(result => {
var mapKey = self.generateResolveMapKey(fileNode.ownerUri, fileNode.fullPath);
self._expandResolveMap[mapKey] = resolve;
}, error => {
reject(error);
});
} else {
reject(Constants.InvalidProvider);
}
});
}
public onFolderNodeExpanded(handle: number, fileBrowserExpandedParams: sqlops.FileBrowserExpandedParams) {
var mapKey = this.generateResolveMapKey(fileBrowserExpandedParams.ownerUri, fileBrowserExpandedParams.expandPath);
var expandResolve = this._expandResolveMap[mapKey];
if (expandResolve) {
if (fileBrowserExpandedParams.succeeded === true) {
// get the expanded folder node
var expandedNode = this._pathToFileNodeMap[fileBrowserExpandedParams.expandPath];
if (expandedNode) {
if (fileBrowserExpandedParams.children && fileBrowserExpandedParams.children.length > 0) {
expandedNode.children = this.convertChildren(expandedNode, fileBrowserExpandedParams.children, fileBrowserExpandedParams.ownerUri);
}
expandResolve(expandedNode.children ? expandedNode.children : []);
this._onExpandFolder.fire(expandedNode);
} else {
expandResolve([]);
}
} else {
expandResolve([]);
}
}
}
public validateFilePaths(ownerUri: string, serviceType: string, selectedFiles: string[]): Thenable<boolean> {
return new Promise<boolean>((resolve, reject) => {
let provider = this.getProvider(ownerUri);
if (provider) {
provider.validateFilePaths(ownerUri, serviceType, selectedFiles).then(result => {
resolve(result);
}, error => {
reject(error);
});
} else {
reject(Constants.InvalidProvider);
}
});
}
public onFilePathsValidated(handle: number, fileBrowserValidatedParams: sqlops.FileBrowserValidatedParams) {
this._onPathValidate.fire(fileBrowserValidatedParams);
}
public closeFileBrowser(ownerUri: string): Thenable<sqlops.FileBrowserCloseResponse> {
let provider = this.getProvider(ownerUri);
if (provider) {
return provider.closeFileBrowser(ownerUri);
}
return Promise.resolve(undefined);
}
private generateResolveMapKey(ownerUri: string, expandPath: string): string {
return ownerUri + ':' + expandPath;
}
private getProvider(connectionUri: string): sqlops.FileBrowserProvider {
let providerId: string = this._connectionService.getProviderIdFromUri(connectionUri);
if (providerId) {
return this._providers[providerId];
} else {
return undefined;
}
}
private convertFileTree(parentNode: FileNode, fileTreeNode: sqlops.FileTreeNode, expandPath: string, ownerUri: string): FileBrowserTree {
FileBrowserService.fileNodeId += 1;
var expandedNodes: FileNode[] = [];
var selectedNode: FileNode;
var fileNode = new FileNode(FileBrowserService.fileNodeId.toString(),
fileTreeNode.name,
fileTreeNode.fullPath,
fileTreeNode.isFile,
fileTreeNode.isExpanded,
ownerUri,
parentNode
);
if (fileNode.isExpanded === true) {
expandedNodes.push(fileNode);
}
if (fileTreeNode.children) {
var convertedChildren = [];
for (var i = 0; i < fileTreeNode.children.length; i++) {
var convertedFileTree: FileBrowserTree = this.convertFileTree(fileNode, fileTreeNode.children[i], expandPath, ownerUri);
convertedChildren.push(convertedFileTree.rootNode);
if (convertedFileTree.expandedNodes.length > 0) {
expandedNodes = expandedNodes.concat(convertedFileTree.expandedNodes);
}
if (convertedFileTree.selectedNode) {
selectedNode = convertedFileTree.selectedNode;
}
}
if (convertedChildren.length > 0) {
fileNode.children = convertedChildren;
}
}
if (!selectedNode && fileTreeNode.fullPath === expandPath) {
selectedNode = fileNode;
}
// Assume every folder has children initially
if (fileTreeNode.isFile === false) {
fileNode.hasChildren = true;
}
return { rootNode: fileNode, selectedNode: selectedNode, expandedNodes: expandedNodes };
}
private convertChildren(expandedNode: FileNode, childrenToConvert: sqlops.FileTreeNode[], ownerUri: string): FileNode[] {
var childrenNodes = [];
for (var i = 0; i < childrenToConvert.length; i++) {
FileBrowserService.fileNodeId += 1;
var childNode = new FileNode(FileBrowserService.fileNodeId.toString(),
childrenToConvert[i].name,
childrenToConvert[i].fullPath,
childrenToConvert[i].isFile,
childrenToConvert[i].isExpanded,
ownerUri,
expandedNode
);
// Assume every folder has children initially
if (childrenToConvert[i].isFile === false) {
childNode.hasChildren = true;
}
childrenNodes.push(childNode);
}
return childrenNodes;
}
}

View File

@@ -8,4 +8,4 @@
*/
export const backup: string = 'Backup';
export const restore: string = 'Restore';
export const restore: string = 'Restore';

View File

@@ -1,74 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as sqlops from 'sqlops';
import { FileBrowserTree } from 'sql/parts/fileBrowser/common/fileBrowserTree';
import { FileNode } from 'sql/parts/fileBrowser/common/fileNode';
import { Event } from 'vs/base/common/event';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const IFileBrowserDialogController = createDecorator<IFileBrowserDialogController>('fileBrowserDialogService');
export interface IFileBrowserDialogController {
_serviceBrand: any;
/**
* Show file browser dialog
*/
showDialog(ownerUri: string,
expandPath: string,
fileFilters: { label: string, filters: string[] }[],
fileValidationServiceType: string,
isWide: boolean,
handleOnOk: (path: string) => void): void;
}
export const IFileBrowserService = createDecorator<IFileBrowserService>('fileBrowserService');
export interface IFileBrowserService {
_serviceBrand: any;
onAddFileTree: Event<FileBrowserTree>;
onExpandFolder: Event<FileNode>;
onPathValidate: Event<sqlops.FileBrowserValidatedParams>;
/**
* Register file browser provider
*/
registerProvider(providerId: string, provider: sqlops.FileBrowserProvider): void;
/**
* Open file browser
*/
openFileBrowser(ownerUri: string, expandPath: string, fileFilters: string[], changeFilter: boolean): Thenable<boolean>;
/**
* Event called when file browser is opened
*/
onFileBrowserOpened(handle: number, fileBrowserOpenedParams: sqlops.FileBrowserOpenedParams);
/**
* Expand folder node
*/
expandFolderNode(fileNode: FileNode): Thenable<FileNode[]>;
/**
* Event called when children nodes are retrieved
*/
onFolderNodeExpanded(handle: number, fileBrowserExpandedParams: sqlops.FileBrowserExpandedParams);
/**
* Validate selected file paths
*/
validateFilePaths(ownerUri: string, serviceType: string, selectedFiles: string[]): Thenable<boolean>;
/**
* Event called when the validation is complete
*/
onFilePathsValidated(handle: number, fileBrowserValidatedParams: sqlops.FileBrowserValidatedParams);
/**
* Close file browser
*/
closeFileBrowser(ownerUri: string): Thenable<sqlops.FileBrowserCloseResponse>;
}

View File

@@ -5,7 +5,7 @@
'use strict';
import { IFileBrowserService } from 'sql/parts/fileBrowser/common/interfaces';
import { IFileBrowserService } from 'sql/platform/fileBrowser/common/interfaces';
import { FileNode } from 'sql/parts/fileBrowser/common/fileNode';
import { TPromise } from 'vs/base/common/winjs.base';
import { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree';

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IFileBrowserDialogController } from 'sql/parts/fileBrowser/common/interfaces';
import { IFileBrowserDialogController } from 'sql/platform/fileBrowser/common/interfaces';
import { FileBrowserDialog } from 'sql/parts/fileBrowser/fileBrowserDialog';
import { localize } from 'vs/nls';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -23,7 +23,7 @@ export class FileBrowserDialogController implements IFileBrowserDialogController
public showDialog(ownerUri: string,
expandPath: string,
fileFilters: [{label: string, filters: string[]}],
fileFilters: [{ label: string, filters: string[] }],
fileValidationServiceType: string,
isWide: boolean,
handleOnOk: (path: string) => void
@@ -35,6 +35,6 @@ export class FileBrowserDialogController implements IFileBrowserDialogController
this._fileBrowserDialog.setWide(isWide);
this._fileBrowserDialog.onOk((filepath) => handleOnOk(filepath));
this._fileBrowserDialog.open(ownerUri, expandPath, fileFilters, fileValidationServiceType);
this._fileBrowserDialog.open(ownerUri, expandPath, fileFilters, fileValidationServiceType);
}
}

View File

@@ -3,17 +3,16 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IErrorMessageService } from 'sql/parts/connection/common/connectionManagement';
import { FileBrowserDataSource } from 'sql/parts/fileBrowser/fileBrowserDataSource';
import { FileBrowserController } from 'sql/parts/fileBrowser/fileBrowserController';
import { FileBrowserRenderer } from 'sql/parts/fileBrowser/fileBrowserRenderer';
import { IFileBrowserService } from 'sql/parts/fileBrowser/common/interfaces';
import { IFileBrowserService } from 'sql/platform/fileBrowser/common/interfaces';
import { FileNode } from 'sql/parts/fileBrowser/common/fileNode';
import errors = require('vs/base/common/errors');
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as DOM from 'vs/base/browser/dom';
import nls = require('vs/nls');
import { DefaultFilter, DefaultAccessibilityProvider, DefaultController, DefaultDragAndDrop } from 'vs/base/parts/tree/browser/treeDefaults';
import { DefaultFilter, DefaultAccessibilityProvider, DefaultDragAndDrop } from 'vs/base/parts/tree/browser/treeDefaults';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { attachListStyler } from 'vs/platform/theme/common/styler';
@@ -30,7 +29,6 @@ export class FileBrowserTreeView implements IDisposable {
constructor(
@IInstantiationService private _instantiationService: IInstantiationService,
@IFileBrowserService private _fileBrowserService: IFileBrowserService,
@IErrorMessageService private _errorMessageService: IErrorMessageService,
@IThemeService private _themeService: IThemeService
) {
}

View File

@@ -5,7 +5,7 @@
'use strict';
import { IFileBrowserService } from 'sql/parts/fileBrowser/common/interfaces';
import { IFileBrowserService } from 'sql/platform/fileBrowser/common/interfaces';
import { localize } from 'vs/nls';
/**
@@ -14,11 +14,11 @@ import { localize } from 'vs/nls';
export class FileBrowserViewModel {
private _ownerUri: string;
private _expandPath: string;
private _fileFilters: [{label: string, filters: string[]}];
private _fileFilters: [{ label: string, filters: string[] }];
private _fileValidationServiceType: string;
public formattedFileFilters: string[];
constructor(@IFileBrowserService private _fileBrowserService: IFileBrowserService) {
constructor( @IFileBrowserService private _fileBrowserService: IFileBrowserService) {
}
public onAddFileTree(onAddFileTreeCallback) {
@@ -31,7 +31,7 @@ export class FileBrowserViewModel {
public initialize(ownerUri: string,
expandPath: string,
fileFilters: [ {label: string, filters: string[]} ],
fileFilters: [{ label: string, filters: string[] }],
fileValidationServiceType: string,
) {
this._ownerUri = ownerUri;
@@ -39,7 +39,7 @@ export class FileBrowserViewModel {
this._fileValidationServiceType = fileValidationServiceType;
if (!fileFilters) {
this._fileFilters = [ {label: localize('allFiles', 'All files'), filters: ['*']} ];
this._fileFilters = [{ label: localize('allFiles', 'All files'), filters: ['*'] }];
} else {
this._fileFilters = fileFilters;
}

View File

@@ -10,8 +10,8 @@ import { Subject } from 'rxjs/Subject';
import { Observer } from 'rxjs/Observer';
import { ResultSetSubset, EditUpdateCellResult, EditSubsetResult, EditCreateRowResult } from 'sqlops';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { ResultSerializer } from 'sql/parts/query/common/resultSerializer';
import { IQueryModelService } from 'sql/platform/query/common/queryModel';
import { ResultSerializer } from 'sql/platform/node/resultSerializer';
import { ISaveRequest } from 'sql/parts/grid/common/interfaces';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';

View File

@@ -6,14 +6,14 @@
'use strict';
import * as GridContentEvents from 'sql/parts/grid/common/gridContentEvents';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { IQueryModelService } from 'sql/platform/query/common/queryModel';
import { QueryEditor } from 'sql/parts/query/editor/queryEditor';
import { EditDataEditor } from 'sql/parts/editData/editor/editDataEditor';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
function runActionOnActiveResultsEditor (accessor: ServicesAccessor, eventName: string): void {
function runActionOnActiveResultsEditor(accessor: ServicesAccessor, eventName: string): void {
let editorService = accessor.get(IEditorService);
const candidates = [editorService.activeControl, ...editorService.visibleControls].filter(e => {
if (e) {
@@ -28,7 +28,7 @@ function runActionOnActiveResultsEditor (accessor: ServicesAccessor, eventName:
if (candidates.length > 0) {
let queryModelService: IQueryModelService = accessor.get(IQueryModelService);
let uri = (<any> candidates[0].input).uri;
let uri = (<any>candidates[0].input).uri;
queryModelService.sendGridContentEvent(uri, eventName);
}
}

View File

@@ -14,11 +14,11 @@ import 'vs/css!sql/parts/grid/media/slickGrid';
import { Subscription, Subject } from 'rxjs/Rx';
import { ElementRef, QueryList, ChangeDetectorRef, ViewChildren } from '@angular/core';
import { SlickGrid } from 'angular2-slickgrid';
import { toDisposableSubscription } from 'sql/parts/common/rxjsUtils';
import { toDisposableSubscription } from 'sql/base/node/rxjsUtils';
import * as Constants from 'sql/parts/query/common/constants';
import * as LocalizedConstants from 'sql/parts/query/common/localizedConstants';
import { IGridInfo, IGridDataSet, SaveFormat } from 'sql/parts/grid/common/interfaces';
import * as Utils from 'sql/parts/connection/common/utils';
import * as Utils from 'sql/platform/connection/common/utils';
import { DataService } from 'sql/parts/grid/services/dataService';
import * as actions from 'sql/parts/grid/views/gridActions';
import * as Services from 'sql/parts/grid/services/sharedServices';
@@ -235,7 +235,7 @@ export abstract class GridParentComponent {
let selection = this.slickgrids.toArray()[index || this.activeGrid].getSelectedRanges();
if (selection) {
selection = selection.map(c => { return <Slick.Range>{ fromCell: c.fromCell - 1, toCell: c.toCell - 1, toRow: c.toRow, fromRow: c.fromRow }; });
return selection;
return selection;
} else {
return undefined;
}

View File

@@ -1,261 +0,0 @@
<div #taskbarContainer></div>
<div style="display: flex; flex-flow: row; overflow: hidden; height: 100%; width: 100%">
<div style="flex:3 3 auto; margin: 5px; overflow: scroll">
<div style="position: relative; width: calc(100% - 20px); height: calc(100% - 20px)">
<ng-template component-host></ng-template>
</div>
</div>
<div class="angular-modal-body-content chart-viewer" style="flex:1 1 auto; border-left: 1px solid; margin: 5px;">
<div style="position: relative; width: 100%">
<div class="dialog-label">{{localizedStrings.CHART_TYPE}}</div>
<select-box #chartTypeSelect class="input-divider"
[aria-label]="localizedStrings.CHART_TYPE"
[options]="insightRegistry.getAllIds()"
[selectedOption]="getDefaultChartType()"
[onlyEmitOnChange]="true"
(onDidSelect)="onChartChanged($event)"></select-box>
<ng-container [ngSwitch]="chartTypesSelectBox.value">
<ng-container *ngSwitchCase="'line'">
<ng-container *ngTemplateOutlet="lineInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'scatter'">
<ng-container *ngTemplateOutlet="scatterInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'timeSeries'">
<ng-container *ngTemplateOutlet="scatterInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'bar'">
<ng-container *ngTemplateOutlet="barInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'horizontalBar'">
<ng-container *ngTemplateOutlet="horizontalBarInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'pie'">
<ng-container *ngTemplateOutlet="pieInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'doughnut'">
<ng-container *ngTemplateOutlet="pieInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'count'">
<ng-container *ngTemplateOutlet="countInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'image'">
<ng-container *ngTemplateOutlet="imageInput"></ng-container>
</ng-container>
<ng-container *ngSwitchCase="'table'">
<ng-container *ngTemplateOutlet="tableInput"></ng-container>
</ng-container>
</ng-container>
</div>
</div>
</div>
<!--
count control interface
-->
<ng-template #countInput>
</ng-template>
<!--
image control interface
-->
<ng-template #imageInput>
</ng-template>
<!--
table control interface
-->
<ng-template #tableInput>
</ng-template>
<!--
Line graph control interface
-->
<ng-template #lineInput>
<ng-container *ngTemplateOutlet="dataTypeInput"></ng-container>
<ng-template [ngIf]="showColumnsAsLabels">
<ng-container *ngTemplateOutlet="columnsAsLabelsInput"></ng-container>
</ng-template>
<ng-template [ngIf]="showLabelFirstColumn">
<ng-container *ngTemplateOutlet="labelFirstColumnInput"></ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="yAxisLabelInput"></ng-container>
<ng-container *ngTemplateOutlet="xAxisLabelInput"></ng-container>
<ng-container *ngTemplateOutlet="legendInput"></ng-container>
</ng-template>
<!--
scatter graph control interface
-->
<ng-template #scatterInput>
<ng-container *ngTemplateOutlet="legendInput"></ng-container>
<ng-container *ngTemplateOutlet="yAxisLabelInput"></ng-container>
<ng-container *ngTemplateOutlet="xAxisLabelInput"></ng-container>
</ng-template>
<!--
bar graph control interface
-->
<ng-template #barInput>
<ng-container *ngTemplateOutlet="dataDirectionInput"></ng-container>
<ng-template [ngIf]="showColumnsAsLabels">
<ng-container *ngTemplateOutlet="columnsAsLabelsInput"></ng-container>
</ng-template>
<ng-template [ngIf]="showLabelFirstColumn">
<ng-container *ngTemplateOutlet="labelFirstColumnInput"></ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="legendInput"></ng-container>
<ng-container *ngTemplateOutlet="yAxisLabelInput"></ng-container>
<ng-container *ngTemplateOutlet="yAxisMinMaxInput"></ng-container>
<ng-container *ngTemplateOutlet="xAxisLabelInput"></ng-container>
</ng-template>
<!--
Horizontal bar graph control interface
-->
<ng-template #horizontalBarInput>
<ng-container *ngTemplateOutlet="dataDirectionInput"></ng-container>
<ng-template [ngIf]="showColumnsAsLabels">
<ng-container *ngTemplateOutlet="columnsAsLabelsInput"></ng-container>
</ng-template>
<ng-template [ngIf]="showLabelFirstColumn">
<ng-container *ngTemplateOutlet="labelFirstColumnInput"></ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="legendInput"></ng-container>
<ng-container *ngTemplateOutlet="xAxisLabelInput"></ng-container>
<ng-container *ngTemplateOutlet="xAxisMinMaxInput"></ng-container>
<ng-container *ngTemplateOutlet="yAxisLabelInput"></ng-container>
</ng-template>
<!--
Pie graph control interface
-->
<ng-template #pieInput>
<ng-container *ngTemplateOutlet="dataDirectionInput"></ng-container>
<ng-template [ngIf]="showColumnsAsLabels">
<ng-container *ngTemplateOutlet="columnsAsLabelsInput"></ng-container>
</ng-template>
<ng-template [ngIf]="showLabelFirstColumn">
<ng-container *ngTemplateOutlet="labelFirstColumnInput"></ng-container>
</ng-template>
<ng-container *ngTemplateOutlet="legendInput"></ng-container>
</ng-template>
<!--
Legend Input Control interface
Valid for any charting type, i.e not image/count
-->
<ng-template #legendInput>
<div class="dialog-label" id="legendLabel">{{localizedStrings.LEGEND}}</div>
<select-box class="input-divider"
[aria-label]="localizedStrings.LEGEND"
[options]="legendOptions"
[selectedOption]="_chartConfig.legendPosition"
[onlyEmitOnChange]="true"
(onDidSelect)="setConfigValue('legendPosition', $event.selected)"></select-box>
</ng-template>
<!--
Data type input control interface
point vs number data type, only valid for the line chart type
-->
<ng-template #dataTypeInput>
<div class="dialog-label" id="dataTypeLabel">{{localizedStrings.DATA_TYPE}}</div>
<div class="radio-indent" role="radiogroup" aria-labelledby="dataTypeLabel">
<div class="option">
<input type="radio" role="radio" name="data-type" value="number" [(ngModel)]="dataType" aria-labelledby="numberLabel"><span id="numberLabel">{{localizedStrings.NUMBER}}</span>
</div>
<div class="option">
<input type="radio" role="radio" name="data-type" value="point" [(ngModel)]="dataType" aria-labelledby="pointLabel"><span id="pointLabel">{{localizedStrings.POINT}}</span>
</div>
</div>
</ng-template>
<!--
Data direction input control
vertical vs horizontal, change which direction is considered a "series" of data
only valid for pie, bar, doughnut charts and line is numbr is the data type
otherwise the direction is assumed for each other type
-->
<ng-template #dataDirectionInput>
<div class="dialog-label" id="dataDirectionLabel">{{localizedStrings.DATA_DIRECTION}}</div>
<div class="radio-indent" role="radiogroup" aria-labelledby="dataDirectionLabel">
<div class="option">
<input type="radio" role="radio" name="data-direction" value="vertical" [(ngModel)]="dataDirection" aria-labelledby="verticalLabel"><span id="verticalLabel">{{localizedStrings.VERTICAL}}</span>
</div>
<div class="option">
<input type="radio" role="radio" name="data-direction" value="horizontal" [(ngModel)]="dataDirection" aria-labelledby="horizontalLabel"><span id="horizontalLabel">{{localizedStrings.HORIZONTAL}}</span>
</div>
</div>
</ng-template>
<!--
Control for specifing if the first column should be used to label the series
Only valid for data direction = horizontal
-->
<ng-template #labelFirstColumnInput>
<checkbox class="input-divider" [label]="localizedStrings.LABEL_FIRST_COLUMN" (onChange)="setConfigValue('labelFirstColumn', $event)"></checkbox>
</ng-template>
<!--
Control whether to use column names as series labels
Only valid for data direction = vertical
-->
<ng-template #columnsAsLabelsInput>
<checkbox class="input-divider" [label]="localizedStrings.COLUMNS_AS_LABELS" (onChange)="setConfigValue('columnsAsLabels', $event)"></checkbox>
</ng-template>
<!--
Y-Axis options controls
valid for any charting value
-->
<ng-template #yAxisLabelInput>
<span>
{{localizedStrings.Y_AXIS_LABEL}}
<input-box (onDidChange)="setConfigValue('yAxisLabel', $event)"
[aria-label]="localizedStrings.Y_AXIS_LABEL"></input-box>
</span>
</ng-template>
<ng-template #yAxisMinMaxInput>
<span>
{{localizedStrings.Y_AXIS_MAX_VAL}}
<input-box [type]="'number'"
(onDidChange)="setConfigValue('yAxisMax', $event)"
[aria-label]="localizedStrings.Y_AXIS_MAX_VAL"></input-box>
</span>
<span>
{{localizedStrings.Y_AXIS_MIN_VAL}}
<input-box [type]="'number'"
(onDidChange)="setConfigValue('yAxisMin', $event)"
[aria-label]="localizedStrings.Y_AXIS_MIN_VAL"></input-box>
</span>
</ng-template>
<!--
X-Axis options controls
valid for any charting value
-->
<ng-template #xAxisLabelInput>
<span>
{{localizedStrings.X_AXIS_LABEL}}
<input-box (onDidChange)="setConfigValue('xAxisLabel', $event)"
[aria-label]="localizedStrings.X_AXIS_LABEL"></input-box>
</span>
</ng-template>
<ng-template #xAxisMinMaxInput>
<span>
{{localizedStrings.X_AXIS_MAX_VAL}}
<input-box [type]="'number'"
(onDidChange)="setConfigValue('xAxisMax', $event)"
[aria-label]="localizedStrings.X_AXIS_MAX_VAL"></input-box>
</span>
<span>
{{localizedStrings.X_AXIS_MIN_VAL}}
<input-box [type]="'number'"
(onDidChange)="setConfigValue('xAxisMin', $event)"
[aria-label]="localizedStrings.X_AXIS_MIN_VAL"></input-box>
</span>
</ng-template>

View File

@@ -1,363 +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 'vs/css!sql/parts/grid/views/query/chartViewer';
import {
Component, Inject, forwardRef, OnInit, ComponentFactoryResolver, ViewChild,
OnDestroy, Input, ElementRef, ChangeDetectorRef
} from '@angular/core';
import { NgGridItemConfig } from 'angular2-grid';
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { ComponentHostDirective } from 'sql/parts/dashboard/common/componentHost.directive';
import { IGridDataSet } from 'sql/parts/grid/common/interfaces';
import { IInsightData, IInsightsView, IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { QueryEditor } from 'sql/parts/query/editor/queryEditor';
import { ILineConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
import * as PathUtilities from 'sql/common/pathUtilities';
import { IChartViewActionContext, CopyAction, CreateInsightAction, SaveImageAction } from 'sql/parts/grid/views/query/chartViewerActions';
import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
import * as Constants from 'sql/parts/query/common/constants';
import { SelectBox as AngularSelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
import { LegendPosition, DataDirection, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
/* Insights */
import {
ChartInsight
} from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { IDisposable } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity';
import URI from 'vs/base/common/uri';
import * as nls from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { mixin } from 'vs/base/common/objects';
import * as paths from 'vs/base/common/paths';
import * as pfs from 'vs/base/node/pfs';
import { ISelectData } from 'vs/base/browser/ui/selectBox/selectBox';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
const insightRegistry = Registry.as<IInsightRegistry>(Extensions.InsightContribution);
const LocalizedStrings = {
CHART_TYPE: nls.localize('chartTypeLabel', 'Chart Type'),
DATA_DIRECTION: nls.localize('dataDirectionLabel', 'Data Direction'),
VERTICAL: nls.localize('verticalLabel', 'Vertical'),
HORIZONTAL: nls.localize('horizontalLabel', 'Horizontal'),
DATA_TYPE: nls.localize('dataTypeLabel', 'Data Type'),
NUMBER: nls.localize('numberLabel', 'Number'),
POINT: nls.localize('pointLabel', 'Point'),
LABEL_FIRST_COLUMN: nls.localize('labelFirstColumnLabel', 'Use first column as row label'),
COLUMNS_AS_LABELS: nls.localize('columnsAsLabelsLabel', 'Use column names as labels'),
LEGEND: nls.localize('legendLabel', 'Legend Position'),
CHART_NOT_FOUND: nls.localize('chartNotFound', 'Could not find chart to save'),
X_AXIS_LABEL: nls.localize('xAxisLabel', 'X Axis Label'),
X_AXIS_MIN_VAL: nls.localize('xAxisMinVal', 'X Axis Minimum Value'),
X_AXIS_MAX_VAL: nls.localize('xAxisMaxVal', 'X Axis Maximum Value'),
Y_AXIS_LABEL: nls.localize('yAxisLabel', 'Y Axis Label'),
Y_AXIS_MIN_VAL: nls.localize('yAxisMinVal', 'Y Axis Minimum Value'),
Y_AXIS_MAX_VAL: nls.localize('yAxisMaxVal', 'Y Axis Maximum Value')
};
@Component({
selector: 'chart-viewer',
templateUrl: decodeURI(require.toUrl('sql/parts/grid/views/query/chartViewer.component.html'))
})
export class ChartViewerComponent implements OnInit, OnDestroy, IChartViewActionContext {
public legendOptions: string[];
@ViewChild('chartTypeSelect') private chartTypesSelectBox: AngularSelectBox;
/* UI */
private _actionBar: Taskbar;
private _createInsightAction: CreateInsightAction;
private _copyAction: CopyAction;
private _saveAction: SaveImageAction;
private _chartConfig: ILineConfig;
private _disposables: Array<IDisposable> = [];
private _executeResult: IInsightData;
private _chartComponent: ChartInsight;
protected localizedStrings = LocalizedStrings;
protected insightRegistry = insightRegistry;
@ViewChild(ComponentHostDirective) private componentHost: ComponentHostDirective;
@ViewChild('taskbarContainer', { read: ElementRef }) private taskbarContainer;
constructor(
@Inject(forwardRef(() => ComponentFactoryResolver)) private _componentFactoryResolver: ComponentFactoryResolver,
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
@Inject(INotificationService) private notificationService: INotificationService,
@Inject(IContextMenuService) private contextMenuService: IContextMenuService,
@Inject(IClipboardService) private clipboardService: IClipboardService,
@Inject(IConfigurationService) private configurationService: IConfigurationService,
@Inject(IWindowsService) private windowsService: IWindowsService,
@Inject(IWorkspaceContextService) private workspaceContextService: IWorkspaceContextService,
@Inject(IWindowService) private windowService: IWindowService,
@Inject(IQueryModelService) private queryModelService: IQueryModelService,
@Inject(IEditorService) private editorService: IEditorService
) {
this.setDefaultChartConfig();
}
ngOnInit() {
this.legendOptions = Object.values(LegendPosition);
this._initActionBar();
}
private setDefaultChartConfig() {
let defaultChart = this.getDefaultChartType();
if (defaultChart === 'timeSeries') {
this._chartConfig = <ILineConfig>{
dataDirection: 'vertical',
dataType: 'point',
legendPosition: 'none'
};
} else {
this._chartConfig = <ILineConfig>{
dataDirection: 'vertical',
dataType: 'number',
legendPosition: 'none'
};
}
}
protected getDefaultChartType(): string {
let defaultChartType = Constants.chartTypeHorizontalBar;
if (this.configurationService) {
let chartSettings = WorkbenchUtils.getSqlConfigSection(this.configurationService, 'chart');
// Only use the value if it's a known chart type. Ideally could query this dynamically but can't figure out how
if (chartSettings && Constants.allChartTypes.indexOf(chartSettings[Constants.defaultChartType]) > -1) {
defaultChartType = chartSettings[Constants.defaultChartType];
}
}
return defaultChartType;
}
private _initActionBar() {
this._createInsightAction = this.instantiationService.createInstance(CreateInsightAction);
this._copyAction = this.instantiationService.createInstance(CopyAction);
this._saveAction = this.instantiationService.createInstance(SaveImageAction);
let taskbar = <HTMLElement>this.taskbarContainer.nativeElement;
this._actionBar = new Taskbar(taskbar, this.contextMenuService);
this._actionBar.context = this;
this._actionBar.setContent([
{ action: this._createInsightAction },
{ action: this._copyAction },
{ action: this._saveAction }
]);
}
public onChartChanged(e: ISelectData): void {
this.setDefaultChartConfig();
if ([Constants.chartTypeScatter, Constants.chartTypeTimeSeries].some(item => item === e.selected)) {
this.dataType = DataType.Point;
this.dataDirection = DataDirection.Horizontal;
}
this.initChart();
}
ngAfterViewInit() {
this.initChart();
}
setConfigValue(key: string, value: any, refresh = true): void {
this._chartConfig[key] = value;
if (refresh) {
this.initChart();
}
}
public set dataType(type: DataType) {
this._chartConfig.dataType = type;
// Requires full chart refresh
this.initChart();
}
public set dataDirection(direction: DataDirection) {
this._chartConfig.dataDirection = direction;
// Requires full chart refresh
this.initChart();
}
public copyChart(): void {
let data = this._chartComponent.getCanvasData();
if (!data) {
this.showError(LocalizedStrings.CHART_NOT_FOUND);
return;
}
this.clipboardService.writeImageDataUrl(data);
}
public saveChart(): void {
this.promptForFilepath().then(filePath => {
let data = this._chartComponent.getCanvasData();
if (!data) {
this.showError(LocalizedStrings.CHART_NOT_FOUND);
return;
}
if (filePath) {
let buffer = this.decodeBase64Image(data);
pfs.writeFile(filePath, buffer).then(undefined, (err) => {
if (err) {
this.showError(err.message);
} else {
let fileUri = URI.from({ scheme: PathUtilities.FILE_SCHEMA, path: filePath });
this.windowsService.openExternal(fileUri.toString());
this.notificationService.notify({
severity: Severity.Error,
message: nls.localize('chartSaved', 'Saved Chart to path: {0}', filePath)
});
}
});
}
});
}
private promptForFilepath(): Thenable<string> {
let filepathPlaceHolder = PathUtilities.resolveCurrentDirectory(this.getActiveUriString(), PathUtilities.getRootPath(this.workspaceContextService));
filepathPlaceHolder = paths.join(filepathPlaceHolder, 'chart.png');
return this.windowService.showSaveDialog({
title: nls.localize('chartViewer.saveAsFileTitle', 'Choose Results File'),
defaultPath: paths.normalize(filepathPlaceHolder, true)
});
}
private decodeBase64Image(data: string): Buffer {
let matches = data.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/);
return new Buffer(matches[2], 'base64');
}
public createInsight(): void {
let uriString: string = this.getActiveUriString();
if (!uriString) {
this.showError(nls.localize('createInsightNoEditor', 'Cannot create insight as the active editor is not a SQL Editor'));
return;
}
let uri: URI = URI.parse(uriString);
let dataService = this.queryModelService.getDataService(uriString);
if (!dataService) {
this.showError(nls.localize('createInsightNoDataService', 'Cannot create insight, backing data model not found'));
return;
}
let queryFile: string = uri.fsPath;
let query: string = undefined;
let type = {};
type[this.chartTypesSelectBox.value] = this._chartConfig;
// create JSON
let config: IInsightsConfig = {
type,
query,
queryFile
};
let widgetConfig = {
name: nls.localize('myWidgetName', 'My-Widget'),
gridItemConfig: this.getGridItemConfig(),
widget: {
'insights-widget': config
}
};
// open in new window as untitled JSON file
dataService.openLink(JSON.stringify(widgetConfig), 'Insight', 'json');
}
private showError(errorMsg: string) {
this.notificationService.notify({
severity: Severity.Error,
message: errorMsg
});
}
private getGridItemConfig(): NgGridItemConfig {
let config: NgGridItemConfig = {
sizex: 2,
sizey: 1
};
return config;
}
private getActiveUriString(): string {
let editorService = this.editorService;
let editor = editorService.activeControl;
if (editor && editor instanceof QueryEditor) {
let queryEditor: QueryEditor = editor;
return queryEditor.uri;
}
return undefined;
}
protected get showDataDirection(): boolean {
return ['pie', 'horizontalBar', 'bar', 'doughnut'].some(item => item === this.chartTypesSelectBox.value) || (this.chartTypesSelectBox.value === 'line' && this.dataType === 'number');
}
protected get showLabelFirstColumn(): boolean {
return this.dataDirection === 'horizontal' && this.dataType !== 'point';
}
protected get showColumnsAsLabels(): boolean {
return this.dataDirection === 'vertical' && this.dataType !== 'point';
}
public get dataDirection(): DataDirection {
return this._chartConfig.dataDirection;
}
public get dataType(): DataType {
return this._chartConfig.dataType;
}
@Input() set dataSet(dataSet: IGridDataSet) {
// Setup the execute result
this._executeResult = <IInsightData>{};
// Remove first column and its value since this is the row number column
this._executeResult.columns = dataSet.columnDefinitions.slice(1).map(def => def.name);
this._executeResult.rows = dataSet.dataRows.getRange(0, dataSet.dataRows.getLength()).map(v => {
return this._executeResult.columns.reduce((p, c) => {
p.push(v[c]);
return p;
}, []);
});
}
public initChart() {
this._cd.detectChanges();
if (this._executeResult) {
// Reinitialize the chart component
let componentFactory = this._componentFactoryResolver.resolveComponentFactory<IInsightsView>(insightRegistry.getCtorFromId(this.chartTypesSelectBox.value));
this.componentHost.viewContainerRef.clear();
let componentRef = this.componentHost.viewContainerRef.createComponent(componentFactory);
this._chartComponent = <ChartInsight>componentRef.instance;
if (this._chartComponent.setConfig) {
this._chartComponent.setConfig(this._chartConfig);
}
this._chartComponent.data = this._executeResult;
this._chartComponent.options = mixin(this._chartComponent.options, { animation: { duration: 0 } });
if (this._chartComponent.init) {
this._chartComponent.init();
}
}
}
ngOnDestroy() {
this._disposables.forEach(i => i.dispose());
}
}

View File

@@ -1,56 +0,0 @@
input[type="radio"] {
margin-top: -2px;
vertical-align: middle;
}
.chart-viewer {
overflow-y: auto;
}
.chart-viewer .indent {
margin-left: 7px;
}
.chart-viewer .radio-indent {
margin-left: 5px;
}
.chart-viewer .option {
width: 100%;
padding-bottom: 7px;
}
.chart-viewer .dialog-label {
width: 100%;
padding-bottom: 5px;
}
.chart-viewer .input-divider {
width: 100%;
padding-bottom: 20px;
}
.chart-viewer .footer {
display: flex;
}
.chart-viewer .footer .right-footer {
display: flex;
flex: 1 1 auto;
justify-content: flex-end;
}
.chart-viewer .footer-button a.monaco-button.monaco-text-button {
width: 100px;
}
.vs-dark.monaco-shell .chart-viewer .footer-button a.monaco-button.monaco-text-button {
outline-color: #8e8c8c;
}
.chart-viewer .footer-button {
margin-right: 5px;
}
.chart-viewer .right-footer .footer-button:last-of-type {
margin-right: none;
}

View File

@@ -1,108 +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 { TPromise } from 'vs/base/common/winjs.base';
import { Action } from 'vs/base/common/actions';
import * as nls from 'vs/nls';
import { INotificationService } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity';
export interface IChartViewActionContext {
copyChart(): void;
saveChart(): void;
createInsight(): void;
}
export class ChartViewActionBase extends Action {
public static BaseClass = 'queryTaskbarIcon';
private _classes: string[];
constructor(
id: string,
label: string,
enabledClass: string,
protected notificationService: INotificationService
) {
super(id, label);
this.enabled = true;
this._setCssClass(enabledClass);
}
protected updateCssClass(enabledClass: string): void {
// set the class, useful on change of label or icon
this._setCssClass(enabledClass);
}
/**
* Sets the CSS classes combining the parent and child classes.
* Public for testing only.
*/
private _setCssClass(enabledClass: string): void {
this._classes = [];
this._classes.push(ChartViewActionBase.BaseClass);
if (enabledClass) {
this._classes.push(enabledClass);
}
this.class = this._classes.join(' ');
}
protected doRun(context: IChartViewActionContext, runAction: Function): TPromise<boolean> {
if (!context) {
// TODO implement support for finding chart view in active window
this.notificationService.notify({
severity: Severity.Error,
message: nls.localize('chartContextRequired', 'Chart View context is required to run this action')
});
return TPromise.as(false);
}
return new TPromise<boolean>((resolve, reject) => {
runAction();
resolve(true);
});
}
}
export class CreateInsightAction extends ChartViewActionBase {
public static ID = 'chartview.createInsight';
public static LABEL = nls.localize('createInsightLabel', "Create Insight");
constructor(@INotificationService notificationService: INotificationService
) {
super(CreateInsightAction.ID, CreateInsightAction.LABEL, 'createInsight', notificationService);
}
public run(context: IChartViewActionContext): TPromise<boolean> {
return this.doRun(context, () => context.createInsight());
}
}
export class CopyAction extends ChartViewActionBase {
public static ID = 'chartview.copy';
public static LABEL = nls.localize('copyChartLabel', "Copy as image");
constructor(@INotificationService notificationService: INotificationService
) {
super(CopyAction.ID, CopyAction.LABEL, 'copyImage', notificationService);
}
public run(context: IChartViewActionContext): TPromise<boolean> {
return this.doRun(context, () => context.copyChart());
}
}
export class SaveImageAction extends ChartViewActionBase {
public static ID = 'chartview.saveImage';
public static LABEL = nls.localize('saveImageLabel', "Save as image");
constructor(@INotificationService notificationService: INotificationService
) {
super(SaveImageAction.ID, SaveImageAction.LABEL, 'saveAsImage', notificationService);
}
public run(context: IChartViewActionContext): TPromise<boolean> {
return this.doRun(context, () => context.saveChart());
}
}

View File

@@ -1,82 +0,0 @@
<!--
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-->
<div class="fullsize vertBox" style="position: relative">
<div #resultsPane role="toolbar" tabindex="0" [attr.aria-label]="LocalizedConstants.resultPaneLabel" [attr.aria-expanded]="resultActive" *ngIf="dataSets.length > 0" id="resultspane" class="boxRow resultsMessageHeader resultsViewCollapsible" [class.collapsed]="!resultActive" (click)="togglePane('results')">
<span> {{LocalizedConstants.resultPaneLabel}} </span>
<span class="queryResultsShortCut"> {{resultShortcut}} </span>
</div>
<div #resultsScrollBox id="results" *ngIf="renderedDataSets.length > 0" class="results vertBox scrollable"
(onScroll)="onScroll($event)" [scrollEnabled]="scrollEnabled" [class.hidden]="!resultActive"
(focusin)="onGridFocus()" (focusout)="onGridFocusout()">
<div class="boxRow content horzBox slickgrid" *ngFor="let dataSet of renderedDataSets; let i = index"
[style.max-height]="dataSet.maxHeight" [style.min-height]="dataSet.minHeight">
<slick-grid #slickgrid id="slickgrid_{{i}}"
class="boxCol content vertBox slickgrid"
enableAsyncPostRender="true"
showHeader="true"
[columnDefinitions]="dataSet.columnDefinitions"
[ngClass]="i === activeGrid ? 'active' : ''"
[dataRows]="dataSet.dataRows"
[resized]="dataSet.resized"
[selectionModel]="selectionModel"
[plugins]="plugins[i]"
[rowHeight]="rowHeight"
(onContextMenu)="openContextMenu($event, dataSet.batchId, dataSet.resultId, i)"
(onActiveCellChanged)="onActiveCellChanged(i)"
(mousedown)="navigateToGrid(i)">
</slick-grid>
<span class="boxCol content vertBox">
<div class="boxRow content maxHeight" *ngFor="let icon of dataIcons">
<div *ngIf="icon.showCondition()" class="gridIconContainer">
<a class="gridIcon" style="cursor: pointer;"
role="button"
(click)="icon.functionality(dataSet.batchId, dataSet.resultId, i)"
[title]="icon.hoverText()" [ngClass]="icon.icon()" tabindex="0">
</a>
</div>
</div>
</span>
</div>
</div>
<div id="messagepane" role="toolbar" tabindex="0" [attr.aria-label]="LocalizedConstants.messagePaneLabel" [attr.aria-expanded]="!(!messageActive && dataSets.length !== 0)" class="boxRow resultsMessageHeader resultsViewCollapsible" [class.collapsed]="!messageActive && dataSets.length !== 0" (click)="togglePane('messages')" style="position: relative">
<div id="messageResizeHandle" [class.hidden]="!_resultsPane || !_messageActive || !resultActive" class="resizableHandle"></div>
<span> {{LocalizedConstants.messagePaneLabel}} </span>
<span class="queryResultsShortCut"> {{messageShortcut}} </span>
</div>
<div #messagesContainer id="messages" class="scrollable messages" [class.hidden]="!messageActive && dataSets.length !== 0"
(contextmenu)="openMessagesContextMenu($event)" (focusin)="onMessagesFocus()" (focusout)="onMessagesFocusout()"
tabindex=0>
<div class="messagesTopSpacing"></div>
<table id="messageTable" class="resultsMessageTable">
<colgroup>
<col span="1" class="wideResultsMessage">
</colgroup>
<tbody>
<ng-template ngFor let-message [ngForOf]="messages">
<tr class='messageRow'>
<td><span *ngIf="message.link">[{{message.time}}]</span></td>
<td class="resultsMessageValue" [class.errorMessage]="message.isError" [class.batchMessage]="!message.link">{{message.message}} <a tabindex="0" #queryLink class="queryLink" *ngIf="message.link" (click)="onSelectionLinkClicked(message.batchId)" (keyup)="onKey($event, message.batchId)">{{message.link.text}}</a>
</td>
</tr>
</ng-template>
<tr id='executionSpinner' *ngIf="!complete">
<td><span *ngIf="messages.length === 0">[{{startString}}]</span></td>
<td>
<img class="icon in-progress" height="18px" />
<span style="vertical-align: bottom">{{LocalizedConstants.executeQueryLabel}}</span>
</td>
</tr>
<tr *ngIf="complete">
<td></td>
<td>{{stringsFormat(LocalizedConstants.elapsedTimeLabel, totalElapsedTimeSpan)}}</td>
</tr>
</tbody>
</table>
</div>
<div id="resizeHandle" [class.hidden]="!resizing" [style.top]="resizeHandleTop"></div>
</div>

View File

@@ -1,732 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import 'vs/css!sql/media/icons/common-icons';
import 'vs/css!sql/parts/grid/media/slickColorTheme';
import 'vs/css!sql/parts/grid/media/flexbox';
import 'vs/css!sql/parts/grid/media/styles';
import 'vs/css!sql/parts/grid/media/slick.grid';
import 'vs/css!sql/parts/grid/media/slickGrid';
import {
ElementRef, QueryList, ChangeDetectorRef, OnInit, OnDestroy, Component, Inject,
ViewChildren, forwardRef, EventEmitter, Input, ViewChild
} from '@angular/core';
import { IGridDataRow, SlickGrid, VirtualizedCollection } from 'angular2-slickgrid';
import * as LocalizedConstants from 'sql/parts/query/common/localizedConstants';
import * as Services from 'sql/parts/grid/services/sharedServices';
import { IGridIcon, IMessage, IGridDataSet } from 'sql/parts/grid/common/interfaces';
import { GridParentComponent } from 'sql/parts/grid/views/gridParentComponent';
import { GridActionProvider } from 'sql/parts/grid/views/gridActions';
import { IQueryComponentParams } from 'sql/services/bootstrap/bootstrapParams';
import { error } from 'sql/base/common/log';
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
import { clone, mixin } from 'sql/base/common/objects';
import { IQueryEditorService } from 'sql/parts/query/common/queryEditorService';
import { escape } from 'sql/base/common/strings';
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
import { format } from 'vs/base/common/strings';
import * as DOM from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { localize } from 'vs/nls';
export const QUERY_SELECTOR: string = 'query-component';
declare type PaneType = 'messages' | 'results';
@Component({
selector: QUERY_SELECTOR,
host: { '(window:keydown)': 'keyEvent($event)', '(window:gridnav)': 'keyEvent($event)' },
templateUrl: decodeURI(require.toUrl('sql/parts/grid/views/query/query.component.html')),
providers: [{ provide: TabChild, useExisting: forwardRef(() => QueryComponent) }]
})
export class QueryComponent extends GridParentComponent implements OnInit, OnDestroy {
// CONSTANTS
// tslint:disable-next-line:no-unused-variable
private scrollTimeOutTime: number = 200;
private windowSize: number = 50;
private messagePaneHeight: number = 22;
// tslint:disable-next-line:no-unused-variable
private maxScrollGrids: number = 8;
// create a function alias to use inside query.component
// tslint:disable-next-line:no-unused-variable
protected stringsFormat: any = format;
protected plugins = new Array<Array<Slick.Plugin<any>>>();
// tslint:disable-next-line:no-unused-variable
private dataIcons: IGridIcon[] = [
{
showCondition: () => { return this.dataSets.length > 1; },
icon: () => {
return this.renderedDataSets.length === 1
? 'exitFullScreen'
: 'extendFullScreen';
},
hoverText: () => {
return this.renderedDataSets.length === 1
? LocalizedConstants.restoreLabel
: LocalizedConstants.maximizeLabel;
},
functionality: (batchId, resultId, index) => {
this.magnify(index);
}
},
{
showCondition: () => { return true; },
icon: () => { return 'saveCsv'; },
hoverText: () => { return LocalizedConstants.saveCSVLabel; },
functionality: (batchId, resultId, index) => {
let selection = this.getSelection(index);
if (selection.length <= 1) {
this.handleContextClick({ type: 'savecsv', batchId: batchId, resultId: resultId, index: index, selection: selection });
} else {
this.dataService.showWarning(LocalizedConstants.msgCannotSaveMultipleSelections);
}
}
},
{
showCondition: () => { return true; },
icon: () => { return 'saveJson'; },
hoverText: () => { return LocalizedConstants.saveJSONLabel; },
functionality: (batchId, resultId, index) => {
let selection = this.getSelection(index);
if (selection.length <= 1) {
this.handleContextClick({ type: 'savejson', batchId: batchId, resultId: resultId, index: index, selection: selection });
} else {
this.dataService.showWarning(LocalizedConstants.msgCannotSaveMultipleSelections);
}
}
},
{
showCondition: () => { return true; },
icon: () => { return 'saveExcel'; },
hoverText: () => { return LocalizedConstants.saveExcelLabel; },
functionality: (batchId, resultId, index) => {
let selection = this.getSelection(index);
if (selection.length <= 1) {
this.handleContextClick({ type: 'saveexcel', batchId: batchId, resultId: resultId, index: index, selection: selection });
} else {
this.dataService.showWarning(LocalizedConstants.msgCannotSaveMultipleSelections);
}
}
},
{
showCondition: () => { return true; },
icon: () => { return 'saveXml'; },
hoverText: () => { return LocalizedConstants.saveXMLLabel; },
functionality: (batchId, resultId, index) => {
let selection = this.getSelection(index);
if (selection.length <= 1) {
this.handleContextClick({ type: 'savexml', batchId: batchId, resultId: resultId, index: index, selection: selection });
} else {
this.dataService.showWarning(LocalizedConstants.msgCannotSaveMultipleSelections);
}
}
},
{
showCondition: () => {
return this.configurationService.getValue('workbench')['enablePreviewFeatures'];
},
icon: () => { return 'viewChart'; },
hoverText: () => { return LocalizedConstants.viewChartLabel; },
functionality: (batchId, resultId, index) => {
this.showChartForGrid(index);
}
}
];
// FIELDS
// Service for interaction with the IQueryModel
// All datasets
private dataSets: IGridDataSet[] = [];
private messages: IMessage[] = [];
private messageStore: IMessage[] = [];
private messageTimeout: number;
private lastMessageHandleTime: number = 0;
private scrollTimeOut: number;
private resizing = false;
private resizeHandleTop: string = '0';
private scrollEnabled = true;
private rowHeight: number;
// tslint:disable-next-line:no-unused-variable
private firstRender = true;
private totalElapsedTimeSpan: number;
private complete = false;
private sentPlans: Map<number, string> = new Map<number, string>();
private hasQueryPlan: boolean = false;
private queryPlanResultSetId: number = 0;
public queryExecutionStatus: EventEmitter<string> = new EventEmitter<string>();
public queryPlanAvailable: EventEmitter<string> = new EventEmitter<string>();
public showChartRequested: EventEmitter<IGridDataSet> = new EventEmitter<IGridDataSet>();
public goToNextQueryOutputTabRequested: EventEmitter<void> = new EventEmitter<void>();
public onActiveCellChanged: (gridIndex: number) => void;
private savedViewState: {
gridSelections: Slick.Range[][];
resultsScroll: number;
messagePaneScroll: number;
slickGridScrolls: { vertical: number; horizontal: number }[];
};
@Input() public queryParameters: IQueryComponentParams;
@ViewChildren('slickgrid') slickgrids: QueryList<SlickGrid>;
// tslint:disable-next-line:no-unused-variable
@ViewChild('resultsPane', { read: ElementRef }) private _resultsPane: ElementRef;
@ViewChild('queryLink', { read: ElementRef }) private _queryLinkElement: ElementRef;
@ViewChild('messagesContainer', { read: ElementRef }) private _messagesContainer: ElementRef;
@ViewChild('resultsScrollBox', { read: ElementRef }) private _resultsScrollBox: ElementRef;
@ViewChildren('slickgrid', { read: ElementRef }) private _slickgridElements: QueryList<ElementRef>;
constructor(
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
@Inject(forwardRef(() => ChangeDetectorRef)) cd: ChangeDetectorRef,
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
@Inject(IKeybindingService) keybindingService: IKeybindingService,
@Inject(IContextKeyService) contextKeyService: IContextKeyService,
@Inject(IConfigurationService) configurationService: IConfigurationService,
@Inject(IClipboardService) clipboardService: IClipboardService,
@Inject(IQueryEditorService) queryEditorService: IQueryEditorService,
@Inject(INotificationService) notificationService: INotificationService,
) {
super(el, cd, contextMenuService, keybindingService, contextKeyService, configurationService, clipboardService, queryEditorService, notificationService);
this._el.nativeElement.className = 'slickgridContainer';
this.rowHeight = configurationService.getValue<any>('resultsGrid').rowHeight;
configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('resultsGrid')) {
this.rowHeight = configurationService.getValue<any>('resultsGrid').rowHeight;
this.slickgrids.forEach(i => {
i.rowHeight = this.rowHeight;
});
this.resizeGrids();
}
});
}
/**
* Called by Angular when the object is initialized
*/
ngOnInit(): void {
const self = this;
this.dataService = this.queryParameters.dataService;
this.actionProvider = this.instantiationService.createInstance(GridActionProvider, this.dataService, this.onGridSelectAll());
this.baseInit();
this.setupResizeBind();
this.subscribeWithDispose(this.dataService.queryEventObserver, (event) => {
switch (event.type) {
case 'start':
self.handleStart(self, event);
break;
case 'complete':
self.handleComplete(self, event);
break;
case 'message':
self.handleMessage(self, event);
break;
case 'resultSet':
self.handleResultSet(self, event);
break;
default:
error('Unexpected query event type "' + event.type + '" sent');
break;
}
self._cd.detectChanges();
});
this.queryParameters.onSaveViewState(() => this.saveViewState());
this.queryParameters.onRestoreViewState(() => this.restoreViewState());
this.dataService.onAngularLoaded();
}
public ngOnDestroy(): void {
this.baseDestroy();
}
protected initShortcuts(shortcuts: { [name: string]: Function }): void {
shortcuts['event.nextGrid'] = () => {
this.navigateToGrid(this.activeGrid + 1);
};
shortcuts['event.prevGrid'] = () => {
this.navigateToGrid(this.activeGrid - 1);
};
shortcuts['event.maximizeGrid'] = () => {
this.magnify(this.activeGrid);
};
}
handleStart(self: QueryComponent, event: any): void {
self.messages = [];
self.dataSets = [];
self.placeHolderDataSets = [];
self.renderedDataSets = self.placeHolderDataSets;
self.totalElapsedTimeSpan = undefined;
self.complete = false;
self.activeGrid = 0;
this.onActiveCellChanged = this.onCellSelect;
// reset query plan info and send notification to subscribers
self.hasQueryPlan = false;
self.sentPlans = new Map<number, string>();
self.queryExecutionStatus.emit('start');
self.firstRender = true;
}
handleComplete(self: QueryComponent, event: any): void {
self.totalElapsedTimeSpan = event.data;
self.complete = true;
}
handleMessage(self: QueryComponent, event: any): void {
self.messageStore.push(event.data);
// Ensure that messages are updated at least every 10 seconds during long-running queries
if (self.messageTimeout !== undefined && Date.now() - self.lastMessageHandleTime < 10000) {
clearTimeout(self.messageTimeout);
} else {
self.lastMessageHandleTime = Date.now();
}
self.messageTimeout = setTimeout(() => {
while (self.messageStore.length > 0) {
let lastMessage = self.messages.length > 0 ? self.messages[self.messages.length - 1] : undefined;
let nextMessage = self.messageStore[0];
// If the next message has the same metadata as the previous one, just append its text to avoid rendering an entirely new message
if (lastMessage !== undefined && lastMessage.batchId === nextMessage.batchId && lastMessage.isError === nextMessage.isError
&& lastMessage.link === nextMessage.link && lastMessage.link === undefined) {
lastMessage.message += '\n' + nextMessage.message;
} else {
self.messages.push(nextMessage);
}
self.messageStore = self.messageStore.slice(1);
}
self._cd.detectChanges();
self.scrollMessages();
}, 100);
}
handleResultSet(self: QueryComponent, event: any): void {
let resultSet = event.data;
// No column info found, so define a column of no name by default
if (!resultSet.columnInfo) {
resultSet.columnInfo = [];
resultSet.columnInfo[0] = { columnName: '' };
}
// Setup a function for generating a promise to lookup result subsets
let loadDataFunction = (offset: number, count: number): Promise<IGridDataRow[]> => {
return new Promise<IGridDataRow[]>((resolve, reject) => {
self.dataService.getQueryRows(offset, count, resultSet.batchId, resultSet.id).subscribe(rows => {
let gridData: IGridDataRow[] = [];
for (let row = 0; row < rows.rows.length; row++) {
// Push row values onto end of gridData for slickgrid
gridData.push({
values: [{}].concat(rows.rows[row].map(c => {
return mixin({ ariaLabel: escape(c.displayValue) }, c);
}))
});
}
// if this is a query plan resultset we haven't processed yet then forward to subscribers
if (self.hasQueryPlan && resultSet.id === self.queryPlanResultSetId && !self.sentPlans[resultSet.id]) {
self.sentPlans[resultSet.id] = rows.rows[0][0].displayValue;
self.queryPlanAvailable.emit(rows.rows[0][0].displayValue);
}
resolve(gridData);
});
});
};
// Precalculate the max height and min height
let maxHeight: string = 'inherit';
if (resultSet.rowCount < self._defaultNumShowingRows) {
let maxHeightNumber: number = Math.max((resultSet.rowCount + 1) * self._rowHeight, self.dataIcons.length * 30) + 10;
maxHeight = maxHeightNumber.toString() + 'px';
}
let minHeight: string = maxHeight;
if (resultSet.rowCount >= self._defaultNumShowingRows) {
let minHeightNumber: number = (self._defaultNumShowingRows + 1) * self._rowHeight + 10;
minHeight = minHeightNumber.toString() + 'px';
}
let rowNumberColumn = new RowNumberColumn({ numberOfRows: resultSet.rowCount });
// Store the result set from the event
let dataSet: IGridDataSet = {
resized: undefined,
batchId: resultSet.batchId,
resultId: resultSet.id,
totalRows: resultSet.rowCount,
maxHeight: maxHeight,
minHeight: minHeight,
dataRows: new VirtualizedCollection(
self.windowSize,
resultSet.rowCount,
loadDataFunction,
index => { return { values: [] }; }
),
columnDefinitions: [rowNumberColumn.getColumnDefinition()].concat(resultSet.columnInfo.map((c, i) => {
let isLinked = c.isXml || c.isJson;
let linkType = c.isXml ? 'xml' : 'json';
return {
id: i.toString(),
name: c.columnName === 'Microsoft SQL Server 2005 XML Showplan'
? 'XML Showplan'
: escape(c.columnName),
field: i.toString(),
formatter: isLinked ? Services.hyperLinkFormatter : Services.textFormatter,
asyncPostRender: isLinked ? self.linkHandler(linkType) : undefined
};
}))
};
self.plugins.push([rowNumberColumn, new AutoColumnSize(), new AdditionalKeyBindings()]);
self.dataSets.push(dataSet);
// check if the resultset is for a query plan
for (let i = 0; i < resultSet.columnInfo.length; ++i) {
let column = resultSet.columnInfo[i];
if (column.columnName === 'Microsoft SQL Server 2005 XML Showplan') {
this.hasQueryPlan = true;
this.queryPlanResultSetId = resultSet.id;
break;
}
}
// Create a dataSet to render without rows to reduce DOM size
let undefinedDataSet = clone(dataSet);
undefinedDataSet.columnDefinitions = dataSet.columnDefinitions;
undefinedDataSet.dataRows = undefined;
undefinedDataSet.resized = new EventEmitter();
self.placeHolderDataSets.push(undefinedDataSet);
self.onScroll(0);
}
onCellSelect(gridIndex: number): void {
this.activeGrid = gridIndex;
}
openMessagesContextMenu(event: any): void {
let self = this;
event.preventDefault();
let selectedRange = this.getSelectedRangeUnderMessages();
let selectAllFunc = () => self.selectAllMessages();
let anchor = { x: event.x + 1, y: event.y };
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => this.actionProvider.getMessagesActions(this.dataService, selectAllFunc),
getKeyBinding: (action) => this._keybindingFor(action),
onHide: (wasCancelled?: boolean) => {
},
getActionsContext: () => (selectedRange)
});
}
/**
* Handles rendering the results to the DOM that are currently being shown
* and destroying any results that have moved out of view
* @param scrollTop The scrolltop value, if not called by the scroll event should be 0
*/
onScroll(scrollTop): void {
const self = this;
clearTimeout(self.scrollTimeOut);
this.scrollTimeOut = setTimeout(() => {
if (self.dataSets.length < self.maxScrollGrids) {
self.scrollEnabled = false;
for (let i = 0; i < self.placeHolderDataSets.length; i++) {
self.placeHolderDataSets[i].dataRows = self.dataSets[i].dataRows;
self.placeHolderDataSets[i].resized.emit();
}
} else {
let gridHeight = self._el.nativeElement.getElementsByTagName('slick-grid')[0].offsetHeight;
let tabHeight = self.getResultsElement().offsetHeight;
let numOfVisibleGrids = Math.ceil((tabHeight / gridHeight)
+ ((scrollTop % gridHeight) / gridHeight));
let min = Math.floor(scrollTop / gridHeight);
let max = min + numOfVisibleGrids;
for (let i = 0; i < self.placeHolderDataSets.length; i++) {
if (i >= min && i < max) {
if (self.placeHolderDataSets[i].dataRows === undefined) {
self.placeHolderDataSets[i].dataRows = self.dataSets[i].dataRows;
self.placeHolderDataSets[i].resized.emit();
}
} else if (self.placeHolderDataSets[i].dataRows !== undefined) {
self.placeHolderDataSets[i].dataRows = undefined;
}
}
}
self._cd.detectChanges();
}, self.scrollTimeOutTime);
}
onSelectionLinkClicked(index: number): void {
this.dataService.setEditorSelection(index);
}
onKey(e: Event, index: number) {
if (DOM.isAncestor(<HTMLElement>e.target, this._queryLinkElement.nativeElement) && e instanceof KeyboardEvent) {
let event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Enter)) {
this.onSelectionLinkClicked(index);
e.stopPropagation();
}
}
}
/**
* Sets up the resize for the messages/results panes bar
*/
setupResizeBind(): void {
const self = this;
let resizeHandleElement: HTMLElement = self._el.nativeElement.querySelector('#messageResizeHandle');
let $resizeHandle = $(resizeHandleElement);
let $messages = $(self.getMessagesElement());
$resizeHandle.bind('dragstart', (e) => {
self.resizing = true;
self.resizeHandleTop = self.calculateResizeHandleTop(e.pageY);
self._cd.detectChanges();
return true;
});
$resizeHandle.bind('drag', (e) => {
// Update the animation if the drag is within the allowed range.
if (self.isDragWithinAllowedRange(e.pageY, resizeHandleElement)) {
self.resizeHandleTop = self.calculateResizeHandleTop(e.pageY);
self.resizing = true;
self._cd.detectChanges();
// Stop the animation if the drag is out of the allowed range.
// The animation is resumed when the drag comes back into the allowed range.
} else {
self.resizing = false;
}
});
$resizeHandle.bind('dragend', (e) => {
self.resizing = false;
// Redefine the min size for the messages based on the final position
// if the drag is within the allowed rang
if (self.isDragWithinAllowedRange(e.pageY, resizeHandleElement)) {
let minHeightNumber = this.getMessagePaneHeightFromDrag(e.pageY);
$messages.css('min-height', minHeightNumber + 'px');
self._cd.detectChanges();
self.resizeGrids();
// Otherwise just update the UI to show that the drag is complete
} else {
self._cd.detectChanges();
}
});
}
/**
* Returns true if the resize of the messagepane given by the drag at top=eventPageY is valid,
* false otherwise. A drag is valid if it is below the bottom of the resultspane and
* this.messagePaneHeight pixels above the bottom of the entire angular component.
*/
isDragWithinAllowedRange(eventPageY: number, resizeHandle: HTMLElement): boolean {
let resultspaneElement: HTMLElement = this._el.nativeElement.querySelector('#resultspane');
let minHeight = this.getMessagePaneHeightFromDrag(eventPageY);
if (resultspaneElement &&
minHeight > 0 &&
resultspaneElement.getBoundingClientRect().bottom < eventPageY
) {
return true;
}
return false;
}
/**
* Calculates the position of the top of the resize handle given the Y-axis drag
* coordinate as eventPageY.
*/
calculateResizeHandleTop(eventPageY: number): string {
let resultsWindowTop: number = this._el.nativeElement.getBoundingClientRect().top;
let relativeTop: number = eventPageY - resultsWindowTop;
return relativeTop + 'px';
}
/**
* Returns the height the message pane would be if it were resized so that its top would be set to eventPageY.
* This will return a negative value if eventPageY is below the bottom limit.
*/
getMessagePaneHeightFromDrag(eventPageY: number): number {
let bottomDragLimit: number = this._el.nativeElement.getBoundingClientRect().bottom - this.messagePaneHeight;
return bottomDragLimit - eventPageY;
}
/**
* Ensures the messages tab is scrolled to the bottom
*/
scrollMessages(): void {
let messagesDiv = this.getMessagesElement();
messagesDiv.scrollTop = messagesDiv.scrollHeight;
}
/**
*
*/
protected tryHandleKeyEvent(e): boolean {
return false;
}
/**
* Handles rendering and unrendering necessary resources in order to properly
* navigate from one grid another. Should be called any time grid navigation is performed
* @param targetIndex The index in the renderedDataSets to navigate to
* @returns A boolean representing if the navigation was successful
*/
navigateToGrid(targetIndex: number): boolean {
// check if the target index is valid
if (targetIndex >= this.renderedDataSets.length || !this.hasFocus()) {
return false;
}
// Deselect any text since we are navigating to a new grid
// Do this even if not switching grids, since this covers clicking on the grid after message selection
window.getSelection().removeAllRanges();
// check if you are actually trying to change navigation
if (this.activeGrid === targetIndex) {
return false;
}
this.slickgrids.toArray()[this.activeGrid].selection = false;
this.slickgrids.toArray()[targetIndex].setActive();
this.activeGrid = targetIndex;
// scrolling logic
let resultsWindow = $('#results');
let scrollTop = resultsWindow.scrollTop();
let scrollBottom = scrollTop + resultsWindow.height();
let gridHeight = $(this._el.nativeElement).find('slick-grid').height();
if (scrollBottom < gridHeight * (targetIndex + 1)) {
scrollTop += (gridHeight * (targetIndex + 1)) - scrollBottom;
resultsWindow.scrollTop(scrollTop);
}
if (scrollTop > gridHeight * targetIndex) {
scrollTop = (gridHeight * targetIndex);
resultsWindow.scrollTop(scrollTop);
}
return true;
}
public hasFocus(): boolean {
return DOM.isAncestor(document.activeElement, this._el.nativeElement);
}
resizeGrids(): void {
const self = this;
setTimeout(() => {
for (let grid of self.renderedDataSets) {
grid.resized.emit();
}
});
}
protected showChartForGrid(index: number) {
if (this.renderedDataSets.length > index) {
this.showChartRequested.emit(this.renderedDataSets[index]);
}
}
protected goToNextQueryOutputTab(): void {
this.goToNextQueryOutputTabRequested.emit();
}
protected toggleResultPane(): void {
this.resultActive = !this.resultActive;
this._cd.detectChanges();
if (this.resultActive) {
this.resizeGrids();
this.slickgrids.toArray()[this.activeGrid].setActive();
}
}
protected toggleMessagePane(): void {
this.messageActive = !this.messageActive;
this._cd.detectChanges();
if (this.messageActive && this._messagesContainer) {
let header = <HTMLElement>this._messagesContainer.nativeElement;
header.focus();
}
}
/* Helper function to toggle messages and results panes */
// tslint:disable-next-line:no-unused-variable
private togglePane(pane: PaneType): void {
if (pane === 'messages') {
this.toggleMessagePane();
} else if (pane === 'results') {
this.toggleResultPane();
}
}
private saveViewState(): void {
let gridSelections = this.slickgrids.map(grid => grid.getSelectedRanges());
let resultsScrollElement = (this._resultsScrollBox.nativeElement as HTMLElement);
let resultsScroll = resultsScrollElement.scrollTop;
let messagePaneScroll = (this._messagesContainer.nativeElement as HTMLElement).scrollTop;
let slickGridScrolls = this._slickgridElements.map(element => {
// Get the slick grid's viewport element and save its scroll position
let scrollElement = (element.nativeElement as HTMLElement).children[0].children[3];
return {
vertical: scrollElement.scrollTop,
horizontal: scrollElement.scrollLeft
};
});
this.savedViewState = {
gridSelections,
messagePaneScroll,
resultsScroll,
slickGridScrolls
};
}
private restoreViewState(): void {
if (this.savedViewState) {
this.slickgrids.forEach((grid, index) => grid.selection = this.savedViewState.gridSelections[index]);
(this._resultsScrollBox.nativeElement as HTMLElement).scrollTop = this.savedViewState.resultsScroll;
(this._messagesContainer.nativeElement as HTMLElement).scrollTop = this.savedViewState.messagePaneScroll;
this._slickgridElements.forEach((element, index) => {
let scrollElement = (element.nativeElement as HTMLElement).children[0].children[3];
let savedScroll = this.savedViewState.slickGridScrolls[index];
scrollElement.scrollTop = savedScroll.vertical;
scrollElement.scrollLeft = savedScroll.horizontal;
});
this.savedViewState = undefined;
}
}
layout() {
this.resizeGrids();
}
}

Some files were not shown because too many files have changed in this diff Show More