Fix #4047 Redesign notebook model to handle single client session (#4371)

* Start single client session based on the default kernel or saved kernel in NB.

* Added kernel displayName to standardKernel.
Modified name to allign wtih Juptyer Kernel.name.
So we can show the displayName during startup and use the name to start the session.

* Change session.OnSessionReady event in KernelDropDown

* Added model.KernelChnaged for switching kernel in the same provider

* Fixed session.Ready sequence

* Fixed merge issues

* Solve merged issue

* Fixed wrong kernel name in saved NB

* Added new event in Model to notify kernel change.
Toolbar depends on ModelReady to load

* Change attachTo to wait for ModelReady like KenelDropDown

* sanitizeSavedKernelInfo to fix invalid kernel and display_name. For example: PySpark1111 and PySpark 1111


* Added _contextsChangingEmitter to change loadContext msg when changing kernel

* Resolve PR comments
This commit is contained in:
Yurong He
2019-03-11 17:59:13 -07:00
committed by GitHub
parent b44d2b1bb3
commit 118d2c7273
20 changed files with 425 additions and 313 deletions

View File

@@ -251,36 +251,41 @@
], ],
"standardKernels": [ "standardKernels": [
{ {
"name": "Python 3", "name": "pyspark3kernel",
"displayName": "PySpark3",
"connectionProviderIds": [
"HADOOP_KNOX",
"MSSQL"
]
},
{
"name": "pysparkkernel",
"displayName": "PySpark",
"connectionProviderIds": [
"HADOOP_KNOX",
"MSSQL"
]
},
{
"name": "sparkkernel",
"displayName": "Spark | Scala",
"connectionProviderIds": [
"HADOOP_KNOX",
"MSSQL"
]
},
{
"name": "sparkrkernel",
"displayName": "Spark | R",
"connectionProviderIds": [
"HADOOP_KNOX",
"MSSQL"
]
},
{
"name": "python3",
"displayName": "Python 3",
"connectionProviderIds": [] "connectionProviderIds": []
},
{
"name": "PySpark",
"connectionProviderIds": [
"HADOOP_KNOX",
"MSSQL"
]
},
{
"name": "PySpark3",
"connectionProviderIds": [
"HADOOP_KNOX",
"MSSQL"
]
},
{
"name": "Spark | R",
"connectionProviderIds": [
"HADOOP_KNOX",
"MSSQL"
]
},
{
"name": "Spark | Scala",
"connectionProviderIds": [
"HADOOP_KNOX",
"MSSQL"
]
} }
] ]
} }

View File

@@ -55,28 +55,7 @@ export class JupyterNotebookProvider implements nb.NotebookProvider {
} }
public get standardKernels(): nb.IStandardKernel[] { public get standardKernels(): nb.IStandardKernel[] {
return [ return [];
{
"name": "Python 3",
"connectionProviderIds": []
},
{
"name": "PySpark",
"connectionProviderIds": ["HADOOP_KNOX"]
},
{
"name": "PySpark3",
"connectionProviderIds": ["HADOOP_KNOX"]
},
{
"name": "Spark | R",
"connectionProviderIds": ["HADOOP_KNOX"]
},
{
"name": "Spark | Scala",
"connectionProviderIds": ["HADOOP_KNOX"]
}
];
} }
} }

View File

