mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
More Layering (#9139)
* move handling generated files to the serilization classes * remove unneeded methods * add more folders to strictire compile, add more strict compile options * update ci * wip * add more layering and fix issues * add more strictness * remove unnecessary assertion * add missing checks * fix indentation * wip * remove jsdoc * fix layering * fix compile * fix compile errors * wip * wip * finish layering * fix css * more layering * rip * reworking results serializer * move some files around * move capabilities to platform wip * implement capabilities register provider * fix capabilities service * fix usage of the regist4ry * add contribution * wip * wip * wip * remove no longer good parts * fix strict-nulls * fix issues with startup * another try * fix startup * fix imports * fix tests * fix tests * fix more tests * fix tests * fix more tests * fix broken test * fix tabbing * fix naming * wip * finished layering * fix imports * fix valid layers * fix layers
This commit is contained in:
@@ -1,367 +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!./media/accountDialog';
|
||||
import 'vs/css!./media/accountActions';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { List } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { SplitView, Sizing } from 'vs/base/browser/ui/splitview/splitview';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { values } from 'vs/base/common/map';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
import { Button } from 'sql/base/browser/ui/button/button';
|
||||
import { Modal } from 'sql/workbench/browser/modal/modal';
|
||||
import { attachButtonStyler } from 'sql/platform/theme/common/styler';
|
||||
import { AccountViewModel } from 'sql/platform/accounts/common/accountViewModel';
|
||||
import { AddAccountAction } from 'sql/platform/accounts/common/accountActions';
|
||||
import { AccountListRenderer, AccountListDelegate } from 'sql/workbench/contrib/accounts/browser/accountListRenderer';
|
||||
import { AccountProviderAddedEventParams, UpdateAccountListEventParams } from 'sql/platform/accounts/common/eventTypes';
|
||||
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||
import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { attachModalDialogStyler, attachPanelStyler } from 'sql/workbench/common/styler';
|
||||
import { IViewDescriptorService } from 'vs/workbench/common/views';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
|
||||
class AccountPanel extends ViewPane {
|
||||
public index: number;
|
||||
private accountList: List<azdata.Account>;
|
||||
|
||||
constructor(
|
||||
private options: IViewPaneOptions,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IContextMenuService contextMenuService: IContextMenuService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
|
||||
@IOpenerService openerService: IOpenerService,
|
||||
) {
|
||||
super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService);
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement): void {
|
||||
this.accountList = new List<azdata.Account>('AccountList', container, new AccountListDelegate(AccountDialog.ACCOUNTLIST_HEIGHT), [this.instantiationService.createInstance(AccountListRenderer)]);
|
||||
this._register(attachListStyler(this.accountList, this.themeService));
|
||||
}
|
||||
|
||||
protected layoutBody(size: number): void {
|
||||
if (this.accountList) {
|
||||
this.accountList.layout(size);
|
||||
}
|
||||
}
|
||||
|
||||
public get length(): number {
|
||||
return this.accountList.length;
|
||||
}
|
||||
|
||||
public focus() {
|
||||
this.accountList.domFocus();
|
||||
}
|
||||
|
||||
public updateAccounts(accounts: azdata.Account[]) {
|
||||
this.accountList.splice(0, this.accountList.length, accounts);
|
||||
}
|
||||
|
||||
public setSelection(indexes: number[]) {
|
||||
this.accountList.setSelection(indexes);
|
||||
}
|
||||
|
||||
public getActions(): IAction[] {
|
||||
return [this.instantiationService.createInstance(
|
||||
AddAccountAction,
|
||||
this.options.id
|
||||
)];
|
||||
}
|
||||
}
|
||||
|
||||
export interface IProviderViewUiComponent {
|
||||
view: AccountPanel;
|
||||
addAccountAction: AddAccountAction;
|
||||
}
|
||||
|
||||
export class AccountDialog extends Modal {
|
||||
public static ACCOUNTLIST_HEIGHT = 77;
|
||||
|
||||
public viewModel: AccountViewModel;
|
||||
|
||||
// MEMBER VARIABLES ////////////////////////////////////////////////////
|
||||
private _providerViewsMap = new Map<string, IProviderViewUiComponent>();
|
||||
|
||||
private _closeButton: Button;
|
||||
private _addAccountButton: Button;
|
||||
private _splitView: SplitView;
|
||||
private _container: HTMLElement;
|
||||
private _splitViewContainer: HTMLElement;
|
||||
private _noaccountViewContainer: HTMLElement;
|
||||
|
||||
// EVENTING ////////////////////////////////////////////////////////////
|
||||
private _onAddAccountErrorEmitter: Emitter<string>;
|
||||
public get onAddAccountErrorEvent(): Event<string> { return this._onAddAccountErrorEmitter.event; }
|
||||
|
||||
private _onCloseEmitter: Emitter<void>;
|
||||
public get onCloseEvent(): Event<void> { return this._onCloseEmitter.event; }
|
||||
|
||||
constructor(
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IContextMenuService private _contextMenuService: IContextMenuService,
|
||||
@IKeybindingService private _keybindingService: IKeybindingService,
|
||||
@IConfigurationService private _configurationService: IConfigurationService,
|
||||
@IAdsTelemetryService telemetryService: IAdsTelemetryService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IClipboardService clipboardService: IClipboardService,
|
||||
@ILogService logService: ILogService,
|
||||
@IViewDescriptorService private viewDescriptorService: IViewDescriptorService,
|
||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService,
|
||||
@IOpenerService protected readonly openerService: IOpenerService
|
||||
) {
|
||||
super(
|
||||
localize('linkedAccounts', "Linked accounts"),
|
||||
TelemetryKeys.Accounts,
|
||||
telemetryService,
|
||||
layoutService,
|
||||
clipboardService,
|
||||
themeService,
|
||||
logService,
|
||||
textResourcePropertiesService,
|
||||
contextKeyService,
|
||||
{ hasSpinner: true }
|
||||
);
|
||||
|
||||
// Setup the event emitters
|
||||
this._onAddAccountErrorEmitter = new Emitter<string>();
|
||||
this._onCloseEmitter = new Emitter<void>();
|
||||
|
||||
// Create the view model and wire up the events
|
||||
this.viewModel = this._instantiationService.createInstance(AccountViewModel);
|
||||
this.viewModel.addProviderEvent(arg => { this.addProvider(arg); });
|
||||
this.viewModel.removeProviderEvent(arg => { this.removeProvider(arg); });
|
||||
this.viewModel.updateAccountListEvent(arg => { this.updateProviderAccounts(arg); });
|
||||
|
||||
// Load the initial contents of the view model
|
||||
this.viewModel.initialize()
|
||||
.then(addedProviders => {
|
||||
for (const addedProvider of addedProviders) {
|
||||
this.addProvider(addedProvider);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// MODAL OVERRIDE METHODS //////////////////////////////////////////////
|
||||
protected layout(height?: number): void {
|
||||
this._splitView.layout(DOM.getContentHeight(this._container));
|
||||
}
|
||||
|
||||
public render() {
|
||||
super.render();
|
||||
attachModalDialogStyler(this, this._themeService);
|
||||
this._closeButton = this.addFooterButton(localize('accountDialog.close', "Close"), () => this.close());
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement) {
|
||||
this._container = container;
|
||||
this._splitViewContainer = DOM.$('div.account-view.monaco-pane-view');
|
||||
DOM.append(container, this._splitViewContainer);
|
||||
this._splitView = new SplitView(this._splitViewContainer);
|
||||
|
||||
this._noaccountViewContainer = DOM.$('div.no-account-view');
|
||||
const noAccountTitle = DOM.append(this._noaccountViewContainer, DOM.$('.no-account-view-label'));
|
||||
const noAccountLabel = localize('accountDialog.noAccountLabel', "There is no linked account. Please add an account.");
|
||||
noAccountTitle.innerText = noAccountLabel;
|
||||
|
||||
// Show the add account button for the first provider
|
||||
// Todo: If we have more than 1 provider, need to show all add account buttons for all providers
|
||||
const buttonSection = DOM.append(this._noaccountViewContainer, DOM.$('div.button-section'));
|
||||
this._addAccountButton = new Button(buttonSection);
|
||||
this._addAccountButton.label = localize('accountDialog.addConnection', "Add an account");
|
||||
|
||||
this._register(this._addAccountButton.onDidClick(() => {
|
||||
(<IProviderViewUiComponent>values(this._providerViewsMap)[0]).addAccountAction.run();
|
||||
}));
|
||||
|
||||
DOM.append(container, this._noaccountViewContainer);
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
// Theme styler
|
||||
this._register(attachButtonStyler(this._closeButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._addAccountButton, this._themeService));
|
||||
}
|
||||
|
||||
/* Overwrite escape key behavior */
|
||||
protected onClose() {
|
||||
this.close();
|
||||
}
|
||||
|
||||
/* Overwrite enter key behavior */
|
||||
protected onAccept() {
|
||||
this.close();
|
||||
}
|
||||
|
||||
public close() {
|
||||
this._onCloseEmitter.fire();
|
||||
this.hide();
|
||||
}
|
||||
|
||||
public open() {
|
||||
this.show();
|
||||
if (!this.isEmptyLinkedAccount()) {
|
||||
this.showSplitView();
|
||||
} else {
|
||||
this.showNoAccountContainer();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private showNoAccountContainer() {
|
||||
this._splitViewContainer.hidden = true;
|
||||
this._noaccountViewContainer.hidden = false;
|
||||
this._addAccountButton.focus();
|
||||
}
|
||||
|
||||
private showSplitView() {
|
||||
this._splitViewContainer.hidden = false;
|
||||
this._noaccountViewContainer.hidden = true;
|
||||
if (values(this._providerViewsMap).length > 0) {
|
||||
const firstView = values(this._providerViewsMap)[0];
|
||||
if (firstView instanceof AccountPanel) {
|
||||
firstView.setSelection([0]);
|
||||
firstView.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private isEmptyLinkedAccount(): boolean {
|
||||
for (const provider of values(this._providerViewsMap)) {
|
||||
const listView = provider.view;
|
||||
if (listView && listView.length > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
for (const provider of values(this._providerViewsMap)) {
|
||||
if (provider.addAccountAction) {
|
||||
provider.addAccountAction.dispose();
|
||||
}
|
||||
if (provider.view) {
|
||||
provider.view.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
||||
private addProvider(newProvider: AccountProviderAddedEventParams) {
|
||||
|
||||
// Skip adding the provider if it already exists
|
||||
if (this._providerViewsMap.get(newProvider.addedProvider.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Account provider doesn't exist, so add it
|
||||
// Create a scoped add account action
|
||||
let addAccountAction = this._instantiationService.createInstance(
|
||||
AddAccountAction,
|
||||
newProvider.addedProvider.id
|
||||
);
|
||||
addAccountAction.addAccountCompleteEvent(() => this.spinner = false);
|
||||
addAccountAction.addAccountErrorEvent(msg => this._onAddAccountErrorEmitter.fire(msg));
|
||||
addAccountAction.addAccountStartEvent(() => this.spinner = true);
|
||||
|
||||
let providerView = new AccountPanel(
|
||||
{
|
||||
id: newProvider.addedProvider.id,
|
||||
title: newProvider.addedProvider.displayName,
|
||||
ariaHeaderLabel: newProvider.addedProvider.displayName
|
||||
},
|
||||
this._keybindingService,
|
||||
this._contextMenuService,
|
||||
this._configurationService,
|
||||
this._themeService,
|
||||
this.contextKeyService,
|
||||
this._instantiationService,
|
||||
this.viewDescriptorService,
|
||||
this.openerService
|
||||
);
|
||||
|
||||
attachPanelStyler(providerView, this._themeService);
|
||||
|
||||
const insertIndex = this._splitView.length;
|
||||
// Append the list view to the split view
|
||||
this._splitView.addView(providerView, Sizing.Distribute, insertIndex);
|
||||
providerView.render();
|
||||
providerView.index = insertIndex;
|
||||
|
||||
this._splitView.layout(DOM.getContentHeight(this._container));
|
||||
|
||||
// Set the initial items of the list
|
||||
providerView.updateAccounts(newProvider.initialAccounts);
|
||||
|
||||
if (newProvider.initialAccounts.length > 0 && this._splitViewContainer.hidden) {
|
||||
this.showSplitView();
|
||||
}
|
||||
|
||||
this.layout();
|
||||
|
||||
// Store the view for the provider and action
|
||||
this._providerViewsMap.set(newProvider.addedProvider.id, { view: providerView, addAccountAction: addAccountAction });
|
||||
}
|
||||
|
||||
private removeProvider(removedProvider: azdata.AccountProviderMetadata) {
|
||||
// Skip removing the provider if it doesn't exist
|
||||
const providerView = this._providerViewsMap.get(removedProvider.id);
|
||||
if (!providerView || !providerView.view) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the list view from the split view
|
||||
this._splitView.removeView(providerView.view.index);
|
||||
this._splitView.layout(DOM.getContentHeight(this._container));
|
||||
|
||||
// Remove the list view from our internal map
|
||||
this._providerViewsMap.delete(removedProvider.id);
|
||||
this.layout();
|
||||
}
|
||||
|
||||
private updateProviderAccounts(args: UpdateAccountListEventParams) {
|
||||
const providerMapping = this._providerViewsMap.get(args.providerId);
|
||||
if (!providerMapping || !providerMapping.view) {
|
||||
return;
|
||||
}
|
||||
providerMapping.view.updateAccounts(args.accountList);
|
||||
|
||||
if (args.accountList.length > 0 && this._splitViewContainer.hidden) {
|
||||
this.showSplitView();
|
||||
}
|
||||
|
||||
if (this.isEmptyLinkedAccount() && this._noaccountViewContainer.hidden) {
|
||||
this.showNoAccountContainer();
|
||||
}
|
||||
|
||||
this.layout();
|
||||
}
|
||||
}
|
||||
@@ -1,47 +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 Severity from 'vs/base/common/severity';
|
||||
import { AccountDialog } from 'sql/workbench/contrib/accounts/browser/accountDialog';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
|
||||
|
||||
export class AccountDialogController {
|
||||
|
||||
// MEMBER VARIABLES ////////////////////////////////////////////////////
|
||||
private _addAccountErrorTitle = localize('accountDialog.addAccountErrorTitle', "Error adding account");
|
||||
|
||||
private _accountDialog: AccountDialog;
|
||||
public get accountDialog(): AccountDialog { return this._accountDialog; }
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Open account dialog
|
||||
*/
|
||||
public openAccountDialog(): void {
|
||||
// Create a new dialog if one doesn't exist
|
||||
if (!this._accountDialog) {
|
||||
this._accountDialog = this._instantiationService.createInstance(AccountDialog);
|
||||
this._accountDialog.onAddAccountErrorEvent(msg => this.handleOnAddAccountError(msg));
|
||||
this._accountDialog.onCloseEvent(() => this.handleOnClose());
|
||||
this._accountDialog.render();
|
||||
}
|
||||
|
||||
// Open the dialog
|
||||
this._accountDialog.open();
|
||||
}
|
||||
|
||||
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
||||
private handleOnClose(): void { }
|
||||
|
||||
private handleOnAddAccountError(msg: string): void {
|
||||
this._errorMessageService.showDialog(Severity.Error, this._addAccountErrorTitle, msg);
|
||||
}
|
||||
}
|
||||
@@ -1,140 +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!./media/accountListRenderer';
|
||||
import 'vs/css!./media/accountActions';
|
||||
import 'vs/css!sql/media/icons/common-icons';
|
||||
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { ActionBar, IActionOptions } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
import { RemoveAccountAction, RefreshAccountAction } from 'sql/platform/accounts/common/accountActions';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
export class AccountListDelegate implements IListVirtualDelegate<azdata.Account> {
|
||||
|
||||
constructor(
|
||||
private _height: number
|
||||
) {
|
||||
}
|
||||
|
||||
public getHeight(element: azdata.Account): number {
|
||||
return this._height;
|
||||
}
|
||||
|
||||
public getTemplateId(element: azdata.Account): string {
|
||||
return 'accountListRenderer';
|
||||
}
|
||||
}
|
||||
|
||||
export interface AccountPickerListTemplate {
|
||||
root: HTMLElement;
|
||||
icon: HTMLElement;
|
||||
badgeContent: HTMLElement;
|
||||
contextualDisplayName: HTMLElement;
|
||||
label: HTMLElement;
|
||||
displayName: HTMLElement;
|
||||
}
|
||||
|
||||
export interface AccountListTemplate extends AccountPickerListTemplate {
|
||||
content: HTMLElement;
|
||||
actions: ActionBar;
|
||||
}
|
||||
|
||||
export class AccountPickerListRenderer implements IListRenderer<azdata.Account, AccountPickerListTemplate> {
|
||||
public static TEMPLATE_ID = 'accountListRenderer';
|
||||
|
||||
public get templateId(): string {
|
||||
return AccountPickerListRenderer.TEMPLATE_ID;
|
||||
}
|
||||
|
||||
public renderTemplate(container: HTMLElement): AccountPickerListTemplate {
|
||||
const tableTemplate: AccountPickerListTemplate = Object.create(null);
|
||||
const badge = DOM.$('div.badge');
|
||||
tableTemplate.root = DOM.append(container, DOM.$('div.list-row.account-picker-list'));
|
||||
tableTemplate.icon = DOM.append(tableTemplate.root, DOM.$('div.codicon'));
|
||||
DOM.append(tableTemplate.icon, badge);
|
||||
tableTemplate.badgeContent = DOM.append(badge, DOM.$('div.badge-content'));
|
||||
tableTemplate.label = DOM.append(tableTemplate.root, DOM.$('div.label'));
|
||||
tableTemplate.contextualDisplayName = DOM.append(tableTemplate.label, DOM.$('div.contextual-display-name'));
|
||||
tableTemplate.displayName = DOM.append(tableTemplate.label, DOM.$('div.display-name'));
|
||||
return tableTemplate;
|
||||
}
|
||||
|
||||
public renderElement(account: azdata.Account, index: number, templateData: AccountPickerListTemplate): void {
|
||||
// Set the account icon
|
||||
templateData.icon.classList.add('account-logo', account.displayInfo.accountType);
|
||||
|
||||
templateData.contextualDisplayName.innerText = account.displayInfo.contextualDisplayName;
|
||||
|
||||
// show the account display name text, something like "User Name (user@email.com)"
|
||||
if (account.displayInfo.userId && account.displayInfo.displayName) {
|
||||
templateData.displayName.innerText = account.displayInfo.displayName + ' (' + account.displayInfo.userId + ')';
|
||||
} else {
|
||||
templateData.displayName.innerText = account.displayInfo.displayName;
|
||||
}
|
||||
|
||||
if (account.isStale) {
|
||||
templateData.badgeContent.className = 'badge-content codicon warning-badge';
|
||||
} else {
|
||||
templateData.badgeContent.className = 'badge-content';
|
||||
}
|
||||
}
|
||||
|
||||
public disposeTemplate(template: AccountPickerListTemplate): void {
|
||||
// noop
|
||||
}
|
||||
|
||||
public disposeElement(element: azdata.Account, index: number, templateData: AccountPickerListTemplate): void {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
export class AccountListRenderer extends AccountPickerListRenderer {
|
||||
constructor(
|
||||
@IInstantiationService private _instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
public get templateId(): string {
|
||||
return AccountListRenderer.TEMPLATE_ID;
|
||||
}
|
||||
|
||||
public renderTemplate(container: HTMLElement): AccountListTemplate {
|
||||
const tableTemplate = super.renderTemplate(container) as AccountListTemplate;
|
||||
tableTemplate.content = DOM.append(tableTemplate.label, DOM.$('div.content'));
|
||||
tableTemplate.actions = new ActionBar(tableTemplate.root, { animated: false });
|
||||
|
||||
return tableTemplate;
|
||||
}
|
||||
|
||||
public renderElement(account: azdata.Account, index: number, templateData: AccountListTemplate): void {
|
||||
super.renderElement(account, index, templateData);
|
||||
if (account.isStale) {
|
||||
templateData.content.innerText = localize('refreshCredentials', "You need to refresh the credentials for this account.");
|
||||
} else {
|
||||
templateData.content.innerText = '';
|
||||
}
|
||||
templateData.actions.clear();
|
||||
|
||||
const actionOptions: IActionOptions = { icon: true, label: false };
|
||||
if (account.isStale) {
|
||||
const refreshAction = this._instantiationService.createInstance(RefreshAccountAction);
|
||||
refreshAction.account = account;
|
||||
templateData.actions.push(refreshAction, actionOptions);
|
||||
} else {
|
||||
// Todo: Will show filter action when API/GUI for filtering is implemented (#3022, #3024)
|
||||
// templateData.actions.push(new ApplyFilterAction(ApplyFilterAction.ID, ApplyFilterAction.LABEL), actionOptions);
|
||||
}
|
||||
|
||||
const removeAction = this._instantiationService.createInstance(RemoveAccountAction, account);
|
||||
templateData.actions.push(removeAction, actionOptions);
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
export const IAccountPickerService = createDecorator<IAccountPickerService>('AccountPickerService');
|
||||
export interface IAccountPickerService {
|
||||
_serviceBrand: undefined;
|
||||
renderAccountPicker(container: HTMLElement): void;
|
||||
addAccountCompleteEvent: Event<void>;
|
||||
addAccountErrorEvent: Event<string>;
|
||||
addAccountStartEvent: Event<void>;
|
||||
onAccountSelectionChangeEvent: Event<azdata.Account | undefined>;
|
||||
selectedAccount: azdata.Account | undefined;
|
||||
}
|
||||
@@ -1,237 +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!./media/accountPicker';
|
||||
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 { 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 { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import { DropdownList } from 'sql/base/browser/ui/dropdownList/dropdownList';
|
||||
import { attachDropdownStyler } from 'sql/platform/theme/common/styler';
|
||||
import { AddAccountAction, RefreshAccountAction } from 'sql/platform/accounts/common/accountActions';
|
||||
import { AccountPickerListRenderer, AccountListDelegate } from 'sql/workbench/contrib/accounts/browser/accountListRenderer';
|
||||
import { AccountPickerViewModel } from 'sql/platform/accounts/common/accountPickerViewModel';
|
||||
import { firstIndex } from 'vs/base/common/arrays';
|
||||
|
||||
export class AccountPicker extends Disposable {
|
||||
public static ACCOUNTPICKERLIST_HEIGHT = 47;
|
||||
public viewModel: AccountPickerViewModel;
|
||||
private _accountList: List<azdata.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<azdata.Account | undefined>;
|
||||
public get onAccountSelectionChangeEvent(): Event<azdata.Account | undefined> { return this._onAccountSelectionChangeEvent.event; }
|
||||
|
||||
constructor(
|
||||
private _providerId: string,
|
||||
@IThemeService private _themeService: IThemeService,
|
||||
@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<azdata.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
|
||||
const delegate = new AccountListDelegate(AccountPicker.ACCOUNTPICKERLIST_HEIGHT);
|
||||
const accountRenderer = new AccountPickerListRenderer();
|
||||
this._listContainer = DOM.$('div.account-list-container');
|
||||
this._accountList = new List<azdata.Account>('AccountPicker', this._listContainer, delegate, [accountRenderer]);
|
||||
this._register(attachListStyler(this._accountList, this._themeService));
|
||||
|
||||
this._rootElement = DOM.$('div.account-picker-container');
|
||||
|
||||
// Create a dropdown for account picker
|
||||
const option: IDropdownOptions = {
|
||||
contextViewProvider: this._contextViewService,
|
||||
labelRenderer: (container) => this.renderLabel(container)
|
||||
};
|
||||
|
||||
// Create the add account action
|
||||
const 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, addAccountAction));
|
||||
this._register(attachDropdownStyler(this._dropdown, this._themeService));
|
||||
this._register(this._accountList.onSelectionChange((e: IListEvent<azdata.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 codicon warning'));
|
||||
const 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 {
|
||||
DOM.hide(this._refreshContainer);
|
||||
}
|
||||
|
||||
this._register(this._themeService.onThemeChange(e => this.updateTheme(e)));
|
||||
this.updateTheme(this._themeService.getTheme());
|
||||
|
||||
// Load the initial contents of the view model
|
||||
this.viewModel.initialize()
|
||||
.then((accounts: azdata.Account[]) => {
|
||||
this.updateAccountList(accounts);
|
||||
});
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
super.dispose();
|
||||
if (this._accountList) {
|
||||
this._accountList.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
||||
private onAccountSelectionChange(account: azdata.Account | undefined) {
|
||||
this.viewModel.selectedAccount = account;
|
||||
if (account && account.isStale) {
|
||||
this._refreshAccountAction.account = account;
|
||||
DOM.show(this._refreshContainer);
|
||||
} else {
|
||||
DOM.hide(this._refreshContainer);
|
||||
}
|
||||
|
||||
this._onAccountSelectionChangeEvent.fire(account);
|
||||
}
|
||||
|
||||
private renderLabel(container: HTMLElement): IDisposable | null {
|
||||
if (container.hasChildNodes()) {
|
||||
for (let i = 0; i < container.childNodes.length; i++) {
|
||||
container.removeChild(container.childNodes.item(i));
|
||||
}
|
||||
}
|
||||
|
||||
const selectedAccounts = this._accountList.getSelectedElements();
|
||||
const account = selectedAccounts ? selectedAccounts[0] : undefined;
|
||||
if (account) {
|
||||
const badge = DOM.$('div.badge');
|
||||
const row = DOM.append(container, DOM.$('div.selected-account-container'));
|
||||
const icon = DOM.append(row, DOM.$('div.codicon'));
|
||||
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 codicon 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: azdata.Account[]): void {
|
||||
// keep the selection to the current one
|
||||
const selectedElements = this._accountList.getSelectedElements();
|
||||
|
||||
// find selected index
|
||||
let selectedIndex: number | undefined;
|
||||
if (selectedElements.length > 0 && accounts.length > 0) {
|
||||
selectedIndex = firstIndex(accounts, (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: ITheme): void {
|
||||
const linkColor = theme.getColor(buttonBackground);
|
||||
const link = linkColor ? linkColor.toString() : null;
|
||||
this._refreshContainer.style.color = link;
|
||||
if (this._refreshContainer) {
|
||||
this._refreshContainer.style.color = link;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,65 +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 { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
import { IAccountPickerService } from 'sql/workbench/contrib/accounts/browser/accountPicker';
|
||||
import { AccountPicker } from 'sql/workbench/contrib/accounts/browser/accountPickerImpl';
|
||||
|
||||
export class AccountPickerService implements IAccountPickerService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
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<azdata.Account | undefined>;
|
||||
public get onAccountSelectionChangeEvent(): Event<azdata.Account | undefined> { 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<azdata.Account>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get selected account
|
||||
*/
|
||||
public get selectedAccount(): azdata.Account | undefined {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,158 +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!./media/autoOAuthDialog';
|
||||
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { $, append } from 'vs/base/browser/dom';
|
||||
|
||||
import { Button } from 'sql/base/browser/ui/button/button';
|
||||
import { Modal } from 'sql/workbench/browser/modal/modal';
|
||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||
import { attachButtonStyler } from 'sql/platform/theme/common/styler';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||
import { attachModalDialogStyler } from 'sql/workbench/common/styler';
|
||||
|
||||
export class AutoOAuthDialog extends Modal {
|
||||
private _copyAndOpenButton: Button;
|
||||
private _closeButton: Button;
|
||||
private _userCodeInputBox: InputBox;
|
||||
private _websiteInputBox: InputBox;
|
||||
private _descriptionElement: HTMLElement;
|
||||
|
||||
// EVENTING ////////////////////////////////////////////////////////////
|
||||
private _onHandleAddAccount = new Emitter<void>();
|
||||
public get onHandleAddAccount(): Event<void> { return this._onHandleAddAccount.event; }
|
||||
|
||||
private _onCancel = new Emitter<void>();
|
||||
public get onCancel(): Event<void> { return this._onCancel.event; }
|
||||
|
||||
|
||||
private _onCloseEvent = new Emitter<void>();
|
||||
public get onCloseEvent(): Event<void> { return this._onCloseEvent.event; }
|
||||
|
||||
constructor(
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IContextViewService private _contextViewService: IContextViewService,
|
||||
@IAdsTelemetryService telemetryService: IAdsTelemetryService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IClipboardService clipboardService: IClipboardService,
|
||||
@ILogService logService: ILogService,
|
||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
|
||||
) {
|
||||
super(
|
||||
'',
|
||||
TelemetryKeys.AutoOAuth,
|
||||
telemetryService,
|
||||
layoutService,
|
||||
clipboardService,
|
||||
themeService,
|
||||
logService,
|
||||
textResourcePropertiesService,
|
||||
contextKeyService,
|
||||
{
|
||||
isFlyout: true,
|
||||
hasBackButton: true,
|
||||
hasSpinner: true
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public render() {
|
||||
super.render();
|
||||
attachModalDialogStyler(this, this._themeService);
|
||||
this.backButton.onDidClick(() => this.cancel());
|
||||
this._register(attachButtonStyler(this.backButton, this._themeService, { buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND }));
|
||||
|
||||
this._copyAndOpenButton = this.addFooterButton(localize('copyAndOpen', "Copy & Open"), () => this.addAccount());
|
||||
this._closeButton = this.addFooterButton(localize('oauthDialog.cancel', "Cancel"), () => this.cancel());
|
||||
this.registerListeners();
|
||||
this._userCodeInputBox.disable();
|
||||
this._websiteInputBox.disable();
|
||||
}
|
||||
|
||||
protected layout(height?: number): void {
|
||||
// NO OP
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement) {
|
||||
const body = append(container, $('.auto-oauth-dialog'));
|
||||
this._descriptionElement = append(body, $('.auto-oauth-description-section.new-section'));
|
||||
|
||||
const addAccountSection = append(body, $('.auto-oauth-info-section.new-section'));
|
||||
this._userCodeInputBox = this.createInputBoxHelper(addAccountSection, localize('userCode', "User code"));
|
||||
this._websiteInputBox = this.createInputBoxHelper(addAccountSection, localize('website', "Website"));
|
||||
}
|
||||
|
||||
private createInputBoxHelper(container: HTMLElement, label: string): InputBox {
|
||||
const inputContainer = append(container, $('.dialog-input-section'));
|
||||
append(inputContainer, $('.dialog-label')).innerText = label;
|
||||
const inputCellContainer = append(inputContainer, $('.dialog-input'));
|
||||
|
||||
return new InputBox(inputCellContainer, this._contextViewService, {
|
||||
ariaLabel: label
|
||||
});
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
// Theme styler
|
||||
this._register(attachButtonStyler(this._copyAndOpenButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._closeButton, this._themeService));
|
||||
this._register(attachInputBoxStyler(this._userCodeInputBox, this._themeService));
|
||||
this._register(attachInputBoxStyler(this._websiteInputBox, this._themeService));
|
||||
|
||||
}
|
||||
|
||||
/* Overwrite escape key behavior */
|
||||
protected onClose() {
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
/* Overwrite enter key behavior */
|
||||
protected onAccept() {
|
||||
this.addAccount();
|
||||
}
|
||||
|
||||
private addAccount() {
|
||||
if (this._copyAndOpenButton.enabled) {
|
||||
this._copyAndOpenButton.enabled = false;
|
||||
this.spinner = true;
|
||||
this._onHandleAddAccount.fire();
|
||||
}
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
this._onCancel.fire();
|
||||
}
|
||||
|
||||
public close() {
|
||||
this._copyAndOpenButton.enabled = true;
|
||||
this._onCloseEvent.fire();
|
||||
this.spinner = false;
|
||||
this.hide();
|
||||
}
|
||||
|
||||
public open(title: string, message: string, userCode: string, uri: string) {
|
||||
// Update dialog
|
||||
this.title = title;
|
||||
this._descriptionElement.innerText = message;
|
||||
this._userCodeInputBox.value = userCode;
|
||||
this._websiteInputBox.value = uri;
|
||||
this.show();
|
||||
this._copyAndOpenButton.focus();
|
||||
}
|
||||
}
|
||||
@@ -1,76 +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 { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
import { AutoOAuthDialog } from 'sql/workbench/contrib/accounts/browser/autoOAuthDialog';
|
||||
import { IAccountManagementService } from 'sql/platform/accounts/common/interfaces';
|
||||
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
|
||||
|
||||
export class AutoOAuthDialogController {
|
||||
// MEMBER VARIABLES ////////////////////////////////////////////////////
|
||||
private _autoOAuthDialog: AutoOAuthDialog;
|
||||
private _providerId?: string;
|
||||
private _userCode: string;
|
||||
private _uri: string;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IAccountManagementService private _accountManagementService: IAccountManagementService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Open auto OAuth dialog
|
||||
*/
|
||||
public openAutoOAuthDialog(providerId: string, title: string, message: string, userCode: string, uri: string): Thenable<void> {
|
||||
if (this._providerId !== undefined) {
|
||||
// If a oauth flyout is already open, return an error
|
||||
const errorMessage = localize('oauthFlyoutIsAlreadyOpen', "Cannot start auto OAuth. An auto OAuth is already in progress.");
|
||||
this._errorMessageService.showDialog(Severity.Error, '', errorMessage);
|
||||
return Promise.reject(new Error('Auto OAuth dialog already open'));
|
||||
}
|
||||
|
||||
// Create a new dialog if one doesn't exist
|
||||
if (!this._autoOAuthDialog) {
|
||||
this._autoOAuthDialog = this._instantiationService.createInstance(AutoOAuthDialog);
|
||||
this._autoOAuthDialog.onHandleAddAccount(this.handleOnAddAccount, this);
|
||||
this._autoOAuthDialog.onCancel(this.handleOnCancel, this);
|
||||
this._autoOAuthDialog.onCloseEvent(this.handleOnClose, this);
|
||||
this._autoOAuthDialog.render();
|
||||
}
|
||||
|
||||
this._userCode = userCode;
|
||||
this._uri = uri;
|
||||
|
||||
// Open the dialog
|
||||
this._autoOAuthDialog.open(title, message, userCode, uri);
|
||||
this._providerId = providerId;
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Close auto OAuth dialog
|
||||
*/
|
||||
public closeAutoOAuthDialog(): void {
|
||||
this._autoOAuthDialog.close();
|
||||
this._providerId = undefined;
|
||||
}
|
||||
|
||||
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
||||
private handleOnCancel(): void {
|
||||
this._accountManagementService.cancelAutoOAuthDeviceCode(this._providerId!); // this should be always true
|
||||
}
|
||||
|
||||
private handleOnClose(): void {
|
||||
this._providerId = undefined;
|
||||
}
|
||||
|
||||
private handleOnAddAccount(): void {
|
||||
this._accountManagementService.copyUserCodeAndOpenBrowser(this._userCode, this._uri);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/firewallRuleDialog';
|
||||
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
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 { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import { Button } from 'sql/base/browser/ui/button/button';
|
||||
import { Modal } from 'sql/workbench/browser/modal/modal';
|
||||
import { FirewallRuleViewModel } from 'sql/platform/accounts/common/firewallRuleViewModel';
|
||||
import { attachButtonStyler } from 'sql/platform/theme/common/styler';
|
||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||
import { IAccountPickerService } from 'sql/workbench/contrib/accounts/browser/accountPicker';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||
import { attachModalDialogStyler } from 'sql/workbench/common/styler';
|
||||
|
||||
// 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
|
||||
const firewallHelpUri = 'https://aka.ms/sqlopsfirewallhelp';
|
||||
|
||||
const LocalizedStrings = {
|
||||
FROM: localize('from', "From"),
|
||||
TO: localize('to', "To")
|
||||
};
|
||||
|
||||
export class FirewallRuleDialog extends Modal {
|
||||
public viewModel: FirewallRuleViewModel;
|
||||
private _createButton: Button;
|
||||
private _closeButton: Button;
|
||||
private _fromRangeinputBox: InputBox;
|
||||
private _toRangeinputBox: InputBox;
|
||||
|
||||
private _helpLink: HTMLElement;
|
||||
private _IPAddressInput: HTMLElement;
|
||||
private _subnetIPRangeInput: HTMLElement;
|
||||
private _IPAddressElement: HTMLElement;
|
||||
|
||||
// EVENTING ////////////////////////////////////////////////////////////
|
||||
private _onAddAccountErrorEmitter: Emitter<string>;
|
||||
public get onAddAccountErrorEvent(): Event<string> { return this._onAddAccountErrorEmitter.event; }
|
||||
|
||||
private _onCancel: Emitter<void>;
|
||||
public get onCancel(): Event<void> { return this._onCancel.event; }
|
||||
|
||||
private _onCreateFirewallRule: Emitter<void>;
|
||||
public get onCreateFirewallRule(): Event<void> { return this._onCreateFirewallRule.event; }
|
||||
|
||||
constructor(
|
||||
@IAccountPickerService private _accountPickerService: IAccountPickerService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IContextViewService private _contextViewService: IContextViewService,
|
||||
@IAdsTelemetryService telemetryService: IAdsTelemetryService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IClipboardService clipboardService: IClipboardService,
|
||||
@ILogService logService: ILogService,
|
||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService,
|
||||
@IOpenerService private readonly openerService: IOpenerService
|
||||
) {
|
||||
super(
|
||||
localize('createNewFirewallRule', "Create new firewall rule"),
|
||||
TelemetryKeys.FireWallRule,
|
||||
telemetryService,
|
||||
layoutService,
|
||||
clipboardService,
|
||||
themeService,
|
||||
logService,
|
||||
textResourcePropertiesService,
|
||||
contextKeyService,
|
||||
{
|
||||
isFlyout: true,
|
||||
hasBackButton: true,
|
||||
hasSpinner: true
|
||||
}
|
||||
);
|
||||
|
||||
// Setup event emitters
|
||||
this._onAddAccountErrorEmitter = new Emitter<string>();
|
||||
this._onCancel = new Emitter<void>();
|
||||
this._onCreateFirewallRule = new Emitter<void>();
|
||||
|
||||
this.viewModel = this._instantiationService.createInstance(FirewallRuleViewModel);
|
||||
}
|
||||
|
||||
public render() {
|
||||
super.render();
|
||||
attachModalDialogStyler(this, this._themeService);
|
||||
this.backButton.onDidClick(() => this.cancel());
|
||||
this._register(attachButtonStyler(this.backButton, this._themeService, { buttonBackground: SIDE_BAR_BACKGROUND, buttonHoverBackground: SIDE_BAR_BACKGROUND }));
|
||||
this._createButton = this.addFooterButton(localize('firewall.ok', "OK"), () => this.createFirewallRule());
|
||||
this._closeButton = this.addFooterButton(localize('firewall.cancel', "Cancel"), () => this.cancel());
|
||||
this.registerListeners();
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement) {
|
||||
const body = DOM.append(container, DOM.$('.firewall-rule-dialog'));
|
||||
const descriptionSection = DOM.append(body, DOM.$('.firewall-rule-description-section.new-section'));
|
||||
|
||||
DOM.append(descriptionSection, DOM.$('div.firewall-rule-icon'));
|
||||
|
||||
const textDescriptionContainer = DOM.append(descriptionSection, DOM.$('div.firewall-rule-description'));
|
||||
const dialogDescription = localize('firewallRuleDialogDescription',
|
||||
"Your client IP address does not have access to the server. Sign in to an Azure account and create a new firewall rule to enable access.");
|
||||
this.createLabelElement(textDescriptionContainer, dialogDescription, false);
|
||||
|
||||
this._helpLink = DOM.append(textDescriptionContainer, DOM.$('a.help-link'));
|
||||
this._helpLink.setAttribute('href', firewallHelpUri);
|
||||
this._helpLink.innerHTML += localize('firewallRuleHelpDescription', "Learn more about firewall settings");
|
||||
this._helpLink.onclick = () => {
|
||||
this.openerService.open(URI.parse(firewallHelpUri));
|
||||
};
|
||||
|
||||
// Create account picker with event handling
|
||||
this._accountPickerService.addAccountCompleteEvent(() => this.spinner = false);
|
||||
this._accountPickerService.addAccountErrorEvent((msg) => {
|
||||
this.spinner = false;
|
||||
this._onAddAccountErrorEmitter.fire(msg);
|
||||
});
|
||||
this._accountPickerService.addAccountStartEvent(() => this.spinner = true);
|
||||
this._accountPickerService.onAccountSelectionChangeEvent((account) => this.onAccountSelectionChange(account));
|
||||
|
||||
const azureAccountSection = DOM.append(body, DOM.$('.azure-account-section.new-section'));
|
||||
const azureAccountLabel = localize('azureAccount', "Azure account");
|
||||
this.createLabelElement(azureAccountSection, azureAccountLabel, true);
|
||||
this._accountPickerService.renderAccountPicker(DOM.append(azureAccountSection, DOM.$('.dialog-input')));
|
||||
|
||||
const firewallRuleSection = DOM.append(body, DOM.$('.firewall-rule-section.new-section'));
|
||||
const firewallRuleLabel = localize('filewallRule', "Firewall rule");
|
||||
this.createLabelElement(firewallRuleSection, firewallRuleLabel, true);
|
||||
const radioContainer = DOM.append(firewallRuleSection, DOM.$('.radio-section'));
|
||||
const form = DOM.append(radioContainer, DOM.$('form.firewall-rule'));
|
||||
const IPAddressDiv = DOM.append(form, DOM.$('div.firewall-ip-address dialog-input'));
|
||||
const subnetIPRangeDiv = DOM.append(form, DOM.$('div.firewall-subnet-ip-range dialog-input'));
|
||||
|
||||
const IPAddressContainer = DOM.append(IPAddressDiv, DOM.$('div.option-container'));
|
||||
this._IPAddressInput = DOM.append(IPAddressContainer, DOM.$('input.option-input'));
|
||||
this._IPAddressInput.setAttribute('type', 'radio');
|
||||
this._IPAddressInput.setAttribute('name', 'firewallRuleChoice');
|
||||
this._IPAddressInput.setAttribute('value', 'ipAddress');
|
||||
const IPAddressDescription = DOM.append(IPAddressContainer, DOM.$('div.option-description'));
|
||||
IPAddressDescription.innerText = localize('addIPAddressLabel', "Add my client IP ");
|
||||
this._IPAddressElement = DOM.append(IPAddressContainer, DOM.$('div.option-ip-address'));
|
||||
|
||||
const subnetIpRangeContainer = DOM.append(subnetIPRangeDiv, DOM.$('div.option-container'));
|
||||
this._subnetIPRangeInput = DOM.append(subnetIpRangeContainer, DOM.$('input.option-input'));
|
||||
this._subnetIPRangeInput.setAttribute('type', 'radio');
|
||||
this._subnetIPRangeInput.setAttribute('name', 'firewallRuleChoice');
|
||||
this._subnetIPRangeInput.setAttribute('value', 'ipRange');
|
||||
const subnetIPRangeDescription = DOM.append(subnetIpRangeContainer, DOM.$('div.option-description'));
|
||||
subnetIPRangeDescription.innerText = localize('addIpRangeLabel', "Add my subnet IP range");
|
||||
const subnetIPRangeSection = DOM.append(subnetIPRangeDiv, DOM.$('.subnet-ip-range-input'));
|
||||
|
||||
const inputContainer = DOM.append(subnetIPRangeSection, DOM.$('.dialog-input-section'));
|
||||
|
||||
DOM.append(inputContainer, DOM.$('.dialog-label')).innerText = LocalizedStrings.FROM;
|
||||
|
||||
this._fromRangeinputBox = new InputBox(DOM.append(inputContainer, DOM.$('.dialog-input')), this._contextViewService, {
|
||||
ariaLabel: LocalizedStrings.FROM
|
||||
});
|
||||
|
||||
DOM.append(inputContainer, DOM.$('.dialog-label')).innerText = LocalizedStrings.TO;
|
||||
|
||||
this._toRangeinputBox = new InputBox(DOM.append(inputContainer, DOM.$('.dialog-input')), this._contextViewService, {
|
||||
ariaLabel: LocalizedStrings.TO
|
||||
});
|
||||
|
||||
this._register(this._themeService.onThemeChange(e => this.updateTheme(e)));
|
||||
this.updateTheme(this._themeService.getTheme());
|
||||
|
||||
this._register(DOM.addDisposableListener(this._IPAddressElement, DOM.EventType.CLICK, () => {
|
||||
this.onFirewallRuleOptionSelected(true);
|
||||
}));
|
||||
|
||||
this._register(DOM.addDisposableListener(this._subnetIPRangeInput, DOM.EventType.CLICK, () => {
|
||||
this.onFirewallRuleOptionSelected(false);
|
||||
}));
|
||||
}
|
||||
|
||||
private onFirewallRuleOptionSelected(isIPAddress: boolean) {
|
||||
this.viewModel.isIPAddressSelected = isIPAddress;
|
||||
if (this._fromRangeinputBox) {
|
||||
isIPAddress ? this._fromRangeinputBox.disable() : this._fromRangeinputBox.enable();
|
||||
}
|
||||
if (this._toRangeinputBox) {
|
||||
isIPAddress ? this._toRangeinputBox.disable() : this._toRangeinputBox.enable();
|
||||
}
|
||||
}
|
||||
|
||||
protected layout(height?: number): void {
|
||||
// Nothing currently laid out statically in this class
|
||||
}
|
||||
|
||||
private createLabelElement(container: HTMLElement, content: string, isHeader?: boolean) {
|
||||
let className = 'dialog-label';
|
||||
if (isHeader) {
|
||||
className += ' header';
|
||||
}
|
||||
DOM.append(container, DOM.$(`.${className}`)).innerText = content;
|
||||
}
|
||||
|
||||
// Update theming that is specific to firewall rule flyout body
|
||||
private updateTheme(theme: ITheme): void {
|
||||
const linkColor = theme.getColor(buttonBackground);
|
||||
const link = linkColor ? linkColor.toString() : null;
|
||||
if (this._helpLink) {
|
||||
this._helpLink.style.color = link;
|
||||
}
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
// Theme styler
|
||||
this._register(attachButtonStyler(this._createButton, this._themeService));
|
||||
this._register(attachButtonStyler(this._closeButton, this._themeService));
|
||||
this._register(attachInputBoxStyler(this._fromRangeinputBox, this._themeService));
|
||||
this._register(attachInputBoxStyler(this._toRangeinputBox, this._themeService));
|
||||
|
||||
// handler for from subnet ip range change events
|
||||
this._register(this._fromRangeinputBox.onDidChange(IPAddress => {
|
||||
this.fromRangeInputChanged(IPAddress);
|
||||
}));
|
||||
|
||||
// handler for to subnet ip range change events
|
||||
this._register(this._toRangeinputBox.onDidChange(IPAddress => {
|
||||
this.toRangeInputChanged(IPAddress);
|
||||
}));
|
||||
}
|
||||
|
||||
private fromRangeInputChanged(IPAddress: string) {
|
||||
this.viewModel.fromSubnetIPRange = IPAddress;
|
||||
}
|
||||
|
||||
private toRangeInputChanged(IPAddress: string) {
|
||||
this.viewModel.toSubnetIPRange = IPAddress;
|
||||
}
|
||||
|
||||
/* Overwrite esapce key behavior */
|
||||
protected onClose() {
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
/* Overwrite enter key behavior */
|
||||
protected onAccept() {
|
||||
this.createFirewallRule();
|
||||
}
|
||||
|
||||
public cancel() {
|
||||
this._onCancel.fire();
|
||||
this.close();
|
||||
}
|
||||
|
||||
public close() {
|
||||
this.hide();
|
||||
}
|
||||
|
||||
public createFirewallRule() {
|
||||
if (this._createButton.enabled) {
|
||||
this._createButton.enabled = false;
|
||||
this.spinner = true;
|
||||
this._onCreateFirewallRule.fire();
|
||||
}
|
||||
}
|
||||
|
||||
public onAccountSelectionChange(account: azdata.Account | undefined): void {
|
||||
this.viewModel.selectedAccount = account;
|
||||
if (account && !account.isStale) {
|
||||
this._createButton.enabled = true;
|
||||
} else {
|
||||
this._createButton.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
public onServiceComplete() {
|
||||
this._createButton.enabled = true;
|
||||
this.spinner = false;
|
||||
}
|
||||
|
||||
public open() {
|
||||
this._IPAddressInput.click();
|
||||
this.onAccountSelectionChange(this._accountPickerService.selectedAccount);
|
||||
this._fromRangeinputBox.setPlaceHolder(this.viewModel.defaultFromSubnetIPRange);
|
||||
this._toRangeinputBox.setPlaceHolder(this.viewModel.defaultToSubnetIPRange);
|
||||
this._IPAddressElement.innerText = '(' + this.viewModel.defaultIPAddress + ')';
|
||||
|
||||
this.show();
|
||||
}
|
||||
}
|
||||
@@ -1,94 +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 { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { localize } from 'vs/nls';
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { FirewallRuleDialog } from 'sql/workbench/contrib/accounts/browser/firewallRuleDialog';
|
||||
import { IAccountManagementService, AzureResource } from 'sql/platform/accounts/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 {
|
||||
|
||||
private _firewallRuleDialog: FirewallRuleDialog;
|
||||
private _connection: IConnectionProfile;
|
||||
private _resourceProviderId: string;
|
||||
|
||||
private _addAccountErrorTitle = localize('firewallDialog.addAccountErrorTitle', "Error adding account");
|
||||
private _firewallRuleErrorTitle = localize('firewallRuleError', "Firewall rule error");
|
||||
private _deferredPromise: Deferred<boolean>;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IResourceProviderService private _resourceProviderService: IResourceProviderService,
|
||||
@IAccountManagementService private _accountManagementService: IAccountManagementService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Open firewall rule dialog
|
||||
*/
|
||||
public openFirewallRuleDialog(connection: IConnectionProfile, ipAddress: string, resourceProviderId: string): Promise<boolean> {
|
||||
if (!this._firewallRuleDialog) {
|
||||
this._firewallRuleDialog = this._instantiationService.createInstance(FirewallRuleDialog);
|
||||
this._firewallRuleDialog.onCancel(this.handleOnCancel, this);
|
||||
this._firewallRuleDialog.onCreateFirewallRule(this.handleOnCreateFirewallRule, this);
|
||||
this._firewallRuleDialog.onAddAccountErrorEvent(this.handleOnAddAccountError, this);
|
||||
this._firewallRuleDialog.render();
|
||||
}
|
||||
this._connection = connection;
|
||||
this._resourceProviderId = resourceProviderId;
|
||||
this._firewallRuleDialog.viewModel.updateDefaultValues(ipAddress);
|
||||
this._firewallRuleDialog.open();
|
||||
this._deferredPromise = new Deferred();
|
||||
return this._deferredPromise.promise;
|
||||
}
|
||||
|
||||
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
||||
private handleOnAddAccountError(message: string): void {
|
||||
this._errorMessageService.showDialog(Severity.Error, this._addAccountErrorTitle, message);
|
||||
}
|
||||
|
||||
private async handleOnCreateFirewallRule(): Promise<void> {
|
||||
const resourceProviderId = this._resourceProviderId;
|
||||
try {
|
||||
const securityTokenMappings = await this._accountManagementService.getSecurityToken(this._firewallRuleDialog.viewModel.selectedAccount!, AzureResource.ResourceManagement);
|
||||
const firewallRuleInfo: azdata.FirewallRuleInfo = {
|
||||
startIpAddress: this._firewallRuleDialog.viewModel.isIPAddressSelected ? this._firewallRuleDialog.viewModel.defaultIPAddress : this._firewallRuleDialog.viewModel.fromSubnetIPRange,
|
||||
endIpAddress: this._firewallRuleDialog.viewModel.isIPAddressSelected ? this._firewallRuleDialog.viewModel.defaultIPAddress : this._firewallRuleDialog.viewModel.toSubnetIPRange,
|
||||
serverName: this._connection.serverName,
|
||||
securityTokenMappings
|
||||
};
|
||||
|
||||
const response = await this._resourceProviderService.createFirewallRule(this._firewallRuleDialog.viewModel.selectedAccount!, firewallRuleInfo, resourceProviderId);
|
||||
if (response.result) {
|
||||
this._firewallRuleDialog.close();
|
||||
this._deferredPromise.resolve(true);
|
||||
} else {
|
||||
this._errorMessageService.showDialog(Severity.Error, this._firewallRuleErrorTitle, response.errorMessage);
|
||||
}
|
||||
this._firewallRuleDialog.onServiceComplete();
|
||||
} catch (e) {
|
||||
this.showError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private showError(error: any): void {
|
||||
this._errorMessageService.showDialog(Severity.Error, this._firewallRuleErrorTitle, error);
|
||||
this._firewallRuleDialog.onServiceComplete();
|
||||
// Note: intentionally not rejecting the promise as we want users to be able to choose a different account
|
||||
}
|
||||
|
||||
private handleOnCancel(): void {
|
||||
this._deferredPromise.resolve(false);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* Icons for various account actions */
|
||||
.vs .action-item .codicon.add-linked-account-action {
|
||||
background-image: url('new_account.svg');
|
||||
}
|
||||
|
||||
.vs-dark .action-item .codicon.add-linked-account-action,
|
||||
.hc-black .action-item .codicon.add-linked-account-action {
|
||||
background-image: url('new_account_inverse.svg');
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.no-account-view {
|
||||
font-size: 12px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.no-account-view .no-account-view-label {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.account-view .header {
|
||||
position: relative;
|
||||
line-height: 22px;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
padding-left: 20px;
|
||||
padding-right: 12px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.account-view .header .title {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.account-view .header .actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.account-view .header .actions .action-item .action-label {
|
||||
width: 30px;
|
||||
background-size: 16px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
margin-right: 0;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.account-view .header .count-badge-wrapper {
|
||||
justify-content: flex-end;
|
||||
/* hide the count badge as it is not providing much value and meanwhile not keyboard accessible*/
|
||||
display: none;
|
||||
}
|
||||
|
||||
.account-view .list-row {
|
||||
padding: 12px;
|
||||
line-height: 18px !important;
|
||||
}
|
||||
|
||||
.account-view .list-row .codicon {
|
||||
flex: 0 0 50px;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
background-size: 50px;
|
||||
}
|
||||
|
||||
.account-view .list-row .codicon .badge {
|
||||
position: absolute;
|
||||
top: 43px;
|
||||
left: 43px;
|
||||
overflow: hidden;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.account-view .list-row .codicon .badge .badge-content {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
background-size: 22px;
|
||||
}
|
||||
|
||||
.account-view .list-row .actions-container {
|
||||
flex: 0 0 50px;
|
||||
}
|
||||
|
||||
.account-view .list-row .actions-container .action-item .action-label {
|
||||
width: 16px;
|
||||
margin-left: 20px;
|
||||
margin-right: 10px;
|
||||
background-size: 16px;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.account-view .list-row .actions-container .action-item .action-label.codicon.remove {
|
||||
background-size: 14px !important;
|
||||
}
|
||||
|
||||
.account-view .list-row .actions-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.account-view .monaco-list .monaco-list-row:hover .list-row .actions-container,
|
||||
.account-view .monaco-list .monaco-list-row.selected .list-row .actions-container,
|
||||
.account-view .monaco-list .monaco-list-row.focused .list-row .actions-container{
|
||||
display: block;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.list-row.account-picker-list {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.list-row.account-picker-list .label {
|
||||
flex: 1 1 auto;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.list-row.account-picker-list .label .contextual-display-name {
|
||||
font-size: 15px;
|
||||
line-height: 15px;
|
||||
}
|
||||
|
||||
.list-row.account-picker-list .label .display-name {
|
||||
font-size: 13px;
|
||||
line-height: 13px;
|
||||
}
|
||||
|
||||
.list-row.account-picker-list .label .content {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.account-logo {
|
||||
background: no-repeat center center;
|
||||
}
|
||||
@@ -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 .codicon {
|
||||
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 .codicon {
|
||||
background-size: 25px;
|
||||
}
|
||||
|
||||
.selected-account-container .codicon .badge {
|
||||
position: relative;
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
overflow: hidden;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.selected-account-container .codicon .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 .codicon {
|
||||
flex: 0 0 35px;
|
||||
height: 35px;
|
||||
width: 35px;
|
||||
background-size: 35px;
|
||||
}
|
||||
|
||||
.account-list-container .list-row .codicon .badge {
|
||||
position: relative;
|
||||
top: 22px;
|
||||
left: 22px;
|
||||
overflow: hidden;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
.account-list-container .list-row .codicon .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 .codicon {
|
||||
flex: 0 0 16px;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.refresh-container .monaco-action-bar {
|
||||
flex: 1 1 auto;
|
||||
margin-left: 10px;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.auto-oauth-dialog {
|
||||
padding: 15px
|
||||
}
|
||||
|
||||
.modal .auto-oauth-dialog .new-section {
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
.modal .auto-oauth-dialog .dialog-input-section {
|
||||
display: flex;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.modal .auto-oauth-dialog .dialog-input-section .dialog-label {
|
||||
flex: 0 0 100px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.modal .auto-oauth-dialog .dialog-input-section .dialog-input {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.firewall-rule-dialog {
|
||||
padding: 15px
|
||||
}
|
||||
|
||||
.modal .firewall-rule-dialog .dialog-label.header {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.modal .firewall-rule-dialog .new-section {
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
.modal .firewall-rule-dialog .dialog-input {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.modal .firewall-rule-dialog .dialog-input-section {
|
||||
display: flex;
|
||||
padding-left: 30px;
|
||||
padding-right: 20px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.modal .firewall-rule-dialog .dialog-input-section .dialog-label {
|
||||
flex: 0 0 15px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.modal .firewall-rule-dialog .dialog-input-section .dialog-input {
|
||||
flex: 0 0 100px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
.modal .firewall-rule-dialog .azure-account-section {
|
||||
height: 92px;
|
||||
}
|
||||
|
||||
/* Firewall rule description section */
|
||||
.modal .firewall-rule-dialog a:link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.modal .firewall-rule-dialog .firewall-rule-description-section {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.modal .firewall-rule-dialog .firewall-rule-description-section .firewall-rule-icon {
|
||||
background: url('secure.svg') center center no-repeat;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
flex: 0 0 50px;
|
||||
}
|
||||
|
||||
.modal .firewall-rule-dialog .firewall-rule-description-section .firewall-rule-description {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
/* Radio button section*/
|
||||
.modal .firewall-rule-dialog .firewall-rule-section .radio-section .option-container {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.modal .firewall-rule-dialog .firewall-rule-section .radio-section .option-container .option-input {
|
||||
margin-top: 0px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.modal .firewall-rule-dialog .firewall-rule-section .radio-section .option-container .option-description {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.modal .firewall-rule-dialog .firewall-rule-section .radio-section .option-container .option-ip-address {
|
||||
padding-left: 3px;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>new_account_16x16</title><path d="M8.09,7.39a5.91,5.91,0,0,1,1.38.74,6.06,6.06,0,0,1,1.13,1.06A6,6,0,0,1,11.88,12h-1a4.94,4.94,0,0,0-.63-1.61A5,5,0,0,0,7.66,8.3a5,5,0,0,0-3-.12,5.09,5.09,0,0,0-1.2.5,5,5,0,0,0-1.79,1.79,5.07,5.07,0,0,0-.5,1.2A4.88,4.88,0,0,0,1,13H0a5.88,5.88,0,0,1,.28-1.8A6,6,0,0,1,1,9.59a6.1,6.1,0,0,1,1.22-1.3,5.8,5.8,0,0,1,1.59-.9A4.2,4.2,0,0,1,2.46,5.94,3.86,3.86,0,0,1,2,4a3.92,3.92,0,0,1,.31-1.56A4,4,0,0,1,4.4.31a4,4,0,0,1,3.12,0A4,4,0,0,1,9.65,2.44,4,4,0,0,1,9.83,5a4,4,0,0,1-1,1.74A3.94,3.94,0,0,1,8.09,7.39ZM3,4A2.92,2.92,0,0,0,3.2,5.17a3,3,0,0,0,1.6,1.6,3,3,0,0,0,2.33,0,3,3,0,0,0,1.6-1.6,3,3,0,0,0,0-2.33,3,3,0,0,0-1.6-1.6,3,3,0,0,0-2.33,0,3,3,0,0,0-1.6,1.6A2.92,2.92,0,0,0,3,4Zm13,9v1H14v2H13V14H11V13h2V11h1v2Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 850 B |
@@ -1 +0,0 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>new_account_inverse_16x16</title><path class="cls-1" d="M8.13,7.39a5.91,5.91,0,0,1,1.38.74,6.06,6.06,0,0,1,1.13,1.06A6,6,0,0,1,11.91,12h-1a4.94,4.94,0,0,0-.63-1.61A5,5,0,0,0,7.7,8.3a5,5,0,0,0-3-.12,5.09,5.09,0,0,0-1.2.5,5,5,0,0,0-1.79,1.79,5.07,5.07,0,0,0-.5,1.2A4.88,4.88,0,0,0,1,13H0a5.88,5.88,0,0,1,.28-1.8,6,6,0,0,1,.79-1.6,6.1,6.1,0,0,1,1.22-1.3,5.8,5.8,0,0,1,1.59-.9A4.2,4.2,0,0,1,2.5,5.94,3.86,3.86,0,0,1,2,4a3.92,3.92,0,0,1,.31-1.56A4,4,0,0,1,4.44.31a4,4,0,0,1,3.12,0A4,4,0,0,1,9.69,2.44,4,4,0,0,1,9.87,5a4,4,0,0,1-1,1.74A3.94,3.94,0,0,1,8.13,7.39ZM3,4a2.92,2.92,0,0,0,.23,1.17,3,3,0,0,0,1.6,1.6,3,3,0,0,0,2.33,0,3,3,0,0,0,1.6-1.6,3,3,0,0,0,0-2.33,3,3,0,0,0-1.6-1.6,3,3,0,0,0-2.33,0,3,3,0,0,0-1.6,1.6A2.92,2.92,0,0,0,3,4Zm13,9v1H14v2H13V14H11V13h2V11h1v2Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 918 B |
@@ -1 +0,0 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36.65 50"><defs><style>.cls-1{fill:#7fba00;}.cls-2{fill:#b8d432;opacity:0.4;isolation:isolate;}.cls-3{fill:#fff;}</style></defs><title>secure</title><path class="cls-1" d="M31.19,6h0c-11-1.22-12.86-6-12.86-6S15.85,6.24,0,6.24V31.85c0,3.1,1.72,6,4.1,8.53h0C9.51,46.13,18.33,50,18.33,50s18.33-8,18.33-18.15V6.24A49.77,49.77,0,0,1,31.19,6Z"/><path class="cls-2" d="M22.86,16.54,31.19,6c-11-1.22-12.86-6-12.86-6S15.85,6.24,0,6.24V31.85c0,3.1,1.72,6,4.1,8.53l6.15-7.82Z"/><path class="cls-3" d="M25.6,24.46H24.53V20.91a6.7,6.7,0,0,0-1.67-4.45h0l-.11-.13a6,6,0,0,0-8.84,0,6.7,6.7,0,0,0-1.78,4.58v3.55H11.06a.8.8,0,0,0-.8.8v7.22h0v2.17a.8.8,0,0,0,.8.8H25.6a.8.8,0,0,0,.8-.8V25.26A.8.8,0,0,0,25.6,24.46Zm-4,0H15.07V20.91a3.75,3.75,0,0,1,1-2.57,3,3,0,0,1,4.54,0,3.67,3.67,0,0,1,.39.5h0a3.8,3.8,0,0,1,.6,2.06Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 894 B |
@@ -6,13 +6,13 @@
|
||||
import * as assert from 'assert';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { AccountDialog } from 'sql/workbench/contrib/accounts/browser/accountDialog';
|
||||
import { AccountDialogController } from 'sql/workbench/contrib/accounts/browser/accountDialogController';
|
||||
import { AccountDialog } from 'sql/workbench/services/accountManagement/browser/accountDialog';
|
||||
import { AccountDialogController } from 'sql/workbench/services/accountManagement/browser/accountDialogController';
|
||||
import { AccountViewModel } from 'sql/platform/accounts/common/accountViewModel';
|
||||
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
|
||||
import { TestErrorMessageService } from 'sql/platform/errorMessage/test/common/testErrorMessageService';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { AccountListRenderer } from 'sql/workbench/contrib/accounts/browser/accountListRenderer';
|
||||
import { AccountListRenderer } from 'sql/workbench/services/accountManagement/browser/accountListRenderer';
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
|
||||
// TESTS ///////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1,122 +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 * as azdata from 'azdata';
|
||||
import * as assert from 'assert';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { EventVerifierSingle } from 'sql/base/test/common/event';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { AccountPicker } from 'sql/workbench/contrib/accounts/browser/accountPickerImpl';
|
||||
import { AccountPickerService } from 'sql/workbench/contrib/accounts/browser/accountPickerService';
|
||||
import { AccountPickerViewModel } from 'sql/platform/accounts/common/accountPickerViewModel';
|
||||
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
|
||||
|
||||
// SUITE STATE /////////////////////////////////////////////////////////////
|
||||
let mockAddAccountCompleteEmitter: Emitter<void>;
|
||||
let mockAddAccountErrorEmitter: Emitter<string>;
|
||||
let mockAddAccountStartEmitter: Emitter<void>;
|
||||
let mockOnAccountSelectionChangeEvent: Emitter<azdata.Account>;
|
||||
|
||||
// TESTS ///////////////////////////////////////////////////////////////////
|
||||
suite('Account picker service tests', () => {
|
||||
setup(() => {
|
||||
// Setup event mocks for the account picker service
|
||||
mockAddAccountCompleteEmitter = new Emitter<void>();
|
||||
mockAddAccountErrorEmitter = new Emitter<string>();
|
||||
mockAddAccountStartEmitter = new Emitter<void>();
|
||||
mockOnAccountSelectionChangeEvent = new Emitter<azdata.Account>();
|
||||
});
|
||||
|
||||
test('Construction - Events are properly defined', () => {
|
||||
// Setup:
|
||||
// ... Create instantiation service
|
||||
let instantiationService = createInstantiationService();
|
||||
|
||||
// ... Create instance of the service and reder account picker
|
||||
let service = new AccountPickerService(instantiationService);
|
||||
service.renderAccountPicker(TypeMoq.It.isAny());
|
||||
|
||||
// Then:
|
||||
// ... All the events for the view models should be properly initialized
|
||||
assert.notEqual(service.addAccountCompleteEvent, undefined);
|
||||
assert.notEqual(service.addAccountErrorEvent, undefined);
|
||||
assert.notEqual(service.addAccountStartEvent, undefined);
|
||||
assert.notEqual(service.onAccountSelectionChangeEvent, undefined);
|
||||
|
||||
|
||||
// ... All the events should properly fire
|
||||
let evAddAccountCompleteEvent = new EventVerifierSingle<void>();
|
||||
service.addAccountCompleteEvent(evAddAccountCompleteEvent.eventHandler);
|
||||
mockAddAccountCompleteEmitter.fire();
|
||||
evAddAccountCompleteEvent.assertFired();
|
||||
|
||||
let errorMsg = 'Error';
|
||||
let evAddAccountErrorEvent = new EventVerifierSingle<string>();
|
||||
service.addAccountErrorEvent(evAddAccountErrorEvent.eventHandler);
|
||||
mockAddAccountErrorEmitter.fire(errorMsg);
|
||||
evAddAccountErrorEvent.assertFired(errorMsg);
|
||||
|
||||
let evAddAccountStartEvent = new EventVerifierSingle<void>();
|
||||
service.addAccountStartEvent(evAddAccountStartEvent.eventHandler);
|
||||
mockAddAccountStartEmitter.fire();
|
||||
evAddAccountStartEvent.assertFired();
|
||||
|
||||
let account = {
|
||||
key: { providerId: 'azure', accountId: 'account1' },
|
||||
name: 'Account 1',
|
||||
displayInfo: {
|
||||
contextualDisplayName: 'Microsoft Account',
|
||||
accountType: 'microsoft',
|
||||
displayName: 'Account 1',
|
||||
userId: 'user@email.com'
|
||||
},
|
||||
properties: [],
|
||||
isStale: false
|
||||
};
|
||||
let evOnAccountSelectionChangeEvent = new EventVerifierSingle<azdata.Account>();
|
||||
service.onAccountSelectionChangeEvent(evOnAccountSelectionChangeEvent.eventHandler);
|
||||
mockOnAccountSelectionChangeEvent.fire(account);
|
||||
evOnAccountSelectionChangeEvent.assertFired(account);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function createInstantiationService(): InstantiationService {
|
||||
// Create a mock account picker view model
|
||||
let providerId = 'azure';
|
||||
let accountPickerViewModel = new AccountPickerViewModel(providerId, new TestAccountManagementService());
|
||||
let mockAccountViewModel = TypeMoq.Mock.ofInstance(accountPickerViewModel);
|
||||
let mockEvent = new Emitter<any>();
|
||||
mockAccountViewModel.setup(x => x.updateAccountListEvent).returns(() => mockEvent.event);
|
||||
|
||||
// Create a mocked out instantiation service
|
||||
let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Strict);
|
||||
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountPickerViewModel), TypeMoq.It.isAny()))
|
||||
.returns(() => mockAccountViewModel.object);
|
||||
|
||||
// Create a mock account picker
|
||||
|
||||
let accountPicker = new AccountPicker('provider', new TestThemeService(), instantiationService.object, undefined!);
|
||||
let mockAccountDialog = TypeMoq.Mock.ofInstance(accountPicker);
|
||||
|
||||
mockAccountDialog.setup(x => x.addAccountCompleteEvent)
|
||||
.returns(() => mockAddAccountCompleteEmitter.event);
|
||||
mockAccountDialog.setup(x => x.addAccountErrorEvent)
|
||||
.returns((msg) => mockAddAccountErrorEmitter.event);
|
||||
mockAccountDialog.setup(x => x.addAccountStartEvent)
|
||||
.returns(() => mockAddAccountStartEmitter.event);
|
||||
mockAccountDialog.setup(x => x.onAccountSelectionChangeEvent)
|
||||
.returns((account) => mockOnAccountSelectionChangeEvent.event);
|
||||
mockAccountDialog.setup(x => x.render(TypeMoq.It.isAny()))
|
||||
.returns((container) => undefined);
|
||||
mockAccountDialog.setup(x => x.createAccountPickerComponent());
|
||||
|
||||
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(AccountPicker), TypeMoq.It.isAny()))
|
||||
.returns(() => mockAccountDialog.object);
|
||||
|
||||
return instantiationService.object;
|
||||
}
|
||||
@@ -8,8 +8,8 @@ import * as assert from 'assert';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
|
||||
import { AutoOAuthDialog } from 'sql/workbench/contrib/accounts/browser/autoOAuthDialog';
|
||||
import { AutoOAuthDialogController } from 'sql/workbench/contrib/accounts/browser/autoOAuthDialogController';
|
||||
import { AutoOAuthDialog } from 'sql/workbench/services/accountManagement/browser/autoOAuthDialog';
|
||||
import { AutoOAuthDialogController } from 'sql/workbench/services/accountManagement/browser/autoOAuthDialogController';
|
||||
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
|
||||
import { TestErrorMessageService } from 'sql/platform/errorMessage/test/common/testErrorMessageService';
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
|
||||
@@ -1,249 +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 * as azdata from 'azdata';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { FirewallRuleDialog } from 'sql/workbench/contrib/accounts/browser/firewallRuleDialog';
|
||||
import { FirewallRuleViewModel } from 'sql/platform/accounts/common/firewallRuleViewModel';
|
||||
import { FirewallRuleDialogController } from 'sql/workbench/contrib/accounts/browser/firewallRuleDialogController';
|
||||
import { TestAccountManagementService } from 'sql/platform/accounts/test/common/testAccountManagementService';
|
||||
import { TestResourceProvider } from 'sql/workbench/services/resourceProvider/test/common/testResourceProviderService';
|
||||
import { TestErrorMessageService } from 'sql/platform/errorMessage/test/common/testErrorMessageService';
|
||||
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
|
||||
// TESTS ///////////////////////////////////////////////////////////////////
|
||||
suite('Firewall rule dialog controller tests', () => {
|
||||
let connectionProfile: IConnectionProfile;
|
||||
let account: azdata.Account;
|
||||
let IPAddress = '250.222.155.198';
|
||||
let mockOnAddAccountErrorEvent: Emitter<string>;
|
||||
let mockOnCreateFirewallRule: Emitter<void>;
|
||||
|
||||
let instantiationService: TypeMoq.Mock<InstantiationService>;
|
||||
let mockFirewallRuleViewModel: TypeMoq.Mock<FirewallRuleViewModel>;
|
||||
let mockFirewallRuleDialog: TypeMoq.Mock<FirewallRuleDialog>;
|
||||
|
||||
setup(() => {
|
||||
account = {
|
||||
key: { providerId: 'azure', accountId: 'account1' },
|
||||
displayInfo: {
|
||||
contextualDisplayName: 'Microsoft Account',
|
||||
accountType: 'microsoft',
|
||||
displayName: 'Account 1',
|
||||
userId: 'user@email.com'
|
||||
},
|
||||
properties: [],
|
||||
isStale: false
|
||||
};
|
||||
|
||||
mockOnAddAccountErrorEvent = new Emitter<string>();
|
||||
mockOnCreateFirewallRule = new Emitter<void>();
|
||||
|
||||
// Create a mock firewall rule view model
|
||||
let firewallRuleViewModel = new FirewallRuleViewModel();
|
||||
mockFirewallRuleViewModel = TypeMoq.Mock.ofInstance(firewallRuleViewModel);
|
||||
mockFirewallRuleViewModel.setup(x => x.updateDefaultValues(TypeMoq.It.isAny()))
|
||||
.returns((ipAddress) => undefined);
|
||||
mockFirewallRuleViewModel.object.selectedAccount = account;
|
||||
mockFirewallRuleViewModel.object.isIPAddressSelected = true;
|
||||
|
||||
// Create a mocked out instantiation service
|
||||
instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Strict);
|
||||
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(FirewallRuleViewModel)))
|
||||
.returns(() => mockFirewallRuleViewModel.object);
|
||||
|
||||
// Create a mock account picker
|
||||
let firewallRuleDialog = new FirewallRuleDialog(undefined!, undefined!, undefined!, instantiationService.object, undefined!, undefined!, new MockContextKeyService(), undefined!, undefined!, undefined!, undefined!);
|
||||
mockFirewallRuleDialog = TypeMoq.Mock.ofInstance(firewallRuleDialog);
|
||||
|
||||
let mockEvent = new Emitter<any>();
|
||||
mockFirewallRuleDialog.setup(x => x.onCancel)
|
||||
.returns(() => mockEvent.event);
|
||||
mockFirewallRuleDialog.setup(x => x.onCreateFirewallRule)
|
||||
.returns(() => mockOnCreateFirewallRule.event);
|
||||
mockFirewallRuleDialog.setup(x => x.onAddAccountErrorEvent)
|
||||
.returns((msg) => mockOnAddAccountErrorEvent.event);
|
||||
mockFirewallRuleDialog.setup(x => x.render());
|
||||
mockFirewallRuleDialog.setup(x => x.open());
|
||||
mockFirewallRuleDialog.setup(x => x.close());
|
||||
|
||||
instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(FirewallRuleDialog)))
|
||||
.returns(() => mockFirewallRuleDialog.object);
|
||||
|
||||
connectionProfile = {
|
||||
connectionName: 'new name',
|
||||
serverName: 'new server',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'g2/g2-2',
|
||||
groupId: 'group id',
|
||||
getOptionsKey: () => '',
|
||||
matches: () => false,
|
||||
providerName: mssqlProviderName,
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: ''
|
||||
};
|
||||
});
|
||||
|
||||
test('Add Account Failure - Error Message Shown', () => {
|
||||
// ... Create a mock instance of the error message service
|
||||
let errorMessageServiceStub = new TestErrorMessageService();
|
||||
let mockErrorMessageService = TypeMoq.Mock.ofInstance(errorMessageServiceStub);
|
||||
mockErrorMessageService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()));
|
||||
|
||||
// ... Create instance of the controller with an opened dialog
|
||||
let controller = new FirewallRuleDialogController(instantiationService.object, undefined!, undefined!, mockErrorMessageService.object);
|
||||
controller.openFirewallRuleDialog(connectionProfile, IPAddress, 'resourceID');
|
||||
|
||||
// If: The firewall rule dialog reports a failure
|
||||
|
||||
mockOnAddAccountErrorEvent.fire('Error message');
|
||||
|
||||
// Then: An error dialog should have been opened
|
||||
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
});
|
||||
|
||||
test('create firewall rule success', async () => {
|
||||
let deferredPromise = new Deferred();
|
||||
|
||||
mockFirewallRuleDialog.setup(x => x.onServiceComplete())
|
||||
.callback(() => {
|
||||
deferredPromise.resolve(true);
|
||||
});
|
||||
|
||||
// ... Create a mock instance of the account management test service
|
||||
let mockAccountManagementService = getMockAccountManagementService(true);
|
||||
|
||||
// ... Create a mock instance of the resource provider
|
||||
let mockResourceProvider = getMockResourceProvider(true, { result: true, errorMessage: '' });
|
||||
|
||||
// ... Create instance of the controller with an opened dialog
|
||||
let controller = new FirewallRuleDialogController(instantiationService.object, mockResourceProvider.object, mockAccountManagementService.object, undefined!);
|
||||
controller.openFirewallRuleDialog(connectionProfile, IPAddress, 'resourceID');
|
||||
|
||||
// If: The firewall rule dialog's create firewall rule get fired
|
||||
mockOnCreateFirewallRule.fire();
|
||||
|
||||
// Then: it should get security token from account management service and call create firewall rule in resource provider
|
||||
await deferredPromise;
|
||||
mockAccountManagementService.verify(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
mockResourceProvider.verify(x => x.createFirewallRule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
mockFirewallRuleDialog.verify(x => x.close(), TypeMoq.Times.once());
|
||||
mockFirewallRuleDialog.verify(x => x.onServiceComplete(), TypeMoq.Times.once());
|
||||
});
|
||||
|
||||
test('create firewall rule fails during getSecurity', async () => {
|
||||
let deferredPromise = new Deferred<{}>();
|
||||
|
||||
// ... Create a mock instance of the error message service
|
||||
let mockErrorMessageService = getMockErrorMessageService(deferredPromise);
|
||||
|
||||
// ... Create a mock instance of the account management test service
|
||||
let mockAccountManagementService = getMockAccountManagementService(false);
|
||||
|
||||
// ... Create a mock instance of the resource provider
|
||||
let mockResourceProvider = getMockResourceProvider(true, { result: true, errorMessage: '' });
|
||||
|
||||
// ... Create instance of the controller with an opened dialog
|
||||
let controller = new FirewallRuleDialogController(instantiationService.object, mockResourceProvider.object, mockAccountManagementService.object, mockErrorMessageService.object);
|
||||
controller.openFirewallRuleDialog(connectionProfile, IPAddress, 'resourceID');
|
||||
|
||||
// If: The firewall rule dialog's create firewall rule get fired
|
||||
mockOnCreateFirewallRule.fire();
|
||||
|
||||
// Then: it should get security token from account management service and an error dialog should have been opened
|
||||
await deferredPromise;
|
||||
mockAccountManagementService.verify(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
mockResourceProvider.verify(x => x.createFirewallRule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.never());
|
||||
});
|
||||
|
||||
test('create firewall rule fails during createFirewallRule in ResourceProvider - result is false', async () => {
|
||||
let deferredPromise = new Deferred<{}>();
|
||||
|
||||
// ... Create a mock instance of the error message service
|
||||
let mockErrorMessageService = getMockErrorMessageService(deferredPromise);
|
||||
|
||||
// ... Create a mock instance of the account management test service
|
||||
let mockAccountManagementService = getMockAccountManagementService(true);
|
||||
|
||||
// ... Create a mock instance of the resource provider
|
||||
let mockResourceProvider = getMockResourceProvider(true, { result: false, errorMessage: '' });
|
||||
|
||||
// ... Create instance of the controller with an opened dialog
|
||||
let controller = new FirewallRuleDialogController(instantiationService.object, mockResourceProvider.object, mockAccountManagementService.object, mockErrorMessageService.object);
|
||||
controller.openFirewallRuleDialog(connectionProfile, IPAddress, 'resourceID');
|
||||
|
||||
// If: The firewall rule dialog's create firewall rule get fired
|
||||
mockOnCreateFirewallRule.fire();
|
||||
|
||||
// Then: it should get security token from account management service and an error dialog should have been opened
|
||||
await deferredPromise;
|
||||
|
||||
mockAccountManagementService.verify(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
mockResourceProvider.verify(x => x.createFirewallRule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
});
|
||||
|
||||
test('create firewall rule fails during createFirewallRule in ResourceProvider - reject promise', async () => {
|
||||
let deferredPromise = new Deferred<{}>();
|
||||
|
||||
// ... Create a mock instance of the error message service
|
||||
let mockErrorMessageService = getMockErrorMessageService(deferredPromise);
|
||||
|
||||
// ... Create a mock instance of the account management test service
|
||||
let mockAccountManagementService = getMockAccountManagementService(true);
|
||||
|
||||
// ... Create a mock instance of the resource provider
|
||||
let mockResourceProvider = getMockResourceProvider(false);
|
||||
|
||||
// ... Create instance of the controller with an opened dialog
|
||||
let controller = new FirewallRuleDialogController(instantiationService.object, mockResourceProvider.object, mockAccountManagementService.object, mockErrorMessageService.object);
|
||||
controller.openFirewallRuleDialog(connectionProfile, IPAddress, 'resourceID');
|
||||
|
||||
// If: The firewall rule dialog's create firewall rule get fired
|
||||
mockOnCreateFirewallRule.fire();
|
||||
|
||||
// Then: it should get security token from account management service and an error dialog should have been opened
|
||||
await deferredPromise;
|
||||
mockAccountManagementService.verify(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
mockResourceProvider.verify(x => x.createFirewallRule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
mockErrorMessageService.verify(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
});
|
||||
});
|
||||
|
||||
function getMockAccountManagementService(resolveSecurityToken: boolean): TypeMoq.Mock<TestAccountManagementService> {
|
||||
let accountManagementTestService = new TestAccountManagementService();
|
||||
let mockAccountManagementService = TypeMoq.Mock.ofInstance(accountManagementTestService);
|
||||
mockAccountManagementService.setup(x => x.getSecurityToken(TypeMoq.It.isAny(), TypeMoq.It.isAny()))
|
||||
.returns(() => resolveSecurityToken ? Promise.resolve({}) : Promise.reject(null));
|
||||
return mockAccountManagementService;
|
||||
}
|
||||
|
||||
function getMockResourceProvider(resolveCreateFirewallRule: boolean, response?: azdata.CreateFirewallRuleResponse): TypeMoq.Mock<TestResourceProvider> {
|
||||
let resourceProviderStub = new TestResourceProvider();
|
||||
let mockResourceProvider = TypeMoq.Mock.ofInstance(resourceProviderStub);
|
||||
mockResourceProvider.setup(x => x.createFirewallRule(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
|
||||
.returns(() => resolveCreateFirewallRule ? Promise.resolve(response) : Promise.reject(null));
|
||||
return mockResourceProvider;
|
||||
}
|
||||
|
||||
function getMockErrorMessageService(deferredPromise: Deferred<{}>): TypeMoq.Mock<TestErrorMessageService> {
|
||||
let errorMessageServiceStub = new TestErrorMessageService();
|
||||
let mockErrorMessageService = TypeMoq.Mock.ofInstance(errorMessageServiceStub);
|
||||
mockErrorMessageService.setup(x => x.showDialog(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).callback(() => {
|
||||
deferredPromise.resolve(true);
|
||||
});
|
||||
return mockErrorMessageService;
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import { IBackupService, TaskExecutionMode } from 'sql/platform/backup/common/ba
|
||||
import * as FileValidationConstants from 'sql/workbench/services/fileBrowser/common/fileValidationServiceConstants';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { IFileBrowserDialogController } from 'sql/workbench/services/fileBrowser/common/fileBrowserDialogController';
|
||||
import { IBackupUiService } from 'sql/workbench/services/backup/common/backupUiService';
|
||||
import { IBackupUiService } from 'sql/workbench/contrib/backup/common/backupUiService';
|
||||
import * as cr from 'vs/platform/theme/common/colorRegistry';
|
||||
|
||||
import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
@@ -33,6 +33,7 @@ import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { fileFiltersSet } from 'sql/workbench/services/restore/common/constants';
|
||||
|
||||
export const BACKUP_SELECTOR: string = 'backup-component';
|
||||
|
||||
@@ -654,7 +655,7 @@ export class BackupComponent extends AngularDisposable {
|
||||
private onAddClick(): void {
|
||||
this.fileBrowserDialogService.showDialog(this._uri,
|
||||
this.defaultNewBackupFolder,
|
||||
BackupConstants.fileFiltersSet,
|
||||
fileFiltersSet,
|
||||
FileValidationConstants.backup,
|
||||
false,
|
||||
(filepath => this.handlePathAdded(filepath)));
|
||||
|
||||
@@ -14,10 +14,10 @@ import { MssqlNodeContext } from 'sql/workbench/services/objectExplorer/browser/
|
||||
import { NodeType } from 'sql/workbench/services/objectExplorer/common/nodeType';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
import { localize } from 'vs/nls';
|
||||
import { OEAction } from 'sql/workbench/contrib/objectExplorer/browser/objectExplorerActions';
|
||||
import { TreeNodeContextKey } from 'sql/workbench/contrib/objectExplorer/common/treeNodeContextKey';
|
||||
import { OEAction } from 'sql/workbench/services/objectExplorer/browser/objectExplorerActions';
|
||||
import { TreeNodeContextKey } from 'sql/workbench/services/objectExplorer/common/treeNodeContextKey';
|
||||
import { ConnectionContextKey } from 'sql/workbench/services/connection/common/connectionContextKey';
|
||||
import { ServerInfoContextKey } from 'sql/workbench/contrib/connection/common/serverInfoContextKey';
|
||||
import { ServerInfoContextKey } from 'sql/workbench/services/connection/common/serverInfoContextKey';
|
||||
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { DatabaseEngineEdition } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/br
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { getCurrentGlobalConnection } from 'sql/workbench/browser/taskUtilities';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
import { IBackupUiService } from 'sql/workbench/services/backup/common/backupUiService';
|
||||
import { IBackupUiService } from 'sql/workbench/contrib/backup/common/backupUiService';
|
||||
import { Task } from 'sql/workbench/services/tasks/browser/tasksRegistry';
|
||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
|
||||
124
src/sql/workbench/contrib/backup/browser/backupUiService.ts
Normal file
124
src/sql/workbench/contrib/backup/browser/backupUiService.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import * as ConnectionUtils from 'sql/platform/connection/common/utils';
|
||||
import { ProviderConnectionInfo } from 'sql/platform/connection/common/providerConnectionInfo';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { BackupDialog } from 'sql/workbench/contrib/backup/browser/backupDialog';
|
||||
import { OptionsDialog } from 'sql/workbench/browser/modal/optionsDialog';
|
||||
import { IBackupService, TaskExecutionMode } from 'sql/platform/backup/common/backupService';
|
||||
import { IBackupUiService } from 'sql/workbench/contrib/backup/common/backupUiService';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
|
||||
export class BackupUiService implements IBackupUiService {
|
||||
public _serviceBrand: undefined;
|
||||
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,
|
||||
@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): azdata.ServiceOption[] {
|
||||
let feature = find(this._capabilitiesService.getLegacyCapabilities(this._currentProvider).features, f => f.featureName === 'backup');
|
||||
if (feature) {
|
||||
return feature.optionsMetadata;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public async showBackupDialog(connection: IConnectionProfile): Promise<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);
|
||||
let uri = this._connectionManagementService.getConnectionUri(connection)
|
||||
+ ProviderConnectionInfo.idSeparator
|
||||
+ ConnectionUtils.ConnectionUriBackupIdAttributeName
|
||||
+ ProviderConnectionInfo.nameValueSeparator
|
||||
+ BackupUiService._connectionUniqueId;
|
||||
|
||||
this._connectionUri = uri;
|
||||
|
||||
BackupUiService._connectionUniqueId++;
|
||||
|
||||
if (backupOptions) {
|
||||
(backupDialog as OptionsDialog).open(backupOptions, self._optionValues);
|
||||
} else {
|
||||
(backupDialog as BackupDialog).open(connection);
|
||||
}
|
||||
|
||||
// Create connection if needed
|
||||
if (!this._connectionManagementService.isConnected(uri)) {
|
||||
await this._connectionManagementService.connect(connection, uri);
|
||||
this._onShowBackupEvent.fire({ connection: connection, ownerUri: uri });
|
||||
}
|
||||
}
|
||||
|
||||
public onShowBackupDialog() {
|
||||
let backupDialog = this._backupDialogs[this._currentProvider];
|
||||
if (backupDialog) {
|
||||
backupDialog.setInitialFocusedElement();
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
35
src/sql/workbench/contrib/backup/common/backupUiService.ts
Normal file
35
src/sql/workbench/contrib/backup/common/backupUiService.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
export const UI_SERVICE_ID = 'backupUiService';
|
||||
export const IBackupUiService = createDecorator<IBackupUiService>(UI_SERVICE_ID);
|
||||
|
||||
export interface IBackupUiService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
@@ -43,7 +43,3 @@ 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[] }[] = [
|
||||
{ label: localize('backup.filterBackupFiles', "Backup Files"), filters: ['*.bak', '*.trn', '*.log'] },
|
||||
{ label: localize('backup.allFiles', "All Files"), filters: ['*'] }
|
||||
];
|
||||
|
||||
@@ -14,7 +14,7 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IInsightsConfig } from 'sql/platform/dashboard/browser/insightRegistry';
|
||||
import { IInsightOptions } from 'sql/workbench/contrib/charts/common/interfaces';
|
||||
import { QueryEditorInput } from 'sql/workbench/contrib/query/common/queryEditorInput';
|
||||
import { QueryEditorInput } from 'sql/workbench/common/editor/query/queryEditorInput';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IFileDialogService, FileFilter } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
|
||||
@@ -23,7 +23,8 @@ import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { CreateInsightAction, CopyAction, SaveImageAction, IChartActionContext } from 'sql/workbench/contrib/charts/browser/actions';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox';
|
||||
import { ChartState, IInsightOptions, ChartType } from 'sql/workbench/contrib/charts/common/interfaces';
|
||||
import { IInsightOptions, ChartType } from 'sql/workbench/contrib/charts/common/interfaces';
|
||||
import { ChartState } from 'sql/workbench/common/editor/query/chartState';
|
||||
import * as nls from 'vs/nls';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
@@ -3,17 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export class ChartState {
|
||||
dataId?: { batchId: number, resultId: number };
|
||||
options: IInsightOptions = {
|
||||
type: ChartType.Bar
|
||||
};
|
||||
|
||||
dispose() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export interface IInsightOptions {
|
||||
type: InsightType | ChartType;
|
||||
dataDirection?: DataDirection;
|
||||
|
||||
@@ -18,7 +18,7 @@ import { ipcRenderer as ipc } from 'electron';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { localize } from 'vs/nls';
|
||||
import { QueryEditorInput } from 'sql/workbench/contrib/query/common/queryEditorInput';
|
||||
import { QueryEditorInput } from 'sql/workbench/common/editor/query/queryEditorInput';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
@@ -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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { OptionsDialog } from 'sql/workbench/browser/modal/optionsDialog';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import * as azdata from 'azdata';
|
||||
import { localize } from 'vs/nls';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
|
||||
export class AdvancedPropertiesController {
|
||||
private _advancedDialog: OptionsDialog;
|
||||
private _options: { [name: string]: any };
|
||||
|
||||
constructor(private _onCloseAdvancedProperties: () => void,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService
|
||||
) {
|
||||
}
|
||||
|
||||
|
||||
private handleOnOk(): void {
|
||||
this._options = this._advancedDialog.optionValues;
|
||||
}
|
||||
|
||||
public showDialog(providerOptions: azdata.ConnectionOption[], options: { [name: string]: any }): void {
|
||||
this._options = options;
|
||||
let serviceOptions = providerOptions.map(option => AdvancedPropertiesController.connectionOptionToServiceOption(option));
|
||||
this.advancedDialog.open(serviceOptions, this._options);
|
||||
}
|
||||
|
||||
public get advancedDialog() {
|
||||
if (!this._advancedDialog) {
|
||||
this._advancedDialog = this._instantiationService.createInstance(
|
||||
OptionsDialog, localize('connectionAdvancedProperties', "Advanced Properties"), TelemetryKeys.ConnectionAdvancedProperties, { hasBackButton: true, cancelLabel: localize('advancedProperties.discard', "Discard") });
|
||||
this._advancedDialog.onCloseEvent(() => this._onCloseAdvancedProperties());
|
||||
this._advancedDialog.onOk(() => this.handleOnOk());
|
||||
this._advancedDialog.render();
|
||||
}
|
||||
return this._advancedDialog;
|
||||
}
|
||||
|
||||
public set advancedDialog(dialog: OptionsDialog) {
|
||||
this._advancedDialog = dialog;
|
||||
}
|
||||
|
||||
public static connectionOptionToServiceOption(connectionOption: azdata.ConnectionOption): azdata.ServiceOption {
|
||||
return {
|
||||
name: connectionOption.name,
|
||||
displayName: connectionOption.displayName,
|
||||
description: connectionOption.description,
|
||||
groupName: connectionOption.groupName,
|
||||
valueType: connectionOption.valueType,
|
||||
defaultValue: connectionOption.defaultValue,
|
||||
objectType: undefined,
|
||||
categoryValues: connectionOption.categoryValues,
|
||||
isRequired: connectionOption.isRequired,
|
||||
isArray: undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
import { IConfigurationRegistry, Extensions as ConfigExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { AddServerGroupAction, AddServerAction } from 'sql/workbench/contrib/objectExplorer/browser/connectionTreeAction';
|
||||
import { ClearRecentConnectionsAction, GetCurrentConnectionStringAction } from 'sql/workbench/contrib/connection/browser/connectionActions';
|
||||
import { AddServerGroupAction, AddServerAction } from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction';
|
||||
import { ClearRecentConnectionsAction, GetCurrentConnectionStringAction } from 'sql/workbench/services/connection/browser/connectionActions';
|
||||
import * as azdata from 'azdata';
|
||||
import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
|
||||
@@ -1,179 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import nls = require('vs/nls');
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
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';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { QueryEditorInput } from 'sql/workbench/contrib/query/common/queryEditorInput';
|
||||
import { EditDataInput } from 'sql/workbench/contrib/editData/browser/editDataInput';
|
||||
import { DashboardInput } from 'sql/workbench/contrib/dashboard/browser/dashboardInput';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
|
||||
/**
|
||||
* Workbench action to clear the recent connnections list
|
||||
*/
|
||||
export class ClearRecentConnectionsAction extends Action {
|
||||
|
||||
public static ID = 'clearRecentConnectionsAction';
|
||||
public static LABEL = nls.localize('ClearRecentlyUsedLabel', "Clear List");
|
||||
public static ICON = 'search-action clear-search-results';
|
||||
|
||||
private _onRecentConnectionsRemoved = new Emitter<void>();
|
||||
public onRecentConnectionsRemoved: Event<void> = this._onRecentConnectionsRemoved.event;
|
||||
|
||||
private _useConfirmationMessage = false;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
|
||||
@INotificationService private _notificationService: INotificationService,
|
||||
@IQuickInputService private _quickInputService: IQuickInputService,
|
||||
@IDialogService private _dialogService: IDialogService,
|
||||
) {
|
||||
super(id, label, ClearRecentConnectionsAction.ICON);
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
public set useConfirmationMessage(value: boolean) {
|
||||
this._useConfirmationMessage = value;
|
||||
}
|
||||
|
||||
public run(): Promise<void> {
|
||||
if (this._useConfirmationMessage) {
|
||||
return this.promptConfirmationMessage().then(result => {
|
||||
if (result.confirmed) {
|
||||
this._connectionManagementService.clearRecentConnectionsList();
|
||||
this._onRecentConnectionsRemoved.fire();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return this.promptQuickOpenService().then(result => {
|
||||
if (result) {
|
||||
this._connectionManagementService.clearRecentConnectionsList();
|
||||
|
||||
const actions: INotificationActions = { primary: [] };
|
||||
this._notificationService.notify({
|
||||
severity: Severity.Info,
|
||||
message: nls.localize('ClearedRecentConnections', "Recent connections list cleared"),
|
||||
actions
|
||||
});
|
||||
this._onRecentConnectionsRemoved.fire();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private promptQuickOpenService(): Promise<boolean> {
|
||||
const self = this;
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
let choices: { key, value }[] = [
|
||||
{ key: nls.localize('connectionAction.yes', "Yes"), value: true },
|
||||
{ key: nls.localize('connectionAction.no', "No"), value: false }
|
||||
];
|
||||
|
||||
self._quickInputService.pick(choices.map(x => x.key), { placeHolder: nls.localize('ClearRecentlyUsedLabel', "Clear List"), ignoreFocusLost: true }).then((choice) => {
|
||||
let confirm = find(choices, x => x.key === choice);
|
||||
resolve(confirm && confirm.value);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private promptConfirmationMessage(): Promise<IConfirmationResult> {
|
||||
let confirm: IConfirmation = {
|
||||
message: nls.localize('clearRecentConnectionMessage', "Are you sure you want to delete all the connections from the list?"),
|
||||
primaryButton: nls.localize('connectionDialog.yes', "Yes"),
|
||||
secondaryButton: nls.localize('connectionDialog.no', "No"),
|
||||
type: 'question'
|
||||
};
|
||||
|
||||
return new Promise<IConfirmationResult>((resolve, reject) => {
|
||||
this._dialogService.confirm(confirm).then((confirmed) => {
|
||||
resolve(confirmed);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to delete one recently used connection from the MRU
|
||||
*/
|
||||
export class ClearSingleRecentConnectionAction extends Action {
|
||||
|
||||
public static ID = 'clearSingleRecentConnectionAction';
|
||||
public static LABEL = nls.localize('delete', "Delete");
|
||||
private _onRecentConnectionRemoved = new Emitter<void>();
|
||||
public onRecentConnectionRemoved: Event<void> = this._onRecentConnectionRemoved.event;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
private _connectionProfile: IConnectionProfile,
|
||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
|
||||
) {
|
||||
super(id, label);
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
public run(): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
resolve(this._connectionManagementService.clearRecentConnection(this._connectionProfile));
|
||||
this._onRecentConnectionRemoved.fire();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to retrieve the current connection string
|
||||
*/
|
||||
export class GetCurrentConnectionStringAction extends Action {
|
||||
|
||||
public static ID = 'getCurrentConnectionStringAction';
|
||||
public static LABEL = nls.localize('connectionAction.GetCurrentConnectionString', "Get Current Connection String");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
|
||||
@IEditorService private _editorService: IEditorService,
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
@IClipboardService private _clipboardService: IClipboardService,
|
||||
) {
|
||||
super(GetCurrentConnectionStringAction.ID, GetCurrentConnectionStringAction.LABEL);
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
public run(): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
let activeInput = this._editorService.activeEditor;
|
||||
if (activeInput && (activeInput instanceof QueryEditorInput || activeInput instanceof EditDataInput || activeInput instanceof DashboardInput)
|
||||
&& this._connectionManagementService.isConnected(activeInput.uri)) {
|
||||
let includePassword = false;
|
||||
let connectionProfile = this._connectionManagementService.getConnectionProfile(activeInput.uri);
|
||||
this._connectionManagementService.getConnectionString(connectionProfile.id, includePassword).then(result => {
|
||||
|
||||
//Copy to clipboard
|
||||
this._clipboardService.writeText(result);
|
||||
|
||||
let message = result
|
||||
? result
|
||||
: nls.localize('connectionAction.connectionString', "Connection string not available");
|
||||
this._notificationService.info(message);
|
||||
});
|
||||
} else {
|
||||
let message = nls.localize('connectionAction.noConnection', "No active connection available");
|
||||
this._notificationService.info(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,136 +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 { DefaultController, ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { ClearSingleRecentConnectionAction } from 'sql/workbench/contrib/connection/browser/connectionActions';
|
||||
import { ContributableActionProvider } from 'vs/workbench/browser/actions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
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/platform/connection/common/interfaces';
|
||||
|
||||
export class RecentConnectionActionsProvider extends ContributableActionProvider {
|
||||
private _onRecentConnectionRemoved = new Emitter<void>();
|
||||
public onRecentConnectionRemoved: Event<void> = this._onRecentConnectionRemoved.event;
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private _instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
private getRecentConnectionActions(tree: ITree, element: any): IAction[] {
|
||||
let actions: IAction[] = [];
|
||||
let clearSingleConnectionAction = this._instantiationService.createInstance(ClearSingleRecentConnectionAction, ClearSingleRecentConnectionAction.ID,
|
||||
ClearSingleRecentConnectionAction.LABEL, <IConnectionProfile>element);
|
||||
clearSingleConnectionAction.onRecentConnectionRemoved(() => this._onRecentConnectionRemoved.fire());
|
||||
actions.push(clearSingleConnectionAction);
|
||||
return actions;
|
||||
}
|
||||
|
||||
public hasActions(tree: ITree, element: any): boolean {
|
||||
return element instanceof ConnectionProfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return actions given an element in the tree
|
||||
*/
|
||||
public getActions(tree: ITree, element: any): IAction[] {
|
||||
if (element instanceof ConnectionProfile) {
|
||||
return this.getRecentConnectionActions(tree, element);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export class RecentConnectionsActionsContext {
|
||||
public connectionProfile: ConnectionProfile;
|
||||
public container: HTMLElement;
|
||||
public tree: ITree;
|
||||
}
|
||||
|
||||
export class RecentConnectionTreeController extends DefaultController {
|
||||
|
||||
private _onRecentConnectionRemoved = new Emitter<void>();
|
||||
public onRecentConnectionRemoved: Event<void> = this._onRecentConnectionRemoved.event;
|
||||
|
||||
constructor(
|
||||
private clickcb: (element: any, eventish: ICancelableEvent, origin: string) => void,
|
||||
private actionProvider: RecentConnectionActionsProvider,
|
||||
private _connectionManagementService: IConnectionManagementService,
|
||||
@IContextMenuService private _contextMenuService: IContextMenuService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected onLeftClick(tree: ITree, element: any, eventish: ICancelableEvent, origin: string = 'mouse'): boolean {
|
||||
this.clickcb(element, eventish, origin);
|
||||
return super.onLeftClick(tree, element, eventish, origin);
|
||||
}
|
||||
|
||||
protected onEnter(tree: ITree, event: IKeyboardEvent): boolean {
|
||||
super.onEnter(tree, event);
|
||||
this.clickcb(tree.getSelection()[0], event, 'keyboard');
|
||||
return true;
|
||||
}
|
||||
|
||||
protected onRightClick(tree: ITree, element: any, eventish: ICancelableEvent, origin: string = 'mouse'): boolean {
|
||||
this.clickcb(element, eventish, origin);
|
||||
this.showContextMenu(tree, element, eventish);
|
||||
return true;
|
||||
}
|
||||
|
||||
public onMouseDown(tree: ITree, element: any, event: mouse.IMouseEvent, origin: string = 'mouse'): boolean {
|
||||
if (event.leftButton || event.middleButton) {
|
||||
return this.onLeftClick(tree, element, event, origin);
|
||||
} else {
|
||||
return this.onRightClick(tree, element, event);
|
||||
}
|
||||
}
|
||||
|
||||
public onKeyDown(tree: ITree, event: IKeyboardEvent): boolean {
|
||||
if (event.keyCode === 20) {
|
||||
let element = tree.getFocus();
|
||||
if (element instanceof ConnectionProfile) {
|
||||
this._connectionManagementService.clearRecentConnection(element);
|
||||
this._onRecentConnectionRemoved.fire();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.onKeyDown(tree, event);
|
||||
}
|
||||
|
||||
public showContextMenu(tree: ITree, element: any, event: any): boolean {
|
||||
let actionContext: any;
|
||||
|
||||
if (element instanceof ConnectionProfile) {
|
||||
actionContext = new RecentConnectionsActionsContext();
|
||||
actionContext.container = event.target;
|
||||
actionContext.connectionProfile = <ConnectionProfile>element;
|
||||
actionContext.tree = tree;
|
||||
} else {
|
||||
actionContext = element;
|
||||
}
|
||||
|
||||
let anchor = { x: event.x + 1, y: event.y };
|
||||
this._contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => this.actionProvider.getActions(tree, element),
|
||||
onHide: (wasCancelled?: boolean) => {
|
||||
if (wasCancelled) {
|
||||
tree.domFocus();
|
||||
}
|
||||
},
|
||||
getActionsContext: () => (actionContext)
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DefaultController, ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
|
||||
export class SavedConnectionTreeController extends DefaultController {
|
||||
constructor(private clickcb: (element: any, eventish: ICancelableEvent, origin: string) => void) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected onLeftClick(tree: ITree, element: any, eventish: ICancelableEvent, origin: string = 'mouse'): boolean {
|
||||
this.clickcb(element, eventish, origin);
|
||||
return super.onLeftClick(tree, element, eventish, origin);
|
||||
}
|
||||
|
||||
protected onEnter(tree: ITree, event: IKeyboardEvent): boolean {
|
||||
super.onEnter(tree, event);
|
||||
|
||||
// grab the current selection for use later
|
||||
let selection = tree.getSelection();
|
||||
|
||||
this.clickcb(selection[0], event, 'keyboard');
|
||||
tree.toggleExpansion(selection[0]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
// ------------------------------- < Cancel Connect Request > ---------------------------------------
|
||||
|
||||
/**
|
||||
* Cancel connect request message format
|
||||
*/
|
||||
export class CancelConnectParams {
|
||||
/**
|
||||
* URI identifying the owner of the connection
|
||||
*/
|
||||
public ownerUri: string;
|
||||
}
|
||||
|
||||
// ------------------------------- </ Cancel Connect Request > --------------------------------------
|
||||
|
||||
// ------------------------------- < Disconnect Request > -------------------------------------------
|
||||
|
||||
// Disconnect request message format
|
||||
export class DisconnectParams {
|
||||
// URI identifying the owner of the connection
|
||||
public ownerUri: string;
|
||||
}
|
||||
|
||||
// ------------------------------- </ Disconnect Request > ------------------------------------------
|
||||
@@ -1,40 +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 { ConnectionSummary } from 'azdata';
|
||||
import * as LocalizedConstants from 'sql/workbench/contrib/connection/common/localizedConstants';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
// Status when making connections from the viewlet
|
||||
export class ConnectionGlobalStatus {
|
||||
|
||||
private _displayTime: number = 5000; // (in ms)
|
||||
|
||||
constructor(
|
||||
@INotificationService private _notificationService: INotificationService
|
||||
) {
|
||||
}
|
||||
|
||||
public setStatusToConnected(connectionSummary: ConnectionSummary): void {
|
||||
if (this._notificationService) {
|
||||
let text: string;
|
||||
let connInfo: string = connectionSummary.serverName;
|
||||
if (connInfo) {
|
||||
if (connectionSummary.databaseName && connectionSummary.databaseName !== '') {
|
||||
connInfo = connInfo + ' : ' + connectionSummary.databaseName;
|
||||
} else {
|
||||
connInfo = connInfo + ' : ' + '<default>';
|
||||
}
|
||||
text = LocalizedConstants.onDidConnectMessage + ' ' + connInfo;
|
||||
}
|
||||
this._notificationService.status(text, { hideAfter: this._displayTime });
|
||||
}
|
||||
}
|
||||
|
||||
public setStatusToDisconnected(fileUri: string): void {
|
||||
if (this._notificationService) {
|
||||
this._notificationService.status(LocalizedConstants.onDidDisconnectMessage, { hideAfter: this._displayTime });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +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 { localize } from 'vs/nls';
|
||||
|
||||
export const onDidConnectMessage = localize('onDidConnectMessage', "Connected to");
|
||||
export const onDidDisconnectMessage = localize('onDidDisconnectMessage', "Disconnected");
|
||||
export const unsavedGroupLabel = localize('unsavedGroupLabel', "Unsaved Connections");
|
||||
@@ -1,54 +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 { RawContextKey, IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ServerInfo } from 'azdata';
|
||||
import { DatabaseEngineEdition } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
export class ServerInfoContextKey implements IContextKey<ServerInfo> {
|
||||
|
||||
static ServerInfo = new RawContextKey<ServerInfo>('serverInfo', undefined);
|
||||
static ServerMajorVersion = new RawContextKey<string>('serverMajorVersion', undefined);
|
||||
static IsCloud = new RawContextKey<boolean>('isCloud', undefined);
|
||||
static IsBigDataCluster = new RawContextKey<boolean>('isBigDataCluster', undefined);
|
||||
static EngineEdition = new RawContextKey<number>('engineEdition', undefined);
|
||||
|
||||
private _serverInfo: IContextKey<ServerInfo>;
|
||||
private _serverMajorVersion: IContextKey<string>;
|
||||
private _isCloud: IContextKey<boolean>;
|
||||
private _isBigDataCluster: IContextKey<boolean>;
|
||||
private _engineEdition: IContextKey<number>;
|
||||
|
||||
constructor(
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
) {
|
||||
this._serverInfo = ServerInfoContextKey.ServerInfo.bindTo(contextKeyService);
|
||||
this._serverMajorVersion = ServerInfoContextKey.ServerMajorVersion.bindTo(contextKeyService);
|
||||
this._isCloud = ServerInfoContextKey.IsCloud.bindTo(contextKeyService);
|
||||
this._isBigDataCluster = ServerInfoContextKey.IsBigDataCluster.bindTo(contextKeyService);
|
||||
this._engineEdition = ServerInfoContextKey.EngineEdition.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
set(value: ServerInfo) {
|
||||
this._serverInfo.set(value);
|
||||
let majorVersion = value && value.serverMajorVersion;
|
||||
this._serverMajorVersion.set(majorVersion && `${majorVersion}`);
|
||||
this._isCloud.set(value && value.isCloud);
|
||||
this._isBigDataCluster.set(value && value.options && value.options['isBigDataCluster']);
|
||||
let engineEditionId = value && value.engineEditionId;
|
||||
engineEditionId ? this._engineEdition.set(engineEditionId) : this._engineEdition.set(DatabaseEngineEdition.Unknown);
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
this._serverMajorVersion.reset();
|
||||
this._isCloud.reset();
|
||||
this._isBigDataCluster.reset();
|
||||
this._engineEdition.reset();
|
||||
}
|
||||
|
||||
public get(): ServerInfo {
|
||||
return this._serverInfo.get();
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { OptionsDialog } from 'sql/workbench/browser/modal/optionsDialog';
|
||||
import { AdvancedPropertiesController } from 'sql/workbench/contrib/connection/browser/advancedPropertiesController';
|
||||
import { AdvancedPropertiesController } from 'sql/workbench/services/connection/browser/advancedPropertiesController';
|
||||
import * as azdata from 'azdata';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import * as assert from 'assert';
|
||||
|
||||
@@ -10,7 +10,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
|
||||
import { IAngularEventingService, AngularEventType, IAngularEvent } from 'sql/platform/angularEventing/browser/angularEventingService';
|
||||
import { INewDashboardTabDialogService } from 'sql/workbench/services/dashboard/browser/newDashboardTabDialog';
|
||||
import { IDashboardTab } from 'sql/workbench/contrib/dashboard/browser/dashboardRegistry';
|
||||
import { IDashboardTab } from 'sql/workbench/services/dashboard/browser/common/interfaces';
|
||||
import { find, firstIndex } from 'vs/base/common/arrays';
|
||||
import { CellContext } from 'sql/workbench/contrib/notebook/browser/cellViews/codeActions';
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ import { CommonServiceInterface, SingleConnectionManagementService } from 'sql/w
|
||||
import { WidgetConfig, TabConfig, TabSettingConfig } from 'sql/workbench/contrib/dashboard/browser/core/dashboardWidget';
|
||||
import { IPropertiesConfig } from 'sql/workbench/contrib/dashboard/browser/pages/serverDashboardPage.contribution';
|
||||
import { PanelComponent } from 'sql/base/browser/ui/panel/panel.component';
|
||||
import { IDashboardRegistry, Extensions as DashboardExtensions, IDashboardTab } from 'sql/workbench/contrib/dashboard/browser/dashboardRegistry';
|
||||
import { IDashboardRegistry, Extensions as DashboardExtensions } from 'sql/workbench/contrib/dashboard/browser/dashboardRegistry';
|
||||
import { IDashboardTab } from 'sql/workbench/services/dashboard/browser/common/interfaces';
|
||||
import { PinUnpinTabAction, AddFeatureTabAction } from './actions';
|
||||
import { TabComponent, TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { AngularEventType, IAngularEventingService } from 'sql/platform/angularEventing/browser/angularEventingService';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { InjectionToken, OnDestroy } from '@angular/core';
|
||||
import { NgGridItemConfig } from 'angular2-grid';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IDashboardTab } from 'sql/workbench/contrib/dashboard/browser/dashboardRegistry';
|
||||
import { IDashboardTab } from 'sql/workbench/services/dashboard/browser/common/interfaces';
|
||||
|
||||
export interface IDashboardWidget {
|
||||
actions: Array<Action>;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DashboardEditor } from 'sql/workbench/contrib/dashboard/browser/dashboardEditor';
|
||||
import { DashboardInput } from 'sql/workbench/contrib/dashboard/browser/dashboardInput';
|
||||
import { DashboardInput } from 'sql/workbench/browser/editor/profiler/dashboardInput';
|
||||
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
|
||||
@@ -17,7 +17,7 @@ import { SERVER_DASHBOARD_PROPERTIES, serverDashboardPropertiesSchema, SERVER_DA
|
||||
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { MssqlNodeContext } from 'sql/workbench/services/objectExplorer/browser/mssqlNodeContext';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { TreeNodeContextKey } from 'sql/workbench/contrib/objectExplorer/common/treeNodeContextKey';
|
||||
import { TreeNodeContextKey } from 'sql/workbench/services/objectExplorer/common/treeNodeContextKey';
|
||||
import { DE_MANAGE_COMMAND_ID, OE_MANAGE_COMMAND_ID } from 'sql/workbench/contrib/dashboard/browser/dashboardActions';
|
||||
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
|
||||
|
||||
@@ -11,11 +11,11 @@ import { generateUri } from 'sql/platform/connection/common/utils';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { TreeSelectionHandler } from 'sql/workbench/contrib/objectExplorer/browser/treeSelectionHandler';
|
||||
import { ObjectExplorerActionsContext, getTreeNode } from 'sql/workbench/contrib/objectExplorer/browser/objectExplorerActions';
|
||||
import { TreeNode } from 'sql/workbench/contrib/objectExplorer/common/treeNode';
|
||||
import { TreeSelectionHandler } from 'sql/workbench/services/objectExplorer/browser/treeSelectionHandler';
|
||||
import { ObjectExplorerActionsContext, getTreeNode } from 'sql/workbench/services/objectExplorer/browser/objectExplorerActions';
|
||||
import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { TreeUpdateUtils } from 'sql/workbench/contrib/objectExplorer/browser/treeUpdateUtils';
|
||||
import { TreeUpdateUtils } from 'sql/workbench/services/objectExplorer/browser/treeUpdateUtils';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService';
|
||||
import { IViewsService } from 'vs/workbench/common/views';
|
||||
|
||||
@@ -11,7 +11,7 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
import { DashboardInput } from './dashboardInput';
|
||||
import { DashboardInput } from 'sql/workbench/browser/editor/profiler/dashboardInput';
|
||||
import { DashboardModule } from './dashboard.module';
|
||||
import { bootstrapAngular } from 'sql/workbench/services/bootstrap/browser/bootstrapService';
|
||||
import { IDashboardComponentParams } from 'sql/workbench/services/bootstrap/common/bootstrapParams';
|
||||
|
||||
@@ -1,173 +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 { EditorInput, EditorModel } from 'vs/workbench/common/editor';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
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/platform/connection/common/interfaces';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
|
||||
export class DashboardInput extends EditorInput {
|
||||
|
||||
private _uri: string;
|
||||
public static ID: string = 'workbench.editorinputs.connectiondashboardinputs';
|
||||
public static SCHEMA: string = 'sqldashboard';
|
||||
|
||||
private _initializedPromise: Thenable<void>;
|
||||
private _onConnectionChanged: IDisposable;
|
||||
|
||||
public get initializedPromise(): Thenable<void> {
|
||||
return this._initializedPromise;
|
||||
}
|
||||
|
||||
private _uniqueSelector: string;
|
||||
|
||||
public hasBootstrapped = false;
|
||||
// Holds the HTML content for the editor when the editor discards this input and loads another
|
||||
private _parentContainer: HTMLElement;
|
||||
|
||||
constructor(
|
||||
_connectionProfile: IConnectionProfile,
|
||||
@IConnectionManagementService private _connectionService: IConnectionManagementService,
|
||||
@IModeService modeService: IModeService,
|
||||
@IModelService model: IModelService
|
||||
) {
|
||||
super();
|
||||
// TODO; possible refactor
|
||||
// basically this is mimicing creating a "model" (the backing model for text for editors)
|
||||
// for dashboard, even though there is no backing text. We need this so that we can
|
||||
// tell the icon theme services that we are a dashboard resource, therefore loading the correct icon
|
||||
|
||||
// vscode has a comment that Mode's will eventually be removed (not sure the state of this comment)
|
||||
// so this might be able to be undone when that happens
|
||||
if (!model.getModel(this.getResource())) {
|
||||
model.createModel('', modeService.create('dashboard'), this.getResource());
|
||||
}
|
||||
this._initializedPromise = _connectionService.connectIfNotConnected(_connectionProfile, 'dashboard').then(
|
||||
u => {
|
||||
this._uri = u;
|
||||
const info = this._connectionService.getConnectionInfo(u);
|
||||
if (info) {
|
||||
this._onConnectionChanged = this._connectionService.onConnectionChanged(e => {
|
||||
if (e.connectionUri === u) {
|
||||
this._onDidChangeLabel.fire();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public setUniqueSelector(uniqueSelector: string): void {
|
||||
this._uniqueSelector = uniqueSelector;
|
||||
}
|
||||
|
||||
public getTypeId(): string {
|
||||
return DashboardInput.ID;
|
||||
}
|
||||
|
||||
public getResource(): URI {
|
||||
return URI.from({
|
||||
scheme: 'dashboard',
|
||||
path: 'dashboard'
|
||||
});
|
||||
}
|
||||
|
||||
public getName(): string {
|
||||
if (!this.connectionProfile) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let name = this.connectionProfile.connectionName ? this.connectionProfile.connectionName : this.connectionProfile.serverName;
|
||||
if (this.connectionProfile.databaseName
|
||||
&& !this.isMasterMssql()) {
|
||||
// Only add DB name if this is a non-default, non-master connection
|
||||
name = name + ':' + this.connectionProfile.databaseName;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
private isMasterMssql(): boolean {
|
||||
return this.connectionProfile.providerName === mssqlProviderName
|
||||
&& this.connectionProfile.databaseName.toLowerCase() === 'master';
|
||||
}
|
||||
|
||||
public get uri(): string {
|
||||
return this._uri;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._disposeContainer();
|
||||
if (this._onConnectionChanged) {
|
||||
this._onConnectionChanged.dispose();
|
||||
}
|
||||
this._connectionService.disconnect(this._uri);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private _disposeContainer() {
|
||||
if (!this._parentContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parentNode = this._parentContainer.parentNode;
|
||||
if (parentNode) {
|
||||
parentNode.removeChild(this._parentContainer);
|
||||
this._parentContainer = null;
|
||||
}
|
||||
}
|
||||
|
||||
set container(container: HTMLElement) {
|
||||
this._disposeContainer();
|
||||
this._parentContainer = container;
|
||||
}
|
||||
|
||||
get container(): HTMLElement {
|
||||
return this._parentContainer;
|
||||
}
|
||||
|
||||
public supportsSplitEditor(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public get connectionProfile(): IConnectionProfile {
|
||||
return this._connectionService.getConnectionProfile(this._uri);
|
||||
}
|
||||
|
||||
public resolve(refresh?: boolean): Promise<EditorModel> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public get hasInitialized(): boolean {
|
||||
return !!this._uniqueSelector;
|
||||
}
|
||||
|
||||
public get uniqueSelector(): string {
|
||||
return this._uniqueSelector;
|
||||
}
|
||||
|
||||
public matches(otherinput: any): boolean {
|
||||
return otherinput instanceof DashboardInput
|
||||
&& DashboardInput.profileMatches(this.connectionProfile, otherinput.connectionProfile);
|
||||
}
|
||||
|
||||
// similar to the default profile match but without databasename
|
||||
public static profileMatches(profile1: IConnectionProfile, profile2: IConnectionProfile): boolean {
|
||||
return profile1 && profile2
|
||||
&& profile1.providerName === profile2.providerName
|
||||
&& profile1.serverName === profile2.serverName
|
||||
&& profile1.userName === profile2.userName
|
||||
&& profile1.authenticationType === profile2.authenticationType
|
||||
&& profile1.groupFullName === profile2.groupFullName;
|
||||
}
|
||||
|
||||
public get tabColor(): string {
|
||||
return this._connectionService.getTabColorForUri(this.uri);
|
||||
}
|
||||
}
|
||||
@@ -14,23 +14,12 @@ import { DATABASE_DASHBOARD_TABS } from 'sql/workbench/contrib/dashboard/browser
|
||||
import { SERVER_DASHBOARD_TABS } from 'sql/workbench/contrib/dashboard/browser/pages/serverDashboardPage.contribution';
|
||||
import { DASHBOARD_CONFIG_ID, DASHBOARD_TABS_KEY_PROPERTY } from 'sql/workbench/contrib/dashboard/browser/pages/dashboardPageContribution';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
import { IDashboardTab } from 'sql/workbench/services/dashboard/browser/common/interfaces';
|
||||
|
||||
export const Extensions = {
|
||||
DashboardContributions: 'dashboard.contributions'
|
||||
};
|
||||
|
||||
export interface IDashboardTab {
|
||||
id: string;
|
||||
title: string;
|
||||
provider: string | string[];
|
||||
publisher: string;
|
||||
description?: string;
|
||||
container?: { [key: string]: any };
|
||||
when?: string;
|
||||
alwaysShow?: boolean;
|
||||
isHomeTab?: boolean;
|
||||
}
|
||||
|
||||
export interface IDashboardRegistry {
|
||||
registerDashboardProvider(id: string, properties: ProviderProperties): void;
|
||||
getProperties(id: string): ProviderProperties;
|
||||
|
||||
@@ -14,7 +14,7 @@ import { IConnectionManagementService } from 'sql/platform/connection/common/con
|
||||
import { IAdminService } from 'sql/workbench/services/admin/common/adminService';
|
||||
import { IQueryManagementService } from 'sql/workbench/services/query/common/queryManagement';
|
||||
import { AngularEventType, IAngularEvent, IAngularEventingService } from 'sql/platform/angularEventing/browser/angularEventingService';
|
||||
import { IDashboardTab } from 'sql/workbench/contrib/dashboard/browser/dashboardRegistry';
|
||||
import { IDashboardTab } from 'sql/workbench/services/dashboard/browser/common/interfaces';
|
||||
import { TabSettingConfig } from 'sql/workbench/contrib/dashboard/browser/core/dashboardWidget';
|
||||
import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service';
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!sql/media/objectTypes/objecttypes';
|
||||
import 'vs/css!sql/media/icons/common-icons';
|
||||
import 'vs/css!./media/explorerWidget';
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import { ServerTreeView } from 'sql/workbench/contrib/objectExplorer/browser/ser
|
||||
import {
|
||||
ActiveConnectionsFilterAction,
|
||||
AddServerAction, AddServerGroupAction
|
||||
} from 'sql/workbench/contrib/objectExplorer/browser/connectionTreeAction';
|
||||
} from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction';
|
||||
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
@@ -55,7 +55,7 @@ export class ConnectionViewletPanel extends ViewPane {
|
||||
this._addServerGroupAction = this.instantiationService.createInstance(AddServerGroupAction,
|
||||
AddServerGroupAction.ID,
|
||||
AddServerGroupAction.LABEL);
|
||||
this._serverTreeView = this.objectExplorerService.getServerTreeView();
|
||||
this._serverTreeView = <any>this.objectExplorerService.getServerTreeView() as ServerTreeView;
|
||||
if (!this._serverTreeView) {
|
||||
this._serverTreeView = this.instantiationService.createInstance(ServerTreeView);
|
||||
this.objectExplorerService.registerServerTreeView(this._serverTreeView);
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { EditDataEditor } from 'sql/workbench/contrib/editData/browser/editDataEditor';
|
||||
import { EditDataInput } from 'sql/workbench/contrib/editData/browser/editDataInput';
|
||||
import { EditDataInput } from 'sql/workbench/browser/editData/editDataInput';
|
||||
import { EditDataResultsEditor } from 'sql/workbench/contrib/editData/browser/editDataResultsEditor';
|
||||
import { EditDataResultsInput } from 'sql/workbench/contrib/editData/browser/editDataResultsInput';
|
||||
import { EditDataResultsInput } from 'sql/workbench/browser/editData/editDataResultsInput';
|
||||
import { EditorDescriptor, IEditorRegistry, Extensions } from 'vs/workbench/browser/editor';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
|
||||
@@ -14,7 +14,7 @@ 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 { EditDataInput } from 'sql/workbench/contrib/editData/browser/editDataInput';
|
||||
import { EditDataInput } from 'sql/workbench/browser/editData/editDataInput';
|
||||
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import * as queryContext from 'sql/workbench/contrib/query/common/queryContext';
|
||||
@@ -32,7 +32,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c
|
||||
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
|
||||
import { IFlexibleSash, HorizontalFlexibleSash } from 'sql/workbench/contrib/query/browser/flexibleSash';
|
||||
import { EditDataResultsEditor } from 'sql/workbench/contrib/editData/browser/editDataResultsEditor';
|
||||
import { EditDataResultsInput } from 'sql/workbench/contrib/editData/browser/editDataResultsInput';
|
||||
import { EditDataResultsInput } from 'sql/workbench/browser/editData/editDataResultsInput';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
@@ -274,7 +274,7 @@ export class EditDataEditor extends BaseEditor {
|
||||
if (!input.results.container) {
|
||||
this._resultsEditorContainer = DOM.append(parentElement, DOM.$('.editDataContainer-horizontal'));
|
||||
|
||||
input.results.container = this._resultsEditorContainer;
|
||||
input.results.setContainer(this._resultsEditorContainer);
|
||||
} else {
|
||||
this._resultsEditorContainer = DOM.append(parentElement, input.results.container);
|
||||
}
|
||||
|
||||
@@ -1,235 +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 { EditorInput, EncodingMode, IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { IConnectionManagementService, IConnectableInput, INewConnectionParams } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { IQueryModelService } from 'sql/workbench/services/query/common/queryModel';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { EditSessionReadyParams } from 'azdata';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as nls from 'vs/nls';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { EditDataResultsInput } from 'sql/workbench/contrib/editData/browser/editDataResultsInput';
|
||||
import { IEditorViewState } from 'vs/editor/common/editorCommon';
|
||||
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
|
||||
import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
|
||||
import { IUntitledTextEditorModel, UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel';
|
||||
|
||||
/**
|
||||
* Input for the EditDataEditor.
|
||||
*/
|
||||
export class EditDataInput extends EditorInput implements IConnectableInput {
|
||||
public static ID: string = 'workbench.editorinputs.editDataInput';
|
||||
private _hasBootstrapped: boolean;
|
||||
private _editorContainer: HTMLElement;
|
||||
private _updateTaskbar: Emitter<EditDataInput>;
|
||||
private _editorInitializing: Emitter<boolean>;
|
||||
private _showResultsEditor: Emitter<EditDataInput>;
|
||||
private _refreshButtonEnabled: boolean;
|
||||
private _stopButtonEnabled: boolean;
|
||||
private _setup: boolean;
|
||||
private _rowLimit: number;
|
||||
private _objectType: string;
|
||||
private _css: HTMLStyleElement;
|
||||
private _useQueryFilter: boolean;
|
||||
|
||||
public savedViewState: IEditorViewState;
|
||||
|
||||
constructor(
|
||||
private _uri: URI,
|
||||
private _schemaName: string,
|
||||
private _tableName: string,
|
||||
private _sql: UntitledTextEditorInput,
|
||||
private _queryString: string,
|
||||
private _results: EditDataResultsInput,
|
||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
|
||||
@IQueryModelService private _queryModelService: IQueryModelService,
|
||||
@INotificationService private notificationService: INotificationService
|
||||
) {
|
||||
super();
|
||||
this._hasBootstrapped = false;
|
||||
this._updateTaskbar = new Emitter<EditDataInput>();
|
||||
this._showResultsEditor = new Emitter<EditDataInput>();
|
||||
this._editorInitializing = new Emitter<boolean>();
|
||||
this._setup = false;
|
||||
this._stopButtonEnabled = false;
|
||||
this._refreshButtonEnabled = false;
|
||||
this._useQueryFilter = false;
|
||||
|
||||
// re-emit sql editor events through this editor if it exists.
|
||||
// also set dirty status to false to prevent rerendering.
|
||||
if (this._sql) {
|
||||
this._register(this._sql.onDidChangeDirty(async () => {
|
||||
const model = await this._sql.resolve() as UntitledTextEditorModel;
|
||||
model.setDirty(false);
|
||||
this._onDidChangeDirty.fire();
|
||||
}));
|
||||
}
|
||||
|
||||
//TODO determine is this is a table or a view
|
||||
this._objectType = 'TABLE';
|
||||
|
||||
// Attach to event callbacks
|
||||
if (this._queryModelService) {
|
||||
let self = this;
|
||||
|
||||
// Register callbacks for the Actions
|
||||
this._register(
|
||||
this._queryModelService.onRunQueryStart(uri => {
|
||||
if (self.uri === uri) {
|
||||
self.initEditStart();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this._register(
|
||||
this._queryModelService.onEditSessionReady((result) => {
|
||||
if (self.uri === result.ownerUri) {
|
||||
self.initEditEnd(result);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
public get tableName(): string { return this._tableName; }
|
||||
public get schemaName(): string { return this._schemaName; }
|
||||
public get uri(): string { return unescape(this._uri.toString()); }
|
||||
public get sql(): UntitledTextEditorInput { return this._sql; }
|
||||
public get results(): EditDataResultsInput { return this._results; }
|
||||
public getResultsInputResource(): string { return this._results.uri; }
|
||||
public get updateTaskbarEvent(): Event<EditDataInput> { return this._updateTaskbar.event; }
|
||||
public get editorInitializingEvent(): Event<boolean> { return this._editorInitializing.event; }
|
||||
public get showResultsEditorEvent(): Event<EditDataInput> { return this._showResultsEditor.event; }
|
||||
public get stopButtonEnabled(): boolean { return this._stopButtonEnabled; }
|
||||
public get refreshButtonEnabled(): boolean { return this._refreshButtonEnabled; }
|
||||
public get container(): HTMLElement { return this._editorContainer; }
|
||||
public get hasBootstrapped(): boolean { return this._hasBootstrapped; }
|
||||
public get setup(): boolean { return this._setup; }
|
||||
public get rowLimit(): number { return this._rowLimit; }
|
||||
public get objectType(): string { return this._objectType; }
|
||||
public showResultsEditor(): void { this._showResultsEditor.fire(undefined); }
|
||||
public isDirty(): boolean { return false; }
|
||||
public save(): Promise<IEditorInput | undefined> { return Promise.resolve(undefined); }
|
||||
public getTypeId(): string { return EditDataInput.ID; }
|
||||
public setBootstrappedTrue(): void { this._hasBootstrapped = true; }
|
||||
public getResource(): URI { return this._uri; }
|
||||
public supportsSplitEditor(): boolean { return false; }
|
||||
public setupComplete() { this._setup = true; }
|
||||
public get queryString(): string {
|
||||
return this._queryString;
|
||||
}
|
||||
public set queryString(queryString: string) {
|
||||
this._queryString = queryString;
|
||||
}
|
||||
public get css(): HTMLStyleElement {
|
||||
return this._css;
|
||||
}
|
||||
public set css(css: HTMLStyleElement) {
|
||||
this._css = css;
|
||||
}
|
||||
public get queryPaneEnabled(): boolean {
|
||||
return this._useQueryFilter;
|
||||
}
|
||||
public set queryPaneEnabled(useQueryFilter: boolean) {
|
||||
this._useQueryFilter = useQueryFilter;
|
||||
}
|
||||
|
||||
// State Update Callbacks
|
||||
public initEditStart(): void {
|
||||
this._editorInitializing.fire(true);
|
||||
this._refreshButtonEnabled = false;
|
||||
this._stopButtonEnabled = true;
|
||||
this._updateTaskbar.fire(this);
|
||||
}
|
||||
|
||||
public initEditEnd(result: EditSessionReadyParams): void {
|
||||
this._refreshButtonEnabled = true;
|
||||
this._stopButtonEnabled = false;
|
||||
if (!result.success) {
|
||||
this.notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: result.message
|
||||
});
|
||||
}
|
||||
this._editorInitializing.fire(false);
|
||||
this._updateTaskbar.fire(this);
|
||||
}
|
||||
|
||||
public onConnectStart(): void {
|
||||
// TODO: Indicate connection started
|
||||
}
|
||||
|
||||
public onConnectReject(error?: string): void {
|
||||
if (error) {
|
||||
|
||||
this.notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: nls.localize('connectionFailure', "Edit Data Session Failed To Connect")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public onConnectCanceled(): void {
|
||||
}
|
||||
|
||||
public onConnectSuccess(params?: INewConnectionParams): void {
|
||||
let rowLimit: number = undefined;
|
||||
let queryString: string = undefined;
|
||||
if (this._useQueryFilter) {
|
||||
queryString = this._queryString;
|
||||
} else {
|
||||
rowLimit = this._rowLimit;
|
||||
}
|
||||
|
||||
this._queryModelService.initializeEdit(this.uri, this.schemaName, this.tableName, this._objectType, rowLimit, queryString);
|
||||
this.showResultsEditor();
|
||||
this._onDidChangeLabel.fire();
|
||||
}
|
||||
|
||||
public onDisconnect(): void {
|
||||
// TODO: deal with disconnections
|
||||
}
|
||||
|
||||
public onRowDropDownSet(rows: number) {
|
||||
this._rowLimit = rows;
|
||||
}
|
||||
|
||||
// Boiler Plate Functions
|
||||
public matches(otherInput: any): boolean {
|
||||
if (otherInput instanceof EditDataInput) {
|
||||
return this._sql.matches(otherInput.sql);
|
||||
}
|
||||
|
||||
return this._sql.matches(otherInput);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
// Dispose our edit session then disconnect our input
|
||||
this._queryModelService.disposeEdit(this.uri).then(() => {
|
||||
return this._connectionManagementService.disconnectEditor(this, true);
|
||||
});
|
||||
this._queryModelService.disposeQuery(this.uri);
|
||||
this._sql.dispose();
|
||||
this._results.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public get tabColor(): string {
|
||||
return this._connectionManagementService.getTabColorForUri(this.uri);
|
||||
}
|
||||
|
||||
public resolve(refresh?: boolean): Promise<IUntitledTextEditorModel & IResolvedTextEditorModel> { return this._sql.resolve(); }
|
||||
public getEncoding(): string { return this._sql.getEncoding(); }
|
||||
public getName(): string { return this._sql.getName(); }
|
||||
public get hasAssociatedFilePath(): boolean { return this._sql.model.hasAssociatedFilePath; }
|
||||
|
||||
public setEncoding(encoding: string, mode: EncodingMode /* ignored, we only have Encode */): void {
|
||||
this._sql.setEncoding(encoding, mode);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ import * as types from 'vs/base/common/types';
|
||||
import { IQueryModelService } from 'sql/workbench/services/query/common/queryModel';
|
||||
import { BareResultsGridInfo, getBareResultsGridInfoStyles } from 'sql/workbench/contrib/query/browser/queryResultsEditor';
|
||||
import { EditDataGridPanel } from 'sql/workbench/contrib/editData/browser/editDataGridPanel';
|
||||
import { EditDataResultsInput } from 'sql/workbench/contrib/editData/browser/editDataResultsInput';
|
||||
import { EditDataResultsInput } from 'sql/workbench/browser/editData/editDataResultsInput';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
|
||||
@@ -1,105 +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 { EditorInput } from 'vs/workbench/common/editor';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
/**
|
||||
* Input for the EditDataResultsEditor. This input helps with logic for the viewing and editing of
|
||||
* data in the results grid.
|
||||
*/
|
||||
export class EditDataResultsInput extends EditorInput {
|
||||
|
||||
// Tracks if the editor that holds this input should be visible (i.e. true if a query has been run)
|
||||
private _visible: boolean;
|
||||
|
||||
// Tracks if the editor has holds this input has has bootstrapped angular yet
|
||||
private _hasBootstrapped: boolean;
|
||||
|
||||
// Holds the HTML content for the editor when the editor discards this input and loads another
|
||||
private _editorContainer: HTMLElement;
|
||||
public css: HTMLStyleElement;
|
||||
|
||||
public readonly onRestoreViewStateEmitter = new Emitter<void>();
|
||||
public readonly onSaveViewStateEmitter = new Emitter<void>();
|
||||
|
||||
constructor(private _uri: string) {
|
||||
super();
|
||||
this._visible = false;
|
||||
this._hasBootstrapped = false;
|
||||
}
|
||||
|
||||
getTypeId(): string {
|
||||
return EditDataResultsInput.ID;
|
||||
}
|
||||
|
||||
matches(other: any): boolean {
|
||||
if (other instanceof EditDataResultsInput) {
|
||||
return (other._uri === this._uri);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
resolve(refresh?: boolean): Promise<any> {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
supportsSplitEditor(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public setBootstrappedTrue(): void {
|
||||
this._hasBootstrapped = true;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._disposeContainer();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private _disposeContainer() {
|
||||
if (!this._editorContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
let parentContainer = this._editorContainer.parentNode;
|
||||
if (parentContainer) {
|
||||
parentContainer.removeChild(this._editorContainer);
|
||||
this._editorContainer = null;
|
||||
}
|
||||
}
|
||||
|
||||
//// Properties
|
||||
|
||||
static get ID() {
|
||||
return 'workbench.editorinputs.editDataResultsInput';
|
||||
}
|
||||
|
||||
set container(container: HTMLElement) {
|
||||
this._disposeContainer();
|
||||
this._editorContainer = container;
|
||||
}
|
||||
|
||||
get container(): HTMLElement {
|
||||
return this._editorContainer;
|
||||
}
|
||||
|
||||
get hasBootstrapped(): boolean {
|
||||
return this._hasBootstrapped;
|
||||
}
|
||||
|
||||
get visible(): boolean {
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
set visible(visible: boolean) {
|
||||
this._visible = visible;
|
||||
}
|
||||
|
||||
get uri(): string {
|
||||
return unescape(this._uri);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ import { IEditorInput, EditorInput } from 'vs/workbench/common/editor';
|
||||
import { ITextEditorOptions, IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
|
||||
import { QueryEditorInput } from 'sql/workbench/contrib/query/common/queryEditorInput';
|
||||
import { QueryEditorInput } from 'sql/workbench/common/editor/query/queryEditorInput';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ILanguageAssociationRegistry, Extensions as LanguageAssociationExtensions } from 'sql/workbench/services/languageAssociation/common/languageAssociation';
|
||||
import { QueryEditorLanguageAssociation } from 'sql/workbench/contrib/query/common/queryInputFactory';
|
||||
@@ -25,7 +25,7 @@ import { workbenchInstantiationService } from 'sql/workbench/test/workbenchTestS
|
||||
import { NotebookEditorInputAssociation } from 'sql/workbench/contrib/notebook/common/models/nodebookInputFactory';
|
||||
import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
|
||||
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
|
||||
import { UntitledQueryEditorInput } from 'sql/workbench/contrib/query/common/untitledQueryEditorInput';
|
||||
import { UntitledQueryEditorInput } from 'sql/workbench/common/editor/query/untitledQueryEditorInput';
|
||||
import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { NotebookServiceStub } from 'sql/workbench/contrib/notebook/test/stubs';
|
||||
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
|
||||
|
||||
@@ -13,7 +13,7 @@ import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { Table } from 'sql/base/browser/ui/table/table';
|
||||
import { AgentViewComponent } from 'sql/workbench/contrib/jobManagement/browser/agentView.component';
|
||||
import { IJobManagementService } from 'sql/workbench/services/jobManagement/common/interfaces';
|
||||
import { EditAlertAction, DeleteAlertAction, NewAlertAction } from 'sql/workbench/services/jobManagement/browser/jobActions';
|
||||
import { EditAlertAction, DeleteAlertAction, NewAlertAction } from 'sql/workbench/contrib/jobManagement/browser/jobActions';
|
||||
import { JobManagementView } from 'sql/workbench/contrib/jobManagement/browser/jobManagementView';
|
||||
import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
|
||||
735
src/sql/workbench/contrib/jobManagement/browser/jobActions.ts
Normal file
735
src/sql/workbench/contrib/jobManagement/browser/jobActions.ts
Normal file
@@ -0,0 +1,735 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as azdata from 'azdata';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { JobHistoryComponent } from 'sql/workbench/contrib/jobManagement/browser/jobHistory.component';
|
||||
import { IJobManagementService } from '../../../services/jobManagement/common/interfaces';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { JobsViewComponent } from 'sql/workbench/contrib/jobManagement/browser/jobsView.component';
|
||||
import { AlertsViewComponent } from 'sql/workbench/contrib/jobManagement/browser/alertsView.component';
|
||||
import { OperatorsViewComponent } from 'sql/workbench/contrib/jobManagement/browser/operatorsView.component';
|
||||
import { ProxiesViewComponent } from 'sql/workbench/contrib/jobManagement/browser/proxiesView.component';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
|
||||
import { JobManagementView } from 'sql/workbench/contrib/jobManagement/browser/jobManagementView';
|
||||
import { NotebooksViewComponent } from 'sql/workbench/contrib/jobManagement/browser/notebooksView.component';
|
||||
|
||||
export const successLabel: string = nls.localize('jobaction.successLabel', "Success");
|
||||
export const errorLabel: string = nls.localize('jobaction.faillabel', "Error");
|
||||
|
||||
export enum JobActions {
|
||||
Run = 'run',
|
||||
Stop = 'stop'
|
||||
}
|
||||
|
||||
export class IJobActionInfo {
|
||||
ownerUri?: string;
|
||||
targetObject?: any;
|
||||
component: JobManagementView;
|
||||
}
|
||||
|
||||
// Job actions
|
||||
|
||||
export class JobsRefreshAction extends Action {
|
||||
public static ID = 'jobaction.refresh';
|
||||
public static LABEL = nls.localize('jobaction.refresh', "Refresh");
|
||||
|
||||
constructor(
|
||||
) {
|
||||
super(JobsRefreshAction.ID, JobsRefreshAction.LABEL, 'refreshIcon');
|
||||
}
|
||||
|
||||
public run(context: IJobActionInfo): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
if (context) {
|
||||
if (context.component) {
|
||||
context.component.refreshJobs();
|
||||
}
|
||||
resolve(true);
|
||||
} else {
|
||||
reject(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class NewJobAction extends Action {
|
||||
public static ID = 'jobaction.newJob';
|
||||
public static LABEL = nls.localize('jobaction.newJob', "New Job");
|
||||
|
||||
constructor(
|
||||
) {
|
||||
super(NewJobAction.ID, NewJobAction.LABEL, 'newStepIcon');
|
||||
}
|
||||
|
||||
public run(context: IJobActionInfo): Promise<boolean> {
|
||||
let component = context.component as JobsViewComponent;
|
||||
return new Promise<boolean>(async (resolve, reject) => {
|
||||
try {
|
||||
await component.openCreateJobDialog();
|
||||
resolve(true);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class RunJobAction extends Action {
|
||||
public static ID = 'jobaction.runJob';
|
||||
public static LABEL = nls.localize('jobaction.run', "Run");
|
||||
|
||||
constructor(
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IErrorMessageService private errorMessageService: IErrorMessageService,
|
||||
@IJobManagementService private jobManagementService: IJobManagementService,
|
||||
@IInstantiationService private instantationService: IInstantiationService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService
|
||||
) {
|
||||
super(RunJobAction.ID, RunJobAction.LABEL, 'start');
|
||||
}
|
||||
|
||||
public run(context: IJobActionInfo): Promise<boolean> {
|
||||
let jobName = context.targetObject.job.name;
|
||||
let ownerUri = context.ownerUri;
|
||||
let refreshAction = this.instantationService.createInstance(JobsRefreshAction);
|
||||
this.telemetryService.publicLog(TelemetryKeys.RunAgentJob);
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Run).then(async (result) => {
|
||||
if (result.success) {
|
||||
let startMsg = nls.localize('jobSuccessfullyStarted', ": The job was successfully started.");
|
||||
this.notificationService.info(jobName + startMsg);
|
||||
await refreshAction.run(context);
|
||||
resolve(true);
|
||||
} else {
|
||||
this.errorMessageService.showDialog(Severity.Error, errorLabel, result.errorMessage);
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class StopJobAction extends Action {
|
||||
public static ID = 'jobaction.stopJob';
|
||||
public static LABEL = nls.localize('jobaction.stop', "Stop");
|
||||
|
||||
constructor(
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IErrorMessageService private errorMessageService: IErrorMessageService,
|
||||
@IJobManagementService private jobManagementService: IJobManagementService,
|
||||
@IInstantiationService private instantationService: IInstantiationService,
|
||||
@ITelemetryService private telemetryService: ITelemetryService
|
||||
) {
|
||||
super(StopJobAction.ID, StopJobAction.LABEL, 'stop');
|
||||
}
|
||||
|
||||
public run(context: IJobActionInfo): Promise<boolean> {
|
||||
let jobName = context.targetObject.name;
|
||||
let ownerUri = context.ownerUri;
|
||||
let refreshAction = this.instantationService.createInstance(JobsRefreshAction);
|
||||
this.telemetryService.publicLog(TelemetryKeys.StopAgentJob);
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
this.jobManagementService.jobAction(ownerUri, jobName, JobActions.Stop).then(async (result) => {
|
||||
if (result.success) {
|
||||
await refreshAction.run(context);
|
||||
let stopMsg = nls.localize('jobSuccessfullyStopped', ": The job was successfully stopped.");
|
||||
this.notificationService.info(jobName + stopMsg);
|
||||
resolve(true);
|
||||
} else {
|
||||
this.errorMessageService.showDialog(Severity.Error, 'Error', result.errorMessage);
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class EditJobAction extends Action {
|
||||
public static ID = 'jobaction.editJob';
|
||||
public static LABEL = nls.localize('jobaction.editJob', "Edit Job");
|
||||
|
||||
constructor(
|
||||
@ICommandService private _commandService: ICommandService
|
||||
) {
|
||||
super(EditJobAction.ID, EditJobAction.LABEL, 'edit');
|
||||
}
|
||||
|
||||
public run(actionInfo: IJobActionInfo): Promise<boolean> {
|
||||
this._commandService.executeCommand(
|
||||
'agent.openJobDialog',
|
||||
actionInfo.ownerUri,
|
||||
actionInfo.targetObject.job);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenMaterializedNotebookAction extends Action {
|
||||
public static ID = 'notebookAction.openNotebook';
|
||||
public static LABEL = nls.localize('notebookAction.openNotebook', "Open");
|
||||
|
||||
constructor() {
|
||||
super(OpenMaterializedNotebookAction.ID, OpenMaterializedNotebookAction.LABEL, 'openNotebook');
|
||||
}
|
||||
|
||||
public run(context: any): Promise<boolean> {
|
||||
context.component.openNotebook(context.history);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
export class DeleteJobAction extends Action {
|
||||
public static ID = 'jobaction.deleteJob';
|
||||
public static LABEL = nls.localize('jobaction.deleteJob', "Delete Job");
|
||||
|
||||
constructor(
|
||||
@INotificationService private _notificationService: INotificationService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService,
|
||||
@IJobManagementService private _jobService: IJobManagementService,
|
||||
@ITelemetryService private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(DeleteJobAction.ID, DeleteJobAction.LABEL);
|
||||
}
|
||||
|
||||
public run(actionInfo: IJobActionInfo): Promise<boolean> {
|
||||
let self = this;
|
||||
let job = actionInfo.targetObject.job as azdata.AgentJobInfo;
|
||||
self._notificationService.prompt(
|
||||
Severity.Info,
|
||||
nls.localize('jobaction.deleteJobConfirm', "Are you sure you'd like to delete the job '{0}'?", job.name),
|
||||
[{
|
||||
label: DeleteJobAction.LABEL,
|
||||
run: () => {
|
||||
this._telemetryService.publicLog(TelemetryKeys.DeleteAgentJob);
|
||||
self._jobService.deleteJob(actionInfo.ownerUri, actionInfo.targetObject.job).then(result => {
|
||||
if (!result || !result.success) {
|
||||
let errorMessage = nls.localize("jobaction.failedToDeleteJob", "Could not delete job '{0}'.\nError: {1}",
|
||||
job.name, result.errorMessage ? result.errorMessage : 'Unknown error');
|
||||
self._errorMessageService.showDialog(Severity.Error, errorLabel, errorMessage);
|
||||
} else {
|
||||
let successMessage = nls.localize('jobaction.deletedJob', "The job was successfully deleted");
|
||||
self._notificationService.info(successMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
label: DeleteAlertAction.CancelLabel,
|
||||
run: () => { }
|
||||
}]
|
||||
);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Step Actions
|
||||
|
||||
export class NewStepAction extends Action {
|
||||
public static ID = 'jobaction.newStep';
|
||||
public static LABEL = nls.localize('jobaction.newStep', "New Step");
|
||||
|
||||
constructor(
|
||||
@ICommandService private _commandService: ICommandService
|
||||
) {
|
||||
super(NewStepAction.ID, NewStepAction.LABEL, 'newStepIcon');
|
||||
}
|
||||
|
||||
public run(context: JobHistoryComponent): Promise<boolean> {
|
||||
let ownerUri = context.ownerUri;
|
||||
let server = context.serverName;
|
||||
let jobInfo = context.agentJobInfo;
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
resolve(this._commandService.executeCommand('agent.openNewStepDialog', ownerUri, server, jobInfo, null));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class DeleteStepAction extends Action {
|
||||
public static ID = 'jobaction.deleteStep';
|
||||
public static LABEL = nls.localize('jobaction.deleteStep', "Delete Step");
|
||||
|
||||
constructor(
|
||||
@INotificationService private _notificationService: INotificationService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService,
|
||||
@IJobManagementService private _jobService: IJobManagementService,
|
||||
@IInstantiationService private instantationService: IInstantiationService,
|
||||
@ITelemetryService private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(DeleteStepAction.ID, DeleteStepAction.LABEL);
|
||||
}
|
||||
|
||||
public run(actionInfo: IJobActionInfo): Promise<boolean> {
|
||||
let self = this;
|
||||
let step = actionInfo.targetObject as azdata.AgentJobStepInfo;
|
||||
let refreshAction = this.instantationService.createInstance(JobsRefreshAction);
|
||||
self._notificationService.prompt(
|
||||
Severity.Info,
|
||||
nls.localize('jobaction.deleteStepConfirm', "Are you sure you'd like to delete the step '{0}'?", step.stepName),
|
||||
[{
|
||||
label: DeleteStepAction.LABEL,
|
||||
run: () => {
|
||||
this._telemetryService.publicLog(TelemetryKeys.DeleteAgentJobStep);
|
||||
self._jobService.deleteJobStep(actionInfo.ownerUri, actionInfo.targetObject).then(async (result) => {
|
||||
if (!result || !result.success) {
|
||||
let errorMessage = nls.localize('jobaction.failedToDeleteStep', "Could not delete step '{0}'.\nError: {1}",
|
||||
step.stepName, result.errorMessage ? result.errorMessage : 'Unknown error');
|
||||
self._errorMessageService.showDialog(Severity.Error, errorLabel, errorMessage);
|
||||
await refreshAction.run(actionInfo);
|
||||
} else {
|
||||
let successMessage = nls.localize('jobaction.deletedStep', "The job step was successfully deleted");
|
||||
self._notificationService.info(successMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
label: DeleteAlertAction.CancelLabel,
|
||||
run: () => { }
|
||||
}]
|
||||
);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Alert Actions
|
||||
|
||||
export class NewAlertAction extends Action {
|
||||
public static ID = 'jobaction.newAlert';
|
||||
public static LABEL = nls.localize('jobaction.newAlert', "New Alert");
|
||||
|
||||
constructor(
|
||||
) {
|
||||
super(NewAlertAction.ID, NewAlertAction.LABEL, 'newStepIcon');
|
||||
}
|
||||
|
||||
public run(context: IJobActionInfo): Promise<boolean> {
|
||||
let component = context.component as AlertsViewComponent;
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
try {
|
||||
component.openCreateAlertDialog();
|
||||
resolve(true);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class EditAlertAction extends Action {
|
||||
public static ID = 'jobaction.editAlert';
|
||||
public static LABEL = nls.localize('jobaction.editAlert', "Edit Alert");
|
||||
|
||||
constructor(
|
||||
@ICommandService private _commandService: ICommandService
|
||||
) {
|
||||
super(EditAlertAction.ID, EditAlertAction.LABEL);
|
||||
}
|
||||
|
||||
public run(actionInfo: IJobActionInfo): Promise<boolean> {
|
||||
this._commandService.executeCommand(
|
||||
'agent.openAlertDialog',
|
||||
actionInfo.ownerUri,
|
||||
actionInfo.targetObject.jobInfo,
|
||||
actionInfo.targetObject.alertInfo);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
export class DeleteAlertAction extends Action {
|
||||
public static ID = 'jobaction.deleteAlert';
|
||||
public static LABEL = nls.localize('jobaction.deleteAlert', "Delete Alert");
|
||||
public static CancelLabel = nls.localize('jobaction.Cancel', "Cancel");
|
||||
|
||||
constructor(
|
||||
@INotificationService private _notificationService: INotificationService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService,
|
||||
@IJobManagementService private _jobService: IJobManagementService,
|
||||
@ITelemetryService private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(DeleteAlertAction.ID, DeleteAlertAction.LABEL);
|
||||
}
|
||||
|
||||
public run(actionInfo: IJobActionInfo): Promise<boolean> {
|
||||
let self = this;
|
||||
let alert = actionInfo.targetObject.alertInfo as azdata.AgentAlertInfo;
|
||||
self._notificationService.prompt(
|
||||
Severity.Info,
|
||||
nls.localize('jobaction.deleteAlertConfirm', "Are you sure you'd like to delete the alert '{0}'?", alert.name),
|
||||
[{
|
||||
label: DeleteAlertAction.LABEL,
|
||||
run: () => {
|
||||
this._telemetryService.publicLog(TelemetryKeys.DeleteAgentAlert);
|
||||
self._jobService.deleteAlert(actionInfo.ownerUri, alert).then(result => {
|
||||
if (!result || !result.success) {
|
||||
let errorMessage = nls.localize("jobaction.failedToDeleteAlert", "Could not delete alert '{0}'.\nError: {1}",
|
||||
alert.name, result.errorMessage ? result.errorMessage : 'Unknown error');
|
||||
self._errorMessageService.showDialog(Severity.Error, errorLabel, errorMessage);
|
||||
} else {
|
||||
let successMessage = nls.localize('jobaction.deletedAlert', "The alert was successfully deleted");
|
||||
self._notificationService.info(successMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
label: DeleteAlertAction.CancelLabel,
|
||||
run: () => { }
|
||||
}]
|
||||
);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Operator Actions
|
||||
|
||||
export class NewOperatorAction extends Action {
|
||||
public static ID = 'jobaction.newOperator';
|
||||
public static LABEL = nls.localize('jobaction.newOperator', "New Operator");
|
||||
|
||||
constructor(
|
||||
) {
|
||||
super(NewOperatorAction.ID, NewOperatorAction.LABEL, 'newStepIcon');
|
||||
}
|
||||
|
||||
public run(context: IJobActionInfo): Promise<boolean> {
|
||||
let component = context.component as OperatorsViewComponent;
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
try {
|
||||
component.openCreateOperatorDialog();
|
||||
resolve(true);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class EditOperatorAction extends Action {
|
||||
public static ID = 'jobaction.editAlert';
|
||||
public static LABEL = nls.localize('jobaction.editOperator', "Edit Operator");
|
||||
|
||||
constructor(
|
||||
@ICommandService private _commandService: ICommandService
|
||||
) {
|
||||
super(EditOperatorAction.ID, EditOperatorAction.LABEL);
|
||||
}
|
||||
|
||||
public run(actionInfo: IJobActionInfo): Promise<boolean> {
|
||||
this._commandService.executeCommand(
|
||||
'agent.openOperatorDialog',
|
||||
actionInfo.ownerUri,
|
||||
actionInfo.targetObject);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
export class DeleteOperatorAction extends Action {
|
||||
public static ID = 'jobaction.deleteOperator';
|
||||
public static LABEL = nls.localize('jobaction.deleteOperator', "Delete Operator");
|
||||
|
||||
constructor(
|
||||
@INotificationService private _notificationService: INotificationService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService,
|
||||
@IJobManagementService private _jobService: IJobManagementService,
|
||||
@ITelemetryService private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(DeleteOperatorAction.ID, DeleteOperatorAction.LABEL);
|
||||
}
|
||||
|
||||
public run(actionInfo: IJobActionInfo): Promise<boolean> {
|
||||
const self = this;
|
||||
let operator = actionInfo.targetObject as azdata.AgentOperatorInfo;
|
||||
self._notificationService.prompt(
|
||||
Severity.Info,
|
||||
nls.localize('jobaction.deleteOperatorConfirm', "Are you sure you'd like to delete the operator '{0}'?", operator.name),
|
||||
[{
|
||||
label: DeleteOperatorAction.LABEL,
|
||||
run: () => {
|
||||
self._telemetryService.publicLog(TelemetryKeys.DeleteAgentOperator);
|
||||
self._jobService.deleteOperator(actionInfo.ownerUri, actionInfo.targetObject).then(result => {
|
||||
if (!result || !result.success) {
|
||||
let errorMessage = nls.localize("jobaction.failedToDeleteOperator", "Could not delete operator '{0}'.\nError: {1}",
|
||||
operator.name, result.errorMessage ? result.errorMessage : 'Unknown error');
|
||||
self._errorMessageService.showDialog(Severity.Error, errorLabel, errorMessage);
|
||||
} else {
|
||||
let successMessage = nls.localize('joaction.deletedOperator', "The operator was deleted successfully");
|
||||
self._notificationService.info(successMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
label: DeleteAlertAction.CancelLabel,
|
||||
run: () => { }
|
||||
}]
|
||||
);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Proxy Actions
|
||||
|
||||
export class NewProxyAction extends Action {
|
||||
public static ID = 'jobaction.newProxy';
|
||||
public static LABEL = nls.localize('jobaction.newProxy', "New Proxy");
|
||||
|
||||
constructor(
|
||||
) {
|
||||
super(NewProxyAction.ID, NewProxyAction.LABEL, 'newStepIcon');
|
||||
}
|
||||
|
||||
public run(context: IJobActionInfo): Promise<boolean> {
|
||||
let component = context.component as ProxiesViewComponent;
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
try {
|
||||
component.openCreateProxyDialog();
|
||||
resolve(true);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class EditProxyAction extends Action {
|
||||
public static ID = 'jobaction.editProxy';
|
||||
public static LABEL = nls.localize('jobaction.editProxy', "Edit Proxy");
|
||||
|
||||
constructor(
|
||||
@ICommandService private _commandService: ICommandService,
|
||||
@IJobManagementService private _jobManagementService: IJobManagementService
|
||||
) {
|
||||
super(EditProxyAction.ID, EditProxyAction.LABEL);
|
||||
}
|
||||
|
||||
public run(actionInfo: IJobActionInfo): Promise<boolean> {
|
||||
return Promise.resolve(this._jobManagementService.getCredentials(actionInfo.ownerUri).then((result) => {
|
||||
if (result && result.credentials) {
|
||||
this._commandService.executeCommand(
|
||||
'agent.openProxyDialog',
|
||||
actionInfo.ownerUri,
|
||||
actionInfo.targetObject,
|
||||
result.credentials);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
export class DeleteProxyAction extends Action {
|
||||
public static ID = 'jobaction.deleteProxy';
|
||||
public static LABEL = nls.localize('jobaction.deleteProxy', "Delete Proxy");
|
||||
|
||||
constructor(
|
||||
@INotificationService private _notificationService: INotificationService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService,
|
||||
@IJobManagementService private _jobService: IJobManagementService,
|
||||
@ITelemetryService private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(DeleteProxyAction.ID, DeleteProxyAction.LABEL);
|
||||
}
|
||||
|
||||
public run(actionInfo: IJobActionInfo): Promise<boolean> {
|
||||
let self = this;
|
||||
let proxy = actionInfo.targetObject as azdata.AgentProxyInfo;
|
||||
self._notificationService.prompt(
|
||||
Severity.Info,
|
||||
nls.localize('jobaction.deleteProxyConfirm', "Are you sure you'd like to delete the proxy '{0}'?", proxy.accountName),
|
||||
[{
|
||||
label: DeleteProxyAction.LABEL,
|
||||
run: () => {
|
||||
this._telemetryService.publicLog(TelemetryKeys.DeleteAgentProxy);
|
||||
self._jobService.deleteProxy(actionInfo.ownerUri, actionInfo.targetObject).then(result => {
|
||||
if (!result || !result.success) {
|
||||
let errorMessage = nls.localize("jobaction.failedToDeleteProxy", "Could not delete proxy '{0}'.\nError: {1}",
|
||||
proxy.accountName, result.errorMessage ? result.errorMessage : 'Unknown error');
|
||||
self._errorMessageService.showDialog(Severity.Error, errorLabel, errorMessage);
|
||||
} else {
|
||||
let successMessage = nls.localize('jobaction.deletedProxy', "The proxy was deleted successfully");
|
||||
self._notificationService.info(successMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
label: DeleteAlertAction.CancelLabel,
|
||||
run: () => { }
|
||||
}]
|
||||
);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
//Notebook Actions
|
||||
|
||||
export class NewNotebookJobAction extends Action {
|
||||
public static ID = 'notebookaction.newJob';
|
||||
public static LABEL = nls.localize('notebookaction.newJob', "New Notebook Job");
|
||||
|
||||
constructor(
|
||||
) {
|
||||
super(NewNotebookJobAction.ID, NewNotebookJobAction.LABEL, 'newStepIcon');
|
||||
}
|
||||
|
||||
public async run(context: IJobActionInfo): Promise<boolean> {
|
||||
let component = context.component as NotebooksViewComponent;
|
||||
await component.openCreateNotebookDialog();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class EditNotebookJobAction extends Action {
|
||||
public static ID = 'notebookaction.editNotebook';
|
||||
public static LABEL = nls.localize('notebookaction.editJob', "Edit");
|
||||
|
||||
constructor(
|
||||
@ICommandService private _commandService: ICommandService
|
||||
) {
|
||||
super(EditNotebookJobAction.ID, EditNotebookJobAction.LABEL, 'edit');
|
||||
}
|
||||
|
||||
public run(actionInfo: IJobActionInfo): Promise<boolean> {
|
||||
this._commandService.executeCommand(
|
||||
'agent.openNotebookDialog',
|
||||
actionInfo.ownerUri,
|
||||
actionInfo.targetObject.job);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenTemplateNotebookAction extends Action {
|
||||
public static ID = 'notebookaction.openTemplate';
|
||||
public static LABEL = nls.localize('notebookaction.openNotebook', "Open Template Notebook");
|
||||
|
||||
constructor() {
|
||||
super(OpenTemplateNotebookAction.ID, OpenTemplateNotebookAction.LABEL, 'opennotebook');
|
||||
}
|
||||
|
||||
public run(actionInfo: any): Promise<boolean> {
|
||||
actionInfo.component.openTemplateNotebook();
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
export class DeleteNotebookAction extends Action {
|
||||
public static ID = 'notebookaction.deleteNotebook';
|
||||
public static LABEL = nls.localize('notebookaction.deleteNotebook', "Delete");
|
||||
|
||||
constructor(
|
||||
@INotificationService private _notificationService: INotificationService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService,
|
||||
@IJobManagementService private _jobService: IJobManagementService,
|
||||
@IInstantiationService private instantationService: IInstantiationService,
|
||||
@ITelemetryService private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(DeleteNotebookAction.ID, DeleteNotebookAction.LABEL);
|
||||
}
|
||||
|
||||
public run(actionInfo: IJobActionInfo): Promise<boolean> {
|
||||
let self = this;
|
||||
let notebook = actionInfo.targetObject.job as azdata.AgentNotebookInfo;
|
||||
let refreshAction = this.instantationService.createInstance(JobsRefreshAction);
|
||||
self._notificationService.prompt(
|
||||
Severity.Info,
|
||||
nls.localize('jobaction.deleteNotebookConfirm', "Are you sure you'd like to delete the notebook '{0}'?", notebook.name),
|
||||
[{
|
||||
label: DeleteNotebookAction.LABEL,
|
||||
run: () => {
|
||||
this._telemetryService.publicLog(TelemetryKeys.DeleteAgentJob);
|
||||
self._jobService.deleteNotebook(actionInfo.ownerUri, actionInfo.targetObject.job).then(async (result) => {
|
||||
if (!result || !result.success) {
|
||||
await refreshAction.run(actionInfo);
|
||||
let errorMessage = nls.localize("jobaction.failedToDeleteNotebook", "Could not delete notebook '{0}'.\nError: {1}",
|
||||
notebook.name, result.errorMessage ? result.errorMessage : 'Unknown error');
|
||||
self._errorMessageService.showDialog(Severity.Error, errorLabel, errorMessage);
|
||||
} else {
|
||||
let successMessage = nls.localize('jobaction.deletedNotebook', "The notebook was successfully deleted");
|
||||
self._notificationService.info(successMessage);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
label: DeleteAlertAction.CancelLabel,
|
||||
run: () => { }
|
||||
}]
|
||||
);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class PinNotebookMaterializedAction extends Action {
|
||||
public static ID = 'notebookaction.openTemplate';
|
||||
public static LABEL = nls.localize('notebookaction.pinNotebook', "Pin");
|
||||
|
||||
constructor() {
|
||||
super(PinNotebookMaterializedAction.ID, PinNotebookMaterializedAction.LABEL);
|
||||
}
|
||||
|
||||
public run(actionInfo: any): Promise<boolean> {
|
||||
actionInfo.component.toggleNotebookPin(actionInfo.history, true);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
export class DeleteMaterializedNotebookAction extends Action {
|
||||
public static ID = 'notebookaction.deleteMaterializedNotebook';
|
||||
public static LABEL = nls.localize('notebookaction.deleteMaterializedNotebook', "Delete");
|
||||
|
||||
constructor() {
|
||||
super(DeleteMaterializedNotebookAction.ID, DeleteMaterializedNotebookAction.LABEL);
|
||||
}
|
||||
|
||||
public run(actionInfo: any): Promise<boolean> {
|
||||
actionInfo.component.deleteMaterializedNotebook(actionInfo.history);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
export class UnpinNotebookMaterializedAction extends Action {
|
||||
public static ID = 'notebookaction.unpinNotebook';
|
||||
public static LABEL = nls.localize('notebookaction.unpinNotebook', "Unpin");
|
||||
|
||||
constructor() {
|
||||
super(UnpinNotebookMaterializedAction.ID, UnpinNotebookMaterializedAction.LABEL);
|
||||
}
|
||||
|
||||
public run(actionInfo: any): Promise<boolean> {
|
||||
actionInfo.component.toggleNotebookPin(actionInfo.history, false);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
export class RenameNotebookMaterializedAction extends Action {
|
||||
public static ID = 'notebookaction.openTemplate';
|
||||
public static LABEL = nls.localize('notebookaction.renameNotebook', "Rename");
|
||||
|
||||
constructor() {
|
||||
super(RenameNotebookMaterializedAction.ID, RenameNotebookMaterializedAction.LABEL);
|
||||
}
|
||||
|
||||
public run(actionInfo: any): Promise<boolean> {
|
||||
actionInfo.component.renameNotebook(actionInfo.history);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenLatestRunMaterializedNotebook extends Action {
|
||||
public static ID = 'notebookaction.openLatestRun';
|
||||
public static LABEL = nls.localize('notebookaction.openLatestRun', "Open Latest Run");
|
||||
|
||||
constructor() {
|
||||
super(OpenLatestRunMaterializedNotebook.ID, OpenLatestRunMaterializedNotebook.LABEL);
|
||||
}
|
||||
|
||||
public run(actionInfo: IJobActionInfo): Promise<boolean> {
|
||||
actionInfo.component.openLastNRun(actionInfo.targetObject.job, 0, 1);
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import { OnInit, Component, Inject, Input, forwardRef, ElementRef, ChangeDetecto
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { AgentViewComponent } from 'sql/workbench/contrib/jobManagement/browser/agentView.component';
|
||||
import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service';
|
||||
import { RunJobAction, StopJobAction, EditJobAction, JobsRefreshAction } from 'sql/workbench/services/jobManagement/browser/jobActions';
|
||||
import { RunJobAction, StopJobAction, EditJobAction, JobsRefreshAction } from 'sql/workbench/contrib/jobManagement/browser/jobActions';
|
||||
import { JobCacheObject } from 'sql/workbench/services/jobManagement/common/jobManagementService';
|
||||
import { JobManagementUtilities } from 'sql/workbench/services/jobManagement/browser/jobManagementUtilities';
|
||||
import { IJobManagementService } from 'sql/workbench/services/jobManagement/common/interfaces';
|
||||
|
||||
@@ -14,7 +14,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { JobsRefreshAction, IJobActionInfo } from 'sql/workbench/services/jobManagement/browser/jobActions';
|
||||
import { JobsRefreshAction, IJobActionInfo } from 'sql/workbench/contrib/jobManagement/browser/jobActions';
|
||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService';
|
||||
import { ITableMouseEvent } from 'sql/base/browser/ui/table/interfaces';
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Table } from 'sql/base/browser/ui/table/table';
|
||||
import { AgentViewComponent } from 'sql/workbench/contrib/jobManagement/browser/agentView.component';
|
||||
import { RowDetailView } from 'sql/base/browser/ui/table/plugins/rowDetailView';
|
||||
import { JobCacheObject } from 'sql/workbench/services/jobManagement/common/jobManagementService';
|
||||
import { EditJobAction, DeleteJobAction, NewJobAction, RunJobAction } from 'sql/workbench/services/jobManagement/browser/jobActions';
|
||||
import { EditJobAction, DeleteJobAction, NewJobAction, RunJobAction } from 'sql/workbench/contrib/jobManagement/browser/jobActions';
|
||||
import { JobManagementUtilities } from 'sql/workbench/services/jobManagement/browser/jobManagementUtilities';
|
||||
import { HeaderFilter } from 'sql/base/browser/ui/table/plugins/headerFilter.plugin';
|
||||
import { IJobManagementService } from 'sql/workbench/services/jobManagement/common/interfaces';
|
||||
|
||||
@@ -12,7 +12,7 @@ import { OnInit, Component, Inject, Input, forwardRef, ElementRef, ChangeDetecto
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { AgentViewComponent } from 'sql/workbench/contrib/jobManagement/browser/agentView.component';
|
||||
import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service';
|
||||
import { RunJobAction, StopJobAction, JobsRefreshAction, EditNotebookJobAction, OpenMaterializedNotebookAction, OpenTemplateNotebookAction, RenameNotebookMaterializedAction, PinNotebookMaterializedAction, UnpinNotebookMaterializedAction, DeleteMaterializedNotebookAction } from 'sql/workbench/services/jobManagement/browser/jobActions';
|
||||
import { RunJobAction, StopJobAction, JobsRefreshAction, EditNotebookJobAction, OpenMaterializedNotebookAction, OpenTemplateNotebookAction, RenameNotebookMaterializedAction, PinNotebookMaterializedAction, UnpinNotebookMaterializedAction, DeleteMaterializedNotebookAction } from 'sql/workbench/contrib/jobManagement/browser/jobActions';
|
||||
import { NotebookCacheObject } from 'sql/workbench/services/jobManagement/common/jobManagementService';
|
||||
import { IJobManagementService } from 'sql/workbench/services/jobManagement/common/interfaces';
|
||||
import { JobStepsViewRow } from 'sql/workbench/contrib/jobManagement/browser/jobStepsViewTree';
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Table } from 'sql/base/browser/ui/table/table';
|
||||
import { AgentViewComponent } from 'sql/workbench/contrib/jobManagement/browser/agentView.component';
|
||||
import { RowDetailView } from 'sql/base/browser/ui/table/plugins/rowDetailView';
|
||||
import { NotebookCacheObject } from 'sql/workbench/services/jobManagement/common/jobManagementService';
|
||||
import { NewNotebookJobAction, RunJobAction, EditNotebookJobAction, JobsRefreshAction, IJobActionInfo, DeleteNotebookAction, OpenLatestRunMaterializedNotebook } from 'sql/workbench/services/jobManagement/browser/jobActions';
|
||||
import { NewNotebookJobAction, RunJobAction, EditNotebookJobAction, JobsRefreshAction, IJobActionInfo, DeleteNotebookAction, OpenLatestRunMaterializedNotebook } from 'sql/workbench/contrib/jobManagement/browser/jobActions';
|
||||
import { JobManagementUtilities } from 'sql/workbench/services/jobManagement/browser/jobManagementUtilities';
|
||||
import { HeaderFilter } from 'sql/base/browser/ui/table/plugins/headerFilter.plugin';
|
||||
import { IJobManagementService } from 'sql/workbench/services/jobManagement/common/interfaces';
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild
|
||||
import { Table } from 'sql/base/browser/ui/table/table';
|
||||
import { AgentViewComponent } from 'sql/workbench/contrib/jobManagement/browser/agentView.component';
|
||||
import { IJobManagementService } from 'sql/workbench/services/jobManagement/common/interfaces';
|
||||
import { EditOperatorAction, DeleteOperatorAction, NewOperatorAction } from 'sql/workbench/services/jobManagement/browser/jobActions';
|
||||
import { EditOperatorAction, DeleteOperatorAction, NewOperatorAction } from 'sql/workbench/contrib/jobManagement/browser/jobActions';
|
||||
import { JobManagementView } from 'sql/workbench/contrib/jobManagement/browser/jobManagementView';
|
||||
import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service';
|
||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
|
||||
@@ -12,7 +12,7 @@ import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild
|
||||
import { Table } from 'sql/base/browser/ui/table/table';
|
||||
import { AgentViewComponent } from 'sql/workbench/contrib/jobManagement/browser/agentView.component';
|
||||
import { IJobManagementService } from 'sql/workbench/services/jobManagement/common/interfaces';
|
||||
import { EditProxyAction, DeleteProxyAction, NewProxyAction } from 'sql/workbench/services/jobManagement/browser/jobActions';
|
||||
import { EditProxyAction, DeleteProxyAction, NewProxyAction } from 'sql/workbench/contrib/jobManagement/browser/jobActions';
|
||||
import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service';
|
||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { JobManagementView } from 'sql/workbench/contrib/jobManagement/browser/jobManagementView';
|
||||
|
||||
@@ -15,10 +15,10 @@ import * as DOM from 'vs/base/browser/dom';
|
||||
import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { CellActionBase, CellContext } from 'sql/workbench/contrib/notebook/browser/cellViews/codeActions';
|
||||
import { CellTypes, CellType } from 'sql/workbench/services/notebook/common/contracts';
|
||||
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
|
||||
import { ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel';
|
||||
import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { ToggleMoreWidgetAction } from 'sql/workbench/contrib/dashboard/browser/core/actions';
|
||||
import { CellModel } from 'sql/workbench/contrib/notebook/browser/models/cell';
|
||||
import { CellModel } from 'sql/workbench/services/notebook/browser/models/cell';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { firstIndex } from 'vs/base/common/arrays';
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ import { OnInit, Component, Input, Inject, ElementRef, ViewChild, Output, EventE
|
||||
|
||||
import { QueryTextEditor } from 'sql/workbench/browser/modelComponents/queryTextEditor';
|
||||
import { CellToggleMoreActions } from 'sql/workbench/contrib/notebook/browser/cellToggleMoreActions';
|
||||
import { ICellModel, notebookConstants, CellExecutionState } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { ICellModel, CellExecutionState } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { RunCellAction, CellContext } from 'sql/workbench/contrib/notebook/browser/cellViews/codeActions';
|
||||
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
|
||||
import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel';
|
||||
|
||||
import { IColorTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import * as themeColors from 'vs/workbench/common/theme';
|
||||
@@ -24,7 +24,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { CellTypes } from 'sql/workbench/services/notebook/common/contracts';
|
||||
import { OVERRIDE_EDITOR_THEMING_SETTING } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import * as notebookUtils from 'sql/workbench/contrib/notebook/browser/models/notebookUtils';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { CollapseComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/collapse.component';
|
||||
@@ -35,6 +34,8 @@ import { UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/u
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IEditorProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { SimpleProgressIndicator } from 'sql/workbench/services/progress/browser/simpleProgressIndicator';
|
||||
import { notebookConstants } from 'sql/workbench/services/notebook/browser/interfaces';
|
||||
import { tryMatchCellMagic } from 'sql/workbench/services/notebook/browser/utils';
|
||||
|
||||
export const CODE_SELECTOR: string = 'code-component';
|
||||
const MARKDOWN_CLASS = 'markdown';
|
||||
@@ -305,7 +306,7 @@ export class CodeComponent extends CellView implements OnInit, OnChanges {
|
||||
}
|
||||
if (this._editorModel && this._editor && this._editorModel.getLineCount() > 1) {
|
||||
// Only try to match once we've typed past the first line
|
||||
let magicName = notebookUtils.tryMatchCellMagic(this._editorModel.getLineContent(1));
|
||||
let magicName = tryMatchCellMagic(this._editorModel.getLineContent(1));
|
||||
if (magicName) {
|
||||
let kernelName = this._model.clientSession && this._model.clientSession.kernel ? this._model.clientSession.kernel.name : undefined;
|
||||
let magic = this._model.notebookOptions.cellMagicMapper.toLanguageMagic(magicName, kernelName);
|
||||
|
||||
@@ -9,8 +9,8 @@ import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
|
||||
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
|
||||
import { ICellModel, CellExecutionState } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel';
|
||||
import { ICellModel, CellExecutionState } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
import { nb } from 'azdata';
|
||||
import { OnInit, Component, Input, Inject, forwardRef, ChangeDetectorRef, SimpleChange, OnChanges, HostListener, ViewChildren, QueryList } from '@angular/core';
|
||||
import { CellView } from 'sql/workbench/contrib/notebook/browser/cellViews/interfaces';
|
||||
import { ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
|
||||
import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { ICellEditorProvider } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { CodeComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/code.component';
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'vs/css!./code';
|
||||
|
||||
import { OnInit, Component, Input, ElementRef, ViewChild, SimpleChange, OnChanges } from '@angular/core';
|
||||
import { CellView } from 'sql/workbench/contrib/notebook/browser/cellViews/interfaces';
|
||||
import { ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
export const COLLAPSE_SELECTOR: string = 'collapse-component';
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
|
||||
import { OnDestroy } from '@angular/core';
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
import { ICellEditorProvider } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { ICellEditorProvider, NotebookRange } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
|
||||
import { NotebookRange } from 'sql/workbench/contrib/notebook/find/notebookFindDecorations';
|
||||
|
||||
export abstract class CellView extends AngularDisposable implements OnDestroy, ICellEditorProvider {
|
||||
constructor() {
|
||||
|
||||
@@ -8,7 +8,7 @@ import 'vs/css!./media/output';
|
||||
import { OnInit, Component, Input, Inject, ElementRef, ViewChild, SimpleChange, AfterViewInit, forwardRef, ChangeDetectorRef, ComponentRef, ComponentFactoryResolver } from '@angular/core';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { nb } from 'azdata';
|
||||
import { ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import * as outputProcessor from 'sql/workbench/contrib/notebook/browser/models/outputProcessor';
|
||||
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
|
||||
@@ -6,7 +6,7 @@ import 'vs/css!./code';
|
||||
import 'vs/css!./outputArea';
|
||||
import { OnInit, Component, Input, Inject, ElementRef, ViewChild, forwardRef, ChangeDetectorRef } from '@angular/core';
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
import { ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import * as themeColors from 'vs/workbench/common/theme';
|
||||
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
@@ -6,8 +6,8 @@ import 'vs/css!./placeholder';
|
||||
|
||||
import { OnInit, Component, Input, Inject, forwardRef, ChangeDetectorRef, SimpleChange, OnChanges } from '@angular/core';
|
||||
import { CellView } from 'sql/workbench/contrib/notebook/browser/cellViews/interfaces';
|
||||
import { ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
|
||||
import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel';
|
||||
import { localize } from 'vs/nls';
|
||||
import { CellType } from 'sql/workbench/services/notebook/common/contracts';
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox';
|
||||
import { attachInputBoxStyler } from 'sql/platform/theme/common/styler';
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { ICellModel, CellExecutionState } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { ICellModel, CellExecutionState } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
|
||||
export const STDIN_SELECTOR: string = 'stdin-component';
|
||||
@Component({
|
||||
|
||||
@@ -20,13 +20,13 @@ import { toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IMarkdownRenderResult } from 'vs/editor/contrib/markdown/markdownRenderer';
|
||||
import { NotebookMarkdownRenderer } from 'sql/workbench/contrib/notebook/browser/outputs/notebookMarkdown';
|
||||
import { CellView } from 'sql/workbench/contrib/notebook/browser/cellViews/interfaces';
|
||||
import { ICellModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
|
||||
import { ISanitizer, defaultSanitizer } from 'sql/workbench/contrib/notebook/browser/outputs/sanitizer';
|
||||
import { ICellModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel';
|
||||
import { ISanitizer, defaultSanitizer } from 'sql/workbench/services/notebook/browser/outputs/sanitizer';
|
||||
import { CellToggleMoreActions } from 'sql/workbench/contrib/notebook/browser/cellToggleMoreActions';
|
||||
import { NotebookRange } from 'sql/workbench/contrib/notebook/find/notebookFindDecorations';
|
||||
import { CodeComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/code.component';
|
||||
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
|
||||
import { NotebookRange } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
|
||||
export const TEXT_SELECTOR: string = 'text-cell-component';
|
||||
const USER_SELECT_CLASS = 'actionselect';
|
||||
|
||||
@@ -1,717 +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 { nb, ServerInfo } from 'azdata';
|
||||
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
import * as notebookUtils from 'sql/workbench/contrib/notebook/browser/models/notebookUtils';
|
||||
import { CellTypes, CellType, NotebookChangeType } from 'sql/workbench/services/notebook/common/contracts';
|
||||
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
|
||||
import { ICellModel, notebookConstants, IOutputChangedEvent, FutureInternal, CellExecutionState, ICellModelOptions } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { optional } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { getErrorMessage, onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
|
||||
import { firstIndex, find } from 'vs/base/common/arrays';
|
||||
import { HideInputTag } from 'sql/platform/notebooks/common/outputRegistry';
|
||||
|
||||
let modelId = 0;
|
||||
|
||||
export class CellModel implements ICellModel {
|
||||
public id: string;
|
||||
|
||||
private _cellType: nb.CellType;
|
||||
private _source: string | string[];
|
||||
private _language: string;
|
||||
private _cellGuid: string;
|
||||
private _future: FutureInternal;
|
||||
private _outputs: nb.ICellOutput[] = [];
|
||||
private _isEditMode: boolean;
|
||||
private _onOutputsChanged = new Emitter<IOutputChangedEvent>();
|
||||
private _onCellModeChanged = new Emitter<boolean>();
|
||||
private _onExecutionStateChanged = new Emitter<CellExecutionState>();
|
||||
private _isTrusted: boolean;
|
||||
private _active: boolean;
|
||||
private _hover: boolean;
|
||||
private _executionCount: number | undefined;
|
||||
private _cellUri: URI;
|
||||
private _connectionManagementService: IConnectionManagementService;
|
||||
private _stdInHandler: nb.MessageHandler<nb.IStdinMessage>;
|
||||
private _onCellLoaded = new Emitter<string>();
|
||||
private _loaded: boolean;
|
||||
private _stdInVisible: boolean;
|
||||
private _metadata: { language?: string; tags?: string[]; cellGuid?: string; };
|
||||
private _isCollapsed: boolean;
|
||||
private _onCollapseStateChanged = new Emitter<boolean>();
|
||||
private _modelContentChangedEvent: IModelContentChangedEvent;
|
||||
private readonly _ariaLabel: string;
|
||||
|
||||
private readonly codeCellLabel = localize('codeCellLabel', "Code Cell");
|
||||
private readonly textCellLabel = localize('textCellLabel', "Text Cell");
|
||||
|
||||
constructor(cellData: nb.ICellContents,
|
||||
private _options: ICellModelOptions,
|
||||
@optional(INotebookService) private _notebookService?: INotebookService
|
||||
) {
|
||||
this.id = `${modelId++}`;
|
||||
if (cellData) {
|
||||
// Read in contents if available
|
||||
this.fromJSON(cellData);
|
||||
} else {
|
||||
this._cellType = CellTypes.Code;
|
||||
this._source = '';
|
||||
}
|
||||
|
||||
if (this._cellType === CellTypes.Code) {
|
||||
this._ariaLabel = this.codeCellLabel;
|
||||
} else {
|
||||
this._ariaLabel = this.textCellLabel;
|
||||
}
|
||||
|
||||
this._isEditMode = this._cellType !== CellTypes.Markdown;
|
||||
this._stdInVisible = false;
|
||||
if (_options && _options.isTrusted) {
|
||||
this._isTrusted = true;
|
||||
} else {
|
||||
this._isTrusted = false;
|
||||
}
|
||||
// if the fromJson() method was already called and _cellGuid was previously set, don't generate another UUID unnecessarily
|
||||
this._cellGuid = this._cellGuid || generateUuid();
|
||||
this.createUri();
|
||||
}
|
||||
|
||||
public equals(other: ICellModel) {
|
||||
return other !== undefined && other.id === this.id;
|
||||
}
|
||||
|
||||
public get ariaLabel(): string {
|
||||
return this._ariaLabel;
|
||||
}
|
||||
|
||||
public get onCollapseStateChanged(): Event<boolean> {
|
||||
return this._onCollapseStateChanged.event;
|
||||
}
|
||||
|
||||
public get onOutputsChanged(): Event<IOutputChangedEvent> {
|
||||
return this._onOutputsChanged.event;
|
||||
}
|
||||
|
||||
public get isEditMode(): boolean {
|
||||
return this._isEditMode;
|
||||
}
|
||||
|
||||
public get future(): FutureInternal {
|
||||
return this._future;
|
||||
}
|
||||
|
||||
public get isCollapsed() {
|
||||
return this._isCollapsed;
|
||||
}
|
||||
|
||||
public set isCollapsed(value: boolean) {
|
||||
if (this.cellType !== CellTypes.Code) {
|
||||
return;
|
||||
}
|
||||
let stateChanged = this._isCollapsed !== value;
|
||||
this._isCollapsed = value;
|
||||
|
||||
let tagIndex = -1;
|
||||
if (this._metadata.tags) {
|
||||
tagIndex = firstIndex(this._metadata.tags, tag => tag === HideInputTag);
|
||||
}
|
||||
|
||||
if (this._isCollapsed) {
|
||||
if (tagIndex === -1) {
|
||||
if (!this._metadata.tags) {
|
||||
this._metadata.tags = [];
|
||||
}
|
||||
this._metadata.tags.push(HideInputTag);
|
||||
}
|
||||
} else {
|
||||
if (tagIndex > -1) {
|
||||
this._metadata.tags.splice(tagIndex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (stateChanged) {
|
||||
this._onCollapseStateChanged.fire(this._isCollapsed);
|
||||
this.sendChangeToNotebook(NotebookChangeType.CellInputVisibilityChanged);
|
||||
}
|
||||
}
|
||||
|
||||
public set isEditMode(isEditMode: boolean) {
|
||||
this._isEditMode = isEditMode;
|
||||
this._onCellModeChanged.fire(this._isEditMode);
|
||||
// Note: this does not require a notebook update as it does not change overall state
|
||||
}
|
||||
|
||||
public get trustedMode(): boolean {
|
||||
return this._isTrusted;
|
||||
}
|
||||
|
||||
public set trustedMode(isTrusted: boolean) {
|
||||
if (this._isTrusted !== isTrusted) {
|
||||
this._isTrusted = isTrusted;
|
||||
let outputEvent: IOutputChangedEvent = {
|
||||
outputs: this._outputs,
|
||||
shouldScroll: false
|
||||
};
|
||||
this._onOutputsChanged.fire(outputEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public get active(): boolean {
|
||||
return this._active;
|
||||
}
|
||||
|
||||
public set active(value: boolean) {
|
||||
this._active = value;
|
||||
this.fireExecutionStateChanged();
|
||||
}
|
||||
|
||||
public get hover(): boolean {
|
||||
return this._hover;
|
||||
}
|
||||
|
||||
public set hover(value: boolean) {
|
||||
this._hover = value;
|
||||
this.fireExecutionStateChanged();
|
||||
}
|
||||
|
||||
public get executionCount(): number | undefined {
|
||||
return this._executionCount;
|
||||
}
|
||||
|
||||
public set executionCount(value: number | undefined) {
|
||||
this._executionCount = value;
|
||||
this.fireExecutionStateChanged();
|
||||
}
|
||||
|
||||
public get cellUri(): URI {
|
||||
return this._cellUri;
|
||||
}
|
||||
|
||||
public get notebookModel(): NotebookModel {
|
||||
return this._options && <NotebookModel>this._options.notebook;
|
||||
}
|
||||
|
||||
public set cellUri(value: URI) {
|
||||
this._cellUri = value;
|
||||
}
|
||||
|
||||
public get cellType(): CellType {
|
||||
return this._cellType;
|
||||
}
|
||||
|
||||
public get source(): string | string[] {
|
||||
return this._source;
|
||||
}
|
||||
|
||||
public set source(newSource: string | string[]) {
|
||||
newSource = this.getMultilineSource(newSource);
|
||||
if (this._source !== newSource) {
|
||||
this._source = newSource;
|
||||
this.sendChangeToNotebook(NotebookChangeType.CellSourceUpdated);
|
||||
}
|
||||
this._modelContentChangedEvent = undefined;
|
||||
}
|
||||
|
||||
public get modelContentChangedEvent(): IModelContentChangedEvent {
|
||||
return this._modelContentChangedEvent;
|
||||
}
|
||||
|
||||
public set modelContentChangedEvent(e: IModelContentChangedEvent) {
|
||||
this._modelContentChangedEvent = e;
|
||||
}
|
||||
|
||||
public get language(): string {
|
||||
if (this._cellType === CellTypes.Markdown) {
|
||||
return 'markdown';
|
||||
}
|
||||
if (this._language) {
|
||||
return this._language;
|
||||
}
|
||||
return this._options.notebook.language;
|
||||
}
|
||||
|
||||
public get cellGuid(): string {
|
||||
return this._cellGuid;
|
||||
}
|
||||
|
||||
public setOverrideLanguage(newLanguage: string) {
|
||||
this._language = newLanguage;
|
||||
}
|
||||
|
||||
public get onExecutionStateChange(): Event<CellExecutionState> {
|
||||
return this._onExecutionStateChanged.event;
|
||||
}
|
||||
|
||||
private fireExecutionStateChanged(): void {
|
||||
this._onExecutionStateChanged.fire(this.executionState);
|
||||
}
|
||||
|
||||
public get onLoaded(): Event<string> {
|
||||
return this._onCellLoaded.event;
|
||||
}
|
||||
|
||||
public get loaded(): boolean {
|
||||
return this._loaded;
|
||||
}
|
||||
|
||||
public set loaded(val: boolean) {
|
||||
this._loaded = val;
|
||||
if (val) {
|
||||
this._onCellLoaded.fire(this._cellType);
|
||||
}
|
||||
}
|
||||
|
||||
public get stdInVisible(): boolean {
|
||||
return this._stdInVisible;
|
||||
}
|
||||
|
||||
public set stdInVisible(val: boolean) {
|
||||
this._stdInVisible = val;
|
||||
}
|
||||
|
||||
private notifyExecutionComplete(): void {
|
||||
if (this._notebookService) {
|
||||
this._notebookService.serializeNotebookStateChange(this.notebookModel.notebookUri, NotebookChangeType.CellExecuted, this)
|
||||
.catch(e => onUnexpectedError(e));
|
||||
}
|
||||
}
|
||||
|
||||
public get executionState(): CellExecutionState {
|
||||
let isRunning = !!(this._future && this._future.inProgress);
|
||||
if (isRunning) {
|
||||
return CellExecutionState.Running;
|
||||
} else if (this.active || this.hover) {
|
||||
return CellExecutionState.Stopped;
|
||||
}
|
||||
// TODO save error state and show the error
|
||||
return CellExecutionState.Hidden;
|
||||
}
|
||||
|
||||
public async runCell(notificationService?: INotificationService, connectionManagementService?: IConnectionManagementService): Promise<boolean> {
|
||||
try {
|
||||
if (!this.active && this !== this.notebookModel.activeCell) {
|
||||
this.notebookModel.updateActiveCell(this);
|
||||
this.active = true;
|
||||
}
|
||||
|
||||
if (connectionManagementService) {
|
||||
this._connectionManagementService = connectionManagementService;
|
||||
}
|
||||
if (this.cellType !== CellTypes.Code) {
|
||||
// TODO should change hidden state to false if we add support
|
||||
// for this property
|
||||
return false;
|
||||
}
|
||||
let kernel = await this.getOrStartKernel(notificationService);
|
||||
if (!kernel) {
|
||||
return false;
|
||||
}
|
||||
// If cell is currently running and user clicks the stop/cancel button, call kernel.interrupt()
|
||||
// This matches the same behavior as JupyterLab
|
||||
if (this.future && this.future.inProgress) {
|
||||
// If stdIn is visible, to prevent a kernel hang, we need to send a dummy input reply
|
||||
if (this._stdInVisible && this._stdInHandler) {
|
||||
this.future.sendInputReply({ value: '' });
|
||||
}
|
||||
this.future.inProgress = false;
|
||||
await kernel.interrupt();
|
||||
this.sendNotification(notificationService, Severity.Info, localize('runCellCancelled', "Cell execution cancelled"));
|
||||
} else {
|
||||
// TODO update source based on editor component contents
|
||||
if (kernel.requiresConnection && !this.notebookModel.context) {
|
||||
let connected = await this.notebookModel.requestConnection();
|
||||
if (!connected) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
let content = this.source;
|
||||
if ((Array.isArray(content) && content.length > 0) || (!Array.isArray(content) && content)) {
|
||||
this.notebookModel.trustedMode = true;
|
||||
|
||||
// requestExecute expects a string for the code parameter
|
||||
content = Array.isArray(content) ? content.join('') : content;
|
||||
const future = kernel.requestExecute({
|
||||
code: content,
|
||||
stop_on_error: true
|
||||
}, false);
|
||||
this.setFuture(future as FutureInternal);
|
||||
this.fireExecutionStateChanged();
|
||||
// For now, await future completion. Later we should just track and handle cancellation based on model notifications
|
||||
let result: nb.IExecuteReplyMsg = <nb.IExecuteReplyMsg><any>await future.done;
|
||||
if (result && result.content) {
|
||||
this.executionCount = result.content.execution_count;
|
||||
if (result.content.status !== 'ok') {
|
||||
// TODO track error state
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
let message: string;
|
||||
if (error.message === 'Canceled') {
|
||||
message = localize('executionCanceled', "Query execution was canceled");
|
||||
} else {
|
||||
message = getErrorMessage(error);
|
||||
}
|
||||
this.sendNotification(notificationService, Severity.Error, message);
|
||||
// TODO track error state for the cell
|
||||
} finally {
|
||||
this.disposeFuture();
|
||||
this.fireExecutionStateChanged();
|
||||
this.notifyExecutionComplete();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async getOrStartKernel(notificationService: INotificationService): Promise<nb.IKernel> {
|
||||
let model = this._options.notebook;
|
||||
let clientSession = model && model.clientSession;
|
||||
if (!clientSession) {
|
||||
this.sendNotification(notificationService, Severity.Error, localize('notebookNotReady', "The session for this notebook is not yet ready"));
|
||||
return undefined;
|
||||
} else if (!clientSession.isReady || clientSession.status === 'dead') {
|
||||
|
||||
this.sendNotification(notificationService, Severity.Info, localize('sessionNotReady', "The session for this notebook will start momentarily"));
|
||||
await clientSession.kernelChangeCompleted;
|
||||
}
|
||||
if (!clientSession.kernel) {
|
||||
let defaultKernel = model && model.defaultKernel && model.defaultKernel.name;
|
||||
if (!defaultKernel) {
|
||||
this.sendNotification(notificationService, Severity.Error, localize('noDefaultKernel', "No kernel is available for this notebook"));
|
||||
return undefined;
|
||||
}
|
||||
await clientSession.changeKernel({
|
||||
name: defaultKernel
|
||||
});
|
||||
}
|
||||
return clientSession.kernel;
|
||||
}
|
||||
|
||||
private sendNotification(notificationService: INotificationService, severity: Severity, message: string): void {
|
||||
if (notificationService) {
|
||||
notificationService.notify({ severity: severity, message: message });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the future which will be used to update the output
|
||||
* area for this cell
|
||||
*/
|
||||
setFuture(future: FutureInternal): void {
|
||||
if (this._future === future) {
|
||||
// Nothing to do
|
||||
return;
|
||||
}
|
||||
// Setting the future indicates the cell is running which enables trusted mode.
|
||||
// See https://jupyter-notebook.readthedocs.io/en/stable/security.html
|
||||
|
||||
this._isTrusted = true;
|
||||
|
||||
if (this._future) {
|
||||
this._future.dispose();
|
||||
}
|
||||
this.clearOutputs();
|
||||
this._future = future;
|
||||
future.setReplyHandler({ handle: (msg) => this.handleReply(msg) });
|
||||
future.setIOPubHandler({ handle: (msg) => this.handleIOPub(msg) });
|
||||
future.setStdInHandler({ handle: (msg) => this.handleSdtIn(msg) });
|
||||
}
|
||||
|
||||
public clearOutputs(): void {
|
||||
this._outputs = [];
|
||||
this.fireOutputsChanged();
|
||||
|
||||
this.executionCount = undefined;
|
||||
}
|
||||
|
||||
private fireOutputsChanged(shouldScroll: boolean = false): void {
|
||||
let outputEvent: IOutputChangedEvent = {
|
||||
outputs: this.outputs,
|
||||
shouldScroll: !!shouldScroll
|
||||
};
|
||||
this._onOutputsChanged.fire(outputEvent);
|
||||
if (this.outputs.length !== 0) {
|
||||
this.sendChangeToNotebook(NotebookChangeType.CellOutputUpdated);
|
||||
} else {
|
||||
this.sendChangeToNotebook(NotebookChangeType.CellOutputCleared);
|
||||
}
|
||||
}
|
||||
|
||||
private sendChangeToNotebook(change: NotebookChangeType): void {
|
||||
if (this._options && this._options.notebook) {
|
||||
this._options.notebook.onCellChange(this, change);
|
||||
}
|
||||
}
|
||||
|
||||
public get outputs(): Array<nb.ICellOutput> {
|
||||
return this._outputs;
|
||||
}
|
||||
|
||||
private handleReply(msg: nb.IShellMessage): void {
|
||||
// TODO #931 we should process this. There can be a payload attached which should be added to outputs.
|
||||
// In all other cases, it is a no-op
|
||||
|
||||
if (!this._future.inProgress) {
|
||||
this.disposeFuture();
|
||||
}
|
||||
}
|
||||
|
||||
private handleIOPub(msg: nb.IIOPubMessage): void {
|
||||
let msgType = msg.header.msg_type;
|
||||
let output: nb.ICellOutput;
|
||||
switch (msgType) {
|
||||
case 'execute_result':
|
||||
case 'display_data':
|
||||
case 'stream':
|
||||
case 'error':
|
||||
output = msg.content as nb.ICellOutput;
|
||||
output.output_type = msgType;
|
||||
break;
|
||||
case 'clear_output':
|
||||
// TODO wait until next message before clearing
|
||||
// let wait = (msg as KernelMessage.IClearOutputMsg).content.wait;
|
||||
this.clearOutputs();
|
||||
break;
|
||||
case 'update_display_data':
|
||||
output = msg.content as nb.ICellOutput;
|
||||
output.output_type = 'display_data';
|
||||
// TODO #930 handle in-place update of displayed data
|
||||
// targets = this._displayIdMap.get(displayId);
|
||||
// if (targets) {
|
||||
// for (let index of targets) {
|
||||
// model.set(index, output);
|
||||
// }
|
||||
// }
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// TODO handle in-place update of displayed data
|
||||
// if (displayId && msgType === 'display_data') {
|
||||
// targets = this._displayIdMap.get(displayId) || [];
|
||||
// targets.push(model.length - 1);
|
||||
// this._displayIdMap.set(displayId, targets);
|
||||
// }
|
||||
if (output) {
|
||||
// deletes transient node in the serialized JSON
|
||||
delete output['transient'];
|
||||
this._outputs.push(this.rewriteOutputUrls(output));
|
||||
// Only scroll on 1st output being added
|
||||
let shouldScroll = this._outputs.length === 1;
|
||||
this.fireOutputsChanged(shouldScroll);
|
||||
}
|
||||
}
|
||||
|
||||
private rewriteOutputUrls(output: nb.ICellOutput): nb.ICellOutput {
|
||||
const driverLog = '/gateway/default/yarn/container';
|
||||
const yarnUi = '/gateway/default/yarn/proxy';
|
||||
const defaultPort = ':30433';
|
||||
// Only rewrite if this is coming back during execution, not when loading from disk.
|
||||
// A good approximation is that the model has a future (needed for execution)
|
||||
if (this.future) {
|
||||
try {
|
||||
let result = output as nb.IDisplayResult;
|
||||
if (result && result.data && result.data['text/html']) {
|
||||
let model = this._options.notebook as NotebookModel;
|
||||
if (model.context) {
|
||||
let gatewayEndpointInfo = this.getGatewayEndpoint(model.context);
|
||||
if (gatewayEndpointInfo) {
|
||||
let hostAndIp = notebookUtils.getHostAndPortFromEndpoint(gatewayEndpointInfo.endpoint);
|
||||
let host = hostAndIp.host ? hostAndIp.host : model.context.serverName;
|
||||
let port = hostAndIp.port ? ':' + hostAndIp.port : defaultPort;
|
||||
let html = result.data['text/html'];
|
||||
// CTP 3.1 and earlier Spark link
|
||||
html = this.rewriteUrlUsingRegex(/(https?:\/\/master.*\/proxy)(.*)/g, html, host, port, yarnUi);
|
||||
// CTP 3.2 and later spark link
|
||||
html = this.rewriteUrlUsingRegex(/(https?:\/\/sparkhead.*\/proxy)(.*)/g, html, host, port, yarnUi);
|
||||
// Driver link
|
||||
html = this.rewriteUrlUsingRegex(/(https?:\/\/storage.*\/containerlogs)(.*)/g, html, host, port, driverLog);
|
||||
(<nb.IDisplayResult>output).data['text/html'] = html;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
private rewriteUrlUsingRegex(regex: RegExp, html: string, host: string, port: string, target: string): string {
|
||||
return html.replace(regex, function (a, b, c) {
|
||||
let ret = '';
|
||||
if (b !== '') {
|
||||
ret = 'https://' + host + port + target;
|
||||
}
|
||||
if (c !== '') {
|
||||
ret = ret + c;
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
}
|
||||
|
||||
public setStdInHandler(handler: nb.MessageHandler<nb.IStdinMessage>): void {
|
||||
this._stdInHandler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* StdIn requires user interaction, so this is deferred to upstream UI
|
||||
* components. If one is registered the cell will call and wait on it, if not
|
||||
* it will immediately return to unblock error handling
|
||||
*/
|
||||
private handleSdtIn(msg: nb.IStdinMessage): void | Thenable<void> {
|
||||
let handler = async () => {
|
||||
if (!this._stdInHandler) {
|
||||
// No-op
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await this._stdInHandler.handle(msg);
|
||||
} catch (err) {
|
||||
if (this.future) {
|
||||
// TODO should we error out in this case somehow? E.g. send Ctrl+C?
|
||||
this.future.sendInputReply({ value: '' });
|
||||
}
|
||||
}
|
||||
};
|
||||
return handler();
|
||||
}
|
||||
|
||||
public toJSON(): nb.ICellContents {
|
||||
let metadata = this._metadata || {};
|
||||
let cellJson: Partial<nb.ICellContents> = {
|
||||
cell_type: this._cellType,
|
||||
source: this._source,
|
||||
metadata: metadata
|
||||
};
|
||||
cellJson.metadata.azdata_cell_guid = this._cellGuid;
|
||||
if (this._cellType === CellTypes.Code) {
|
||||
cellJson.metadata.language = this._language;
|
||||
cellJson.metadata.tags = metadata.tags;
|
||||
cellJson.outputs = this._outputs;
|
||||
cellJson.execution_count = this.executionCount ? this.executionCount : null;
|
||||
}
|
||||
return cellJson as nb.ICellContents;
|
||||
}
|
||||
|
||||
public fromJSON(cell: nb.ICellContents): void {
|
||||
if (!cell) {
|
||||
return;
|
||||
}
|
||||
this._cellType = cell.cell_type;
|
||||
this.executionCount = cell.execution_count;
|
||||
this._source = this.getMultilineSource(cell.source);
|
||||
this._metadata = cell.metadata || {};
|
||||
|
||||
if (this._metadata.tags && this._metadata.tags.some(x => x === HideInputTag) && this._cellType === CellTypes.Code) {
|
||||
this._isCollapsed = true;
|
||||
} else {
|
||||
this._isCollapsed = false;
|
||||
}
|
||||
|
||||
this._cellGuid = cell.metadata && cell.metadata.azdata_cell_guid ? cell.metadata.azdata_cell_guid : generateUuid();
|
||||
this.setLanguageFromContents(cell);
|
||||
if (cell.outputs) {
|
||||
for (let output of cell.outputs) {
|
||||
// For now, we're assuming it's OK to save these as-is with no modification
|
||||
this.addOutput(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setLanguageFromContents(cell: nb.ICellContents): void {
|
||||
if (cell.cell_type === CellTypes.Markdown) {
|
||||
this._language = 'markdown';
|
||||
} else if (cell.metadata && cell.metadata.language) {
|
||||
this._language = cell.metadata.language;
|
||||
}
|
||||
// else skip, we set default language anyhow
|
||||
}
|
||||
|
||||
private addOutput(output: nb.ICellOutput) {
|
||||
this._normalize(output);
|
||||
this._outputs.push(output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize an output.
|
||||
*/
|
||||
private _normalize(value: nb.ICellOutput): void {
|
||||
if (notebookUtils.isStream(value)) {
|
||||
if (Array.isArray(value.text)) {
|
||||
value.text = (value.text as string[]).join('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private createUri(): void {
|
||||
let uri = URI.from({ scheme: Schemas.untitled, path: `notebook-editor-${this.id}` });
|
||||
// Use this to set the internal (immutable) and public (shared with extension) uri properties
|
||||
this.cellUri = uri;
|
||||
}
|
||||
|
||||
// Get Knox endpoint from IConnectionProfile
|
||||
// TODO: this will be refactored out into the notebooks extension as a contribution point
|
||||
private getGatewayEndpoint(activeConnection: IConnectionProfile): notebookUtils.IEndpoint {
|
||||
let endpoint;
|
||||
if (this._connectionManagementService && activeConnection && activeConnection.providerName.toLowerCase() === notebookConstants.SQL_CONNECTION_PROVIDER.toLowerCase()) {
|
||||
let serverInfo: ServerInfo = this._connectionManagementService.getServerInfo(activeConnection.id);
|
||||
if (serverInfo) {
|
||||
let endpoints: notebookUtils.IEndpoint[] = notebookUtils.getClusterEndpoints(serverInfo);
|
||||
if (endpoints && endpoints.length > 0) {
|
||||
endpoint = find(endpoints, ep => ep.serviceName.toLowerCase() === notebookUtils.hadoopEndpointNameGateway);
|
||||
}
|
||||
}
|
||||
}
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
|
||||
private getMultilineSource(source: string | string[]): string | string[] {
|
||||
if (source === undefined) {
|
||||
return [];
|
||||
}
|
||||
if (typeof source === 'string') {
|
||||
let sourceMultiline = source.split('\n');
|
||||
// If source is one line (i.e. no '\n'), return it immediately
|
||||
if (sourceMultiline.length === 1) {
|
||||
return [source];
|
||||
} else if (sourceMultiline.length === 0) {
|
||||
return [];
|
||||
}
|
||||
// Otherwise, add back all of the newlines here
|
||||
// Note: for Windows machines that require '/r/n',
|
||||
// splitting on '\n' and putting back the '\n' will still
|
||||
// retain the '\r', so that isn't lost in the process
|
||||
// Note: the last line will not include a newline at the end
|
||||
for (let i = 0; i < sourceMultiline.length - 1; i++) {
|
||||
sourceMultiline[i] += '\n';
|
||||
}
|
||||
return sourceMultiline;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
// Dispose and set current future to undefined
|
||||
private disposeFuture() {
|
||||
if (this._future) {
|
||||
this._future.dispose();
|
||||
}
|
||||
this._future = undefined;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ICellMagicMapper } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { ICellMagicMapper } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { ILanguageMagic } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
|
||||
|
||||
@@ -1,366 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// This code is based on @jupyterlab/packages/apputils/src/clientsession.tsx
|
||||
|
||||
import { nb } from 'azdata';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { localize } from 'vs/nls';
|
||||
import { getErrorMessage } from 'vs/base/common/errors';
|
||||
|
||||
import { IClientSession, IKernelPreference, IClientSessionOptions } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { INotebookManager } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
|
||||
type KernelChangeHandler = (kernel: nb.IKernelChangedArgs) => Promise<void>;
|
||||
/**
|
||||
* Implementation of a client session. This is a model over session operations,
|
||||
* which may come from the session manager or a specific session.
|
||||
*/
|
||||
export class ClientSession implements IClientSession {
|
||||
//#region private fields with public accessors
|
||||
private _terminatedEmitter = new Emitter<void>();
|
||||
private _kernelChangedEmitter = new Emitter<nb.IKernelChangedArgs>();
|
||||
private _statusChangedEmitter = new Emitter<nb.ISession>();
|
||||
private _iopubMessageEmitter = new Emitter<nb.IMessage>();
|
||||
private _unhandledMessageEmitter = new Emitter<nb.IMessage>();
|
||||
private _propertyChangedEmitter = new Emitter<'path' | 'name' | 'type'>();
|
||||
private _notebookUri: URI;
|
||||
private _type: string;
|
||||
private _name: string;
|
||||
private _isReady: boolean;
|
||||
private _ready: Deferred<void>;
|
||||
private _kernelChangeCompleted: Deferred<void>;
|
||||
private _kernelPreference: IKernelPreference;
|
||||
private _kernelDisplayName: string;
|
||||
private _errorMessage: string;
|
||||
private _cachedKernelSpec: nb.IKernelSpec;
|
||||
private _kernelChangeHandlers: KernelChangeHandler[] = [];
|
||||
private _defaultKernel: nb.IKernelSpec;
|
||||
|
||||
//#endregion
|
||||
|
||||
private _serverLoadFinished: Promise<void>;
|
||||
private _session: nb.ISession;
|
||||
private isServerStarted: boolean;
|
||||
private notebookManager: INotebookManager;
|
||||
private _kernelConfigActions: ((kernelName: string) => Promise<any>)[] = [];
|
||||
|
||||
constructor(private options: IClientSessionOptions) {
|
||||
this._notebookUri = options.notebookUri;
|
||||
this.notebookManager = options.notebookManager;
|
||||
this._isReady = false;
|
||||
this._ready = new Deferred<void>();
|
||||
this._kernelChangeCompleted = new Deferred<void>();
|
||||
this._defaultKernel = options.kernelSpec;
|
||||
}
|
||||
|
||||
public async initialize(): Promise<void> {
|
||||
try {
|
||||
this._serverLoadFinished = this.startServer();
|
||||
await this._serverLoadFinished;
|
||||
await this.initializeSession();
|
||||
await this.updateCachedKernelSpec();
|
||||
} catch (err) {
|
||||
this._errorMessage = getErrorMessage(err) || localize('clientSession.unknownError', "An error occurred while starting the notebook session");
|
||||
}
|
||||
// Always resolving for now. It's up to callers to check for error case
|
||||
this._isReady = true;
|
||||
this._ready.resolve();
|
||||
if (!this.isInErrorState && this._session && this._session.kernel) {
|
||||
await this.notifyKernelChanged(undefined, this._session.kernel);
|
||||
}
|
||||
}
|
||||
|
||||
private async startServer(): Promise<void> {
|
||||
let serverManager = this.notebookManager.serverManager;
|
||||
if (serverManager) {
|
||||
await serverManager.startServer();
|
||||
if (!serverManager.isStarted) {
|
||||
throw new Error(localize('ServerNotStarted', "Server did not start for unknown reason"));
|
||||
}
|
||||
this.isServerStarted = serverManager.isStarted;
|
||||
} else {
|
||||
this.isServerStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
private async initializeSession(): Promise<void> {
|
||||
await this._serverLoadFinished;
|
||||
if (this.isServerStarted) {
|
||||
if (!this.notebookManager.sessionManager.isReady) {
|
||||
await this.notebookManager.sessionManager.ready;
|
||||
}
|
||||
if (this._defaultKernel) {
|
||||
await this.startSessionInstance(this._defaultKernel.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async startSessionInstance(kernelName: string): Promise<void> {
|
||||
let session: nb.ISession;
|
||||
try {
|
||||
// TODO #3164 should use URI instead of path for startNew
|
||||
session = await this.notebookManager.sessionManager.startNew({
|
||||
path: this.notebookUri.fsPath,
|
||||
kernelName: kernelName
|
||||
// TODO add kernel name if saved in the document
|
||||
});
|
||||
session.defaultKernelLoaded = true;
|
||||
} catch (err) {
|
||||
// TODO move registration
|
||||
if (err && err.response && err.response.status === 501) {
|
||||
this.options.notificationService.warn(localize('kernelRequiresConnection', "Kernel {0} was not found. The default kernel will be used instead.", kernelName));
|
||||
session = await this.notebookManager.sessionManager.startNew({
|
||||
path: this.notebookUri.fsPath,
|
||||
kernelName: undefined
|
||||
});
|
||||
session.defaultKernelLoaded = false;
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
this._session = session;
|
||||
await this.runKernelConfigActions(kernelName);
|
||||
this._statusChangedEmitter.fire(session);
|
||||
}
|
||||
|
||||
private async runKernelConfigActions(kernelName: string): Promise<void> {
|
||||
for (let startAction of this._kernelConfigActions) {
|
||||
await startAction(kernelName);
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
// No-op for now
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the server has finished loading. It may have failed to load in
|
||||
* which case the view will be in an error state.
|
||||
*/
|
||||
public get serverLoadFinished(): Promise<void> {
|
||||
return this._serverLoadFinished;
|
||||
}
|
||||
|
||||
|
||||
//#region IClientSession Properties
|
||||
public get terminated(): Event<void> {
|
||||
return this._terminatedEmitter.event;
|
||||
}
|
||||
public get kernelChanged(): Event<nb.IKernelChangedArgs> {
|
||||
return this._kernelChangedEmitter.event;
|
||||
}
|
||||
|
||||
public onKernelChanging(changeHandler: (kernel: nb.IKernelChangedArgs) => Promise<void>): void {
|
||||
if (changeHandler) {
|
||||
this._kernelChangeHandlers.push(changeHandler);
|
||||
}
|
||||
}
|
||||
public get statusChanged(): Event<nb.ISession> {
|
||||
return this._statusChangedEmitter.event;
|
||||
}
|
||||
public get iopubMessage(): Event<nb.IMessage> {
|
||||
return this._iopubMessageEmitter.event;
|
||||
}
|
||||
public get unhandledMessage(): Event<nb.IMessage> {
|
||||
return this._unhandledMessageEmitter.event;
|
||||
}
|
||||
public get propertyChanged(): Event<'path' | 'name' | 'type'> {
|
||||
return this._propertyChangedEmitter.event;
|
||||
}
|
||||
public get kernel(): nb.IKernel | null {
|
||||
return this._session ? this._session.kernel : undefined;
|
||||
}
|
||||
public get notebookUri(): URI {
|
||||
return this._notebookUri;
|
||||
}
|
||||
public get name(): string {
|
||||
return this._name;
|
||||
}
|
||||
public get type(): string {
|
||||
return this._type;
|
||||
}
|
||||
public get status(): nb.KernelStatus {
|
||||
if (!this.isReady) {
|
||||
return 'starting';
|
||||
}
|
||||
return this._session ? this._session.status : 'dead';
|
||||
}
|
||||
public get isReady(): boolean {
|
||||
return this._isReady;
|
||||
}
|
||||
public get ready(): Promise<void> {
|
||||
return this._ready.promise;
|
||||
}
|
||||
public get kernelChangeCompleted(): Promise<void> {
|
||||
return this._kernelChangeCompleted.promise;
|
||||
}
|
||||
public get kernelPreference(): IKernelPreference {
|
||||
return this._kernelPreference;
|
||||
}
|
||||
public set kernelPreference(value: IKernelPreference) {
|
||||
this._kernelPreference = value;
|
||||
}
|
||||
public get kernelDisplayName(): string {
|
||||
return this._kernelDisplayName;
|
||||
}
|
||||
public get errorMessage(): string {
|
||||
return this._errorMessage;
|
||||
}
|
||||
public get isInErrorState(): boolean {
|
||||
return !!this._errorMessage;
|
||||
}
|
||||
|
||||
public get cachedKernelSpec(): nb.IKernelSpec {
|
||||
return this._cachedKernelSpec;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Not Yet Implemented
|
||||
/**
|
||||
* Change the current kernel associated with the document.
|
||||
*/
|
||||
async changeKernel(options: nb.IKernelSpec, oldValue?: nb.IKernel): Promise<nb.IKernel> {
|
||||
this._kernelChangeCompleted = new Deferred<void>();
|
||||
this._isReady = false;
|
||||
let oldKernel = oldValue ? oldValue : this.kernel;
|
||||
|
||||
let kernel = await this.doChangeKernel(options);
|
||||
try {
|
||||
await kernel.ready;
|
||||
} catch (error) {
|
||||
// Cleanup some state before re-throwing
|
||||
this._isReady = kernel.isReady;
|
||||
this._kernelChangeCompleted.resolve();
|
||||
throw error;
|
||||
}
|
||||
let newKernel = this._session ? kernel : this._session.kernel;
|
||||
this._isReady = kernel.isReady;
|
||||
await this.updateCachedKernelSpec();
|
||||
// Send resolution events to listeners
|
||||
await this.notifyKernelChanged(oldKernel, newKernel);
|
||||
return kernel;
|
||||
}
|
||||
|
||||
private async notifyKernelChanged(oldKernel: nb.IKernel, newKernel: nb.IKernel): Promise<void> {
|
||||
let changeArgs: nb.IKernelChangedArgs = {
|
||||
oldValue: oldKernel,
|
||||
newValue: newKernel
|
||||
};
|
||||
let changePromises = this._kernelChangeHandlers.map(handler => handler(changeArgs));
|
||||
await Promise.all(changePromises);
|
||||
// Wait on connection configuration to complete before resolving full kernel change
|
||||
this._kernelChangeCompleted.resolve();
|
||||
this._kernelChangedEmitter.fire(changeArgs);
|
||||
}
|
||||
|
||||
private async updateCachedKernelSpec(): Promise<void> {
|
||||
this._cachedKernelSpec = undefined;
|
||||
let kernel = this.kernel;
|
||||
if (kernel) {
|
||||
await kernel.ready;
|
||||
if (kernel.isReady) {
|
||||
this._cachedKernelSpec = await kernel.getSpec();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to either call ChangeKernel on current session, or start a new session
|
||||
*/
|
||||
private async doChangeKernel(options: nb.IKernelSpec): Promise<nb.IKernel> {
|
||||
let kernel: nb.IKernel;
|
||||
if (this._session) {
|
||||
kernel = await this._session.changeKernel(options);
|
||||
await this.runKernelConfigActions(kernel.name);
|
||||
} else {
|
||||
kernel = await this.startSessionInstance(options.name).then(() => this.kernel);
|
||||
}
|
||||
return kernel;
|
||||
}
|
||||
|
||||
public async configureKernel(options: nb.IKernelSpec): Promise<void> {
|
||||
if (this._session) {
|
||||
await this._session.configureKernel(options);
|
||||
}
|
||||
}
|
||||
|
||||
public async updateConnection(connection: IConnectionProfile): Promise<void> {
|
||||
if (!this.kernel) {
|
||||
// TODO is there any case where skipping causes errors? So far it seems like it gets called twice
|
||||
return;
|
||||
}
|
||||
if (connection.id !== '-1') {
|
||||
await this._session.configureConnection(connection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill the kernel and shutdown the session.
|
||||
*
|
||||
* @returns A promise that resolves when the session is shut down.
|
||||
*/
|
||||
public async shutdown(): Promise<void> {
|
||||
// Always try to shut down session
|
||||
if (this._session && this._session.id && this.notebookManager && this.notebookManager.sessionManager) {
|
||||
await this.notebookManager.sessionManager.shutdown(this._session.id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a kernel for the session.
|
||||
*/
|
||||
selectKernel(): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart the session.
|
||||
*
|
||||
* @returns A promise that resolves with whether the kernel has restarted.
|
||||
*
|
||||
* #### Notes
|
||||
* If there is a running kernel, present a dialog.
|
||||
* If there is no kernel, we start a kernel with the last run
|
||||
* kernel name and resolves with `true`. If no kernel has been started,
|
||||
* this is a no-op, and resolves with `false`.
|
||||
*/
|
||||
restart(): Promise<boolean> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the session path.
|
||||
*
|
||||
* @param path - The new session path.
|
||||
*
|
||||
* @returns A promise that resolves when the session has renamed.
|
||||
*
|
||||
* #### Notes
|
||||
* This uses the Jupyter REST API, and the response is validated.
|
||||
* The promise is fulfilled on a valid response and rejected otherwise.
|
||||
*/
|
||||
setPath(path: string): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the session name.
|
||||
*/
|
||||
setName(name: string): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the session type.
|
||||
*/
|
||||
setType(type: string): Promise<void> {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
//#endregion
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/*-----------------------------------------------------------------------------
|
||||
| Copyright (c) Jupyter Development Team.
|
||||
| Distributed under the terms of the Modified BSD License.
|
||||
|----------------------------------------------------------------------------*/
|
||||
import { IRenderMime } from './renderMimeInterfaces';
|
||||
import { ReadonlyJSONObject } from '../../../../services/notebook/common/jsonext';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
/**
|
||||
* The default mime model implementation.
|
||||
*/
|
||||
export class MimeModel implements IRenderMime.IMimeModel {
|
||||
/**
|
||||
* Construct a new mime model.
|
||||
*/
|
||||
constructor(options: MimeModel.IOptions = {}) {
|
||||
this.trusted = !!options.trusted;
|
||||
this._data = options.data || {};
|
||||
this._metadata = options.metadata || {};
|
||||
this._callback = options.callback;
|
||||
this._themeService = options.themeService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the model is trusted.
|
||||
*/
|
||||
readonly trusted: boolean;
|
||||
|
||||
/**
|
||||
* The data associated with the model.
|
||||
*/
|
||||
get data(): ReadonlyJSONObject {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
/**
|
||||
* The metadata associated with the model.
|
||||
*/
|
||||
get metadata(): ReadonlyJSONObject {
|
||||
return this._metadata;
|
||||
}
|
||||
|
||||
get themeService(): IThemeService {
|
||||
return this._themeService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the data associated with the model.
|
||||
*
|
||||
* #### Notes
|
||||
* Depending on the implementation of the mime model,
|
||||
* this call may or may not have deferred effects,
|
||||
*/
|
||||
setData(options: IRenderMime.ISetDataOptions): void {
|
||||
this._data = options.data || this._data;
|
||||
this._metadata = options.metadata || this._metadata;
|
||||
this._callback(options);
|
||||
}
|
||||
|
||||
private _callback: (options: IRenderMime.ISetDataOptions) => void;
|
||||
private _data: ReadonlyJSONObject;
|
||||
private _metadata: ReadonlyJSONObject;
|
||||
private _themeService: IThemeService;
|
||||
}
|
||||
|
||||
/**
|
||||
* The namespace for MimeModel class statics.
|
||||
*/
|
||||
export namespace MimeModel {
|
||||
/**
|
||||
* The options used to create a mime model.
|
||||
*/
|
||||
export interface IOptions {
|
||||
/**
|
||||
* Whether the model is trusted. Defaults to `false`.
|
||||
*/
|
||||
trusted?: boolean;
|
||||
|
||||
/**
|
||||
* A callback function for when the data changes.
|
||||
*/
|
||||
callback?: (options: IRenderMime.ISetDataOptions) => void;
|
||||
|
||||
/**
|
||||
* The initial mime data.
|
||||
*/
|
||||
data?: ReadonlyJSONObject;
|
||||
|
||||
/**
|
||||
* The initial mime metadata.
|
||||
*/
|
||||
metadata?: ReadonlyJSONObject;
|
||||
|
||||
/**
|
||||
* Theme service used to react to theme change events
|
||||
*/
|
||||
themeService?: IThemeService;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +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 { nb } from 'azdata';
|
||||
|
||||
import { CellModel } from 'sql/workbench/contrib/notebook/browser/models/cell';
|
||||
import { IClientSession, IClientSessionOptions, ICellModelOptions, ICellModel, IModelFactory } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { ClientSession } from 'sql/workbench/contrib/notebook/browser/models/clientSession';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export class ModelFactory implements IModelFactory {
|
||||
|
||||
constructor(private instantiationService: IInstantiationService) {
|
||||
|
||||
}
|
||||
public createCell(cell: nb.ICellContents, options: ICellModelOptions): ICellModel {
|
||||
return this.instantiationService.createInstance(CellModel, cell, options);
|
||||
}
|
||||
|
||||
public createClientSession(options: IClientSessionOptions): IClientSession {
|
||||
return new ClientSession(options);
|
||||
}
|
||||
}
|
||||
@@ -1,616 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
// This code is based on @jupyterlab/packages/apputils/src/clientsession.tsx
|
||||
|
||||
import { nb } from 'azdata';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
|
||||
import { CellType, NotebookChangeType } from 'sql/workbench/services/notebook/common/contracts';
|
||||
import { INotebookManager, ILanguageMagic } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { IStandardKernelWithProvider } from 'sql/workbench/contrib/notebook/browser/models/notebookUtils';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import { localize } from 'vs/nls';
|
||||
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model';
|
||||
import { IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
|
||||
import { NotebookRange, NotebookFindMatch } from 'sql/workbench/contrib/notebook/find/notebookFindDecorations';
|
||||
|
||||
export interface IClientSessionOptions {
|
||||
notebookUri: URI;
|
||||
notebookManager: INotebookManager;
|
||||
notificationService: INotificationService;
|
||||
kernelSpec: nb.IKernelSpec;
|
||||
}
|
||||
|
||||
/**
|
||||
* The interface of client session object.
|
||||
*
|
||||
* The client session represents the link between
|
||||
* a path and its kernel for the duration of the lifetime
|
||||
* of the session object. The session can have no current
|
||||
* kernel, and can start a new kernel at any time.
|
||||
*/
|
||||
export interface IClientSession extends IDisposable {
|
||||
/**
|
||||
* A signal emitted when the session is shut down.
|
||||
*/
|
||||
readonly terminated: Event<void>;
|
||||
|
||||
/**
|
||||
* A signal emitted when the kernel changes.
|
||||
*/
|
||||
readonly kernelChanged: Event<nb.IKernelChangedArgs>;
|
||||
|
||||
/**
|
||||
* A signal emitted when the kernel status changes.
|
||||
*/
|
||||
readonly statusChanged: Event<nb.ISession>;
|
||||
|
||||
/**
|
||||
* A signal emitted for a kernel messages.
|
||||
*/
|
||||
readonly iopubMessage: Event<nb.IMessage>;
|
||||
|
||||
/**
|
||||
* A signal emitted for an unhandled kernel message.
|
||||
*/
|
||||
readonly unhandledMessage: Event<nb.IMessage>;
|
||||
|
||||
/**
|
||||
* A signal emitted when a session property changes.
|
||||
*/
|
||||
readonly propertyChanged: Event<'path' | 'name' | 'type'>;
|
||||
|
||||
/**
|
||||
* The current kernel associated with the document.
|
||||
*/
|
||||
readonly kernel: nb.IKernel | null;
|
||||
|
||||
/**
|
||||
* The current path associated with the client session.
|
||||
*/
|
||||
readonly notebookUri: URI;
|
||||
|
||||
/**
|
||||
* The current name associated with the client session.
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* The type of the client session.
|
||||
*/
|
||||
readonly type: string;
|
||||
|
||||
/**
|
||||
* The current status of the client session.
|
||||
*/
|
||||
readonly status: nb.KernelStatus;
|
||||
|
||||
/**
|
||||
* Whether the session is ready.
|
||||
*/
|
||||
readonly isReady: boolean;
|
||||
|
||||
/**
|
||||
* Whether the session is in an unusable state
|
||||
*/
|
||||
readonly isInErrorState: boolean;
|
||||
/**
|
||||
* The error information, if this session is in an error state
|
||||
*/
|
||||
readonly errorMessage: string;
|
||||
|
||||
/**
|
||||
* A promise that is fulfilled when the session is ready.
|
||||
*/
|
||||
readonly ready: Promise<void>;
|
||||
|
||||
/**
|
||||
* A promise that is fulfilled when the session completes a kernel change.
|
||||
*/
|
||||
readonly kernelChangeCompleted: Promise<void>;
|
||||
|
||||
/**
|
||||
* The kernel preference.
|
||||
*/
|
||||
kernelPreference: IKernelPreference;
|
||||
|
||||
/**
|
||||
* The display name of the kernel.
|
||||
*/
|
||||
readonly kernelDisplayName: string;
|
||||
|
||||
readonly cachedKernelSpec: nb.IKernelSpec;
|
||||
|
||||
/**
|
||||
* Initializes the ClientSession, by starting the server and
|
||||
* connecting to the SessionManager.
|
||||
* This will optionally start a session if the kernel preferences
|
||||
* indicate this is desired
|
||||
*/
|
||||
initialize(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Change the current kernel associated with the document.
|
||||
*/
|
||||
changeKernel(
|
||||
options: nb.IKernelSpec,
|
||||
oldKernel?: nb.IKernel
|
||||
): Promise<nb.IKernel>;
|
||||
|
||||
/**
|
||||
* Configure the current kernel associated with the document.
|
||||
*/
|
||||
configureKernel(
|
||||
options: nb.IKernelSpec
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Kill the kernel and shutdown the session.
|
||||
*
|
||||
* @returns A promise that resolves when the session is shut down.
|
||||
*/
|
||||
shutdown(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Select a kernel for the session.
|
||||
*/
|
||||
selectKernel(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Restart the session.
|
||||
*
|
||||
* @returns A promise that resolves with whether the kernel has restarted.
|
||||
*
|
||||
* #### Notes
|
||||
* If there is a running kernel, present a dialog.
|
||||
* If there is no kernel, we start a kernel with the last run
|
||||
* kernel name and resolves with `true`. If no kernel has been started,
|
||||
* this is a no-op, and resolves with `false`.
|
||||
*/
|
||||
restart(): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Change the session path.
|
||||
*
|
||||
* @param path - The new session path.
|
||||
*
|
||||
* @returns A promise that resolves when the session has renamed.
|
||||
*
|
||||
* #### Notes
|
||||
* This uses the Jupyter REST API, and the response is validated.
|
||||
* The promise is fulfilled on a valid response and rejected otherwise.
|
||||
*/
|
||||
setPath(path: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Change the session name.
|
||||
*/
|
||||
setName(name: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Change the session type.
|
||||
*/
|
||||
setType(type: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Updates the connection
|
||||
*/
|
||||
updateConnection(connection: IConnectionProfile): Promise<void>;
|
||||
|
||||
/**
|
||||
* Supports registering a handler to run during kernel change and implement any calls needed to configure
|
||||
* the kernel before actions such as run should be allowed
|
||||
*/
|
||||
onKernelChanging(changeHandler: ((kernel: nb.IKernelChangedArgs) => Promise<void>)): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A kernel preference.
|
||||
*/
|
||||
export interface IKernelPreference {
|
||||
/**
|
||||
* The name of the kernel.
|
||||
*/
|
||||
readonly name?: string;
|
||||
|
||||
/**
|
||||
* The preferred kernel language.
|
||||
*/
|
||||
readonly language?: string;
|
||||
|
||||
/**
|
||||
* The id of an existing kernel.
|
||||
*/
|
||||
readonly id?: string;
|
||||
|
||||
/**
|
||||
* Whether to prefer starting a kernel.
|
||||
*/
|
||||
readonly shouldStart?: boolean;
|
||||
|
||||
/**
|
||||
* Whether a kernel can be started.
|
||||
*/
|
||||
readonly canStart?: boolean;
|
||||
|
||||
/**
|
||||
* Whether to auto-start the default kernel if no matching kernel is found.
|
||||
*/
|
||||
readonly autoStartDefault?: boolean;
|
||||
}
|
||||
|
||||
export interface INotebookModel {
|
||||
/**
|
||||
* Cell List for this model
|
||||
*/
|
||||
readonly cells: ReadonlyArray<ICellModel>;
|
||||
|
||||
/**
|
||||
* The active cell for this model. May be undefined
|
||||
*/
|
||||
activeCell: ICellModel | undefined;
|
||||
|
||||
/**
|
||||
* Client Session in the notebook, used for sending requests to the notebook service
|
||||
*/
|
||||
readonly clientSession: IClientSession;
|
||||
/**
|
||||
* LanguageInfo saved in the notebook
|
||||
*/
|
||||
readonly languageInfo: nb.ILanguageInfo;
|
||||
/**
|
||||
* Current default language for the notebook
|
||||
*/
|
||||
readonly language: string;
|
||||
|
||||
/**
|
||||
* All notebook managers applicable for a given notebook
|
||||
*/
|
||||
readonly notebookManagers: INotebookManager[];
|
||||
|
||||
/**
|
||||
* Event fired on first initialization of the kernel and
|
||||
* on subsequent change events
|
||||
*/
|
||||
readonly kernelChanged: Event<nb.IKernelChangedArgs>;
|
||||
|
||||
/**
|
||||
* Fired on notifications that notebook components should be re-laid out.
|
||||
*/
|
||||
readonly layoutChanged: Event<void>;
|
||||
|
||||
/**
|
||||
* Event fired on first initialization of the kernels and
|
||||
* on subsequent change events
|
||||
*/
|
||||
readonly kernelsChanged: Event<nb.IKernelSpec>;
|
||||
|
||||
/**
|
||||
* Default kernel
|
||||
*/
|
||||
defaultKernel?: nb.IKernelSpec;
|
||||
|
||||
/**
|
||||
* Event fired on first initialization of the contexts and
|
||||
* on subsequent change events
|
||||
*/
|
||||
readonly contextsChanged: Event<void>;
|
||||
|
||||
/**
|
||||
* Event fired on when switching kernel and should show loading context
|
||||
*/
|
||||
readonly contextsLoading: Event<void>;
|
||||
|
||||
/**
|
||||
* The specs for available kernels, or undefined if these have
|
||||
* not been loaded yet
|
||||
*/
|
||||
readonly specs: nb.IAllKernels | undefined;
|
||||
|
||||
/**
|
||||
* The specs for available context, or undefined if this has
|
||||
* not been loaded yet
|
||||
*/
|
||||
readonly context: ConnectionProfile | undefined;
|
||||
|
||||
/**
|
||||
* Event fired on first initialization of the cells and
|
||||
* on subsequent change events
|
||||
*/
|
||||
readonly contentChanged: Event<NotebookContentChange>;
|
||||
|
||||
/**
|
||||
* Event fired on notebook provider change
|
||||
*/
|
||||
readonly onProviderIdChange: Event<string>;
|
||||
|
||||
/**
|
||||
* Event fired on active cell change
|
||||
*/
|
||||
readonly onActiveCellChanged: Event<ICellModel>;
|
||||
|
||||
/**
|
||||
* The trusted mode of the Notebook
|
||||
*/
|
||||
trustedMode: boolean;
|
||||
|
||||
/**
|
||||
* Current notebook provider id
|
||||
*/
|
||||
providerId: string;
|
||||
|
||||
/**
|
||||
* Change the current kernel from the Kernel dropdown
|
||||
* @param displayName kernel name (as displayed in Kernel dropdown)
|
||||
*/
|
||||
changeKernel(displayName: string): void;
|
||||
|
||||
/**
|
||||
* Change the current context (if applicable)
|
||||
*/
|
||||
changeContext(host: string, connection?: IConnectionProfile, hideErrorMessage?: boolean): Promise<void>;
|
||||
|
||||
/**
|
||||
* Find a cell's index given its model
|
||||
*/
|
||||
findCellIndex(cellModel: ICellModel): number;
|
||||
|
||||
/**
|
||||
* Adds a cell to the index of the model
|
||||
*/
|
||||
addCell(cellType: CellType, index?: number): void;
|
||||
|
||||
/**
|
||||
* Deletes a cell
|
||||
*/
|
||||
deleteCell(cellModel: ICellModel): void;
|
||||
|
||||
/**
|
||||
* Serialize notebook cell content to JSON
|
||||
*/
|
||||
toJSON(type?: NotebookChangeType): nb.INotebookContents;
|
||||
|
||||
/**
|
||||
* Notifies the notebook of a change in the cell
|
||||
*/
|
||||
onCellChange(cell: ICellModel, change: NotebookChangeType): void;
|
||||
|
||||
|
||||
/**
|
||||
* Push edit operations, basically editing the model. This is the preferred way of
|
||||
* editing the model. Long-term, this will ensure edit operations can be added to the undo stack
|
||||
* @param edits The edit operations to perform
|
||||
*/
|
||||
pushEditOperations(edits: ISingleNotebookEditOperation[]): void;
|
||||
|
||||
getApplicableConnectionProviderIds(kernelName: string): string[];
|
||||
|
||||
updateActiveCell(cell: ICellModel): void;
|
||||
|
||||
/**
|
||||
* Get the standardKernelWithProvider by name
|
||||
* @param name The kernel name
|
||||
*/
|
||||
getStandardKernelFromName(name: string): IStandardKernelWithProvider;
|
||||
|
||||
/** Event fired once we get call back from ConfigureConnection method in sqlops extension */
|
||||
readonly onValidConnectionSelected: Event<boolean>;
|
||||
|
||||
serializationStateChanged(changeType: NotebookChangeType, cell?: ICellModel): void;
|
||||
|
||||
standardKernels: IStandardKernelWithProvider[];
|
||||
|
||||
requestConnection(): Promise<boolean>;
|
||||
|
||||
}
|
||||
|
||||
export interface INotebookFindModel {
|
||||
/** Get the find count */
|
||||
getFindCount(): number;
|
||||
|
||||
/** Get the find index */
|
||||
getFindIndex(): number;
|
||||
|
||||
/** find the next match */
|
||||
findNext(): Promise<NotebookRange>;
|
||||
|
||||
/** find the previous match */
|
||||
findPrevious(): Promise<NotebookRange>;
|
||||
|
||||
/** search the notebook model for the given exp up to maxMatch occurances */
|
||||
find(exp: string, matchCase?: boolean, wholeWord?: boolean, maxMatches?: number): Promise<NotebookRange>;
|
||||
|
||||
/** clear the results of the find */
|
||||
clearFind(): void;
|
||||
|
||||
/** return the find results with their ranges */
|
||||
findArray: NotebookRange[];
|
||||
|
||||
/**
|
||||
* Get the range associated with a decoration.
|
||||
* @param id The decoration id.
|
||||
* @return The decoration range or null if the decoration was not found.
|
||||
*/
|
||||
getDecorationRange(id: string): NotebookRange | null;
|
||||
|
||||
/**
|
||||
* Get the range associated with a decoration.
|
||||
* @param callback that accepts changeAccessor which applies the decorations
|
||||
* @param ownerId the owner id
|
||||
* @return The decoration range or null if the decoration was not found.
|
||||
*/
|
||||
changeDecorations<T>(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T, ownerId: number): T | null;
|
||||
|
||||
/**
|
||||
* Get the maximum legal column for line at `lineNumber`
|
||||
*/
|
||||
getLineMaxColumn(lineNumber: number): number;
|
||||
|
||||
/**
|
||||
* Get the number of lines in the model.
|
||||
*/
|
||||
getLineCount(): number;
|
||||
|
||||
findMatches: NotebookFindMatch[];
|
||||
|
||||
findExpression: string;
|
||||
|
||||
/** Emit event when the find count changes */
|
||||
onFindCountChange: Event<number>;
|
||||
}
|
||||
|
||||
export interface NotebookContentChange {
|
||||
/**
|
||||
* The type of change that occurred
|
||||
*/
|
||||
changeType: NotebookChangeType;
|
||||
/**
|
||||
* Optional cells that were changed
|
||||
*/
|
||||
cells?: ICellModel | ICellModel[];
|
||||
/**
|
||||
* Optional index of the change, indicating the cell at which an insert or
|
||||
* delete occurred
|
||||
*/
|
||||
cellIndex?: number;
|
||||
/**
|
||||
* Optional value indicating if the notebook is in a dirty or clean state after this change
|
||||
*/
|
||||
isDirty?: boolean;
|
||||
|
||||
/**
|
||||
* Text content changed event for cell edits
|
||||
*/
|
||||
modelContentChangedEvent?: IModelContentChangedEvent;
|
||||
}
|
||||
|
||||
export interface ICellModelOptions {
|
||||
notebook: INotebookModel;
|
||||
isTrusted: boolean;
|
||||
}
|
||||
|
||||
export enum CellExecutionState {
|
||||
Hidden = 0,
|
||||
Stopped = 1,
|
||||
Running = 2,
|
||||
Error = 3
|
||||
}
|
||||
|
||||
export interface IOutputChangedEvent {
|
||||
outputs: ReadonlyArray<nb.ICellOutput>;
|
||||
shouldScroll: boolean;
|
||||
}
|
||||
|
||||
export interface ICellModel {
|
||||
cellUri: URI;
|
||||
id: string;
|
||||
readonly language: string;
|
||||
readonly cellGuid: string;
|
||||
source: string | string[];
|
||||
cellType: CellType;
|
||||
trustedMode: boolean;
|
||||
active: boolean;
|
||||
hover: boolean;
|
||||
executionCount: number | undefined;
|
||||
readonly future: FutureInternal;
|
||||
readonly outputs: ReadonlyArray<nb.ICellOutput>;
|
||||
readonly onOutputsChanged: Event<IOutputChangedEvent>;
|
||||
readonly onExecutionStateChange: Event<CellExecutionState>;
|
||||
readonly executionState: CellExecutionState;
|
||||
readonly notebookModel: NotebookModel;
|
||||
setFuture(future: FutureInternal): void;
|
||||
setStdInHandler(handler: nb.MessageHandler<nb.IStdinMessage>): void;
|
||||
runCell(notificationService?: INotificationService, connectionManagementService?: IConnectionManagementService): Promise<boolean>;
|
||||
setOverrideLanguage(language: string);
|
||||
equals(cellModel: ICellModel): boolean;
|
||||
toJSON(): nb.ICellContents;
|
||||
loaded: boolean;
|
||||
stdInVisible: boolean;
|
||||
readonly onLoaded: Event<string>;
|
||||
isCollapsed: boolean;
|
||||
readonly onCollapseStateChanged: Event<boolean>;
|
||||
modelContentChangedEvent: IModelContentChangedEvent;
|
||||
isEditMode: boolean;
|
||||
readonly ariaLabel: string;
|
||||
}
|
||||
|
||||
export interface FutureInternal extends nb.IFuture {
|
||||
inProgress: boolean;
|
||||
}
|
||||
|
||||
export interface IModelFactory {
|
||||
|
||||
createCell(cell: nb.ICellContents, options: ICellModelOptions): ICellModel;
|
||||
createClientSession(options: IClientSessionOptions): IClientSession;
|
||||
}
|
||||
|
||||
export interface IContentManager {
|
||||
/**
|
||||
* This is a specialized method intended to load for a default context - just the current Notebook's URI
|
||||
*/
|
||||
loadContent(): Promise<nb.INotebookContents>;
|
||||
}
|
||||
|
||||
export interface INotebookModelOptions {
|
||||
/**
|
||||
* Path to the local or remote notebook
|
||||
*/
|
||||
notebookUri: URI;
|
||||
|
||||
/**
|
||||
* Factory for creating cells and client sessions
|
||||
*/
|
||||
factory: IModelFactory;
|
||||
|
||||
contentManager: IContentManager;
|
||||
notebookManagers: INotebookManager[];
|
||||
providerId: string;
|
||||
defaultKernel: nb.IKernelSpec;
|
||||
cellMagicMapper: ICellMagicMapper;
|
||||
|
||||
layoutChanged: Event<void>;
|
||||
|
||||
notificationService: INotificationService;
|
||||
connectionService: IConnectionManagementService;
|
||||
capabilitiesService: ICapabilitiesService;
|
||||
editorLoadedTimestamp?: number;
|
||||
}
|
||||
|
||||
export interface ICellMagicMapper {
|
||||
/**
|
||||
* Tries to find a language mapping for an identified cell magic
|
||||
* @param magic a string defining magic. For example for %%sql the magic text is sql
|
||||
* @param kernelId the name of the current kernel to use when looking up magics
|
||||
*/
|
||||
toLanguageMagic(magic: string, kernelId: string): ILanguageMagic | undefined;
|
||||
}
|
||||
|
||||
export namespace notebookConstants {
|
||||
export const SQL = 'SQL';
|
||||
export const SQL_CONNECTION_PROVIDER = mssqlProviderName;
|
||||
export const sqlKernel: string = localize('sqlKernel', "SQL");
|
||||
export const sqlKernelSpec: nb.IKernelSpec = ({
|
||||
name: sqlKernel,
|
||||
language: 'sql',
|
||||
display_name: sqlKernel
|
||||
});
|
||||
}
|
||||
|
||||
export interface INotebookContentsEditable {
|
||||
cells: nb.ICellContents[];
|
||||
metadata: nb.INotebookMetadata;
|
||||
nbformat: number;
|
||||
nbformat_minor: number;
|
||||
}
|
||||
@@ -1,46 +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 { localize } from 'vs/nls';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
|
||||
|
||||
export class NotebookContexts {
|
||||
|
||||
public static get DefaultContext(): ConnectionProfile {
|
||||
return <any>{
|
||||
providerName: mssqlProviderName,
|
||||
id: '-1',
|
||||
serverName: localize('selectConnection', "Select Connection")
|
||||
};
|
||||
}
|
||||
|
||||
public static get LocalContext(): ConnectionProfile {
|
||||
return <any>{
|
||||
providerName: mssqlProviderName,
|
||||
id: '-1',
|
||||
serverName: localize('localhost', "localhost")
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the applicable context for a given kernel
|
||||
* @param context current connection profile
|
||||
* @param connProviderIds array of connection provider ids applicable for a kernel
|
||||
*/
|
||||
public static getContextForKernel(context: ConnectionProfile, connProviderIds: string[]): ConnectionProfile {
|
||||
// If no connection provider ids exist for a given kernel, the attach to should show localhost
|
||||
if (connProviderIds.length === 0) {
|
||||
return NotebookContexts.LocalContext;
|
||||
}
|
||||
if (context && context.providerName && connProviderIds.filter(p => p === context.providerName).length > 0) {
|
||||
return context;
|
||||
}
|
||||
return NotebookContexts.DefaultContext;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model';
|
||||
import { NotebookFindMatch } from 'sql/workbench/contrib/notebook/find/notebookFindDecorations';
|
||||
import { NotebookRange } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
|
||||
export interface INotebookFindModel {
|
||||
/** Get the find count */
|
||||
getFindCount(): number;
|
||||
/** Get the find index */
|
||||
getFindIndex(): number;
|
||||
/** find the next match */
|
||||
findNext(): Promise<NotebookRange>;
|
||||
/** find the previous match */
|
||||
findPrevious(): Promise<NotebookRange>;
|
||||
/** search the notebook model for the given exp up to maxMatch occurances */
|
||||
find(exp: string, matchCase?: boolean, wholeWord?: boolean, maxMatches?: number): Promise<NotebookRange>;
|
||||
/** clear the results of the find */
|
||||
clearFind(): void;
|
||||
/** return the find results with their ranges */
|
||||
findArray: NotebookRange[];
|
||||
/**
|
||||
* Get the range associated with a decoration.
|
||||
* @param id The decoration id.
|
||||
* @return The decoration range or null if the decoration was not found.
|
||||
*/
|
||||
getDecorationRange(id: string): NotebookRange | null;
|
||||
/**
|
||||
* Get the range associated with a decoration.
|
||||
* @param callback that accepts changeAccessor which applies the decorations
|
||||
* @param ownerId the owner id
|
||||
* @return The decoration range or null if the decoration was not found.
|
||||
*/
|
||||
changeDecorations<T>(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T, ownerId: number): T | null;
|
||||
/**
|
||||
* Get the maximum legal column for line at `lineNumber`
|
||||
*/
|
||||
getLineMaxColumn(lineNumber: number): number;
|
||||
/**
|
||||
* Get the number of lines in the model.
|
||||
*/
|
||||
getLineCount(): number;
|
||||
findMatches: NotebookFindMatch[];
|
||||
findExpression: string;
|
||||
/** Emit event when the find count changes */
|
||||
onFindCountChange: Event<number>;
|
||||
}
|
||||
@@ -9,11 +9,11 @@ import { URI } from 'vs/base/common/uri';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
import { IStandardKernelWithProvider, getProvidersForFileName, getStandardKernelsForProvider } from 'sql/workbench/contrib/notebook/browser/models/notebookUtils';
|
||||
import { IStandardKernelWithProvider, getProvidersForFileName, getStandardKernelsForProvider } from 'sql/workbench/services/notebook/browser/models/notebookUtils';
|
||||
import { INotebookService, DEFAULT_NOTEBOOK_PROVIDER, IProviderInfo } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { INotebookModel, IContentManager, NotebookContentChange } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { INotebookModel, IContentManager, NotebookContentChange } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { ITextFileSaveOptions, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
||||
|
||||
import { Range, IRange } from 'vs/editor/common/core/range';
|
||||
import { FindMatch } from 'vs/editor/common/model';
|
||||
import { NotebookContentChange, INotebookModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookContentChange, INotebookModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { NotebookChangeType } from 'sql/workbench/services/notebook/common/contracts';
|
||||
import { repeat } from 'vs/base/common/strings';
|
||||
import { ITextEditorModel } from 'vs/workbench/common/editor';
|
||||
|
||||
@@ -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.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { nb, ServerInfo } from 'azdata';
|
||||
import { DEFAULT_NOTEBOOK_PROVIDER, DEFAULT_NOTEBOOK_FILETYPE, INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
|
||||
export const clusterEndpointsProperty = 'clusterEndpoints';
|
||||
export const hadoopEndpointNameGateway = 'gateway';
|
||||
/**
|
||||
* Test whether an output is from a stream.
|
||||
*/
|
||||
export function isStream(output: nb.ICellOutput): output is nb.IStreamResult {
|
||||
return output.output_type === 'stream';
|
||||
}
|
||||
|
||||
export function getProvidersForFileName(fileName: string, notebookService: INotebookService): string[] {
|
||||
let fileExt = path.extname(fileName);
|
||||
let providers: string[];
|
||||
// First try to get provider for actual file type
|
||||
if (fileExt && startsWith(fileExt, '.')) {
|
||||
fileExt = fileExt.slice(1, fileExt.length);
|
||||
providers = notebookService.getProvidersForFileType(fileExt);
|
||||
}
|
||||
// Fallback to provider for default file type (assume this is a global handler)
|
||||
if (!providers) {
|
||||
providers = notebookService.getProvidersForFileType(DEFAULT_NOTEBOOK_FILETYPE);
|
||||
}
|
||||
// Finally if all else fails, use the built-in handler
|
||||
if (!providers) {
|
||||
providers = [DEFAULT_NOTEBOOK_PROVIDER];
|
||||
}
|
||||
return providers;
|
||||
}
|
||||
|
||||
export function getStandardKernelsForProvider(providerId: string, notebookService: INotebookService): IStandardKernelWithProvider[] {
|
||||
if (!providerId || !notebookService) {
|
||||
return [];
|
||||
}
|
||||
let standardKernels = notebookService.getStandardKernelsForProvider(providerId);
|
||||
standardKernels.forEach(kernel => {
|
||||
assign(<IStandardKernelWithProvider>kernel, {
|
||||
name: kernel.name,
|
||||
connectionProviderIds: kernel.connectionProviderIds,
|
||||
notebookProvider: providerId
|
||||
});
|
||||
});
|
||||
return <IStandardKernelWithProvider[]>(standardKernels);
|
||||
}
|
||||
|
||||
export interface IStandardKernelWithProvider {
|
||||
readonly name: string;
|
||||
readonly displayName: string;
|
||||
readonly connectionProviderIds: string[];
|
||||
readonly notebookProvider: string;
|
||||
}
|
||||
|
||||
|
||||
export interface IEndpoint {
|
||||
serviceName: string;
|
||||
description: string;
|
||||
endpoint: string;
|
||||
protocol: string;
|
||||
}
|
||||
|
||||
export function tryMatchCellMagic(input: string): string {
|
||||
if (!input) {
|
||||
return input;
|
||||
}
|
||||
let firstLine = input.trimLeft();
|
||||
let magicRegex = /^%%(\w+)/g;
|
||||
let match = magicRegex.exec(firstLine);
|
||||
let magicName = match && match[1];
|
||||
return magicName;
|
||||
}
|
||||
|
||||
export async function asyncForEach(array: any[], callback: Function): Promise<any> {
|
||||
if (array && callback) {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
await callback(array[index], index, array);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getClusterEndpoints(serverInfo: ServerInfo): IEndpoint[] | undefined {
|
||||
let endpoints: RawEndpoint[] = serverInfo.options[clusterEndpointsProperty];
|
||||
if (!endpoints || endpoints.length === 0) { return []; }
|
||||
|
||||
return endpoints.map(e => {
|
||||
// If endpoint is missing, we're on CTP bits. All endpoints from the CTP serverInfo should be treated as HTTPS
|
||||
let endpoint = e.endpoint ? e.endpoint : `https://${e.ipAddress}:${e.port}`;
|
||||
let updatedEndpoint: IEndpoint = {
|
||||
serviceName: e.serviceName,
|
||||
description: e.description,
|
||||
endpoint: endpoint,
|
||||
protocol: e.protocol
|
||||
};
|
||||
return updatedEndpoint;
|
||||
});
|
||||
}
|
||||
|
||||
export type HostAndIp = { host: string, port: string };
|
||||
|
||||
export function getHostAndPortFromEndpoint(endpoint: string): HostAndIp {
|
||||
let authority = URI.parse(endpoint).authority;
|
||||
let hostAndPortRegex = /^(.*)([,:](\d+))/g;
|
||||
let match = hostAndPortRegex.exec(authority);
|
||||
if (match) {
|
||||
return {
|
||||
host: match[1],
|
||||
port: match[3]
|
||||
};
|
||||
}
|
||||
return {
|
||||
host: authority,
|
||||
port: undefined
|
||||
};
|
||||
}
|
||||
|
||||
export interface RawEndpoint {
|
||||
serviceName: string;
|
||||
description?: string;
|
||||
endpoint?: string;
|
||||
protocol?: string;
|
||||
ipAddress?: string;
|
||||
port?: number;
|
||||
}
|
||||
|
||||
export interface IEndpoint {
|
||||
serviceName: string;
|
||||
description: string;
|
||||
endpoint: string;
|
||||
protocol: string;
|
||||
}
|
||||
@@ -4,10 +4,10 @@
|
||||
| Distributed under the terms of the Modified BSD License.
|
||||
|----------------------------------------------------------------------------*/
|
||||
|
||||
import { MimeModel } from './mimemodel';
|
||||
import { nb } from 'azdata';
|
||||
import { JSONObject, isPrimitive } from 'sql/workbench/services/notebook/common/jsonext';
|
||||
import { nbformat } from 'sql/workbench/services/notebook/common/nbformat';
|
||||
import { MimeModel } from 'sql/workbench/services/notebook/browser/outputs/mimemodel';
|
||||
|
||||
/**
|
||||
* A multiline string.
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
| Copyright (c) Jupyter Development Team.
|
||||
| Distributed under the terms of the Modified BSD License.
|
||||
|----------------------------------------------------------------------------*/
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ReadonlyJSONObject } from 'sql/workbench/services/notebook/common/jsonext';
|
||||
|
||||
/**
|
||||
* A namespace for rendermime associated interfaces.
|
||||
*/
|
||||
export namespace IRenderMime {
|
||||
/**
|
||||
* A model for mime data.
|
||||
*/
|
||||
export interface IMimeModel {
|
||||
/**
|
||||
* Whether the data in the model is trusted.
|
||||
*/
|
||||
readonly trusted: boolean;
|
||||
|
||||
/**
|
||||
* The data associated with the model.
|
||||
*/
|
||||
readonly data: ReadonlyJSONObject;
|
||||
|
||||
/**
|
||||
* The metadata associated with the model.
|
||||
*/
|
||||
readonly metadata: ReadonlyJSONObject;
|
||||
|
||||
/**
|
||||
* Set the data associated with the model.
|
||||
*
|
||||
* #### Notes
|
||||
* Calling this function may trigger an asynchronous operation
|
||||
* that could cause the renderer to be rendered with a new model
|
||||
* containing the new data.
|
||||
*/
|
||||
setData(options: ISetDataOptions): void;
|
||||
|
||||
/**
|
||||
* Theme service used to react to theme change events
|
||||
*/
|
||||
readonly themeService: IThemeService;
|
||||
}
|
||||
|
||||
/**
|
||||
* The options used to update a mime model.
|
||||
*/
|
||||
export interface ISetDataOptions {
|
||||
/**
|
||||
* The new data object.
|
||||
*/
|
||||
data?: ReadonlyJSONObject;
|
||||
|
||||
/**
|
||||
* The new metadata object.
|
||||
*/
|
||||
metadata?: ReadonlyJSONObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* A widget which displays the contents of a mime model.
|
||||
*/
|
||||
export interface IRenderer {
|
||||
/**
|
||||
* Render a mime model.
|
||||
*
|
||||
* @param model - The mime model to render.
|
||||
*
|
||||
* @returns A promise which resolves when rendering is complete.
|
||||
*
|
||||
* #### Notes
|
||||
* This method may be called multiple times during the lifetime
|
||||
* of the widget to update it if and when new data is available.
|
||||
*/
|
||||
renderModel(model: IRenderMime.IMimeModel): Promise<void>;
|
||||
|
||||
/**
|
||||
* Node to be updated by the renderer
|
||||
*/
|
||||
node: HTMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* The interface for a renderer factory.
|
||||
*/
|
||||
export interface IRendererFactory {
|
||||
/**
|
||||
* Whether the factory is a "safe" factory.
|
||||
*
|
||||
* #### Notes
|
||||
* A "safe" factory produces renderer widgets which can render
|
||||
* untrusted model data in a usable way. *All* renderers must
|
||||
* handle untrusted data safely, but some may simply failover
|
||||
* with a "Run cell to view output" message. A "safe" renderer
|
||||
* is an indication that its sanitized output will be useful.
|
||||
*/
|
||||
readonly safe: boolean;
|
||||
|
||||
/**
|
||||
* The mime types handled by this factory.
|
||||
*/
|
||||
readonly mimeTypes: ReadonlyArray<string>;
|
||||
|
||||
/**
|
||||
* The default rank of the factory. If not given, defaults to 100.
|
||||
*/
|
||||
readonly defaultRank?: number;
|
||||
|
||||
/**
|
||||
* Create a renderer which displays the mime data.
|
||||
*
|
||||
* @param options - The options used to render the data.
|
||||
*/
|
||||
createRenderer(options: IRendererOptions): IRenderer;
|
||||
}
|
||||
|
||||
/**
|
||||
* The options used to create a renderer.
|
||||
*/
|
||||
export interface IRendererOptions {
|
||||
/**
|
||||
* The preferred mimeType to render.
|
||||
*/
|
||||
mimeType: string;
|
||||
|
||||
/**
|
||||
* The html sanitizer.
|
||||
*/
|
||||
sanitizer: ISanitizer;
|
||||
|
||||
/**
|
||||
* An optional url resolver.
|
||||
*/
|
||||
resolver?: IResolver | null;
|
||||
|
||||
/**
|
||||
* An optional link handler.
|
||||
*/
|
||||
linkHandler?: ILinkHandler | null;
|
||||
|
||||
/**
|
||||
* The LaTeX typesetter.
|
||||
*/
|
||||
latexTypesetter?: ILatexTypesetter | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* An object that handles html sanitization.
|
||||
*/
|
||||
export interface ISanitizer {
|
||||
/**
|
||||
* Sanitize an HTML string.
|
||||
*/
|
||||
sanitize(dirty: string): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* An object that handles links on a node.
|
||||
*/
|
||||
export interface ILinkHandler {
|
||||
/**
|
||||
* Add the link handler to the node.
|
||||
*
|
||||
* @param node: the node for which to handle the link.
|
||||
*
|
||||
* @param path: the path to open when the link is clicked.
|
||||
*
|
||||
* @param id: an optional element id to scroll to when the path is opened.
|
||||
*/
|
||||
handleLink(node: HTMLElement, path: string, id?: string): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* An object that resolves relative URLs.
|
||||
*/
|
||||
export interface IResolver {
|
||||
/**
|
||||
* Resolve a relative url to a correct server path.
|
||||
*/
|
||||
resolveUrl(url: string): Promise<string>;
|
||||
|
||||
/**
|
||||
* Get the download url of a given absolute server path.
|
||||
*/
|
||||
getDownloadUrl(path: string): Promise<string>;
|
||||
|
||||
/**
|
||||
* Whether the URL should be handled by the resolver
|
||||
* or not.
|
||||
*
|
||||
* #### Notes
|
||||
* This is similar to the `isLocal` check in `URLExt`,
|
||||
* but can also perform additional checks on whether the
|
||||
* resolver should handle a given URL.
|
||||
*/
|
||||
isLocal?: (url: string) => boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* The interface for a LaTeX typesetter.
|
||||
*/
|
||||
export interface ILatexTypesetter {
|
||||
/**
|
||||
* Typeset a DOM element.
|
||||
*
|
||||
* @param element - the DOM element to typeset. The typesetting may
|
||||
* happen synchronously or asynchronously.
|
||||
*
|
||||
* #### Notes
|
||||
* The application-wide rendermime object has a settable
|
||||
* `latexTypesetter` property which is used wherever LaTeX
|
||||
* typesetting is required. Extensions wishing to provide their
|
||||
* own typesetter may replace that on the global `lab.rendermime`.
|
||||
*/
|
||||
typeset(element: HTMLElement): void;
|
||||
}
|
||||
}
|
||||
@@ -22,12 +22,12 @@ import * as DOM from 'vs/base/browser/dom';
|
||||
|
||||
import { AngularDisposable } from 'sql/base/browser/lifecycle';
|
||||
import { CellTypes, CellType, NotebookChangeType } from 'sql/workbench/services/notebook/common/contracts';
|
||||
import { ICellModel, IModelFactory, INotebookModel, NotebookContentChange } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { ICellModel, IModelFactory, INotebookModel, NotebookContentChange } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { INotebookService, INotebookParams, INotebookManager, INotebookEditor, DEFAULT_NOTEBOOK_PROVIDER, SQL_NOTEBOOK_PROVIDER, INotebookSection, INavigationProvider, ICellEditorProvider } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
|
||||
import { ModelFactory } from 'sql/workbench/contrib/notebook/browser/models/modelFactory';
|
||||
import * as notebookUtils from 'sql/workbench/contrib/notebook/browser/models/notebookUtils';
|
||||
import { INotebookService, INotebookParams, INotebookManager, INotebookEditor, DEFAULT_NOTEBOOK_PROVIDER, SQL_NOTEBOOK_PROVIDER, INotebookSection, INavigationProvider, ICellEditorProvider, NotebookRange } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel';
|
||||
import { ModelFactory } from 'sql/workbench/services/notebook/browser/models/modelFactory';
|
||||
import * as notebookUtils from 'sql/workbench/services/notebook/browser/models/notebookUtils';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
@@ -36,7 +36,7 @@ import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHos
|
||||
import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService';
|
||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import { CellMagicMapper } from 'sql/workbench/contrib/notebook/browser/models/cellMagicMapper';
|
||||
import { CellModel } from 'sql/workbench/contrib/notebook/browser/models/cell';
|
||||
import { CellModel } from 'sql/workbench/services/notebook/browser/models/cell';
|
||||
import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
|
||||
import { isValidBasename } from 'vs/base/common/extpath';
|
||||
import { basename } from 'vs/base/common/resources';
|
||||
@@ -53,7 +53,7 @@ import { getErrorMessage, onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { find, firstIndex } from 'vs/base/common/arrays';
|
||||
import { CodeCellComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/codeCell.component';
|
||||
import { TextCellComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/textCell.component';
|
||||
import { NotebookRange } from 'sql/workbench/contrib/notebook/find/notebookFindDecorations';
|
||||
import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
|
||||
|
||||
|
||||
export const NOTEBOOK_SELECTOR: string = 'notebook-component';
|
||||
@@ -261,7 +261,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
let editors = this.editorService.visibleControls;
|
||||
for (let editor of editors) {
|
||||
if (editor && editor.input.getResource() === this._notebookParams.input.notebookUri) {
|
||||
await editor.group.closeEditor(this._notebookParams.input, { preserveFocus: true });
|
||||
await editor.group.closeEditor(this._notebookParams.input as NotebookInput, { preserveFocus: true }); // sketchy
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -532,12 +532,12 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
}
|
||||
|
||||
isActive(): boolean {
|
||||
return this.editorService.activeEditor === this.notebookParams.input;
|
||||
return this.editorService.activeEditor.matches(this.notebookParams.input);
|
||||
}
|
||||
|
||||
isVisible(): boolean {
|
||||
let notebookEditor = this.notebookParams.input;
|
||||
return this.editorService.visibleEditors.some(e => e === notebookEditor);
|
||||
return this.editorService.visibleEditors.some(e => e.matches(notebookEditor));
|
||||
}
|
||||
|
||||
isDirty(): boolean {
|
||||
|
||||
@@ -34,14 +34,17 @@ import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { TreeViewItemHandleArg } from 'sql/workbench/common/views';
|
||||
import { ConnectedContext } from 'azdata';
|
||||
import { TreeNodeContextKey } from 'sql/workbench/contrib/objectExplorer/common/treeNodeContextKey';
|
||||
import { ObjectExplorerActionsContext } from 'sql/workbench/contrib/objectExplorer/browser/objectExplorerActions';
|
||||
import { TreeNodeContextKey } from 'sql/workbench/services/objectExplorer/common/treeNodeContextKey';
|
||||
import { ObjectExplorerActionsContext } from 'sql/workbench/services/objectExplorer/browser/objectExplorerActions';
|
||||
import { ItemContextKey } from 'sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerTreeContext';
|
||||
import { ManageActionContext } from 'sql/workbench/browser/actions';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { MarkdownOutputComponent } from 'sql/workbench/contrib/notebook/browser/outputs/markdownOutput.component';
|
||||
import { registerCellComponent } from 'sql/platform/notebooks/common/outputRegistry';
|
||||
import { TextCellComponent } from 'sql/workbench/contrib/notebook/browser/cellViews/textCell.component';
|
||||
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
|
||||
import { NotebookThemingContribution } from 'sql/workbench/contrib/notebook/browser/notebookThemingContribution';
|
||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
|
||||
Registry.as<IEditorInputFactoryRegistry>(EditorInputFactoryExtensions.EditorInputFactories)
|
||||
.registerEditorInputFactory(FileNotebookInput.ID, FileNoteBookEditorInputFactory);
|
||||
@@ -55,6 +58,8 @@ Registry.as<ILanguageAssociationRegistry>(LanguageAssociationExtensions.Language
|
||||
Registry.as<IEditorRegistry>(EditorExtensions.Editors)
|
||||
.registerEditor(new EditorDescriptor(NotebookEditor, NotebookEditor.ID, localize('notebookEditor.name', "Notebook Editor")), [new SyncDescriptor(UntitledNotebookInput), new SyncDescriptor(FileNotebookInput)]);
|
||||
|
||||
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
|
||||
.registerWorkbenchContribution(NotebookThemingContribution, LifecyclePhase.Restored);
|
||||
|
||||
// Global Actions
|
||||
const actionRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionsExtensions.WorkbenchActions);
|
||||
|
||||
@@ -16,15 +16,15 @@ import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilit
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { noKernel } from 'sql/workbench/services/notebook/browser/sessionManager';
|
||||
import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService';
|
||||
import { NotebookModel } from 'sql/workbench/contrib/notebook/browser/models/notebookModel';
|
||||
import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { CellType } from 'sql/workbench/services/notebook/common/contracts';
|
||||
import { getErrorMessage } from 'vs/base/common/errors';
|
||||
import { IEditorAction } from 'vs/editor/common/editorCommon';
|
||||
import { IFindNotebookController } from 'sql/workbench/contrib/notebook/find/notebookFindWidget';
|
||||
import { INotebookModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { INotebookModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService';
|
||||
import { TreeUpdateUtils } from 'sql/workbench/contrib/objectExplorer/browser/treeUpdateUtils';
|
||||
import { TreeUpdateUtils } from 'sql/workbench/services/objectExplorer/browser/treeUpdateUtils';
|
||||
import { find, firstIndex } from 'vs/base/common/arrays';
|
||||
import { INotebookEditor } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { NotebookInput } from 'sql/workbench/contrib/notebook/browser/models/notebookInput';
|
||||
import { NotebookModule } from 'sql/workbench/contrib/notebook/browser/notebook.module';
|
||||
import { NOTEBOOK_SELECTOR } from 'sql/workbench/contrib/notebook/browser/notebook.component';
|
||||
import { INotebookParams, INotebookService } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { INotebookParams, INotebookService, NotebookRange } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { ACTION_IDS, NOTEBOOK_MAX_MATCHES, IFindNotebookController, FindWidget, IConfigurationChangedEvent } from 'sql/workbench/contrib/notebook/find/notebookFindWidget';
|
||||
import { IOverlayWidget } from 'vs/editor/browser/editorBrowser';
|
||||
@@ -26,10 +26,11 @@ import { NotebookFindNextAction, NotebookFindPreviousAction } from 'sql/workbenc
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { INotebookModel, INotebookFindModel } from 'sql/workbench/contrib/notebook/browser/models/modelInterfaces';
|
||||
import { INotebookModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces';
|
||||
import { INotebookFindModel } from 'sql/workbench/contrib/notebook/browser/models/notebookFindModel';
|
||||
import { IDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IModelDecorationsChangeAccessor, IModelDeltaDecoration } from 'vs/editor/common/model';
|
||||
import { NotebookFindDecorations, NotebookRange } from 'sql/workbench/contrib/notebook/find/notebookFindDecorations';
|
||||
import { NotebookFindDecorations } from 'sql/workbench/contrib/notebook/find/notebookFindDecorations';
|
||||
import { TimeoutTimer } from 'vs/base/common/async';
|
||||
import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { OVERRIDE_EDITOR_THEMING_SETTING } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { registerNotebookThemes } from 'sql/workbench/contrib/notebook/browser/notebookStyles';
|
||||
|
||||
export class NotebookThemingContribution extends Disposable implements IWorkbenchContribution {
|
||||
private themeParticipant?: IDisposable;
|
||||
private overrideEditorThemeSetting?: boolean;
|
||||
|
||||
constructor(@IConfigurationService private readonly configurationService: IConfigurationService) {
|
||||
super();
|
||||
this.hookNotebookThemesAndConfigListener();
|
||||
}
|
||||
|
||||
private hookNotebookThemesAndConfigListener(): void {
|
||||
if (this.configurationService) {
|
||||
this.updateNotebookThemes();
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration(OVERRIDE_EDITOR_THEMING_SETTING)
|
||||
|| e.affectsConfiguration('resultsGrid')) {
|
||||
this.updateNotebookThemes();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private updateNotebookThemes() {
|
||||
let overrideEditorSetting = this.configurationService.getValue<boolean>(OVERRIDE_EDITOR_THEMING_SETTING);
|
||||
if (overrideEditorSetting !== this.overrideEditorThemeSetting) {
|
||||
// Re-add the participant since this will trigger update of theming rules, can't just
|
||||
// update something and ask to change
|
||||
if (this.themeParticipant) {
|
||||
this.themeParticipant.dispose();
|
||||
}
|
||||
this.overrideEditorThemeSetting = overrideEditorSetting;
|
||||
this.themeParticipant = registerNotebookThemes(overrideEditorSetting, this.configurationService);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/*-----------------------------------------------------------------------------
|
||||
| Copyright (c) Jupyter Development Team.
|
||||
| Distributed under the terms of the Modified BSD License.
|
||||
|----------------------------------------------------------------------------*/
|
||||
|
||||
import * as widgets from './widgets';
|
||||
import { IRenderMime } from '../models/renderMimeInterfaces';
|
||||
|
||||
/**
|
||||
* A mime renderer factory for raw html.
|
||||
*/
|
||||
export const htmlRendererFactory: IRenderMime.IRendererFactory = {
|
||||
safe: true,
|
||||
mimeTypes: ['text/html'],
|
||||
defaultRank: 50,
|
||||
createRenderer: options => new widgets.RenderedHTML(options)
|
||||
};
|
||||
|
||||
/**
|
||||
* A mime renderer factory for images.
|
||||
*/
|
||||
export const imageRendererFactory: IRenderMime.IRendererFactory = {
|
||||
safe: true,
|
||||
mimeTypes: ['image/bmp', 'image/png', 'image/jpeg', 'image/gif'],
|
||||
defaultRank: 90,
|
||||
createRenderer: options => new widgets.RenderedImage(options)
|
||||
};
|
||||
|
||||
// /**
|
||||
// * A mime renderer factory for LaTeX.
|
||||
// */
|
||||
// export const latexRendererFactory: IRenderMime.IRendererFactory = {
|
||||
// safe: true,
|
||||
// mimeTypes: ['text/latex'],
|
||||
// defaultRank: 70,
|
||||
// createRenderer: options => new widgets.RenderedLatex(options)
|
||||
// };
|
||||
|
||||
/**
|
||||
* A mime renderer factory for svg.
|
||||
*/
|
||||
export const svgRendererFactory: IRenderMime.IRendererFactory = {
|
||||
safe: false,
|
||||
mimeTypes: ['image/svg+xml'],
|
||||
defaultRank: 80,
|
||||
createRenderer: options => new widgets.RenderedSVG(options)
|
||||
};
|
||||
|
||||
/**
|
||||
* A mime renderer factory for plain and jupyter console text data.
|
||||
*/
|
||||
export const textRendererFactory: IRenderMime.IRendererFactory = {
|
||||
safe: true,
|
||||
mimeTypes: [
|
||||
'text/plain',
|
||||
'application/vnd.jupyter.stdout',
|
||||
'application/vnd.jupyter.stderr'
|
||||
],
|
||||
defaultRank: 120,
|
||||
createRenderer: options => new widgets.RenderedText(options)
|
||||
};
|
||||
|
||||
/**
|
||||
* A placeholder factory for deprecated rendered JavaScript.
|
||||
*/
|
||||
export const javaScriptRendererFactory: IRenderMime.IRendererFactory = {
|
||||
safe: false,
|
||||
mimeTypes: ['text/javascript', 'application/javascript'],
|
||||
defaultRank: 110,
|
||||
createRenderer: options => new widgets.RenderedJavaScript(options)
|
||||
};
|
||||
|
||||
export const dataResourceRendererFactory: IRenderMime.IRendererFactory = {
|
||||
safe: true,
|
||||
mimeTypes: [
|
||||
'application/vnd.dataresource+json',
|
||||
'application/vnd.dataresource'
|
||||
],
|
||||
defaultRank: 40,
|
||||
createRenderer: options => new widgets.RenderedDataResource(options)
|
||||
};
|
||||
|
||||
export const ipywidgetFactory: IRenderMime.IRendererFactory = {
|
||||
safe: false,
|
||||
mimeTypes: [
|
||||
'application/vnd.jupyter.widget-view',
|
||||
'application/vnd.jupyter.widget-view+json'
|
||||
],
|
||||
defaultRank: 45,
|
||||
createRenderer: options => new widgets.RenderedIPyWidget(options)
|
||||
};
|
||||
|
||||
/**
|
||||
* The standard factories provided by the rendermime package.
|
||||
*/
|
||||
export const standardRendererFactories: ReadonlyArray<IRenderMime.IRendererFactory> = [
|
||||
htmlRendererFactory,
|
||||
// latexRendererFactory,
|
||||
svgRendererFactory,
|
||||
imageRendererFactory,
|
||||
javaScriptRendererFactory,
|
||||
textRendererFactory,
|
||||
dataResourceRendererFactory,
|
||||
ipywidgetFactory
|
||||
];
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user