mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-28 17:23:19 -05:00
SQL Kernel Improvements/Removing Spark Code from Core/Attach to Changes (#3790)
* Scenarios work besides loading saved kernel * Fix compilation issue * Save and load functional * Fix loading kernesl issue when sql kernel is not enabled * Fix language mapping to not be hardcoded any longer * Remove unnecessary comment * PR Comments vol. 1 * Code cleanup, use ConnectionProfile instead of IConnectionProfile when accessing serverName * PR changes vol. 2 * One final comment for PR * Fix linting issue
This commit is contained in:
@@ -15,7 +15,7 @@ import { IQueryEditorOptions } from 'sql/parts/query/common/queryEditorService';
|
||||
import { QueryPlanInput } from 'sql/parts/queryPlan/queryPlanInput';
|
||||
import { NotebookInput, NotebookInputModel, NotebookInputValidator } from 'sql/parts/notebook/notebookInput';
|
||||
import { DEFAULT_NOTEBOOK_PROVIDER, INotebookService } from 'sql/workbench/services/notebook/common/notebookService';
|
||||
import { getProvidersForFileName } from 'sql/parts/notebook/notebookUtils';
|
||||
import { getProvidersForFileName, getStandardKernelsForProvider } from 'sql/parts/notebook/notebookUtils';
|
||||
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
|
||||
|
||||
const fs = require('fs');
|
||||
@@ -69,6 +69,10 @@ export function convertEditorInput(input: EditorInput, options: IQueryEditorOpti
|
||||
let notebookInputModel = new NotebookInputModel(uri, undefined, false, undefined);
|
||||
notebookInputModel.providerId = providerIds.filter(provider => provider !== DEFAULT_NOTEBOOK_PROVIDER)[0];
|
||||
notebookInputModel.providers = providerIds;
|
||||
notebookInputModel.providers.forEach(provider => {
|
||||
let standardKernels = getStandardKernelsForProvider(provider, notebookService);
|
||||
notebookInputModel.standardKernels = standardKernels;
|
||||
});
|
||||
let notebookInput: NotebookInput = instantiationService.createInstance(NotebookInput, fileName, notebookInputModel);
|
||||
return notebookInput;
|
||||
});
|
||||
|
||||
@@ -20,8 +20,6 @@ let modelId = 0;
|
||||
|
||||
|
||||
export class CellModel implements ICellModel {
|
||||
private static LanguageMapping: Map<string, string>;
|
||||
|
||||
private _cellType: nb.CellType;
|
||||
private _source: string;
|
||||
private _language: string;
|
||||
@@ -37,7 +35,6 @@ export class CellModel implements ICellModel {
|
||||
|
||||
constructor(private factory: IModelFactory, cellData?: nb.ICellContents, private _options?: ICellModelOptions) {
|
||||
this.id = `${modelId++}`;
|
||||
CellModel.CreateLanguageMappings();
|
||||
if (cellData) {
|
||||
// Read in contents if available
|
||||
this.fromJSON(cellData);
|
||||
@@ -238,9 +235,9 @@ export class CellModel implements ICellModel {
|
||||
try {
|
||||
let result = output as nb.IDisplayResult;
|
||||
if (result && result.data && result.data['text/html']) {
|
||||
let nbm = (this as CellModel).options.notebook as NotebookModel;
|
||||
if (nbm.hadoopConnection) {
|
||||
let host = nbm.hadoopConnection.host;
|
||||
let model = (this as CellModel).options.notebook as NotebookModel;
|
||||
if (model.activeConnection) {
|
||||
let host = model.activeConnection.serverName;
|
||||
let html = result.data['text/html'];
|
||||
html = html.replace(/(https?:\/\/mssql-master.*\/proxy)(.*)/g, function (a, b, c) {
|
||||
let ret = '';
|
||||
@@ -321,18 +318,6 @@ export class CellModel implements ICellModel {
|
||||
}
|
||||
}
|
||||
|
||||
private static CreateLanguageMappings(): void {
|
||||
if (CellModel.LanguageMapping) {
|
||||
return;
|
||||
}
|
||||
CellModel.LanguageMapping = new Map<string, string>();
|
||||
CellModel.LanguageMapping['pyspark'] = 'python';
|
||||
CellModel.LanguageMapping['pyspark3'] = 'python';
|
||||
CellModel.LanguageMapping['python'] = 'python';
|
||||
CellModel.LanguageMapping['scala'] = 'scala';
|
||||
CellModel.LanguageMapping['sql'] = 'sql';
|
||||
}
|
||||
|
||||
private get languageInfo(): nb.ILanguageInfo {
|
||||
if (this._options && this._options.notebook && this._options.notebook.languageInfo) {
|
||||
return this._options.notebook.languageInfo;
|
||||
@@ -371,17 +356,15 @@ export class CellModel implements ICellModel {
|
||||
// Otherwise, default to python as the language
|
||||
let languageInfo = this.languageInfo;
|
||||
if (languageInfo) {
|
||||
if (languageInfo.name) {
|
||||
// check the LanguageMapping to determine if a mapping is necessary (example 'pyspark' -> 'python')
|
||||
if (CellModel.LanguageMapping[languageInfo.name]) {
|
||||
this._language = CellModel.LanguageMapping[languageInfo.name];
|
||||
if (languageInfo.codemirror_mode) {
|
||||
let codeMirrorMode: nb.ICodeMirrorMode = <nb.ICodeMirrorMode>(languageInfo.codemirror_mode);
|
||||
if (codeMirrorMode && codeMirrorMode.name) {
|
||||
this._language = codeMirrorMode.name;
|
||||
}
|
||||
else {
|
||||
this._language = languageInfo.name;
|
||||
}
|
||||
}
|
||||
else if (languageInfo.mimetype) {
|
||||
} else if (languageInfo.mimetype) {
|
||||
this._language = languageInfo.mimetype;
|
||||
} else if (languageInfo.name) {
|
||||
this._language = languageInfo.name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,10 +17,8 @@ import { IClientSession, IKernelPreference, IClientSessionOptions } from './mode
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
|
||||
import * as notebookUtils from '../notebookUtils';
|
||||
import * as sparkUtils from '../spark/sparkUtils';
|
||||
import { INotebookManager } from 'sql/workbench/services/notebook/common/notebookService';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { NotebookConnection } from 'sql/parts/notebook/models/notebookConnection';
|
||||
|
||||
/**
|
||||
* Implementation of a client session. This is a model over session operations,
|
||||
@@ -49,7 +47,6 @@ export class ClientSession implements IClientSession {
|
||||
private _session: nb.ISession;
|
||||
private isServerStarted: boolean;
|
||||
private notebookManager: INotebookManager;
|
||||
private _connection: NotebookConnection;
|
||||
private _kernelConfigActions: ((kernelName: string) => Promise<any>)[] = [];
|
||||
|
||||
constructor(private options: IClientSessionOptions) {
|
||||
@@ -60,10 +57,8 @@ export class ClientSession implements IClientSession {
|
||||
this._kernelChangeCompleted = new Deferred<void>();
|
||||
}
|
||||
|
||||
public async initialize(connection?: NotebookConnection): Promise<void> {
|
||||
public async initialize(): Promise<void> {
|
||||
try {
|
||||
this._kernelConfigActions.push((kernelName: string) => { return this.runTasksBeforeSessionStart(kernelName); });
|
||||
this._connection = connection;
|
||||
this._serverLoadFinished = this.startServer();
|
||||
await this._serverLoadFinished;
|
||||
await this.initializeSession();
|
||||
@@ -114,7 +109,7 @@ export class ClientSession implements IClientSession {
|
||||
} catch (err) {
|
||||
// TODO move registration
|
||||
if (err && err.response && err.response.status === 501) {
|
||||
this.options.notificationService.warn(nls.localize('sparkKernelRequiresConnection', 'Kernel {0} was not found. The default kernel will be used instead.', kernelName));
|
||||
this.options.notificationService.warn(nls.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
|
||||
@@ -256,41 +251,22 @@ export class ClientSession implements IClientSession {
|
||||
return kernel;
|
||||
}
|
||||
|
||||
public async runTasksBeforeSessionStart(kernelName: string): Promise<void> {
|
||||
// TODO we should move all Spark-related code to SparkMagicContext
|
||||
if (this._session && this._connection && this.isSparkKernel(kernelName)) {
|
||||
// TODO may need to reenable a way to get the credential
|
||||
// await this._connection.getCredential();
|
||||
// %_do_not_call_change_endpoint is a SparkMagic command that lets users change endpoint options,
|
||||
// such as user/profile/host name/auth type
|
||||
|
||||
let server = URI.parse(sparkUtils.getLivyUrl(this._connection.host, this._connection.knoxport)).toString();
|
||||
let doNotCallChangeEndpointParams =
|
||||
`%_do_not_call_change_endpoint --username=${this._connection.user} --password=${this._connection.password} --server=${server} --auth=Basic_Access`;
|
||||
let future = this._session.kernel.requestExecute({
|
||||
code: doNotCallChangeEndpointParams
|
||||
}, true);
|
||||
await future.done;
|
||||
public async configureKernel(options: nb.IKernelSpec): Promise<void> {
|
||||
if (this._session) {
|
||||
await this._session.configureKernel(options);
|
||||
}
|
||||
}
|
||||
|
||||
public async updateConnection(connection: NotebookConnection): Promise<void> {
|
||||
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;
|
||||
}
|
||||
this._connection = (connection.connectionProfile.id !== '-1') ? connection : this._connection;
|
||||
// if kernel is not set, don't run kernel config actions
|
||||
// this should only occur when a cell is cancelled, which interrupts the kernel
|
||||
if (this.kernel && this.kernel.name) {
|
||||
await this.runKernelConfigActions(this.kernel.name);
|
||||
if (connection.id !== '-1') {
|
||||
await this._session.configureConnection(connection);
|
||||
}
|
||||
}
|
||||
|
||||
isSparkKernel(kernelName: string): any {
|
||||
return kernelName && kernelName.toLowerCase().indexOf('spark') > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill the kernel and shutdown the session.
|
||||
*
|
||||
|
||||
@@ -17,9 +17,11 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
|
||||
import { CellType, NotebookChangeType } from 'sql/parts/notebook/models/contracts';
|
||||
import { INotebookManager } from 'sql/workbench/services/notebook/common/notebookService';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { NotebookConnection } from 'sql/parts/notebook/models/notebookConnection';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { IStandardKernelWithProvider } from 'sql/parts/notebook/notebookUtils';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
|
||||
export interface IClientSessionOptions {
|
||||
notebookUri: URI;
|
||||
@@ -131,7 +133,7 @@ export interface IClientSession extends IDisposable {
|
||||
* This will optionally start a session if the kernel preferences
|
||||
* indicate this is desired
|
||||
*/
|
||||
initialize(connection?: NotebookConnection): Promise<void>;
|
||||
initialize(connection?: IConnectionProfile): Promise<void>;
|
||||
|
||||
/**
|
||||
* Change the current kernel associated with the document.
|
||||
@@ -140,6 +142,13 @@ export interface IClientSession extends IDisposable {
|
||||
options: nb.IKernelSpec
|
||||
): Promise<nb.IKernel>;
|
||||
|
||||
/**
|
||||
* Configure the current kernel associated with the document.
|
||||
*/
|
||||
configureKernel(
|
||||
options: nb.IKernelSpec
|
||||
): Promise<void>;
|
||||
|
||||
/**
|
||||
* Kill the kernel and shutdown the session.
|
||||
*
|
||||
@@ -191,12 +200,12 @@ export interface IClientSession extends IDisposable {
|
||||
/**
|
||||
* Updates the connection
|
||||
*/
|
||||
updateConnection(connection: NotebookConnection): void;
|
||||
updateConnection(connection: IConnectionProfile): void;
|
||||
}
|
||||
|
||||
export interface IDefaultConnection {
|
||||
defaultConnection: IConnectionProfile;
|
||||
otherConnections: IConnectionProfile[];
|
||||
defaultConnection: ConnectionProfile;
|
||||
otherConnections: ConnectionProfile[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -348,6 +357,8 @@ export interface INotebookModel {
|
||||
* @param edits The edit operations to perform
|
||||
*/
|
||||
pushEditOperations(edits: ISingleNotebookEditOperation[]): void;
|
||||
|
||||
getApplicableConnectionProviderIds(kernelName: string): string[];
|
||||
}
|
||||
|
||||
export interface NotebookContentChange {
|
||||
@@ -418,16 +429,14 @@ export interface INotebookModelOptions {
|
||||
|
||||
notebookManagers: INotebookManager[];
|
||||
providerId: string;
|
||||
standardKernels: IStandardKernelWithProvider[];
|
||||
defaultKernel: nb.IKernelSpec;
|
||||
|
||||
notificationService: INotificationService;
|
||||
connectionService: IConnectionManagementService;
|
||||
capabilitiesService: ICapabilitiesService;
|
||||
}
|
||||
|
||||
// TODO would like to move most of these constants to an extension
|
||||
export namespace notebookConstants {
|
||||
export const hadoopKnoxProviderName = 'HADOOP_KNOX';
|
||||
export const python3 = 'python3';
|
||||
export const python3DisplayName = 'Python 3';
|
||||
export const defaultSparkKernel = 'pyspark3kernel';
|
||||
export const hostPropName = 'host';
|
||||
export const SQL = 'SQL';
|
||||
}
|
||||
@@ -11,7 +11,6 @@ import { localize } from 'vs/nls';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
|
||||
export namespace constants {
|
||||
export const hostPropName = 'host';
|
||||
export const userPropName = 'user';
|
||||
export const knoxPortPropName = 'knoxport';
|
||||
export const clusterPropName = 'clustername';
|
||||
@@ -52,7 +51,7 @@ export class NotebookConnection {
|
||||
* preference to the built in port.
|
||||
*/
|
||||
private ensureHostAndPort(): void {
|
||||
this._host = this.connectionProfile.options[constants.hostPropName];
|
||||
this._host = this.connectionProfile.serverName;
|
||||
this._knoxPort = NotebookConnection.getKnoxPortOrDefault(this.connectionProfile);
|
||||
// determine whether the host has either a ',' or ':' in it
|
||||
this.setHostAndPort(',');
|
||||
|
||||
152
src/sql/parts/notebook/models/notebookContexts.ts
Normal file
152
src/sql/parts/notebook/models/notebookContexts.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { nb } from 'sqlops';
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { IDefaultConnection, notebookConstants, INotebookModelOptions } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
|
||||
export class NotebookContexts {
|
||||
private static MSSQL_PROVIDER = 'MSSQL';
|
||||
|
||||
private static get DefaultContext(): IDefaultConnection {
|
||||
let defaultConnection: ConnectionProfile = <any>{
|
||||
providerName: NotebookContexts.MSSQL_PROVIDER,
|
||||
id: '-1',
|
||||
serverName: localize('selectConnection', 'Select connection')
|
||||
};
|
||||
|
||||
return {
|
||||
// default context if no other contexts are applicable
|
||||
defaultConnection: defaultConnection,
|
||||
otherConnections: [defaultConnection]
|
||||
};
|
||||
}
|
||||
|
||||
private static get LocalContext(): IDefaultConnection {
|
||||
let localConnection: ConnectionProfile = <any>{
|
||||
providerName: NotebookContexts.MSSQL_PROVIDER,
|
||||
id: '-1',
|
||||
serverName: localize('localhost', 'localhost')
|
||||
};
|
||||
|
||||
return {
|
||||
// default context if no other contexts are applicable
|
||||
defaultConnection: localConnection,
|
||||
otherConnections: [localConnection]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the applicable contexts for a given kernel
|
||||
* @param connectionService connection management service
|
||||
* @param connProviderIds array of connection provider ids applicable for a kernel
|
||||
* @param kernelChangedArgs kernel changed args (both old and new kernel info)
|
||||
* @param profile current connection profile
|
||||
*/
|
||||
public static async getContextsForKernel(connectionService: IConnectionManagementService, connProviderIds: string[], kernelChangedArgs?: nb.IKernelChangedArgs, profile?: IConnectionProfile): Promise<IDefaultConnection> {
|
||||
let connections: IDefaultConnection = this.DefaultContext;
|
||||
if (!profile) {
|
||||
if (!kernelChangedArgs || !kernelChangedArgs.newValue ||
|
||||
(kernelChangedArgs.oldValue && kernelChangedArgs.newValue.id === kernelChangedArgs.oldValue.id)) {
|
||||
// nothing to do, kernels are the same or new kernel is undefined
|
||||
return connections;
|
||||
}
|
||||
}
|
||||
if (kernelChangedArgs && kernelChangedArgs.newValue && kernelChangedArgs.newValue.name && connProviderIds.length < 1) {
|
||||
return connections;
|
||||
} else {
|
||||
connections = await this.getActiveContexts(connectionService, connProviderIds, profile);
|
||||
}
|
||||
return connections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active contexts and sort them
|
||||
* @param apiWrapper ApiWrapper
|
||||
* @param profile current connection profile
|
||||
*/
|
||||
public static async getActiveContexts(connectionService: IConnectionManagementService, connProviderIds: string[], profile: IConnectionProfile): Promise<IDefaultConnection> {
|
||||
let defaultConnection: ConnectionProfile = NotebookContexts.DefaultContext.defaultConnection;
|
||||
let activeConnections: ConnectionProfile[] = await connectionService.getActiveConnections();
|
||||
if (activeConnections && activeConnections.length > 0) {
|
||||
activeConnections = activeConnections.filter(conn => conn.id !== '-1');
|
||||
}
|
||||
// If no connection provider ids exist for a given kernel, the attach to should show localhost
|
||||
if (connProviderIds.length === 0) {
|
||||
return NotebookContexts.LocalContext;
|
||||
}
|
||||
// If no active connections exist, show "Select connection" as the default value
|
||||
if (activeConnections.length === 0) {
|
||||
return NotebookContexts.DefaultContext;
|
||||
}
|
||||
// Filter active connections by their provider ids to match kernel's supported connection providers
|
||||
else if (activeConnections.length > 0) {
|
||||
let connections = activeConnections.filter(connection => {
|
||||
return connProviderIds.includes(connection.providerName);
|
||||
});
|
||||
if (connections && connections.length > 0) {
|
||||
defaultConnection = connections[0];
|
||||
if (profile && profile.options) {
|
||||
if (connections.find(connection => connection.serverName === profile.serverName)) {
|
||||
defaultConnection = connections.find(connection => connection.serverName === profile.serverName);
|
||||
}
|
||||
}
|
||||
}
|
||||
activeConnections = [];
|
||||
connections.forEach(connection => activeConnections.push(connection));
|
||||
}
|
||||
if (defaultConnection === NotebookContexts.DefaultContext.defaultConnection) {
|
||||
let newConnection = <ConnectionProfile><any>{
|
||||
providerName: 'SQL',
|
||||
id: '-2',
|
||||
serverName: localize('addConnection', 'Add new connection')
|
||||
};
|
||||
activeConnections.push(newConnection);
|
||||
}
|
||||
|
||||
return {
|
||||
otherConnections: activeConnections,
|
||||
defaultConnection: defaultConnection
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param specs kernel specs (comes from session manager)
|
||||
* @param connectionInfo connection profile
|
||||
* @param savedKernelInfo kernel info loaded from
|
||||
*/
|
||||
public static getDefaultKernel(specs: nb.IAllKernels, connectionInfo: IConnectionProfile, savedKernelInfo: nb.IKernelInfo): nb.IKernelSpec {
|
||||
let defaultKernel: nb.IKernelSpec;
|
||||
if (specs) {
|
||||
// find the saved kernel (if it exists)
|
||||
if (savedKernelInfo) {
|
||||
defaultKernel = specs.kernels.find((kernel) => kernel.name === savedKernelInfo.name);
|
||||
}
|
||||
// if no saved kernel exists, use the default KernelSpec
|
||||
if (!defaultKernel) {
|
||||
defaultKernel = specs.kernels.find((kernel) => kernel.name === specs.defaultKernel);
|
||||
}
|
||||
if (defaultKernel) {
|
||||
return defaultKernel;
|
||||
}
|
||||
}
|
||||
|
||||
// If no default kernel specified (should never happen), default to SQL
|
||||
if (!defaultKernel) {
|
||||
defaultKernel = {
|
||||
name: notebookConstants.SQL,
|
||||
display_name: notebookConstants.SQL
|
||||
};
|
||||
}
|
||||
return defaultKernel;
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,13 @@ import { NotebookChangeType, CellType } from 'sql/parts/notebook/models/contract
|
||||
import { nbversion } from '../notebookConstants';
|
||||
import * as notebookUtils from '../notebookUtils';
|
||||
import { INotebookManager, SQL_NOTEBOOK_PROVIDER, DEFAULT_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/common/notebookService';
|
||||
import { SparkMagicContexts } from 'sql/parts/notebook/models/sparkMagicContexts';
|
||||
import { NotebookContexts } from 'sql/parts/notebook/models/notebookContexts';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { NotebookConnection } from 'sql/parts/notebook/models/notebookConnection';
|
||||
import { INotification, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
|
||||
/*
|
||||
* Used to control whether a message in a dialog/wizard is displayed as an error,
|
||||
@@ -58,11 +58,12 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
private _savedKernelInfo: nb.IKernelInfo;
|
||||
private readonly _nbformat: number = nbversion.MAJOR_VERSION;
|
||||
private readonly _nbformatMinor: number = nbversion.MINOR_VERSION;
|
||||
private _hadoopConnection: NotebookConnection;
|
||||
private _defaultKernel: nb.IKernelSpec;
|
||||
private _activeConnection: ConnectionProfile;
|
||||
private _activeCell: ICellModel;
|
||||
private _providerId: string;
|
||||
private _isNewNotebook: boolean = true;
|
||||
private _defaultKernel: nb.IKernelSpec;
|
||||
private _kernelDisplayNameToConnectionProviderIds: Map<string, string[]> = new Map<string, string[]>();
|
||||
private _kernelDisplayNameToNotebookProviderIds: Map<string, string> = new Map<string, string>();
|
||||
|
||||
constructor(private notebookOptions: INotebookModelOptions, startSessionImmediately?: boolean, private connectionProfile?: IConnectionProfile) {
|
||||
super();
|
||||
@@ -74,6 +75,11 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
}
|
||||
this._trustedMode = false;
|
||||
this._providerId = notebookOptions.providerId;
|
||||
this.notebookOptions.standardKernels.forEach(kernel => {
|
||||
this._kernelDisplayNameToConnectionProviderIds.set(kernel.name, kernel.connectionProviderIds);
|
||||
this._kernelDisplayNameToNotebookProviderIds.set(kernel.name, kernel.notebookProvider);
|
||||
});
|
||||
this._defaultKernel = notebookOptions.defaultKernel;
|
||||
}
|
||||
|
||||
public get notebookManagers(): INotebookManager[] {
|
||||
@@ -171,10 +177,6 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
return this._trustedMode;
|
||||
}
|
||||
|
||||
public get isNewNotebook(): boolean {
|
||||
return this._isNewNotebook;
|
||||
}
|
||||
|
||||
public get providerId(): string {
|
||||
return this._providerId;
|
||||
}
|
||||
@@ -188,8 +190,8 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
}
|
||||
}
|
||||
|
||||
public get hadoopConnection(): NotebookConnection {
|
||||
return this._hadoopConnection;
|
||||
public get activeConnection(): IConnectionProfile {
|
||||
return this._activeConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,6 +209,14 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
return this._onClientSessionReady.event;
|
||||
}
|
||||
|
||||
public getApplicableConnectionProviderIds(kernelDisplayName: string): string[] {
|
||||
let ids = [];
|
||||
if (kernelDisplayName) {
|
||||
ids = this._kernelDisplayNameToConnectionProviderIds.get(kernelDisplayName);
|
||||
}
|
||||
return !ids ? [] : ids;
|
||||
}
|
||||
|
||||
public async requestModelLoad(isTrusted: boolean = false): Promise<void> {
|
||||
try {
|
||||
this._trustedMode = isTrusted;
|
||||
@@ -223,9 +233,12 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
version: ''
|
||||
};
|
||||
if (contents) {
|
||||
this._isNewNotebook = false;
|
||||
this._defaultLanguageInfo = this.getDefaultLanguageInfo(contents);
|
||||
this._savedKernelInfo = this.getSavedKernelInfo(contents);
|
||||
this.setProviderIdForKernel(this._savedKernelInfo);
|
||||
if (this._savedKernelInfo) {
|
||||
this._defaultKernel = this._savedKernelInfo;
|
||||
}
|
||||
if (contents.cells && contents.cells.length > 0) {
|
||||
this._cells = contents.cells.map(c => factory.createCell(c, { notebook: this, isTrusted: isTrusted }));
|
||||
}
|
||||
@@ -338,15 +351,15 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
if (!this._activeClientSession) {
|
||||
this._activeClientSession = clientSession;
|
||||
}
|
||||
let profile = this.connectionProfile as IConnectionProfile;
|
||||
let profile = new ConnectionProfile(this.notebookOptions.capabilitiesService, this.connectionProfile);
|
||||
|
||||
if (this.isValidKnoxConnection(profile)) {
|
||||
this._hadoopConnection = new NotebookConnection(this.connectionProfile);
|
||||
if (this.isValidConnection(profile)) {
|
||||
this._activeConnection = profile;
|
||||
} else {
|
||||
this._hadoopConnection = undefined;
|
||||
this._activeConnection = undefined;
|
||||
}
|
||||
|
||||
clientSession.initialize(this._hadoopConnection);
|
||||
clientSession.initialize(this._activeConnection);
|
||||
this._sessionLoadFinished = clientSession.ready.then(async () => {
|
||||
if (clientSession.isInErrorState) {
|
||||
this.setErrorState(clientSession.errorMessage);
|
||||
@@ -360,8 +373,10 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
});
|
||||
}
|
||||
|
||||
private isValidKnoxConnection(profile: IConnectionProfile | connection.Connection) {
|
||||
return profile && profile.providerName === notebookConstants.hadoopKnoxProviderName && profile.options[notebookConstants.hostPropName] !== undefined;
|
||||
private isValidConnection(profile: IConnectionProfile | connection.Connection) {
|
||||
let standardKernels = this.notebookOptions.standardKernels.find(kernel => this._savedKernelInfo && kernel.name === this._savedKernelInfo.display_name);
|
||||
let connectionProviderIds = standardKernels ? standardKernels.connectionProviderIds : undefined;
|
||||
return profile && connectionProviderIds && connectionProviderIds.find(provider => provider === profile.providerName) !== undefined;
|
||||
}
|
||||
|
||||
public get languageInfo(): nb.ILanguageInfo {
|
||||
@@ -380,45 +395,49 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
}
|
||||
|
||||
public doChangeKernel(kernelSpec: nb.IKernelSpec): Promise<void> {
|
||||
this.findProviderIdForKernel(kernelSpec);
|
||||
return this._activeClientSession.changeKernel(kernelSpec)
|
||||
.then((kernel) => {
|
||||
kernel.ready.then(() => {
|
||||
if (kernel.info) {
|
||||
this.updateLanguageInfo(kernel.info.language_info);
|
||||
}
|
||||
}, err => undefined);
|
||||
return this.updateKernelInfo(kernel);
|
||||
}).catch((err) => {
|
||||
this.notifyError(localize('changeKernelFailed', 'Failed to change kernel: {0}', notebookUtils.getErrorMessage(err)));
|
||||
// TODO should revert kernels dropdown
|
||||
});
|
||||
this.setProviderIdForKernel(kernelSpec);
|
||||
if (this._activeClientSession && this._activeClientSession.isReady) {
|
||||
return this._activeClientSession.changeKernel(kernelSpec)
|
||||
.then((kernel) => {
|
||||
this.updateKernelInfo(kernel);
|
||||
kernel.ready.then(() => {
|
||||
if (kernel.info) {
|
||||
this.updateLanguageInfo(kernel.info.language_info);
|
||||
}
|
||||
}, err => undefined);
|
||||
}).catch((err) => {
|
||||
this.notifyError(localize('changeKernelFailed', 'Failed to change kernel: {0}', notebookUtils.getErrorMessage(err)));
|
||||
// TODO should revert kernels dropdown
|
||||
});
|
||||
}
|
||||
this.notifyError(localize('noActiveClientSessionFound', 'No active client session was found.'));
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public changeContext(host: string, newConnection?: IConnectionProfile): void {
|
||||
public changeContext(server: string, newConnection?: IConnectionProfile): void {
|
||||
try {
|
||||
if (!newConnection) {
|
||||
newConnection = this._activeContexts.otherConnections.find((connection) => connection.options['host'] === host);
|
||||
newConnection = this._activeContexts.otherConnections.find((connection) => connection.serverName === server);
|
||||
}
|
||||
if (!newConnection && this._activeContexts.defaultConnection.options['host'] === host) {
|
||||
if (!newConnection && (this._activeContexts.defaultConnection.serverName === server)) {
|
||||
newConnection = this._activeContexts.defaultConnection;
|
||||
}
|
||||
SparkMagicContexts.configureContext();
|
||||
this._hadoopConnection = new NotebookConnection(newConnection);
|
||||
this.refreshConnections(newConnection);
|
||||
this._activeClientSession.updateConnection(this._hadoopConnection);
|
||||
let newConnectionProfile = new ConnectionProfile(this.notebookOptions.capabilitiesService, newConnection);
|
||||
this._activeConnection = newConnectionProfile;
|
||||
this.refreshConnections(newConnectionProfile);
|
||||
this._activeClientSession.updateConnection(this._activeConnection);
|
||||
} catch (err) {
|
||||
let msg = notebookUtils.getErrorMessage(err);
|
||||
this.notifyError(localize('changeContextFailed', 'Changing context failed: {0}', msg));
|
||||
}
|
||||
}
|
||||
|
||||
private refreshConnections(newConnection: IConnectionProfile) {
|
||||
if (this.isValidKnoxConnection(newConnection) &&
|
||||
this._hadoopConnection.connectionProfile.id !== '-1' &&
|
||||
this._hadoopConnection.connectionProfile.id !== this._activeContexts.defaultConnection.id) {
|
||||
private refreshConnections(newConnection: ConnectionProfile) {
|
||||
if (this.isValidConnection(newConnection) &&
|
||||
this._activeConnection.id !== '-1' &&
|
||||
this._activeConnection.id !== this._activeContexts.defaultConnection.id) {
|
||||
// Put the defaultConnection to the head of otherConnections
|
||||
if (this.isValidKnoxConnection(this._activeContexts.defaultConnection)) {
|
||||
if (this.isValidConnection(this._activeContexts.defaultConnection)) {
|
||||
this._activeContexts.otherConnections = this._activeContexts.otherConnections.filter(conn => conn.id !== this._activeContexts.defaultConnection.id);
|
||||
this._activeContexts.otherConnections.unshift(this._activeContexts.defaultConnection);
|
||||
}
|
||||
@@ -439,18 +458,15 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
try {
|
||||
let sessionManager = this.notebookManager.sessionManager;
|
||||
if (sessionManager) {
|
||||
let defaultKernel = SparkMagicContexts.getDefaultKernel(sessionManager.specs, this.connectionProfile, this._savedKernelInfo, this.notebookOptions.notificationService);
|
||||
this._defaultKernel = defaultKernel;
|
||||
if (!this._defaultKernel) {
|
||||
this._defaultKernel = NotebookContexts.getDefaultKernel(sessionManager.specs, this.connectionProfile, this._savedKernelInfo);
|
||||
}
|
||||
this._clientSessions.forEach(clientSession => {
|
||||
clientSession.statusChanged(async (session) => {
|
||||
if (session && session.defaultKernelLoaded === true) {
|
||||
this._kernelsChangedEmitter.fire(defaultKernel);
|
||||
} else if (session && !session.defaultKernelLoaded) {
|
||||
this._kernelsChangedEmitter.fire({ name: notebookConstants.python3, display_name: notebookConstants.python3DisplayName });
|
||||
}
|
||||
this._kernelsChangedEmitter.fire(session.kernel);
|
||||
});
|
||||
});
|
||||
this.doChangeKernel(defaultKernel);
|
||||
this.doChangeKernel(this._defaultKernel);
|
||||
}
|
||||
} catch (err) {
|
||||
let msg = notebookUtils.getErrorMessage(err);
|
||||
@@ -484,6 +500,15 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
return kernel;
|
||||
}
|
||||
|
||||
private getDisplayNameFromSpecName(kernelid: string): string {
|
||||
let newKernel = this.notebookManager.sessionManager.specs.kernels.find(kernel => kernel.name === kernelid);
|
||||
let newKernelDisplayName;
|
||||
if (newKernel) {
|
||||
newKernelDisplayName = newKernel.display_name;
|
||||
}
|
||||
return newKernelDisplayName;
|
||||
}
|
||||
|
||||
private setErrorState(errMsg: string): void {
|
||||
this._inErrorState = true;
|
||||
let msg = localize('startSessionFailed', 'Could not start session: {0}', errMsg);
|
||||
@@ -499,6 +524,11 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
public async handleClosed(): Promise<void> {
|
||||
try {
|
||||
if (this._activeClientSession) {
|
||||
try {
|
||||
await this._activeClientSession.ready;
|
||||
} catch(err) {
|
||||
this.notifyError(localize('shutdownClientSessionError', 'A client session error occurred when closing the notebook: {0}', err));
|
||||
}
|
||||
await this._activeClientSession.shutdown();
|
||||
this._clientSessions = undefined;
|
||||
this._activeClientSession = undefined;
|
||||
@@ -509,11 +539,13 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
}
|
||||
|
||||
private async loadActiveContexts(kernelChangedArgs: nb.IKernelChangedArgs): Promise<void> {
|
||||
this._activeContexts = await SparkMagicContexts.getContextsForKernel(this.notebookOptions.connectionService, kernelChangedArgs, this.connectionProfile);
|
||||
this._contextsChangedEmitter.fire();
|
||||
if (this.contexts.defaultConnection !== undefined && this.contexts.defaultConnection.options !== undefined) {
|
||||
let defaultHadoopConnection = new NotebookConnection(this.contexts.defaultConnection);
|
||||
this.changeContext(defaultHadoopConnection.host);
|
||||
if (kernelChangedArgs && kernelChangedArgs.newValue && kernelChangedArgs.newValue.name) {
|
||||
let kernelDisplayName = this.getDisplayNameFromSpecName(kernelChangedArgs.newValue.name);
|
||||
this._activeContexts = await NotebookContexts.getContextsForKernel(this.notebookOptions.connectionService, this.getApplicableConnectionProviderIds(kernelDisplayName), kernelChangedArgs, this.connectionProfile);
|
||||
this._contextsChangedEmitter.fire();
|
||||
if (this.contexts.defaultConnection !== undefined && this.contexts.defaultConnection.serverName !== undefined) {
|
||||
this.changeContext(this.contexts.defaultConnection.serverName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -555,6 +587,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
display_name: spec.display_name,
|
||||
language: spec.language
|
||||
};
|
||||
this.clientSession.configureKernel(this._savedKernelInfo);
|
||||
} catch (err) {
|
||||
// Don't worry about this for now. Just use saved values
|
||||
}
|
||||
@@ -565,17 +598,27 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
||||
* Set _providerId and _activeClientSession based on a kernelSpec representing new kernel
|
||||
* @param kernelSpec KernelSpec for new kernel
|
||||
*/
|
||||
private findProviderIdForKernel(kernelSpec: nb.IKernelSpec): void {
|
||||
private setProviderIdForKernel(kernelSpec: nb.IKernelSpec): void {
|
||||
let sessionManagerFound: boolean = false;
|
||||
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._activeClientSession = this._clientSessions[i];
|
||||
this._providerId = this.notebookManagers[i].providerId;
|
||||
sessionManagerFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no SessionManager exists, utilize passed in StandardKernels to see if we can intelligently set _providerId
|
||||
if (!sessionManagerFound) {
|
||||
let provider = this._kernelDisplayNameToNotebookProviderIds.get(kernelSpec.display_name);
|
||||
if (provider) {
|
||||
this._providerId = provider;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Serialize the model to JSON.
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as path from 'path';
|
||||
import { nb } from 'sqlops';
|
||||
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { IDefaultConnection, notebookConstants, INotebookModelOptions } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import * as notebookUtils from '../notebookUtils';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
|
||||
const configBase = {
|
||||
'kernel_python_credentials': {
|
||||
'url': ''
|
||||
},
|
||||
'kernel_scala_credentials': {
|
||||
'url': ''
|
||||
},
|
||||
'kernel_r_credentials': {
|
||||
'url': ''
|
||||
},
|
||||
|
||||
'ignore_ssl_errors': true,
|
||||
|
||||
'logging_config': {
|
||||
'version': 1,
|
||||
'formatters': {
|
||||
'magicsFormatter': {
|
||||
'format': '%(asctime)s\t%(levelname)s\t%(message)s',
|
||||
'datefmt': ''
|
||||
}
|
||||
},
|
||||
'handlers': {
|
||||
'magicsHandler': {
|
||||
'class': 'hdijupyterutils.filehandler.MagicsFileHandler',
|
||||
'formatter': 'magicsFormatter',
|
||||
'home_path': ''
|
||||
}
|
||||
},
|
||||
'loggers': {
|
||||
'magicsLogger': {
|
||||
'handlers': ['magicsHandler'],
|
||||
'level': 'DEBUG',
|
||||
'propagate': 0
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
export class SparkMagicContexts {
|
||||
|
||||
public static get DefaultContext(): IDefaultConnection {
|
||||
// TODO NOTEBOOK REFACTOR fix default connection handling
|
||||
let defaultConnection: IConnectionProfile = <any>{
|
||||
providerName: notebookConstants.hadoopKnoxProviderName,
|
||||
id: '-1',
|
||||
options:
|
||||
{
|
||||
host: localize('selectConnection', 'Select connection')
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
// default context if no other contexts are applicable
|
||||
defaultConnection: defaultConnection,
|
||||
otherConnections: [defaultConnection]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all of the applicable contexts for a given kernel
|
||||
* @param apiWrapper ApiWrapper
|
||||
* @param kernelChangedArgs kernel changed args (both old and new kernel info)
|
||||
* @param profile current connection profile
|
||||
*/
|
||||
public static async getContextsForKernel(connectionService: IConnectionManagementService, kernelChangedArgs?: nb.IKernelChangedArgs, profile?: IConnectionProfile): Promise<IDefaultConnection> {
|
||||
let connections: IDefaultConnection = this.DefaultContext;
|
||||
if (!profile) {
|
||||
if (!kernelChangedArgs || !kernelChangedArgs.newValue ||
|
||||
(kernelChangedArgs.oldValue && kernelChangedArgs.newValue.id === kernelChangedArgs.oldValue.id)) {
|
||||
// nothing to do, kernels are the same or new kernel is undefined
|
||||
return connections;
|
||||
}
|
||||
}
|
||||
if (kernelChangedArgs && kernelChangedArgs.newValue && kernelChangedArgs.newValue.name) {
|
||||
switch (kernelChangedArgs.newValue.name) {
|
||||
case (notebookConstants.python3):
|
||||
// python3 case, use this.DefaultContext for the only connection
|
||||
break;
|
||||
//TO DO: Handle server connections based on kernel type. Right now, we call the same method for all kernel types.
|
||||
default:
|
||||
connections = await this.getActiveContexts(connectionService, profile);
|
||||
}
|
||||
} else {
|
||||
connections = await this.getActiveContexts(connectionService, profile);
|
||||
}
|
||||
return connections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active contexts and sort them
|
||||
* @param apiWrapper ApiWrapper
|
||||
* @param profile current connection profile
|
||||
*/
|
||||
public static async getActiveContexts(connectionService: IConnectionManagementService, profile: IConnectionProfile): Promise<IDefaultConnection> {
|
||||
let defaultConnection: IConnectionProfile = SparkMagicContexts.DefaultContext.defaultConnection;
|
||||
let activeConnections: IConnectionProfile[] = await connectionService.getActiveConnections();
|
||||
// If no connections exist, only show 'n/a'
|
||||
if (activeConnections && activeConnections.length > 0) {
|
||||
// Remove all non-Spark connections
|
||||
activeConnections = activeConnections.filter(conn => conn.providerName === notebookConstants.hadoopKnoxProviderName);
|
||||
}
|
||||
if (activeConnections.length === 0) {
|
||||
return SparkMagicContexts.DefaultContext;
|
||||
}
|
||||
|
||||
// If launched from the right click or server dashboard, connection profile data exists, so use that as default
|
||||
if (profile && profile.options) {
|
||||
let profileConnection = activeConnections.filter(conn => conn.options['host'] === profile.options['host']);
|
||||
if (profileConnection) {
|
||||
defaultConnection = profileConnection[0];
|
||||
}
|
||||
} else {
|
||||
if (activeConnections.length > 0) {
|
||||
defaultConnection = activeConnections[0];
|
||||
} else {
|
||||
// TODO NOTEBOOK REFACTOR change this so it's no longer incompatible with IConnectionProfile
|
||||
defaultConnection = <IConnectionProfile><any>{
|
||||
providerName: notebookConstants.hadoopKnoxProviderName,
|
||||
id: '-1',
|
||||
options:
|
||||
{
|
||||
host: localize('addConnection', 'Add new connection')
|
||||
}
|
||||
};
|
||||
activeConnections.push(defaultConnection);
|
||||
}
|
||||
}
|
||||
return {
|
||||
otherConnections: activeConnections,
|
||||
defaultConnection: defaultConnection
|
||||
};
|
||||
}
|
||||
|
||||
public static async configureContext(): Promise<object> {
|
||||
let sparkmagicConfDir = path.join(notebookUtils.getUserHome(), '.sparkmagic');
|
||||
// TODO NOTEBOOK REFACTOR re-enable this or move to extension. Requires config files to be available in order to work
|
||||
await notebookUtils.mkDir(sparkmagicConfDir);
|
||||
|
||||
// Default to localhost in config file.
|
||||
let creds: ICredentials = {
|
||||
'url': 'http://localhost:8088'
|
||||
};
|
||||
|
||||
let config: ISparkMagicConfig = Object.assign({}, configBase);
|
||||
SparkMagicContexts.updateConfig(config, creds, sparkmagicConfDir);
|
||||
|
||||
let configFilePath = path.join(sparkmagicConfDir, 'config.json');
|
||||
await pfs.writeFile(configFilePath, JSON.stringify(config));
|
||||
|
||||
return { 'SPARKMAGIC_CONF_DIR': sparkmagicConfDir };
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param specs kernel specs (comes from session manager)
|
||||
* @param connectionInfo connection profile
|
||||
* @param savedKernelInfo kernel info loaded from
|
||||
*/
|
||||
public static getDefaultKernel(specs: nb.IAllKernels, connectionInfo: IConnectionProfile, savedKernelInfo: nb.IKernelInfo, notificationService: INotificationService): nb.IKernelSpec {
|
||||
let foundSavedKernelInSpecs;
|
||||
let defaultKernel;
|
||||
if (specs) {
|
||||
defaultKernel = specs.kernels.find((kernel) => kernel.name === specs.defaultKernel);
|
||||
if (savedKernelInfo) {
|
||||
foundSavedKernelInSpecs = specs.kernels.find((kernel) => kernel.name === savedKernelInfo.name);
|
||||
}
|
||||
}
|
||||
let profile = connectionInfo as IConnectionProfile;
|
||||
if (specs && connectionInfo && profile.providerName === notebookConstants.hadoopKnoxProviderName) {
|
||||
// set default kernel to default spark kernel if profile exists
|
||||
// otherwise, set default to kernel info loaded from existing file
|
||||
defaultKernel = !foundSavedKernelInSpecs ? specs.kernels.find((spec) => spec.name === notebookConstants.defaultSparkKernel) : foundSavedKernelInSpecs;
|
||||
} else {
|
||||
// Handle kernels
|
||||
if (savedKernelInfo && savedKernelInfo.name.toLowerCase().indexOf('spark') > -1) {
|
||||
notificationService.warn(localize('sparkKernelRequiresConnection', 'Cannot use kernel {0} as no connection is active. The default kernel of {1} will be used instead.', savedKernelInfo.display_name, defaultKernel.display_name));
|
||||
}
|
||||
}
|
||||
|
||||
// If no default kernel specified (should never happen), default to python3
|
||||
if (!defaultKernel) {
|
||||
defaultKernel = {
|
||||
name: notebookConstants.python3,
|
||||
display_name: notebookConstants.python3DisplayName
|
||||
};
|
||||
}
|
||||
return defaultKernel;
|
||||
}
|
||||
|
||||
private static updateConfig(config: ISparkMagicConfig, creds: ICredentials, homePath: string): void {
|
||||
config.kernel_python_credentials = creds;
|
||||
config.kernel_scala_credentials = creds;
|
||||
config.kernel_r_credentials = creds;
|
||||
config.logging_config.handlers.magicsHandler.home_path = homePath;
|
||||
}
|
||||
}
|
||||
|
||||
interface ICredentials {
|
||||
'url': string;
|
||||
}
|
||||
|
||||
interface ISparkMagicConfig {
|
||||
kernel_python_credentials: ICredentials;
|
||||
kernel_scala_credentials: ICredentials;
|
||||
kernel_r_credentials: ICredentials;
|
||||
ignore_ssl_errors?: boolean;
|
||||
logging_config: {
|
||||
handlers: {
|
||||
magicsHandler: {
|
||||
home_path: string;
|
||||
class?: string;
|
||||
formatter?: string
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export interface IKernelJupyterID {
|
||||
id: string;
|
||||
jupyterId: string;
|
||||
}
|
||||
@@ -49,6 +49,7 @@ import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHos
|
||||
import { IResourceInput } from 'vs/platform/editor/common/editor';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
|
||||
export const NOTEBOOK_SELECTOR: string = 'notebook-component';
|
||||
|
||||
@@ -92,7 +93,8 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
@Inject(IWindowService) private windowService: IWindowService,
|
||||
@Inject(IViewletService) private viewletService: IViewletService,
|
||||
@Inject(IUntitledEditorService) private untitledEditorService: IUntitledEditorService,
|
||||
@Inject(IEditorGroupsService) private editorGroupService: IEditorGroupsService
|
||||
@Inject(IEditorGroupsService) private editorGroupService: IEditorGroupsService,
|
||||
@Inject(ICapabilitiesService) private capabilitiesService: ICapabilitiesService
|
||||
) {
|
||||
super();
|
||||
this.updateProfile();
|
||||
@@ -105,11 +107,11 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
// use global connection if possible
|
||||
let profile = TaskUtilities.getCurrentGlobalConnection(this.objectExplorerService, this.connectionManagementService, this.editorService);
|
||||
// TODO use generic method to match kernel with valid connection that's compatible. For now, we only have 1
|
||||
if (profile && profile.providerName === notebookConstants.hadoopKnoxProviderName) {
|
||||
if (profile && profile.providerName) {
|
||||
this.profile = profile;
|
||||
} else {
|
||||
// if not, try 1st active connection that matches our filter
|
||||
let profiles = this.connectionManagementService.getActiveConnections([notebookConstants.hadoopKnoxProviderName]);
|
||||
let profiles = this.connectionManagementService.getActiveConnections();
|
||||
if (profiles && profiles.length > 0) {
|
||||
this.profile = profiles[0];
|
||||
}
|
||||
@@ -239,7 +241,10 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
connectionService: this.connectionManagementService,
|
||||
notificationService: this.notificationService,
|
||||
notebookManagers: this.notebookManagers,
|
||||
providerId: notebookUtils.sqlNotebooksEnabled() ? 'sql' : 'jupyter' // this is tricky; really should also depend on the connection profile
|
||||
standardKernels: this._notebookParams.input.standardKernels,
|
||||
providerId: notebookUtils.sqlNotebooksEnabled() ? 'sql' : 'jupyter', // this is tricky; really should also depend on the connection profile
|
||||
defaultKernel: this._notebookParams.input.defaultKernel,
|
||||
capabilitiesService: this.capabilitiesService
|
||||
}, false, this.profile);
|
||||
model.onError((errInfo: INotification) => this.handleModelError(errInfo));
|
||||
await model.requestModelLoad(this._notebookParams.isTrusted);
|
||||
@@ -324,7 +329,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
|
||||
let attachToContainer = document.createElement('div');
|
||||
let attachTodropdwon = new AttachToDropdown(attachToContainer, this.contextViewService, this.modelRegistered,
|
||||
this.connectionManagementService, this.connectionDialogService, this.notificationService);
|
||||
this.connectionManagementService, this.connectionDialogService, this.notificationService, this.capabilitiesService);
|
||||
attachTodropdwon.render(attachToContainer);
|
||||
attachSelectBoxStyler(attachTodropdwon, this.themeService);
|
||||
|
||||
|
||||
@@ -12,12 +12,13 @@ import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview
|
||||
import { INotificationService, Severity, INotificationActions } from 'vs/platform/notification/common/notification';
|
||||
|
||||
import { SelectBox, ISelectBoxOptionsWithLabel } from 'sql/base/browser/ui/selectBox/selectBox';
|
||||
import { INotebookModel, notebookConstants } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import { INotebookModel } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import { CellType } from 'sql/parts/notebook/models/contracts';
|
||||
import { NotebookComponent } from 'sql/parts/notebook/notebook.component';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { IConnectionManagementService, IConnectionDialogService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { getErrorMessage } from 'sql/parts/notebook/notebookUtils';
|
||||
import { IConnectionManagementService, IConnectionDialogService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { noKernel } from 'sql/workbench/services/notebook/common/sessionManager';
|
||||
|
||||
const msgLoading = localize('loading', 'Loading kernels...');
|
||||
@@ -26,7 +27,7 @@ const attachToLabel: string = localize('AttachTo', 'Attach to: ');
|
||||
const msgLoadingContexts = localize('loadingContexts', 'Loading contexts...');
|
||||
const msgAddNewConnection = localize('addNewConnection', 'Add new connection');
|
||||
const msgSelectConnection = localize('selectConnection', 'Select connection');
|
||||
const msgLocalHost = localize('localhost', 'Localhost');
|
||||
const msgLocalHost = localize('localhost', 'localhost');
|
||||
|
||||
// Action to add a cell to notebook based on cell type(code/markdown).
|
||||
export class AddCellAction extends Action {
|
||||
@@ -209,7 +210,8 @@ export class AttachToDropdown extends SelectBox {
|
||||
constructor(container: HTMLElement, contextViewProvider: IContextViewProvider, modelRegistered: Promise<INotebookModel>,
|
||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
|
||||
@IConnectionDialogService private _connectionDialogService: IConnectionDialogService,
|
||||
@INotificationService private _notificationService: INotificationService) {
|
||||
@INotificationService private _notificationService: INotificationService,
|
||||
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService) {
|
||||
super([msgLoadingContexts], msgLoadingContexts, contextViewProvider, container, { labelText: attachToLabel, labelOnTop: false } as ISelectBoxOptionsWithLabel);
|
||||
if (modelRegistered) {
|
||||
modelRegistered
|
||||
@@ -219,8 +221,8 @@ export class AttachToDropdown extends SelectBox {
|
||||
});
|
||||
}
|
||||
this.onDidSelect(e => {
|
||||
let connection = this.model.contexts.otherConnections.find((c) => c.options.host === e.selected);
|
||||
this.doChangeContext(connection);
|
||||
let connection = this.model.contexts.otherConnections.find((c) => c.serverName === e.selected);
|
||||
this.doChangeContext(new ConnectionProfile(this._capabilitiesService, connection));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -228,54 +230,55 @@ export class AttachToDropdown extends SelectBox {
|
||||
this.model = model;
|
||||
model.contextsChanged(() => {
|
||||
if (this.model.clientSession.kernel && this.model.clientSession.kernel.name) {
|
||||
let currentKernel = this.model.clientSession.kernel.name;
|
||||
this.loadAttachToDropdown(this.model, currentKernel);
|
||||
let currentKernelSpec = this.model.specs.kernels.find(kernel => kernel.name === this.model.clientSession.kernel.name);
|
||||
this.loadAttachToDropdown(this.model, currentKernelSpec.display_name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Load "Attach To" dropdown with the values corresponding to Kernel dropdown
|
||||
public async loadAttachToDropdown(model: INotebookModel, currentKernel: string): Promise<void> {
|
||||
if (currentKernel === notebookConstants.python3 || currentKernel === noKernel) {
|
||||
let connProviderIds = this.model.getApplicableConnectionProviderIds(currentKernel);
|
||||
if ((connProviderIds && connProviderIds.length === 0) || currentKernel === noKernel) {
|
||||
this.setOptions([msgLocalHost]);
|
||||
}
|
||||
else {
|
||||
let hadoopConnections = this.getHadoopConnections(model);
|
||||
let connections = this.getConnections(model);
|
||||
this.enable();
|
||||
if (hadoopConnections.length === 1 && hadoopConnections[0] === msgAddNewConnection) {
|
||||
hadoopConnections.unshift(msgSelectConnection);
|
||||
if (connections.length === 1 && connections[0] === msgAddNewConnection) {
|
||||
connections.unshift(msgSelectConnection);
|
||||
this.selectWithOptionName(msgSelectConnection);
|
||||
}
|
||||
else {
|
||||
hadoopConnections.push(msgAddNewConnection);
|
||||
connections.push(msgAddNewConnection);
|
||||
}
|
||||
this.setOptions(hadoopConnections);
|
||||
this.setOptions(connections);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//Get hadoop connections from context
|
||||
public getHadoopConnections(model: INotebookModel): string[] {
|
||||
let otherHadoopConnections: IConnectionProfile[] = [];
|
||||
model.contexts.otherConnections.forEach((conn) => { otherHadoopConnections.push(conn); });
|
||||
this.selectWithOptionName(model.contexts.defaultConnection.options.host);
|
||||
otherHadoopConnections = this.setHadoopConnectionsList(model.contexts.defaultConnection, model.contexts.otherConnections);
|
||||
let hadoopConnections = otherHadoopConnections.map((context) => context.options.host);
|
||||
return hadoopConnections;
|
||||
//Get connections from context
|
||||
public getConnections(model: INotebookModel): string[] {
|
||||
let otherConnections: ConnectionProfile[] = [];
|
||||
model.contexts.otherConnections.forEach((conn) => { otherConnections.push(conn); });
|
||||
this.selectWithOptionName(model.contexts.defaultConnection.serverName);
|
||||
otherConnections = this.setConnectionsList(model.contexts.defaultConnection, model.contexts.otherConnections);
|
||||
let connections = otherConnections.map((context) => context.serverName);
|
||||
return connections;
|
||||
}
|
||||
|
||||
private setHadoopConnectionsList(defaultHadoopConnection: IConnectionProfile, otherHadoopConnections: IConnectionProfile[]) {
|
||||
if (defaultHadoopConnection.options.host !== msgSelectConnection) {
|
||||
otherHadoopConnections = otherHadoopConnections.filter(conn => conn.options.host !== defaultHadoopConnection.options.host);
|
||||
otherHadoopConnections.unshift(defaultHadoopConnection);
|
||||
if (otherHadoopConnections.length > 1) {
|
||||
otherHadoopConnections = otherHadoopConnections.filter(val => val.options.host !== msgSelectConnection);
|
||||
private setConnectionsList(defaultConnection: ConnectionProfile, otherConnections: ConnectionProfile[]) {
|
||||
if (defaultConnection.serverName !== msgSelectConnection) {
|
||||
otherConnections = otherConnections.filter(conn => conn.serverName !== defaultConnection.serverName);
|
||||
otherConnections.unshift(defaultConnection);
|
||||
if (otherConnections.length > 1) {
|
||||
otherConnections = otherConnections.filter(val => val.serverName !== msgSelectConnection);
|
||||
}
|
||||
}
|
||||
return otherHadoopConnections;
|
||||
return otherConnections;
|
||||
}
|
||||
|
||||
public doChangeContext(connection?: IConnectionProfile): void {
|
||||
public doChangeContext(connection?: ConnectionProfile): void {
|
||||
if (this.value === msgAddNewConnection) {
|
||||
this.openConnectionDialog();
|
||||
} else {
|
||||
@@ -291,15 +294,15 @@ export class AttachToDropdown extends SelectBox {
|
||||
**/
|
||||
public async openConnectionDialog(): Promise<void> {
|
||||
try {
|
||||
//TODO: Figure out how to plumb through the correct provider here
|
||||
await this._connectionDialogService.openDialogAndWait(this._connectionManagementService, { connectionType: 1, providers: [notebookConstants.hadoopKnoxProviderName] }).then(connection => {
|
||||
await this._connectionDialogService.openDialogAndWait(this._connectionManagementService, { connectionType: 1, providers: this.model.getApplicableConnectionProviderIds(this.model.clientSession.kernel.name) }).then(connection => {
|
||||
let attachToConnections = this.values;
|
||||
if (!connection) {
|
||||
this.loadAttachToDropdown(this.model, this.model.clientSession.kernel.name);
|
||||
return;
|
||||
}
|
||||
let connectedServer = connection.options[notebookConstants.hostPropName];
|
||||
//Check to see if the same host is already there in dropdown. We only have host names in dropdown
|
||||
let connectionProfile = new ConnectionProfile(this._capabilitiesService, connection);
|
||||
let connectedServer = connectionProfile.serverName;
|
||||
//Check to see if the same server is already there in dropdown. We only have server names in dropdown
|
||||
if (attachToConnections.some(val => val === connectedServer)) {
|
||||
this.loadAttachToDropdown(this.model, this.model.clientSession.kernel.name);
|
||||
this.doChangeContext();
|
||||
@@ -320,7 +323,7 @@ export class AttachToDropdown extends SelectBox {
|
||||
this.select(index);
|
||||
|
||||
// Call doChangeContext to set the newly chosen connection in the model
|
||||
this.doChangeContext(connection);
|
||||
this.doChangeContext(connectionProfile);
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
|
||||
@@ -13,7 +13,9 @@ import { Emitter, Event } from 'vs/base/common/event';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import * as sqlops from 'sqlops';
|
||||
|
||||
import { IStandardKernelWithProvider } from 'sql/parts/notebook/notebookUtils';
|
||||
import { INotebookService, INotebookEditor } from 'sql/workbench/services/notebook/common/notebookService';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
@@ -27,10 +29,13 @@ export class NotebookInputModel extends EditorModel {
|
||||
private dirty: boolean;
|
||||
private readonly _onDidChangeDirty: Emitter<void> = this._register(new Emitter<void>());
|
||||
private _providerId: string;
|
||||
private _standardKernels: IStandardKernelWithProvider[];
|
||||
private _defaultKernel: sqlops.nb.IKernelSpec;
|
||||
constructor(public readonly notebookUri: URI, private readonly handle: number, private _isTrusted: boolean = false, private saveHandler?: ModeViewSaveHandler, provider?: string, private _providers?: string[]) {
|
||||
super();
|
||||
this.dirty = false;
|
||||
this._providerId = provider;
|
||||
this._standardKernels = [];
|
||||
}
|
||||
|
||||
public get providerId(): string {
|
||||
@@ -49,6 +54,28 @@ export class NotebookInputModel extends EditorModel {
|
||||
this._providers = value;
|
||||
}
|
||||
|
||||
public get standardKernels(): IStandardKernelWithProvider[] {
|
||||
return this._standardKernels;
|
||||
}
|
||||
|
||||
public set standardKernels(value: IStandardKernelWithProvider[]) {
|
||||
value.forEach(kernel => {
|
||||
this._standardKernels.push({
|
||||
connectionProviderIds: kernel.connectionProviderIds,
|
||||
name: kernel.name,
|
||||
notebookProvider: kernel.notebookProvider
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public get defaultKernel(): sqlops.nb.IKernelSpec {
|
||||
return this._defaultKernel;
|
||||
}
|
||||
|
||||
public set defaultKernel(kernel: sqlops.nb.IKernelSpec) {
|
||||
this._defaultKernel = kernel;
|
||||
}
|
||||
|
||||
get isTrusted(): boolean {
|
||||
return this._isTrusted;
|
||||
}
|
||||
@@ -116,6 +143,14 @@ export class NotebookInput extends EditorInput {
|
||||
return this._model.providers;
|
||||
}
|
||||
|
||||
public get standardKernels(): IStandardKernelWithProvider[] {
|
||||
return this._model.standardKernels;
|
||||
}
|
||||
|
||||
public get defaultKernel(): sqlops.nb.IKernelSpec {
|
||||
return this._model.defaultKernel;
|
||||
}
|
||||
|
||||
public getTypeId(): string {
|
||||
return NotebookInput.ID;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,28 @@ export function getProvidersForFileName(fileName: string, notebookService: INote
|
||||
return providers;
|
||||
}
|
||||
|
||||
export function getStandardKernelsForProvider(providerId: string, notebookService: INotebookService) : IStandardKernelWithProvider[] {
|
||||
if (!providerId || !notebookService) {
|
||||
return [];
|
||||
}
|
||||
let standardKernels = notebookService.getStandardKernelsForProvider(providerId);
|
||||
standardKernels.forEach(kernel => {
|
||||
Object.assign(<IStandardKernelWithProvider>kernel, {
|
||||
name: kernel.name,
|
||||
connectionProviderIds: kernel.connectionProviderIds,
|
||||
notebookProvider: providerId
|
||||
});
|
||||
});
|
||||
return <IStandardKernelWithProvider[]>(standardKernels);
|
||||
}
|
||||
|
||||
// Private feature flag to enable Sql Notebook experience
|
||||
export function sqlNotebooksEnabled() {
|
||||
return process.env['SQLOPS_SQL_NOTEBOOK'] !== undefined;
|
||||
}
|
||||
|
||||
export interface IStandardKernelWithProvider {
|
||||
readonly name: string;
|
||||
readonly connectionProviderIds: string[];
|
||||
readonly notebookProvider: string;
|
||||
}
|
||||
@@ -325,7 +325,7 @@ output-component .jp-RenderedHTMLCommon table {
|
||||
border: none;
|
||||
color: var(--jp-ui-font-color1);
|
||||
font-size: 12px;
|
||||
table-layout: fixed;
|
||||
table-layout: auto;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
@@ -338,7 +338,7 @@ output-component .jp-RenderedHTMLCommon thead {
|
||||
output-component .jp-RenderedHTMLCommon td,
|
||||
output-component .jp-RenderedHTMLCommon th,
|
||||
output-component .jp-RenderedHTMLCommon tr {
|
||||
text-align: right;
|
||||
text-align: left;
|
||||
vertical-align: middle;
|
||||
padding: 0.5em 0.5em;
|
||||
line-height: normal;
|
||||
@@ -370,6 +370,7 @@ output-component .jp-RenderedHTMLCommon tbody tr:hover {
|
||||
|
||||
output-component .jp-RenderedHTMLCommon table {
|
||||
margin-bottom: 1em;
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
output-component .jp-RenderedHTMLCommon p {
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
// TODO: The content of this file should be refactored to an extension
|
||||
export function getKnoxUrl(host: string, port: string): string {
|
||||
return `https://${host}:${port}/gateway`;
|
||||
}
|
||||
|
||||
export function getLivyUrl(serverName: string, port: string): string {
|
||||
return getKnoxUrl(serverName, port) + '/default/livy/v1/';
|
||||
}
|
||||
16
src/sql/sqlops.proposed.d.ts
vendored
16
src/sql/sqlops.proposed.d.ts
vendored
@@ -1594,6 +1594,11 @@ declare module 'sqlops' {
|
||||
* Optional ID indicating the initial connection to use for this editor
|
||||
*/
|
||||
connectionId?: string;
|
||||
|
||||
/**
|
||||
* Default kernel for notebook
|
||||
*/
|
||||
defaultKernel?: nb.IKernelSpec;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1666,9 +1671,14 @@ declare module 'sqlops' {
|
||||
*/
|
||||
export function registerNotebookProvider(provider: NotebookProvider): vscode.Disposable;
|
||||
|
||||
export interface IStandardKernel {
|
||||
readonly name: string;
|
||||
readonly connectionProviderIds: string[];
|
||||
}
|
||||
|
||||
export interface NotebookProvider {
|
||||
readonly providerId: string;
|
||||
readonly standardKernels: string[];
|
||||
readonly standardKernels: IStandardKernel[];
|
||||
getNotebookManager(notebookUri: vscode.Uri): Thenable<NotebookManager>;
|
||||
handleNotebookClosed(notebookUri: vscode.Uri): void;
|
||||
}
|
||||
@@ -1945,6 +1955,10 @@ declare module 'sqlops' {
|
||||
defaultKernelLoaded?: boolean;
|
||||
|
||||
changeKernel(kernelInfo: IKernelSpec): Thenable<IKernel>;
|
||||
|
||||
configureKernel(kernelInfo: IKernelSpec): Thenable<void>;
|
||||
|
||||
configureConnection(connection: IConnectionProfile): Thenable<void>;
|
||||
}
|
||||
|
||||
export interface ISessionOptions {
|
||||
|
||||
@@ -125,6 +125,16 @@ export class ExtHostNotebook implements ExtHostNotebookShape {
|
||||
return session.changeKernel(kernelInfo).then(kernel => this.saveKernel(kernel));
|
||||
}
|
||||
|
||||
$configureKernel(sessionId: number, kernelInfo: sqlops.nb.IKernelSpec): Thenable<void> {
|
||||
let session = this._getAdapter<sqlops.nb.ISession>(sessionId);
|
||||
return session.configureKernel(kernelInfo).then(() => null);
|
||||
}
|
||||
|
||||
$configureConnection(sessionId: number, connection: sqlops.IConnectionProfile): Thenable<void> {
|
||||
let session = this._getAdapter<sqlops.nb.ISession>(sessionId);
|
||||
return session.configureConnection(connection).then(() => null);
|
||||
}
|
||||
|
||||
$getKernelReadyStatus(kernelId: number): Thenable<sqlops.nb.IInfoReply> {
|
||||
let kernel = this._getAdapter<sqlops.nb.IKernel>(kernelId);
|
||||
return kernel.ready.then(success => kernel.info);
|
||||
|
||||
@@ -168,6 +168,7 @@ export class ExtHostNotebookDocumentsAndEditors implements ExtHostNotebookDocume
|
||||
options.position = showOptions.viewColumn;
|
||||
options.providerId = showOptions.providerId;
|
||||
options.connectionId = showOptions.connectionId;
|
||||
options.defaultKernel = showOptions.defaultKernel;
|
||||
}
|
||||
let id = await this._proxy.$tryShowNotebookDocument(uri, options);
|
||||
let editor = this.getEditor(id);
|
||||
|
||||
@@ -288,11 +288,30 @@ class SessionWrapper implements sqlops.nb.ISession {
|
||||
return this.doChangeKernel(kernelInfo);
|
||||
}
|
||||
|
||||
configureKernel(kernelInfo: sqlops.nb.IKernelSpec): Thenable<void> {
|
||||
return this.doConfigureKernel(kernelInfo);
|
||||
}
|
||||
|
||||
configureConnection(connection: sqlops.IConnectionProfile): Thenable<void> {
|
||||
if (connection['capabilitiesService'] !== undefined) {
|
||||
connection['capabilitiesService'] = undefined;
|
||||
}
|
||||
return this.doConfigureConnection(connection);
|
||||
}
|
||||
|
||||
private async doChangeKernel(kernelInfo: sqlops.nb.IKernelSpec): Promise<sqlops.nb.IKernel> {
|
||||
let kernelDetails = await this._proxy.ext.$changeKernel(this.sessionDetails.sessionId, kernelInfo);
|
||||
this._kernel = new KernelWrapper(this._proxy, kernelDetails);
|
||||
return this._kernel;
|
||||
}
|
||||
|
||||
private async doConfigureKernel(kernelInfo: sqlops.nb.IKernelSpec): Promise<void> {
|
||||
await this._proxy.ext.$configureKernel(this.sessionDetails.sessionId, kernelInfo);
|
||||
}
|
||||
|
||||
private async doConfigureConnection(connection: sqlops.IConnectionProfile): Promise<void> {
|
||||
await this._proxy.ext.$configureConnection(this.sessionDetails.sessionId, connection);
|
||||
}
|
||||
}
|
||||
|
||||
class KernelWrapper implements sqlops.nb.IKernel {
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
import { NotebookInputModel, NotebookInput } from 'sql/parts/notebook/notebookInput';
|
||||
import { INotebookService, INotebookEditor, DEFAULT_NOTEBOOK_PROVIDER } from 'sql/workbench/services/notebook/common/notebookService';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { getProvidersForFileName } from 'sql/parts/notebook/notebookUtils';
|
||||
import { getProvidersForFileName, getStandardKernelsForProvider } from 'sql/parts/notebook/notebookUtils';
|
||||
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { disposed } from 'vs/base/common/errors';
|
||||
import { ICellModel, NotebookContentChange } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
@@ -332,6 +332,11 @@ export class MainThreadNotebookDocumentsAndEditors extends Disposable implements
|
||||
}
|
||||
model.providers = providers;
|
||||
model.providerId = providerId;
|
||||
model.defaultKernel = options && options.defaultKernel;
|
||||
model.providers.forEach(provider => {
|
||||
let standardKernels = getStandardKernelsForProvider(provider, this._notebookService);
|
||||
model.standardKernels = standardKernels;
|
||||
});
|
||||
let input = this._instantiationService.createInstance(NotebookInput, undefined, model);
|
||||
|
||||
let editor = await this._editorService.openEditor(input, editorOptions, viewColumnToEditorGroup(this._editorGroupService, options.position));
|
||||
|
||||
@@ -773,6 +773,8 @@ export interface ExtHostNotebookShape {
|
||||
|
||||
// Session APIs
|
||||
$changeKernel(sessionId: number, kernelInfo: sqlops.nb.IKernelSpec): Thenable<INotebookKernelDetails>;
|
||||
$configureKernel(sessionId: number, kernelInfo: sqlops.nb.IKernelSpec): Thenable<void>;
|
||||
$configureConnection(sessionId: number, connection: sqlops.IConnectionProfile): Thenable<void>;
|
||||
|
||||
// Kernel APIs
|
||||
$getKernelReadyStatus(kernelId: number): Thenable<sqlops.nb.IInfoReply>;
|
||||
@@ -829,6 +831,7 @@ export interface INotebookShowOptions {
|
||||
preview?: boolean;
|
||||
providerId?: string;
|
||||
connectionId?: string;
|
||||
defaultKernel?: sqlops.nb.IKernelSpec;
|
||||
}
|
||||
|
||||
export interface ExtHostNotebookDocumentsAndEditorsShape {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { localize } from 'vs/nls';
|
||||
import * as platform from 'vs/platform/registry/common/platform';
|
||||
import * as sqlops from 'sqlops';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
|
||||
export const Extensions = {
|
||||
@@ -18,7 +19,7 @@ export const Extensions = {
|
||||
export interface NotebookProviderRegistration {
|
||||
provider: string;
|
||||
fileExtensions: string | string[];
|
||||
standardKernels: string | string[];
|
||||
standardKernels: sqlops.nb.IStandardKernel | sqlops.nb.IStandardKernel[];
|
||||
}
|
||||
|
||||
let notebookProviderType: IJSONSchema = {
|
||||
@@ -44,11 +45,38 @@ let notebookProviderType: IJSONSchema = {
|
||||
standardKernels: {
|
||||
description: localize('carbon.extension.contributes.notebook.standardKernels', 'What kernels should be standard with this notebook provider'),
|
||||
oneOf: [
|
||||
{ type: 'string' },
|
||||
{
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
connectionProviderIds: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string'
|
||||
type: 'object',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
connectionProviderIds: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -48,6 +48,8 @@ export interface INotebookService {
|
||||
|
||||
getProvidersForFileType(fileType: string): string[];
|
||||
|
||||
getStandardKernelsForProvider(provider: string): sqlops.nb.IStandardKernel[];
|
||||
|
||||
/**
|
||||
* Initializes and returns a Notebook manager that can handle all important calls to open, display, and
|
||||
* run cells in a notebook.
|
||||
|
||||
@@ -80,6 +80,7 @@ export class NotebookService extends Disposable implements INotebookService {
|
||||
private _onNotebookEditorRename = new Emitter<INotebookEditor>();
|
||||
private _editors = new Map<string, INotebookEditor>();
|
||||
private _fileToProviders = new Map<string, NotebookProviderRegistration[]>();
|
||||
private _providerToStandardKernels = new Map<string, nb.IStandardKernel[]>();
|
||||
private _registrationComplete = new Deferred<void>();
|
||||
private _isRegistrationComplete = false;
|
||||
|
||||
@@ -121,6 +122,9 @@ export class NotebookService extends Disposable implements INotebookService {
|
||||
this.addFileProvider(registration.fileExtensions, registration);
|
||||
}
|
||||
}
|
||||
if (registration.standardKernels) {
|
||||
this.addStandardKernels(registration);
|
||||
}
|
||||
}
|
||||
|
||||
registerProvider(providerId: string, instance: INotebookProvider): void {
|
||||
@@ -154,6 +158,26 @@ export class NotebookService extends Disposable implements INotebookService {
|
||||
this._fileToProviders.set(fileType.toUpperCase(), providers);
|
||||
}
|
||||
|
||||
// Standard kernels are contributed where a list of kernels are defined that can be shown
|
||||
// in the kernels dropdown list before a SessionManager has been started; this way,
|
||||
// every NotebookProvider doesn't need to have an active SessionManager in order to contribute
|
||||
// kernels to the dropdown
|
||||
private addStandardKernels(provider: NotebookProviderRegistration) {
|
||||
let providerUpperCase = provider.provider.toUpperCase();
|
||||
let standardKernels = this._providerToStandardKernels.get(providerUpperCase);
|
||||
if (!standardKernels) {
|
||||
standardKernels = [];
|
||||
}
|
||||
if (Array.isArray(provider.standardKernels)) {
|
||||
provider.standardKernels.forEach(kernel => {
|
||||
standardKernels.push(kernel);
|
||||
});
|
||||
} else {
|
||||
standardKernels.push(provider.standardKernels);
|
||||
}
|
||||
this._providerToStandardKernels.set(providerUpperCase, standardKernels);
|
||||
}
|
||||
|
||||
getSupportedFileExtensions(): string[] {
|
||||
return Array.from(this._fileToProviders.keys());
|
||||
}
|
||||
@@ -165,6 +189,10 @@ export class NotebookService extends Disposable implements INotebookService {
|
||||
return providers ? providers.map(provider => provider.provider) : undefined;
|
||||
}
|
||||
|
||||
getStandardKernelsForProvider(provider: string): nb.IStandardKernel[] {
|
||||
return this._providerToStandardKernels.get(provider.toUpperCase());
|
||||
}
|
||||
|
||||
public shutdown(): void {
|
||||
this._managersMap.forEach(manager => {
|
||||
manager.forEach(m => {
|
||||
@@ -337,7 +365,7 @@ export class NotebookService extends Disposable implements INotebookService {
|
||||
notebookRegistry.registerNotebookProvider({
|
||||
provider: sqlProvider.providerId,
|
||||
fileExtensions: DEFAULT_NOTEBOOK_FILETYPE,
|
||||
standardKernels: ['SQL']
|
||||
standardKernels: { name: 'SQL', connectionProviderIds: ['MSSQL'] }
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -444,5 +472,4 @@ export class SqlNotebookManager implements INotebookManager {
|
||||
public get sessionManager(): nb.SessionManager {
|
||||
return this._sessionManager;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
import { nb } from 'sqlops';
|
||||
import { localize } from 'vs/nls';
|
||||
import { FutureInternal } from 'sql/parts/notebook/models/modelInterfaces';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
|
||||
export const noKernel: string = localize('noKernel', 'No Kernel');
|
||||
const runNotebookDisabled = localize('runNotebookDisabled', 'Cannot run cells as no kernel has been configured');
|
||||
@@ -91,6 +92,15 @@ export class EmptySession implements nb.ISession {
|
||||
changeKernel(kernelInfo: nb.IKernelSpec): Thenable<nb.IKernel> {
|
||||
return Promise.resolve(this.kernel);
|
||||
}
|
||||
|
||||
// No kernel config necessary for empty session
|
||||
configureKernel(kernelInfo: nb.IKernelSpec): Thenable<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
configureConnection(connection: ConnectionProfile): Thenable<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
class EmptyKernel implements nb.IKernel {
|
||||
|
||||
@@ -14,8 +14,9 @@ import Severity from 'vs/base/common/severity';
|
||||
import * as Utils from 'sql/platform/connection/common/utils';
|
||||
import { Deferred } from 'sql/base/common/promise';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
|
||||
export const sqlKernel: string = localize('sqlKernel', 'SQL');
|
||||
export const sqlKernelError: string = localize("sqlKernelError", "SQL kernel error");
|
||||
@@ -63,6 +64,7 @@ export class SqlSessionManager implements nb.SessionManager {
|
||||
export class SqlSession implements nb.ISession {
|
||||
private _kernel: SqlKernel;
|
||||
private _defaultKernelLoaded = false;
|
||||
private _currentConnection: IConnectionProfile;
|
||||
|
||||
public set defaultKernelLoaded(value) {
|
||||
this._defaultKernelLoaded = value;
|
||||
@@ -107,12 +109,25 @@ export class SqlSession implements nb.ISession {
|
||||
changeKernel(kernelInfo: nb.IKernelSpec): Thenable<nb.IKernel> {
|
||||
return Promise.resolve(this.kernel);
|
||||
}
|
||||
|
||||
configureKernel(kernelInfo: nb.IKernelSpec): Thenable<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
configureConnection(connection: ConnectionProfile): Thenable<void> {
|
||||
if (this._kernel) {
|
||||
this._kernel.connection = connection;
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
class SqlKernel extends Disposable implements nb.IKernel {
|
||||
private _queryRunner: QueryRunner;
|
||||
private _columns: IDbColumn[];
|
||||
private _rows: DbCellValue[][];
|
||||
private _currentConnection: IConnectionProfile;
|
||||
static kernelId: number = 0;
|
||||
|
||||
constructor( @IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@@ -121,7 +136,7 @@ class SqlKernel extends Disposable implements nb.IKernel {
|
||||
}
|
||||
|
||||
public get id(): string {
|
||||
return '-1';
|
||||
return (SqlKernel.kernelId++).toString();
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
@@ -159,6 +174,12 @@ class SqlKernel extends Disposable implements nb.IKernel {
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
public set connection(conn: IConnectionProfile) {
|
||||
this._currentConnection = conn;
|
||||
this._queryRunner = undefined;
|
||||
}
|
||||
|
||||
getSpec(): Thenable<nb.IKernelSpec> {
|
||||
return Promise.resolve(sqlKernelSpec);
|
||||
}
|
||||
@@ -167,11 +188,10 @@ class SqlKernel extends Disposable implements nb.IKernel {
|
||||
if (this._queryRunner) {
|
||||
this._queryRunner.runQuery(content.code);
|
||||
} else {
|
||||
let connections = this._connectionManagementService.getActiveConnections();
|
||||
let connectionProfile = connections.find(connection => connection.providerName === mssqlProviderName);
|
||||
let connectionUri = Utils.generateUri(connectionProfile, 'notebook');
|
||||
let connectionUri = Utils.generateUri(this._currentConnection, 'notebook');
|
||||
this._queryRunner = this._instantiationService.createInstance(QueryRunner, connectionUri, undefined);
|
||||
this._connectionManagementService.connect(connectionProfile, connectionUri).then((result) => {
|
||||
this._connectionManagementService.connect(this._currentConnection, connectionUri).then((result) =>
|
||||
{
|
||||
this.addQueryEventListeners(this._queryRunner);
|
||||
this._queryRunner.runQuery(content.code);
|
||||
});
|
||||
@@ -186,7 +206,9 @@ class SqlKernel extends Disposable implements nb.IKernel {
|
||||
}
|
||||
|
||||
interrupt(): Thenable<void> {
|
||||
return Promise.resolve(undefined);
|
||||
// TODO: figure out what to do with the QueryCancelResult
|
||||
return this._queryRunner.cancelQuery().then((cancelResult) => {
|
||||
});
|
||||
}
|
||||
|
||||
private addQueryEventListeners(queryRunner: QueryRunner): void {
|
||||
@@ -234,7 +256,11 @@ export class SQLFuture extends Disposable implements FutureInternal {
|
||||
get inProgress(): boolean {
|
||||
return !this._queryRunner.hasCompleted;
|
||||
}
|
||||
|
||||
set inProgress(val: boolean) {
|
||||
if (this._queryRunner && !val) {
|
||||
this._queryRunner.cancelQuery();
|
||||
}
|
||||
}
|
||||
get msg(): nb.IMessage {
|
||||
return this._msg;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user