@@ -4098,6 +4098,7 @@ declare module 'azdata' {
export interface IStandardKernel { export interface IStandardKernel {
readonly name: string; readonly name: string;
readonly displayName: string;
readonly connectionProviderIds: string[]; readonly connectionProviderIds: string[];
} }
@@ -4206,7 +4207,7 @@ declare module 'azdata' {
export interface ILanguageInfo { export interface ILanguageInfo {
name: string; name: string;
version: string; version?: string;
mimetype?: string; mimetype?: string;
codemirror_mode?: string | ICodeMirrorMode; codemirror_mode?: string | ICodeMirrorMode;
} }

View File

@@ -44,6 +44,7 @@ export class ClientSession implements IClientSession {
private _errorMessage: string; private _errorMessage: string;
private _cachedKernelSpec: nb.IKernelSpec; private _cachedKernelSpec: nb.IKernelSpec;
private _kernelChangeHandlers: KernelChangeHandler[] = []; private _kernelChangeHandlers: KernelChangeHandler[] = [];
private _defaultKernel: nb.IKernelSpec;
//#endregion //#endregion
@@ -59,6 +60,7 @@ export class ClientSession implements IClientSession {
this._isReady = false; this._isReady = false;
this._ready = new Deferred<void>(); this._ready = new Deferred<void>();
this._kernelChangeCompleted = new Deferred<void>(); this._kernelChangeCompleted = new Deferred<void>();
this._defaultKernel = options.kernelSpec;
} }
public async initialize(): Promise<void> { public async initialize(): Promise<void> {
@@ -95,8 +97,8 @@ export class ClientSession implements IClientSession {
if (!this.notebookManager.sessionManager.isReady) { if (!this.notebookManager.sessionManager.isReady) {
await this.notebookManager.sessionManager.ready; await this.notebookManager.sessionManager.ready;
} }
if (this._kernelPreference && this._kernelPreference.shouldStart) { if (this._defaultKernel) {
await this.startSessionInstance(this._kernelPreference.name); await this.startSessionInstance(this._defaultKernel.name);
} }
} }
} }

View File

@@ -22,11 +22,13 @@ import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHos
import { IStandardKernelWithProvider } from 'sql/parts/notebook/notebookUtils'; import { IStandardKernelWithProvider } from 'sql/parts/notebook/notebookUtils';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { localize } from 'vs/nls';
export interface IClientSessionOptions { export interface IClientSessionOptions {
notebookUri: URI; notebookUri: URI;
notebookManager: INotebookManager; notebookManager: INotebookManager;
notificationService: INotificationService; notificationService: INotificationService;
kernelSpec: nb.IKernelSpec;
} }
/** /**
@@ -309,6 +311,11 @@ export interface INotebookModel {
*/ */
readonly contextsChanged: Event<void>; 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 * The specs for available kernels, or undefined if these have
* not been loaded yet * not been loaded yet
@@ -389,6 +396,12 @@ export interface INotebookModel {
getApplicableConnectionProviderIds(kernelName: string): string[]; getApplicableConnectionProviderIds(kernelName: string): string[];
/**
* 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 */ /** Event fired once we get call back from ConfigureConnection method in sqlops extension */
readonly onValidConnectionSelected: Event<boolean>; readonly onValidConnectionSelected: Event<boolean>;
} }
@@ -510,4 +523,11 @@ export interface ICellMagicMapper {
export namespace notebookConstants { export namespace notebookConstants {
export const SQL = 'SQL'; export const SQL = 'SQL';
export const SQL_CONNECTION_PROVIDER = 'MSSQL';
export const sqlKernel: string = localize('sqlKernel', 'SQL');
export const sqlKernelSpec: nb.IKernelSpec = ({
name: sqlKernel,
language: 'sql',
display_name: sqlKernel
});
} }

View File

@@ -123,15 +123,14 @@ export class NotebookContexts {
/** /**
* *
* @param specs kernel specs (comes from session manager) * @param specs kernel specs (comes from session manager)
* @param connectionInfo connection profile * @param displayName kernel info loaded from
* @param savedKernelInfo kernel info loaded from
*/ */
public static getDefaultKernel(specs: nb.IAllKernels, connectionInfo: IConnectionProfile, savedKernelInfo: nb.IKernelInfo): nb.IKernelSpec { public static getDefaultKernel(specs: nb.IAllKernels, displayName: string): nb.IKernelSpec {
let defaultKernel: nb.IKernelSpec; let defaultKernel: nb.IKernelSpec;
if (specs) { if (specs) {
// find the saved kernel (if it exists) // find the saved kernel (if it exists)
if (savedKernelInfo) { if (displayName) {
defaultKernel = specs.kernels.find((kernel) => kernel.name === savedKernelInfo.name); defaultKernel = specs.kernels.find((kernel) => kernel.display_name === displayName);
} }
// if no saved kernel exists, use the default KernelSpec // if no saved kernel exists, use the default KernelSpec
if (!defaultKernel) { if (!defaultKernel) {
@@ -144,10 +143,7 @@ export class NotebookContexts {
// If no default kernel specified (should never happen), default to SQL // If no default kernel specified (should never happen), default to SQL
if (!defaultKernel) { if (!defaultKernel) {
defaultKernel = { defaultKernel = notebookConstants.sqlKernelSpec;
name: notebookConstants.SQL,
display_name: notebookConstants.SQL
};
} }
return defaultKernel; return defaultKernel;
} }

View File

@@ -9,10 +9,10 @@ import { nb, connection } from 'azdata';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { Event, Emitter } from 'vs/base/common/event'; import { Event, Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { CellModel } from './cell'; import { CellModel } from './cell';
import { IClientSession, INotebookModel, IDefaultConnection, INotebookModelOptions, ICellModel, NotebookContentChange } from './modelInterfaces'; import { IClientSession, INotebookModel, IDefaultConnection, INotebookModelOptions, ICellModel, NotebookContentChange, notebookConstants } from './modelInterfaces';
import { NotebookChangeType, CellType } from 'sql/parts/notebook/models/contracts'; import { NotebookChangeType, CellType } from 'sql/parts/notebook/models/contracts';
import { nbversion } from '../notebookConstants'; import { nbversion } from '../notebookConstants';
import * as notebookUtils from '../notebookUtils'; import * as notebookUtils from '../notebookUtils';
@@ -41,13 +41,13 @@ export class ErrorInfo {
export class NotebookModel extends Disposable implements INotebookModel { export class NotebookModel extends Disposable implements INotebookModel {
private _contextsChangedEmitter = new Emitter<void>(); private _contextsChangedEmitter = new Emitter<void>();
private _contextsLoadingEmitter = new Emitter<void>();
private _contentChangedEmitter = new Emitter<NotebookContentChange>(); private _contentChangedEmitter = new Emitter<NotebookContentChange>();
private _kernelsChangedEmitter = new Emitter<nb.IKernelSpec>(); private _kernelsChangedEmitter = new Emitter<nb.IKernelSpec>();
private _kernelChangedEmitter = new Emitter<nb.IKernelChangedArgs>();
private _layoutChanged = new Emitter<void>(); private _layoutChanged = new Emitter<void>();
private _inErrorState: boolean = false; private _inErrorState: boolean = false;
private _clientSessions: IClientSession[] = [];
private _activeClientSession: IClientSession; private _activeClientSession: IClientSession;
private _oldClientSession: IClientSession;
private _sessionLoadFinished: Promise<void>; private _sessionLoadFinished: Promise<void>;
private _onClientSessionReady = new Emitter<IClientSession>(); private _onClientSessionReady = new Emitter<IClientSession>();
private _onProviderIdChanged = new Emitter<string>(); private _onProviderIdChanged = new Emitter<string>();
@@ -69,21 +69,24 @@ export class NotebookModel extends Disposable implements INotebookModel {
private _kernelDisplayNameToConnectionProviderIds: Map<string, string[]> = new Map<string, string[]>(); private _kernelDisplayNameToConnectionProviderIds: Map<string, string[]> = new Map<string, string[]>();
private _kernelDisplayNameToNotebookProviderIds: Map<string, string> = new Map<string, string>(); private _kernelDisplayNameToNotebookProviderIds: Map<string, string> = new Map<string, string>();
private _onValidConnectionSelected = new Emitter<boolean>(); private _onValidConnectionSelected = new Emitter<boolean>();
private _oldKernel: nb.IKernel;
private _clientSessionListeners: IDisposable[] = [];
constructor(private _notebookOptions: INotebookModelOptions, startSessionImmediately?: boolean, private connectionProfile?: IConnectionProfile) { constructor(private _notebookOptions: INotebookModelOptions, startSessionImmediately?: boolean, private connectionProfile?: IConnectionProfile) {
super(); super();
if (!_notebookOptions || !_notebookOptions.notebookUri || !_notebookOptions.notebookManagers) { if (!_notebookOptions || !_notebookOptions.notebookUri || !_notebookOptions.notebookManagers) {
throw new Error('path or notebook service not defined'); throw new Error('path or notebook service not defined');
} }
if (startSessionImmediately) {
this.backgroundStartSession();
}
this._trustedMode = false; this._trustedMode = false;
this._providerId = _notebookOptions.providerId; this._providerId = _notebookOptions.providerId;
this._onProviderIdChanged.fire(this._providerId); this._onProviderIdChanged.fire(this._providerId);
this._notebookOptions.standardKernels.forEach(kernel => { this._notebookOptions.standardKernels.forEach(kernel => {
this._kernelDisplayNameToConnectionProviderIds.set(kernel.name, kernel.connectionProviderIds); let displayName = kernel.displayName;
this._kernelDisplayNameToNotebookProviderIds.set(kernel.name, kernel.notebookProvider); if (!displayName) {
displayName = kernel.name;
}
this._kernelDisplayNameToConnectionProviderIds.set(displayName, kernel.connectionProviderIds);
this._kernelDisplayNameToNotebookProviderIds.set(displayName, kernel.notebookProvider);
}); });
if (this._notebookOptions.layoutChanged) { if (this._notebookOptions.layoutChanged) {
this._notebookOptions.layoutChanged(() => this._layoutChanged.fire()); this._notebookOptions.layoutChanged(() => this._layoutChanged.fire());
@@ -109,6 +112,13 @@ export class NotebookModel extends Disposable implements INotebookModel {
return manager; return manager;
} }
public getNotebookManager(providerId: string): INotebookManager {
if (providerId) {
return this.notebookManagers.find(manager => manager.providerId === providerId);
}
return undefined;
}
public get notebookOptions(): INotebookModelOptions { public get notebookOptions(): INotebookModelOptions {
return this._notebookOptions; return this._notebookOptions;
} }
@@ -144,7 +154,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
} }
public get kernelChanged(): Event<nb.IKernelChangedArgs> { public get kernelChanged(): Event<nb.IKernelChangedArgs> {
return this._activeClientSession.kernelChanged; return this._kernelChangedEmitter.event;
} }
public get kernelsChanged(): Event<nb.IKernelSpec> { public get kernelsChanged(): Event<nb.IKernelSpec> {
@@ -163,6 +173,10 @@ export class NotebookModel extends Disposable implements INotebookModel {
return this._contextsChangedEmitter.event; return this._contextsChangedEmitter.event;
} }
public get contextsLoading(): Event<void> {
return this._contextsLoadingEmitter.event;
}
public get cells(): ICellModel[] { public get cells(): ICellModel[] {
return this._cells; return this._cells;
} }
@@ -189,6 +203,10 @@ export class NotebookModel extends Disposable implements INotebookModel {
return specs; return specs;
} }
public standardKernelsDisplayName(): string[] {
return Array.from(this._kernelDisplayNameToNotebookProviderIds.keys());
}
public get inErrorState(): boolean { public get inErrorState(): boolean {
return this._inErrorState; return this._inErrorState;
} }
@@ -257,25 +275,17 @@ export class NotebookModel extends Disposable implements INotebookModel {
if (this._notebookOptions && this._notebookOptions.contentManager) { if (this._notebookOptions && this._notebookOptions.contentManager) {
contents = await this._notebookOptions.contentManager.loadContent(); contents = await this._notebookOptions.contentManager.loadContent();
} }
let factory = this._notebookOptions.factory; let factory = this._notebookOptions.factory;
// if cells already exist, create them with language info (if it is saved) // if cells already exist, create them with language info (if it is saved)
this._cells = []; this._cells = [];
this._defaultLanguageInfo = {
name: this._providerId === SQL_NOTEBOOK_PROVIDER ? 'sql' : 'python',
version: ''
};
if (contents) { if (contents) {
this._defaultLanguageInfo = this.getDefaultLanguageInfo(contents); this._defaultLanguageInfo = this.getDefaultLanguageInfo(contents);
this._savedKernelInfo = this.getSavedKernelInfo(contents); this._savedKernelInfo = this.getSavedKernelInfo(contents);
this.setProviderIdForKernel(this._savedKernelInfo);
if (this._savedKernelInfo) {
this._defaultKernel = this._savedKernelInfo;
}
if (contents.cells && contents.cells.length > 0) { if (contents.cells && contents.cells.length > 0) {
this._cells = contents.cells.map(c => factory.createCell(c, { notebook: this, isTrusted: isTrusted })); this._cells = contents.cells.map(c => factory.createCell(c, { notebook: this, isTrusted: isTrusted }));
} }
} }
this.setDefaultKernelAndProviderId();
this.trySetLanguageFromLangInfo(); this.trySetLanguageFromLangInfo();
} catch (error) { } catch (error) {
this._inErrorState = true; this._inErrorState = true;
@@ -380,17 +390,21 @@ export class NotebookModel extends Disposable implements INotebookModel {
this._onErrorEmitter.fire({ message: error, severity: Severity.Error }); this._onErrorEmitter.fire({ message: error, severity: Severity.Error });
} }
public backgroundStartSession(): void { public async startSession(manager: INotebookManager, displayName?: string): Promise<void> {
// TODO: only one session should be active at a time, depending on the current provider if (displayName) {
this.notebookManagers.forEach(manager => { let standardKernel = this._notebookOptions.standardKernels.find(kernel => kernel.displayName === displayName);
this._defaultKernel = displayName ? { name: standardKernel.name, display_name: standardKernel.displayName } : this._defaultKernel;
}
if (this._defaultKernel) {
let clientSession = this._notebookOptions.factory.createClientSession({ let clientSession = this._notebookOptions.factory.createClientSession({
notebookUri: this._notebookOptions.notebookUri, notebookUri: this._notebookOptions.notebookUri,
notebookManager: manager, notebookManager: manager,
notificationService: this._notebookOptions.notificationService notificationService: this._notebookOptions.notificationService,
kernelSpec: this._defaultKernel
}); });
this._clientSessions.push(clientSession);
if (!this._activeClientSession) { if (!this._activeClientSession) {
this._activeClientSession = clientSession; this.updateActiveClientSession(clientSession);
} }
let profile = new ConnectionProfile(this._notebookOptions.capabilitiesService, this.connectionProfile); let profile = new ConnectionProfile(this._notebookOptions.capabilitiesService, this.connectionProfile);
@@ -400,25 +414,84 @@ export class NotebookModel extends Disposable implements INotebookModel {
this._activeConnection = undefined; this._activeConnection = undefined;
} }
clientSession.initialize(); await clientSession.initialize();
this._sessionLoadFinished = clientSession.ready.then(async () => { // By somehow we have to wait for ready, otherwise may not be called for some cases.
await clientSession.ready;
if (clientSession.isInErrorState) { if (clientSession.isInErrorState) {
this.setErrorState(clientSession.errorMessage); this.setErrorState(clientSession.errorMessage);
} else { } else {
this._onClientSessionReady.fire(clientSession); this._onClientSessionReady.fire(clientSession);
// Once session is loaded, can use the session manager to retrieve useful info // Once session is loaded, can use the session manager to retrieve useful info
this.loadKernelInfo(clientSession); this.loadKernelInfo(clientSession, this.defaultKernel.display_name);
} }
}); }
}); }
// When changing kernel, update the active session and register the kernel change event
// So KernelDropDown could get the event fired when added listerner on Model.KernelChange
private updateActiveClientSession(clientSession: IClientSession) {
this.clearClientSessionListeners();
this._activeClientSession = clientSession;
this._clientSessionListeners.push(this._activeClientSession.kernelChanged(e => this._kernelChangedEmitter.fire(e)));
}
private clearClientSessionListeners() {
this._clientSessionListeners.forEach(listener => listener.dispose());
this._clientSessionListeners = [];
}
public setDefaultKernelAndProviderId() {
if (this._savedKernelInfo) {
this.sanitizeSavedKernelInfo();
let provider = this._kernelDisplayNameToNotebookProviderIds.get(this._savedKernelInfo.display_name);
if (provider && provider !== this._providerId) {
this._providerId = provider;
}
this._defaultKernel = this._savedKernelInfo;
} else if (this._defaultKernel) {
let providerId = this._kernelDisplayNameToNotebookProviderIds.get(this._defaultKernel.display_name);
if (providerId) {
if (this._providerId !== providerId) {
this._providerId = providerId;
}
} else {
this._defaultKernel = notebookConstants.sqlKernelSpec;
this._providerId = SQL_NOTEBOOK_PROVIDER;
}
} else {
this._defaultKernel = notebookConstants.sqlKernelSpec;
this._providerId = SQL_NOTEBOOK_PROVIDER;
}
// update default language
this._defaultLanguageInfo = {
name: this._providerId === SQL_NOTEBOOK_PROVIDER ? 'sql' : 'python',
version: ''
};
} }
private isValidConnection(profile: IConnectionProfile | connection.Connection) { private isValidConnection(profile: IConnectionProfile | connection.Connection) {
let standardKernels = this._notebookOptions.standardKernels.find(kernel => this._savedKernelInfo && kernel.name === this._savedKernelInfo.display_name); let standardKernels = this._notebookOptions.standardKernels.find(kernel => this._savedKernelInfo && kernel.displayName === this._savedKernelInfo.display_name);
let connectionProviderIds = standardKernels ? standardKernels.connectionProviderIds : undefined; let connectionProviderIds = standardKernels ? standardKernels.connectionProviderIds : undefined;
return profile && connectionProviderIds && connectionProviderIds.find(provider => provider === profile.providerName) !== undefined; return profile && connectionProviderIds && connectionProviderIds.find(provider => provider === profile.providerName) !== undefined;
} }
public getStandardKernelFromName(name: string): notebookUtils.IStandardKernelWithProvider {
if (name) {
let kernel = this._notebookOptions.standardKernels.find(kernel => kernel.name.toLowerCase() === name.toLowerCase());
return kernel;
}
return undefined;
}
public getStandardKernelFromDisplayName(displayName: string): notebookUtils.IStandardKernelWithProvider {
if (displayName) {
let kernel = this._notebookOptions.standardKernels.find(kernel => kernel.displayName.toLowerCase() === displayName.toLowerCase());
return kernel;
}
return undefined;
}
public get languageInfo(): nb.ILanguageInfo { public get languageInfo(): nb.ILanguageInfo {
return this._defaultLanguageInfo; return this._defaultLanguageInfo;
} }
@@ -469,20 +542,26 @@ export class NotebookModel extends Disposable implements INotebookModel {
} }
public changeKernel(displayName: string): void { public changeKernel(displayName: string): void {
let spec = this.getKernelSpecFromDisplayName(displayName); this._contextsLoadingEmitter.fire();
this.doChangeKernel(spec); this.doChangeKernel(displayName, true);
} }
public doChangeKernel(kernelSpec: nb.IKernelSpec): Promise<void> { public async doChangeKernel(displayName: string, mustSetProvider: boolean = true): Promise<void> {
this.setProviderIdForKernel(kernelSpec); if (mustSetProvider) {
await this.setProviderIdAndStartSession(displayName);
}
let spec = this.getKernelSpecFromDisplayName(displayName);
if (spec) {
// Ensure that the kernel we try to switch to is a valid kernel; if not, use the default // Ensure that the kernel we try to switch to is a valid kernel; if not, use the default
let kernelSpecs = this.getKernelSpecs(); let kernelSpecs = this.getKernelSpecs();
if (kernelSpecs && kernelSpecs.length > 0 && kernelSpecs.findIndex(k => k.name === kernelSpec.name) < 0) { if (kernelSpecs && kernelSpecs.length > 0 && kernelSpecs.findIndex(k => k.display_name === spec.display_name) < 0) {
kernelSpec = kernelSpecs.find(spec => spec.name === this.notebookManager.sessionManager.specs.defaultKernel); spec = kernelSpecs.find(spec => spec.name === this.notebookManager.sessionManager.specs.defaultKernel);
}
} else {
spec = notebookConstants.sqlKernelSpec;
} }
if (this._activeClientSession && this._activeClientSession.isReady) { if (this._activeClientSession && this._activeClientSession.isReady) {
let oldKernel = this._oldClientSession ? this._oldClientSession.kernel : null; return this._activeClientSession.changeKernel(spec, this._oldKernel)
return this._activeClientSession.changeKernel(kernelSpec, oldKernel)
.then((kernel) => { .then((kernel) => {
this.updateKernelInfo(kernel); this.updateKernelInfo(kernel);
kernel.ready.then(() => { kernel.ready.then(() => {
@@ -546,32 +625,15 @@ export class NotebookModel extends Disposable implements INotebookModel {
} }
} }
private loadKernelInfo(clientSession: IClientSession): void { private loadKernelInfo(clientSession: IClientSession, displayName: string): void {
clientSession.onKernelChanging(async (e) => { clientSession.onKernelChanging(async (e) => {
await this.loadActiveContexts(e); await this.loadActiveContexts(e);
}); });
clientSession.statusChanged(async (session) => { clientSession.statusChanged(async (session) => {
this._kernelsChangedEmitter.fire(session.kernel); this._kernelsChangedEmitter.fire(session.kernel);
}); });
if (!this.notebookManager) {
return; this.doChangeKernel(displayName, false);
}
try {
let sessionManager = this.notebookManager.sessionManager;
if (sessionManager) {
if (!this._defaultKernel) {
this._defaultKernel = NotebookContexts.getDefaultKernel(sessionManager.specs, this.connectionProfile, this._savedKernelInfo);
}
let spec = this.getKernelSpecFromDisplayName(this._defaultKernel.display_name);
if (spec) {
this._defaultKernel = spec;
}
this.doChangeKernel(this._defaultKernel);
}
} catch (err) {
let msg = notebookUtils.getErrorMessage(err);
this.notifyError(localize('loadKernelFailed', 'Loading kernel info failed: {0}', msg));
}
} }
// Get default language if saved in notebook file // Get default language if saved in notebook file
@@ -600,7 +662,23 @@ export class NotebookModel extends Disposable implements INotebookModel {
return kernel; return kernel;
} }
private getDisplayNameFromSpecName(kernel: nb.IKernel): string { private sanitizeSavedKernelInfo() {
if (this._savedKernelInfo) {
let displayName = this.sanitizeDisplayName(this._savedKernelInfo.display_name);
if (this._savedKernelInfo.display_name !== displayName) {
this._savedKernelInfo.display_name = displayName;
}
let standardKernel = this._notebookOptions.standardKernels.find(kernel => kernel.displayName === displayName || displayName.startsWith(kernel.displayName));
if (standardKernel && this._savedKernelInfo.name && this._savedKernelInfo.name !== standardKernel.name) {
this._savedKernelInfo.name = standardKernel.name;
this._savedKernelInfo.display_name = standardKernel.displayName;
}
}
}
public getDisplayNameFromSpecName(kernel: nb.IKernel): string {
let specs = this.notebookManager.sessionManager.specs; let specs = this.notebookManager.sessionManager.specs;
if (!specs || !specs.kernels) { if (!specs || !specs.kernels) {
return kernel.name; return kernel.name;
@@ -638,19 +716,23 @@ export class NotebookModel extends Disposable implements INotebookModel {
this._activeConnection = undefined; this._activeConnection = undefined;
} }
} }
await this.shutdownActiveSession();
} catch (err) {
this.notifyError(localize('shutdownError', 'An error occurred when closing the notebook: {0}', err));
}
}
private async shutdownActiveSession() {
if (this._activeClientSession) { if (this._activeClientSession) {
try { try {
await this._activeClientSession.ready; await this._activeClientSession.ready;
} catch (err) { }
catch (err) {
this.notifyError(localize('shutdownClientSessionError', 'A client session error occurred when closing the notebook: {0}', err)); this.notifyError(localize('shutdownClientSessionError', 'A client session error occurred when closing the notebook: {0}', err));
} }
await this._activeClientSession.shutdown(); await this._activeClientSession.shutdown();
this._clientSessions = undefined; this.clearClientSessionListeners();
this._activeClientSession = undefined; this._activeClientSession = undefined;
}
} catch (err) {
this.notifyError(localize('shutdownError', 'An error occurred when closing the notebook: {0}', err));
} }
} }
@@ -711,48 +793,52 @@ export class NotebookModel extends Disposable implements INotebookModel {
} }
/** /**
* Set _providerId and _activeClientSession based on a kernelSpec representing new kernel * Set _providerId and start session if it is new provider
* @param kernelSpec KernelSpec for new kernel * @param displayName Kernel dispay name
*/ */
private setProviderIdForKernel(kernelSpec: nb.IKernelSpec): void { private async setProviderIdAndStartSession(displayName: string): Promise<void> {
if (!kernelSpec) { if (displayName) {
// Just use the 1st non-default provider, we don't have a better heuristic if (this._activeClientSession && this._activeClientSession.isReady) {
let notebookManagers = this._notebookOptions.notebookManagers.filter(manager => manager.providerId !== DEFAULT_NOTEBOOK_PROVIDER); this._oldKernel = this._activeClientSession.kernel;
if (!notebookManagers.length) { let providerId = this.tryFindProviderForKernel(displayName);
notebookManagers = this._notebookOptions.notebookManagers;
if (providerId) {
if (providerId !== this._providerId) {
this._providerId = providerId;
this._onProviderIdChanged.fire(this._providerId);
await this.shutdownActiveSession();
try {
let manager = this.getNotebookManager(providerId);
if (manager) {
await this.startSession(manager, displayName);
} else {
throw new Error(localize('ProviderNoManager', "Can't find notebook manager for provider {0}", providerId));
} }
if (notebookManagers.length > 0) { }
this._providerId = notebookManagers[0].providerId; catch (err) {
console.log(err);
} }
} else { } else {
let sessionManagerFound: boolean = false; console.log(`No provider found supporting the kernel: ${displayName}`);
for (let i = 0; i < this.notebookManagers.length; i++) {
if (this.notebookManagers[i].sessionManager && this.notebookManagers[i].sessionManager.specs && this.notebookManagers[i].sessionManager.specs.kernels) {
let index = this.notebookManagers[i].sessionManager.specs.kernels.findIndex(kernel => kernel.name === kernelSpec.name);
if (index >= 0 && this._clientSessions && this._clientSessions.length > 0) {
if (this._activeClientSession) {
this._oldClientSession = this._activeClientSession;
} }
this._activeClientSession = this._clientSessions[i];
if (this.notebookManagers[i].providerId !== this._providerId) {
this._providerId = this.notebookManagers[i].providerId;
this._onProviderIdChanged.fire(this._providerId);
} }
sessionManagerFound = true;
break;
} }
} }
} }
// If no SessionManager exists, utilize passed in StandardKernels to see if we can intelligently set _providerId private tryFindProviderForKernel(displayName: string) {
if (!sessionManagerFound) { if (!displayName) {
let provider = this._kernelDisplayNameToNotebookProviderIds.get(kernelSpec.display_name); return undefined;
if (provider && provider !== this._providerId) { }
this._providerId = provider; let standardKernel = this.getStandardKernelFromDisplayName(displayName);
this._onProviderIdChanged.fire(this._providerId); if (standardKernel && this._oldKernel && this._oldKernel.name !== standardKernel.name) {
} if (this._kernelDisplayNameToNotebookProviderIds.has(displayName)) {
return this._kernelDisplayNameToNotebookProviderIds.get(displayName);
} }
} }
return undefined;
} }
// Get kernel specs from current sessionManager // Get kernel specs from current sessionManager

View File

@@ -220,6 +220,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
private async doLoad(): Promise<void> { private async doLoad(): Promise<void> {
try { try {
await this.setNotebookManager();
await this.loadModel(); await this.loadModel();
this.setLoading(false); this.setLoading(false);
this._modelReadyDeferred.resolve(this._model); this._modelReadyDeferred.resolve(this._model);
@@ -243,10 +244,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
let providerId = 'sql'; // this is tricky; really should also depend on the connection profile let providerId = 'sql'; // this is tricky; really should also depend on the connection profile
this.setContextKeyServiceWithProviderId(providerId); this.setContextKeyServiceWithProviderId(providerId);
this.fillInActionsForCurrentContext(); this.fillInActionsForCurrentContext();
for (let providerId of this._notebookParams.providers) {
let notebookManager = await this.notebookService.getOrCreateNotebookManager(providerId, this._notebookParams.notebookUri);
this.notebookManagers.push(notebookManager);
}
let model = new NotebookModel({ let model = new NotebookModel({
factory: this.modelFactory, factory: this.modelFactory,
notebookUri: this._notebookParams.notebookUri, notebookUri: this._notebookParams.notebookUri,
@@ -268,10 +266,17 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
this._model = this._register(model); this._model = this._register(model);
this.updateToolbarComponents(this._model.trustedMode); this.updateToolbarComponents(this._model.trustedMode);
this._modelRegisteredDeferred.resolve(this._model); this._modelRegisteredDeferred.resolve(this._model);
model.backgroundStartSession(); await model.startSession(this.model.notebookManager);
this.detectChanges(); this.detectChanges();
} }
private async setNotebookManager() {
for (let providerId of this._notebookParams.providers) {
let notebookManager = await this.notebookService.getOrCreateNotebookManager(providerId, this._notebookParams.notebookUri);
this.notebookManagers.push(notebookManager);
}
}
private async awaitNonDefaultProvider(): Promise<void> { private async awaitNonDefaultProvider(): Promise<void> {
// Wait on registration for now. Long-term would be good to cache and refresh // Wait on registration for now. Long-term would be good to cache and refresh
await this.notebookService.registrationComplete; await this.notebookService.registrationComplete;
@@ -348,12 +353,12 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
protected initActionBar() { protected initActionBar() {
let kernelContainer = document.createElement('div'); let kernelContainer = document.createElement('div');
let kernelDropdown = new KernelsDropdown(kernelContainer, this.contextViewService, this.modelRegistered); let kernelDropdown = new KernelsDropdown(kernelContainer, this.contextViewService, this.modelReady);
kernelDropdown.render(kernelContainer); kernelDropdown.render(kernelContainer);
attachSelectBoxStyler(kernelDropdown, this.themeService); attachSelectBoxStyler(kernelDropdown, this.themeService);
let attachToContainer = document.createElement('div'); let attachToContainer = document.createElement('div');
let attachToDropdown = new AttachToDropdown(attachToContainer, this.contextViewService, this.modelRegistered, let attachToDropdown = new AttachToDropdown(attachToContainer, this.contextViewService, this.modelReady,
this.connectionManagementService, this.connectionDialogService, this.notificationService, this.capabilitiesService); this.connectionManagementService, this.connectionDialogService, this.notificationService, this.capabilitiesService);
attachToDropdown.render(attachToContainer); attachToDropdown.render(attachToContainer);
attachSelectBoxStyler(attachToDropdown, this.themeService); attachSelectBoxStyler(attachToDropdown, this.themeService);

View File

@@ -12,7 +12,7 @@ import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview
import { INotificationService, Severity, INotificationActions } from 'vs/platform/notification/common/notification'; import { INotificationService, Severity, INotificationActions } from 'vs/platform/notification/common/notification';
import { SelectBox, ISelectBoxOptionsWithLabel } from 'sql/base/browser/ui/selectBox/selectBox'; import { SelectBox, ISelectBoxOptionsWithLabel } from 'sql/base/browser/ui/selectBox/selectBox';
import { INotebookModel, IDefaultConnection } from 'sql/parts/notebook/models/modelInterfaces'; import { INotebookModel } from 'sql/parts/notebook/models/modelInterfaces';
import { CellType } from 'sql/parts/notebook/models/contracts'; import { CellType } from 'sql/parts/notebook/models/contracts';
import { NotebookComponent } from 'sql/parts/notebook/notebook.component'; import { NotebookComponent } from 'sql/parts/notebook/notebook.component';
import { getErrorMessage, formatServerNameWithDatabaseNameForAttachTo, getServerFromFormattedAttachToName, getDatabaseFromFormattedAttachToName } from 'sql/parts/notebook/notebookUtils'; import { getErrorMessage, formatServerNameWithDatabaseNameForAttachTo, getServerFromFormattedAttachToName, getDatabaseFromFormattedAttachToName } from 'sql/parts/notebook/notebookUtils';
@@ -21,6 +21,7 @@ import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilit
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { noKernel } from 'sql/workbench/services/notebook/common/sessionManager'; import { noKernel } from 'sql/workbench/services/notebook/common/sessionManager';
import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService'; import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService';
import { NotebookModel } from 'sql/parts/notebook/models/notebookModel';
const msgLoading = localize('loading', 'Loading kernels...'); const msgLoading = localize('loading', 'Loading kernels...');
const kernelLabel: string = localize('Kernel', 'Kernel: '); const kernelLabel: string = localize('Kernel', 'Kernel: ');
@@ -233,16 +234,12 @@ export class TrustedAction extends ToggleableAction {
} }
export class KernelsDropdown extends SelectBox { export class KernelsDropdown extends SelectBox {
private model: INotebookModel; private model: NotebookModel;
constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, modelRegistered: Promise<INotebookModel> constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, modelReady: Promise<INotebookModel>) {
) { super([msgLoading], msgLoading, contextViewProvider, container, { labelText: kernelLabel, labelOnTop: false } as ISelectBoxOptionsWithLabel);
let selectBoxOptionsWithLabel: ISelectBoxOptionsWithLabel = {
labelText: kernelLabel, if (modelReady) {
labelOnTop: false modelReady
};
super([msgLoading], msgLoading, contextViewProvider, container, selectBoxOptionsWithLabel);
if (modelRegistered) {
modelRegistered
.then((model) => this.updateModel(model)) .then((model) => this.updateModel(model))
.catch((err) => { .catch((err) => {
// No-op for now // No-op for now
@@ -253,44 +250,42 @@ export class KernelsDropdown extends SelectBox {
} }
updateModel(model: INotebookModel): void { updateModel(model: INotebookModel): void {
this.model = model; this.model = model as NotebookModel;
this._register(model.kernelsChanged((defaultKernel) => { this._register(this.model.kernelChanged((changedArgs: azdata.nb.IKernelChangedArgs) => {
this.updateKernel(defaultKernel);
}));
if (model.clientSession) {
this._register(model.clientSession.kernelChanged((changedArgs: azdata.nb.IKernelChangedArgs) => {
if (changedArgs.newValue) {
this.updateKernel(changedArgs.newValue); this.updateKernel(changedArgs.newValue);
}
})); }));
} }
}
// Update SelectBox values // Update SelectBox values
private updateKernel(defaultKernel: azdata.nb.IKernelSpec) { public updateKernel(kernel: azdata.nb.IKernel) {
let specs = this.model.specs; if (kernel) {
if (specs && specs.kernels) { let standardKernel = this.model.getStandardKernelFromName(kernel.name);
let index = specs.kernels.findIndex((kernel => kernel.name === defaultKernel.name));
this.setOptions(specs.kernels.map(kernel => kernel.display_name), index); let kernels: string[] = this.model.standardKernelsDisplayName();
if (kernels && standardKernel) {
let index = kernels.findIndex((kernel => kernel === standardKernel.displayName));
this.setOptions(kernels, index);
}
} }
} }
public doChangeKernel(displayName: string): void { public doChangeKernel(displayName: string): void {
this.setOptions([msgLoading], 0);
this.model.changeKernel(displayName); this.model.changeKernel(displayName);
} }
} }
export class AttachToDropdown extends SelectBox { export class AttachToDropdown extends SelectBox {
private model: INotebookModel; private model: NotebookModel;
constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, modelRegistered: Promise<INotebookModel>, constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, modelReady: Promise<INotebookModel>,
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService, @IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IConnectionDialogService private _connectionDialogService: IConnectionDialogService, @IConnectionDialogService private _connectionDialogService: IConnectionDialogService,
@INotificationService private _notificationService: INotificationService, @INotificationService private _notificationService: INotificationService,
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService) { @ICapabilitiesService private _capabilitiesService: ICapabilitiesService) {
super([msgLoadingContexts], msgLoadingContexts, contextViewProvider, container, { labelText: attachToLabel, labelOnTop: false } as ISelectBoxOptionsWithLabel); super([msgLoadingContexts], msgLoadingContexts, contextViewProvider, container, { labelText: attachToLabel, labelOnTop: false } as ISelectBoxOptionsWithLabel);
if (modelRegistered) { if (modelReady) {
modelRegistered modelReady
.then(model => { .then(model => {
this.updateModel(model); this.updateModel(model);
this.updateAttachToDropdown(model); this.updateAttachToDropdown(model);
@@ -305,17 +300,19 @@ export class AttachToDropdown extends SelectBox {
} }
public updateModel(model: INotebookModel): void { public updateModel(model: INotebookModel): void {
this.model = model; this.model = model as NotebookModel;
this._register(model.contextsChanged(() => { this._register(model.contextsChanged(() => {
let kernelDisplayName: string = this.getKernelDisplayName(); let kernelDisplayName: string = this.getKernelDisplayName();
if (kernelDisplayName) { if (kernelDisplayName) {
this.loadAttachToDropdown(this.model, kernelDisplayName); this.loadAttachToDropdown(this.model, kernelDisplayName);
} }
})); }));
this._register(this.model.contextsLoading(() => {
this.setOptions([msgLoadingContexts], 0);
}));
} }
private updateAttachToDropdown(model: INotebookModel): void { private updateAttachToDropdown(model: INotebookModel): void {
this.model = model;
model.onValidConnectionSelected(validConnection => { model.onValidConnectionSelected(validConnection => {
let kernelDisplayName: string = this.getKernelDisplayName(); let kernelDisplayName: string = this.getKernelDisplayName();
if (kernelDisplayName) { if (kernelDisplayName) {

View File

@@ -222,6 +222,7 @@ export class NotebookInput extends EditorInput {
this._standardKernels.push({ this._standardKernels.push({
connectionProviderIds: kernel.connectionProviderIds, connectionProviderIds: kernel.connectionProviderIds,
name: kernel.name, name: kernel.name,
displayName: kernel.displayName,
notebookProvider: kernel.notebookProvider notebookProvider: kernel.notebookProvider
}); });
}); });

View File

@@ -12,7 +12,6 @@ import * as pfs from 'vs/base/node/pfs';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { IOutputChannel } from 'vs/workbench/parts/output/common/output'; import { IOutputChannel } from 'vs/workbench/parts/output/common/output';
import { DEFAULT_NOTEBOOK_PROVIDER, DEFAULT_NOTEBOOK_FILETYPE, INotebookService } from 'sql/workbench/services/notebook/common/notebookService'; import { DEFAULT_NOTEBOOK_PROVIDER, DEFAULT_NOTEBOOK_FILETYPE, INotebookService } from 'sql/workbench/services/notebook/common/notebookService';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
@@ -97,6 +96,7 @@ export function getDatabaseFromFormattedAttachToName(name: string): string {
export interface IStandardKernelWithProvider { export interface IStandardKernelWithProvider {
readonly name: string; readonly name: string;
readonly displayName: string;
readonly connectionProviderIds: string[]; readonly connectionProviderIds: string[];
readonly notebookProvider: string; readonly notebookProvider: string;
} }

View File

@@ -2050,6 +2050,7 @@ declare module 'sqlops' {
export interface IStandardKernel { export interface IStandardKernel {
readonly name: string; readonly name: string;
readonly displayName: string;
readonly connectionProviderIds: string[]; readonly connectionProviderIds: string[];
} }

View File

@@ -52,6 +52,9 @@ let notebookProviderType: IJSONSchema = {
name: { name: {
type: 'string', type: 'string',
}, },
displayName: {
type: 'string',
},
connectionProviderIds: { connectionProviderIds: {
type: 'array', type: 'array',
items: { items: {

View File

@@ -175,6 +175,7 @@ export class NotebookService extends Disposable implements INotebookService {
if (provider) { if (provider) {
this._providerToStandardKernels.set(notebookConstants.SQL, [{ this._providerToStandardKernels.set(notebookConstants.SQL, [{
name: notebookConstants.SQL, name: notebookConstants.SQL,
displayName: notebookConstants.SQL,
connectionProviderIds: sqlConnectionTypes connectionProviderIds: sqlConnectionTypes
}]); }]);
} }
@@ -451,7 +452,7 @@ export class NotebookService extends Disposable implements INotebookService {
notebookRegistry.registerNotebookProvider({ notebookRegistry.registerNotebookProvider({
provider: sqlProvider.providerId, provider: sqlProvider.providerId,
fileExtensions: DEFAULT_NOTEBOOK_FILETYPE, fileExtensions: DEFAULT_NOTEBOOK_FILETYPE,
standardKernels: { name: 'SQL', connectionProviderIds: ['MSSQL'] } standardKernels: { name: notebookConstants.SQL, displayName: notebookConstants.SQL, connectionProviderIds: [notebookConstants.SQL_CONNECTION_PROVIDER] }
}); });
} }

View File

@@ -6,13 +6,14 @@
'use strict'; 'use strict';
import { nb } from 'azdata'; import { nb } from 'azdata';
import * as vscode from 'vscode';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { INotebookManager, SQL_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/common/notebookService'; import { SQL_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/common/notebookService';
import { LocalContentManager } from 'sql/workbench/services/notebook/node/localContentManager'; import { LocalContentManager } from 'sql/workbench/services/notebook/node/localContentManager';
import { SqlSessionManager } from 'sql/workbench/services/notebook/sql/sqlSessionManager'; import { SqlSessionManager } from 'sql/workbench/services/notebook/sql/sqlSessionManager';
export class SqlNotebookManager implements INotebookManager { export class SqlNotebookManager implements nb.NotebookProvider {
private _contentManager: nb.ContentManager; private _contentManager: nb.ContentManager;
private _sessionManager: nb.SessionManager; private _sessionManager: nb.SessionManager;
@@ -36,4 +37,16 @@ export class SqlNotebookManager implements INotebookManager {
public get sessionManager(): nb.SessionManager { public get sessionManager(): nb.SessionManager {
return this._sessionManager; return this._sessionManager;
} }
getNotebookManager(notebookUri: vscode.Uri): Thenable<nb.NotebookManager> {
throw new Error('Method not implemented.');
}
handleNotebookClosed(notebookUri: vscode.Uri): void {
throw new Error('Method not implemented.');
}
public get standardKernels(): nb.IStandardKernel[] {
return [];
}
} }

View File

@@ -8,7 +8,7 @@ import * as os from 'os';
import { nb, QueryExecuteSubsetResult, IDbColumn, BatchSummary, IResultMessage, ResultSetSummary } from 'azdata'; import { nb, QueryExecuteSubsetResult, IDbColumn, BatchSummary, IResultMessage, ResultSetSummary } from 'azdata';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import * as strings from 'vs/base/common/strings'; import * as strings from 'vs/base/common/strings';
import { FutureInternal, ILanguageMagic } from 'sql/parts/notebook/models/modelInterfaces'; import { FutureInternal, ILanguageMagic, notebookConstants } from 'sql/parts/notebook/models/modelInterfaces';
import QueryRunner, { EventType } from 'sql/platform/query/common/queryRunner'; import QueryRunner, { EventType } from 'sql/platform/query/common/queryRunner';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -24,18 +24,11 @@ import { elapsedTimeLabel } from 'sql/parts/query/common/localizedConstants';
import * as notebookUtils from 'sql/parts/notebook/notebookUtils'; import * as notebookUtils from 'sql/parts/notebook/notebookUtils';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
export const sqlKernel: string = localize('sqlKernel', "SQL"); export const sqlKernelError: string = localize("sqlKernelError", "SQL kernel error");
export const sqlKernelError: string = localize('sqlKernelError', "SQL kernel error");
export const MAX_ROWS = 5000; export const MAX_ROWS = 5000;
export const NotebookConfigSectionName = 'notebook'; export const NotebookConfigSectionName = 'notebook';
export const MaxTableRowsConfigName = 'maxTableRows'; export const MaxTableRowsConfigName = 'maxTableRows';
const sqlKernelSpec: nb.IKernelSpec = ({
name: sqlKernel,
language: 'sql',
display_name: sqlKernel
});
const languageMagics: ILanguageMagic[] = [{ const languageMagics: ILanguageMagic[] = [{
language: 'Python', language: 'Python',
magic: 'lang_python' magic: 'lang_python'
@@ -65,8 +58,8 @@ export class SqlSessionManager implements nb.SessionManager {
public get specs(): nb.IAllKernels { public get specs(): nb.IAllKernels {
let allKernels: nb.IAllKernels = { let allKernels: nb.IAllKernels = {
defaultKernel: sqlKernel, defaultKernel: notebookConstants.sqlKernel,
kernels: [sqlKernelSpec] kernels: [notebookConstants.sqlKernelSpec]
}; };
return allKernels; return allKernels;
} }
@@ -176,7 +169,7 @@ class SqlKernel extends Disposable implements nb.IKernel {
} }
public get name(): string { public get name(): string {
return sqlKernel; return notebookConstants.sqlKernel;
} }
public get supportsIntellisense(): boolean { public get supportsIntellisense(): boolean {
@@ -217,7 +210,7 @@ class SqlKernel extends Disposable implements nb.IKernel {
} }
getSpec(): Thenable<nb.IKernelSpec> { getSpec(): Thenable<nb.IKernelSpec> {
return Promise.resolve(sqlKernelSpec); return Promise.resolve(notebookConstants.sqlKernelSpec);
} }
requestExecute(content: nb.IExecuteRequest, disposeOnDone?: boolean): nb.IFuture { requestExecute(content: nb.IExecuteRequest, disposeOnDone?: boolean): nb.IFuture {

View File

@@ -12,6 +12,7 @@ import { INotebookModel, ICellModel, IClientSession, IDefaultConnection, Noteboo
import { NotebookChangeType, CellType } from 'sql/parts/notebook/models/contracts'; import { NotebookChangeType, CellType } from 'sql/parts/notebook/models/contracts';
import { INotebookManager } from 'sql/workbench/services/notebook/common/notebookService'; import { INotebookManager } from 'sql/workbench/services/notebook/common/notebookService';
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes'; import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
import { IStandardKernelWithProvider } from 'sql/parts/notebook/notebookUtils';
export class NotebookModelStub implements INotebookModel { export class NotebookModelStub implements INotebookModel {
constructor(private _languageInfo?: nb.ILanguageInfo) { constructor(private _languageInfo?: nb.ILanguageInfo) {
@@ -52,6 +53,9 @@ export class NotebookModelStub implements INotebookModel {
get contextsChanged(): Event<void> { get contextsChanged(): Event<void> {
throw new Error('method not implemented.'); throw new Error('method not implemented.');
} }
get contextsLoading(): Event<void> {
throw new Error('method not implemented.');
}
get contentChanged(): Event<NotebookContentChange> { get contentChanged(): Event<NotebookContentChange> {
throw new Error('method not implemented.'); throw new Error('method not implemented.');
} }
@@ -67,6 +71,9 @@ export class NotebookModelStub implements INotebookModel {
get applicableConnectionProviderIds(): string[] { get applicableConnectionProviderIds(): string[] {
throw new Error('method not implemented.'); throw new Error('method not implemented.');
} }
getStandardKernelFromName(name: string): IStandardKernelWithProvider {
throw new Error('Method not implemented.');
}
changeKernel(displayName: string): void { changeKernel(displayName: string): void {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');
} }

View File

@@ -38,7 +38,8 @@ suite('Client Session', function (): void {
session = new ClientSession({ session = new ClientSession({
notebookManager: notebookManager, notebookManager: notebookManager,
notebookUri: path, notebookUri: path,
notificationService: notificationService.object notificationService: notificationService.object,
kernelSpec: undefined
}); });
let serverlessNotebookManager = new NotebookManagerStub(); let serverlessNotebookManager = new NotebookManagerStub();
@@ -46,7 +47,8 @@ suite('Client Session', function (): void {
remoteSession = new ClientSession({ remoteSession = new ClientSession({
notebookManager: serverlessNotebookManager, notebookManager: serverlessNotebookManager,
notebookUri: path, notebookUri: path,
notificationService: notificationService.object notificationService: notificationService.object,
kernelSpec: undefined
}); });
}); });
@@ -135,50 +137,50 @@ suite('Client Session', function (): void {
should(session.errorMessage).equal('error'); should(session.errorMessage).equal('error');
}); });
test('Should start session automatically if kernel preference requests it', async function (): Promise<void> { // test('Should start session automatically if kernel preference requests it', async function (): Promise<void> {
serverManager.isStarted = true; // serverManager.isStarted = true;
mockSessionManager.setup(s => s.ready).returns(() => Promise.resolve()); // mockSessionManager.setup(s => s.ready).returns(() => Promise.resolve());
let sessionMock = TypeMoq.Mock.ofType(EmptySession); // let sessionMock = TypeMoq.Mock.ofType(EmptySession);
let startOptions: nb.ISessionOptions = undefined; // let startOptions: nb.ISessionOptions = undefined;
mockSessionManager.setup(s => s.startNew(TypeMoq.It.isAny())).returns((options) => { // mockSessionManager.setup(s => s.startNew(TypeMoq.It.isAny())).returns((options) => {
startOptions = options; // startOptions = options;
return Promise.resolve(sessionMock.object); // return Promise.resolve(sessionMock.object);
}); // });
// When I call initialize after defining kernel preferences // // When I call initialize after defining kernel preferences
session.kernelPreference = { // session.kernelPreference = {
shouldStart: true, // shouldStart: true,
name: 'python' // name: 'python'
}; // };
await session.initialize(); // await session.initialize();
// Then // // Then
should(session.isReady).be.true(); // should(session.isReady).be.true();
should(session.isInErrorState).be.false(); // should(session.isInErrorState).be.false();
should(startOptions.kernelName).equal('python'); // should(startOptions.kernelName).equal('python');
should(startOptions.path).equal(path.fsPath); // should(startOptions.path).equal(path.fsPath);
}); // });
test('Should shutdown session even if no serverManager is set', async function (): Promise<void> { // test('Should shutdown session even if no serverManager is set', async function (): Promise<void> {
// Given a session against a remote server // // Given a session against a remote server
let expectedId = 'abc'; // let expectedId = 'abc';
mockSessionManager.setup(s => s.isReady).returns(() => true); // mockSessionManager.setup(s => s.isReady).returns(() => true);
mockSessionManager.setup(s => s.shutdown(TypeMoq.It.isAny())).returns(() => Promise.resolve()); // mockSessionManager.setup(s => s.shutdown(TypeMoq.It.isAny())).returns(() => Promise.resolve());
let sessionMock = TypeMoq.Mock.ofType(EmptySession); // let sessionMock = TypeMoq.Mock.ofType(EmptySession);
sessionMock.setup(s => s.id).returns(() => expectedId); // sessionMock.setup(s => s.id).returns(() => expectedId);
mockSessionManager.setup(s => s.startNew(TypeMoq.It.isAny())).returns(() => Promise.resolve(sessionMock.object)); // mockSessionManager.setup(s => s.startNew(TypeMoq.It.isAny())).returns(() => Promise.resolve(sessionMock.object));
remoteSession.kernelPreference = { // remoteSession.kernelPreference = {
shouldStart: true, // shouldStart: true,
name: 'python' // name: 'python'
}; // };
await remoteSession.initialize(); // await remoteSession.initialize();
// When I call shutdown // // When I call shutdown
await remoteSession.shutdown(); // await remoteSession.shutdown();
// Then // // Then
mockSessionManager.verify(s => s.shutdown(TypeMoq.It.isValue(expectedId)), TypeMoq.Times.once()); // mockSessionManager.verify(s => s.shutdown(TypeMoq.It.isValue(expectedId)), TypeMoq.Times.once());
}); // });
}); });

View File

@@ -96,7 +96,7 @@ suite('notebook model', function (): void {
notificationService: notificationService.object, notificationService: notificationService.object,
connectionService: queryConnectionService.object, connectionService: queryConnectionService.object,
providerId: 'SQL', providerId: 'SQL',
standardKernels: [{ name: 'SQL', connectionProviderIds: ['MSSQL'], notebookProvider: 'sql' }], standardKernels: [{ name: 'SQL', displayName: 'SQL', connectionProviderIds: ['MSSQL'], notebookProvider: 'sql' }],
cellMagicMapper: undefined, cellMagicMapper: undefined,
defaultKernel: undefined, defaultKernel: undefined,
layoutChanged: undefined, layoutChanged: undefined,
@@ -188,7 +188,7 @@ suite('notebook model', function (): void {
// let model = new NotebookModel(options); // let model = new NotebookModel(options);
// model.onClientSessionReady((session) => sessionFired = true); // model.onClientSessionReady((session) => sessionFired = true);
// await model.requestModelLoad(); // await model.requestModelLoad();
// model.backgroundStartSession(); // model.startSession(notebookManagers[0]);
// // Then I expect load to succeed // // Then I expect load to succeed
// shouldHaveOneCell(model); // shouldHaveOneCell(model);
@@ -223,7 +223,7 @@ suite('notebook model', function (): void {
let model = new NotebookModel(options, undefined); let model = new NotebookModel(options, undefined);
model.onClientSessionReady((session) => actualSession = session); model.onClientSessionReady((session) => actualSession = session);
await model.requestModelLoad(); await model.requestModelLoad();
model.backgroundStartSession(); await model.startSession(notebookManagers[0]);
// Then I expect load to succeed // Then I expect load to succeed
should(model.clientSession).not.be.undefined(); should(model.clientSession).not.be.undefined();

View File

@@ -122,7 +122,7 @@ suite('ExtHostNotebook Tests', () => {
class NotebookProviderStub implements azdata.nb.NotebookProvider { class NotebookProviderStub implements azdata.nb.NotebookProvider {
providerId: string = 'TestProvider'; providerId: string = 'TestProvider';
standardKernels: azdata.nb.IStandardKernel[] = [{name: 'fakeKernel', connectionProviderIds: ['MSSQL']}]; standardKernels: azdata.nb.IStandardKernel[] = [{name: 'fakeKernel', displayName: 'fakeKernel', connectionProviderIds: ['MSSQL']}];
getNotebookManager(notebookUri: vscode.Uri): Thenable<azdata.nb.NotebookManager> { getNotebookManager(notebookUri: vscode.Uri): Thenable<azdata.nb.NotebookManager> {
throw new Error('Method not implemented.'); throw new Error('Method not implemented.');