mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-24 01:25:37 -05:00
Notebooks: Save connection information in metadata (#13060)
* save connection info in notebook metadata * update attachTo dropdown based on saved alias * add setting for saving connection (default=false) * dont show saved conn if seting off + added test * show conn dialog if save conn name setting off * address PR comments and fix unit test * change connectionName to connection_name
This commit is contained in:
@@ -331,7 +331,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
layoutChanged: this._notebookParams.input.layoutChanged,
|
||||
capabilitiesService: this.capabilitiesService,
|
||||
editorLoadedTimestamp: this._notebookParams.input.editorOpenedTimestamp
|
||||
}, this.profile, this.logService, this.notificationService, this.adstelemetryService, this.capabilitiesService);
|
||||
}, this.profile, this.logService, this.notificationService, this.adstelemetryService, this.connectionManagementService, this._configurationService, this.capabilitiesService);
|
||||
let trusted = await this.notebookService.isNotebookTrustCached(this._notebookParams.notebookUri, this.isDirty());
|
||||
this._register(model.onError((errInfo: INotification) => this.handleModelError(errInfo)));
|
||||
this._register(model.contentChanged((change) => this.handleContentChanged(change)));
|
||||
@@ -429,7 +429,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
|
||||
let attachToContainer = document.createElement('li');
|
||||
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, this._configurationService);
|
||||
attachToDropdown.render(attachToContainer);
|
||||
attachSelectBoxStyler(attachToDropdown, this.themeService);
|
||||
|
||||
@@ -493,7 +493,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
||||
|
||||
let attachToContainer = document.createElement('div');
|
||||
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, this._configurationService);
|
||||
attachToDropdown.render(attachToContainer);
|
||||
attachSelectBoxStyler(attachToDropdown, this.themeService);
|
||||
|
||||
|
||||
@@ -230,7 +230,12 @@ configurationRegistry.registerConfiguration({
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': localize('notebook.setRichTextViewByDefault', "Set Rich Text View mode by default for text cells")
|
||||
}
|
||||
},
|
||||
'notebook.saveConnectionName': {
|
||||
'type': 'boolean',
|
||||
'default': false,
|
||||
'description': localize('notebook.saveConnectionName', "(Preview) Save connection name in notebook metadata.")
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -355,6 +355,7 @@ export class KernelsDropdown extends SelectBox {
|
||||
}
|
||||
|
||||
const attachToDropdownElementId = 'attach-to-dropdown';
|
||||
const saveConnectionNameConfigName = 'notebook.saveConnectionName';
|
||||
|
||||
export class AttachToDropdown extends SelectBox {
|
||||
private model: NotebookModel;
|
||||
@@ -365,6 +366,7 @@ export class AttachToDropdown extends SelectBox {
|
||||
@IConnectionDialogService private _connectionDialogService: IConnectionDialogService,
|
||||
@INotificationService private _notificationService: INotificationService,
|
||||
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
|
||||
@IConfigurationService private _configurationService: IConfigurationService
|
||||
) {
|
||||
super([msgLoadingContexts], msgLoadingContexts, contextViewProvider, container, { labelText: attachToLabel, labelOnTop: false, ariaLabel: attachToLabel, id: attachToDropdownElementId } as ISelectBoxOptionsWithLabel);
|
||||
if (modelReady) {
|
||||
@@ -428,7 +430,14 @@ export class AttachToDropdown extends SelectBox {
|
||||
if ((connProviderIds && connProviderIds.length === 0) || currentKernel === noKernel) {
|
||||
this.setOptions([msgLocalHost]);
|
||||
} else {
|
||||
let connections: string[] = model.context && model.context.title && (connProviderIds.includes(this.model.context.providerName)) ? [model.context.title] : [msgSelectConnection];
|
||||
let connections: string[] = [];
|
||||
if (model.context && model.context.title && (connProviderIds.includes(this.model.context.providerName))) {
|
||||
connections.push(model.context.title);
|
||||
} else if (this._configurationService.getValue(saveConnectionNameConfigName) && model.savedConnectionName) {
|
||||
connections.push(model.savedConnectionName);
|
||||
} else {
|
||||
connections.push(msgSelectConnection);
|
||||
}
|
||||
if (!connections.find(x => x === msgChangeConnection)) {
|
||||
connections.push(msgChangeConnection);
|
||||
}
|
||||
|
||||
@@ -213,5 +213,5 @@ export async function createandLoadNotebookModel(codeContent?: nb.INotebookConte
|
||||
layoutChanged: undefined,
|
||||
capabilitiesService: undefined
|
||||
};
|
||||
return new NotebookModel(defaultModelOptions, undefined, undefined, undefined, undefined);
|
||||
return new NotebookModel(defaultModelOptions, undefined, undefined, undefined, undefined, undefined, undefined);
|
||||
}
|
||||
|
||||
@@ -971,7 +971,7 @@ suite('Notebook Editor Model', function (): void {
|
||||
let options: INotebookModelOptions = assign({}, defaultModelOptions, <Partial<INotebookModelOptions>><unknown>{
|
||||
factory: mockModelFactory.object
|
||||
});
|
||||
notebookModel = new NotebookModel(options, undefined, logService, undefined, new NullAdsTelemetryService());
|
||||
notebookModel = new NotebookModel(options, undefined, logService, undefined, new NullAdsTelemetryService(), queryConnectionService.object, configurationService);
|
||||
await notebookModel.loadContents();
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ import { NotebookEditorContentManager } from 'sql/workbench/contrib/notebook/bro
|
||||
import { NotebookRange } from 'sql/workbench/services/notebook/browser/notebookService';
|
||||
import { NotebookMarkdownRenderer } from 'sql/workbench/contrib/notebook/browser/outputs/notebookMarkdown';
|
||||
import { NullAdsTelemetryService } from 'sql/platform/telemetry/common/adsTelemetryService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
|
||||
|
||||
let expectedNotebookContent: nb.INotebookContents = {
|
||||
cells: [{
|
||||
@@ -73,6 +75,7 @@ suite('Notebook Find Model', function (): void {
|
||||
const logService = new NullLogService();
|
||||
let model: NotebookModel;
|
||||
let markdownRenderer: NotebookMarkdownRenderer = new NotebookMarkdownRenderer();
|
||||
let configurationService: IConfigurationService;
|
||||
|
||||
setup(async () => {
|
||||
sessionReady = new Deferred<void>();
|
||||
@@ -84,6 +87,7 @@ suite('Notebook Find Model', function (): void {
|
||||
queryConnectionService.callBase = true;
|
||||
|
||||
instantiationService = new InstantiationService(serviceCollection, true);
|
||||
configurationService = new TestConfigurationService();
|
||||
defaultModelOptions = {
|
||||
notebookUri: defaultUri,
|
||||
factory: new ModelFactory(instantiationService),
|
||||
@@ -433,7 +437,7 @@ suite('Notebook Find Model', function (): void {
|
||||
mockContentManager.setup(c => c.loadContent()).returns(() => Promise.resolve(contents));
|
||||
defaultModelOptions.contentManager = mockContentManager.object;
|
||||
// Initialize the model
|
||||
model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService());
|
||||
model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService(), queryConnectionService.object, configurationService);
|
||||
await model.loadContents();
|
||||
await model.requestModelLoad();
|
||||
}
|
||||
|
||||
@@ -36,6 +36,9 @@ import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { uriPrefixes } from 'sql/platform/connection/common/utils';
|
||||
import { NullAdsTelemetryService } from 'sql/platform/telemetry/common/adsTelemetryService';
|
||||
import { TestConfigurationService } from 'sql/platform/connection/test/common/testConfigurationService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
|
||||
let expectedNotebookContent: nb.INotebookContents = {
|
||||
cells: [{
|
||||
@@ -87,6 +90,7 @@ let mockModelFactory: TypeMoq.Mock<ModelFactory>;
|
||||
let notificationService: TypeMoq.Mock<INotificationService>;
|
||||
let capabilitiesService: ICapabilitiesService;
|
||||
let instantiationService: IInstantiationService;
|
||||
let configurationService: IConfigurationService;
|
||||
|
||||
suite('notebook model', function (): void {
|
||||
let notebookManagers = [new NotebookManagerStub()];
|
||||
@@ -107,6 +111,7 @@ suite('notebook model', function (): void {
|
||||
queryConnectionService.callBase = true;
|
||||
let serviceCollection = new ServiceCollection();
|
||||
instantiationService = new InstantiationService(serviceCollection, true);
|
||||
configurationService = new TestConfigurationService();
|
||||
defaultModelOptions = {
|
||||
notebookUri: defaultUri,
|
||||
factory: new ModelFactory(instantiationService),
|
||||
@@ -154,7 +159,7 @@ suite('notebook model', function (): void {
|
||||
mockContentManager.setup(c => c.loadContent()).returns(() => Promise.resolve(emptyNotebook));
|
||||
defaultModelOptions.contentManager = mockContentManager.object;
|
||||
// When I initialize the model
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService());
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService(), queryConnectionService.object, configurationService);
|
||||
await model.loadContents();
|
||||
|
||||
// Then I expect to have 0 code cell as the contents
|
||||
@@ -170,7 +175,7 @@ suite('notebook model', function (): void {
|
||||
mockContentManager.setup(c => c.loadContent()).returns(() => Promise.resolve(expectedNotebookContent));
|
||||
defaultModelOptions.contentManager = mockContentManager.object;
|
||||
// When I initialize the model
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService());
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService(), queryConnectionService.object, configurationService);
|
||||
await model.loadContents(true);
|
||||
await model.requestModelLoad();
|
||||
|
||||
@@ -187,7 +192,7 @@ suite('notebook model', function (): void {
|
||||
|
||||
// When I initalize the model
|
||||
// Then it should throw
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService());
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService(), queryConnectionService.object, configurationService);
|
||||
assert.equal(model.inErrorState, false);
|
||||
await assert.rejects(async () => { await model.loadContents(); });
|
||||
assert.equal(model.inErrorState, true);
|
||||
@@ -200,7 +205,7 @@ suite('notebook model', function (): void {
|
||||
defaultModelOptions.contentManager = mockContentManager.object;
|
||||
|
||||
// When I initalize the model
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService());
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService(), queryConnectionService.object, configurationService);
|
||||
await model.loadContents();
|
||||
|
||||
// Then I expect all cells to be in the model
|
||||
@@ -228,7 +233,7 @@ suite('notebook model', function (): void {
|
||||
defaultModelOptions.providerId = 'jupyter';
|
||||
|
||||
// When I initalize the model
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService());
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService(), queryConnectionService.object, configurationService);
|
||||
await model.loadContents();
|
||||
|
||||
// I expect the default provider to be jupyter
|
||||
@@ -238,7 +243,7 @@ suite('notebook model', function (): void {
|
||||
defaultModelOptions.providerId = 'SQL';
|
||||
|
||||
// When I initalize the model
|
||||
model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService());
|
||||
model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService(), queryConnectionService.object, configurationService);
|
||||
await model.loadContents();
|
||||
|
||||
// I expect the default provider to be SQL
|
||||
@@ -263,7 +268,7 @@ suite('notebook model', function (): void {
|
||||
defaultModelOptions.contentManager = mockContentManager.object;
|
||||
|
||||
// When I initalize the model
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService());
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService(), queryConnectionService.object, configurationService);
|
||||
await model.loadContents();
|
||||
|
||||
let activeCellChangeCount = 0;
|
||||
@@ -320,7 +325,7 @@ suite('notebook model', function (): void {
|
||||
defaultModelOptions.contentManager = mockContentManager.object;
|
||||
|
||||
// When I initalize the model
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService());
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService(), queryConnectionService.object, configurationService);
|
||||
await model.loadContents();
|
||||
|
||||
// Count number of times onError event is fired
|
||||
@@ -375,7 +380,7 @@ suite('notebook model', function (): void {
|
||||
sessionReady.resolve();
|
||||
let sessionFired = false;
|
||||
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService());
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService(), queryConnectionService.object, configurationService);
|
||||
model.onClientSessionReady((session) => sessionFired = true);
|
||||
await model.loadContents();
|
||||
await model.requestModelLoad();
|
||||
@@ -405,7 +410,7 @@ suite('notebook model', function (): void {
|
||||
let mockContentManager = TypeMoq.Mock.ofType(NotebookEditorContentManager);
|
||||
mockContentManager.setup(c => c.loadContent()).returns(() => Promise.resolve(expectedNotebookContent));
|
||||
defaultModelOptions.contentManager = mockContentManager.object;
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService());
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService(), queryConnectionService.object, configurationService);
|
||||
await model.requestModelLoad();
|
||||
|
||||
let actualChanged: NotebookContentChange;
|
||||
@@ -474,7 +479,7 @@ suite('notebook model', function (): void {
|
||||
mockContentManager.setup(c => c.loadContent()).returns(() => Promise.resolve(expectedNotebookContent));
|
||||
defaultModelOptions.contentManager = mockContentManager.object;
|
||||
// When I initialize the model
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, undefined);
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, undefined, queryConnectionService.object, configurationService);
|
||||
await model.loadContents();
|
||||
|
||||
let output = model.toJSON();
|
||||
@@ -570,6 +575,57 @@ suite('notebook model', function (): void {
|
||||
queryConnectionService.verify((c) => c.disconnect(TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
});
|
||||
|
||||
test('Should read connection name from notebook metadata and use its corresponding connection profile', async function () {
|
||||
const connectionName = 'connectionName';
|
||||
// Given a notebook with a connection name in metadata
|
||||
let notebook: nb.INotebookContents = {
|
||||
cells: [],
|
||||
metadata: {
|
||||
connection_name: connectionName
|
||||
},
|
||||
nbformat: 4,
|
||||
nbformat_minor: 5
|
||||
};
|
||||
let mockContentManager = TypeMoq.Mock.ofType(NotebookEditorContentManager);
|
||||
mockContentManager.setup(c => c.loadContent()).returns(() => Promise.resolve(notebook));
|
||||
defaultModelOptions.contentManager = mockContentManager.object;
|
||||
|
||||
// And a matching connection profile
|
||||
let expectedConnectionProfile: IConnectionProfile = {
|
||||
connectionName: connectionName,
|
||||
serverName: '',
|
||||
databaseName: '',
|
||||
userName: '',
|
||||
password: '',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: '',
|
||||
groupId: '',
|
||||
getOptionsKey: () => '',
|
||||
matches: undefined,
|
||||
providerName: '',
|
||||
saveProfile: true,
|
||||
id: '',
|
||||
options: {}
|
||||
};
|
||||
sinon.stub(queryConnectionService.object, 'getConnections').returns([expectedConnectionProfile]);
|
||||
sinon.stub(configurationService, 'getValue').returns(true);
|
||||
|
||||
// When I initialize the model
|
||||
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, new NullAdsTelemetryService(), queryConnectionService.object, configurationService);
|
||||
await model.loadContents();
|
||||
|
||||
// I expect the saved connection name to be read
|
||||
assert.equal(model.savedConnectionName, connectionName);
|
||||
|
||||
// When I request a connection
|
||||
let spy = sinon.stub(model, 'changeContext').returns(Promise.resolve());
|
||||
model.requestConnection();
|
||||
|
||||
// I expect the connection profile matching the saved connection name to be used
|
||||
assert.ok(spy.calledWith(connectionName, expectedConnectionProfile));
|
||||
});
|
||||
|
||||
async function loadModelAndStartClientSession(): Promise<NotebookModel> {
|
||||
let mockContentManager = TypeMoq.Mock.ofType(NotebookEditorContentManager);
|
||||
mockContentManager.setup(c => c.loadContent()).returns(() => Promise.resolve(expectedNotebookContent));
|
||||
@@ -583,7 +639,7 @@ suite('notebook model', function (): void {
|
||||
let options: INotebookModelOptions = assign({}, defaultModelOptions, <Partial<INotebookModelOptions>>{
|
||||
factory: mockModelFactory.object
|
||||
});
|
||||
let model = new NotebookModel(options, undefined, logService, undefined, new NullAdsTelemetryService(), capabilitiesService);
|
||||
let model = new NotebookModel(options, undefined, logService, undefined, new NullAdsTelemetryService(), queryConnectionService.object, configurationService, capabilitiesService);
|
||||
model.onClientSessionReady((session) => actualSession = session);
|
||||
await model.requestModelLoad();
|
||||
|
||||
|
||||
@@ -73,6 +73,9 @@ export class NotebookModelStub implements INotebookModel {
|
||||
get context(): ConnectionProfile {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
get savedConnectionName(): string {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
get providerId(): string {
|
||||
throw new Error('method not implemented.');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user