Add grid streaming support for notebooks (#12175)

* add onResultUpdate handler in gridoutput

* convert rows to mimetype and html

* wait for data conversion to finish before saving

* detach changeRef after output is created

* fix save grid action

* move data conversion check to each cell

* move conversion logic to dataprovider

* notify data converting when user saves

* add comments and remove unused methods

* fix method return type

* fix tests

* fix convertData method header

* move azdata changes to azdata proposed

* address PR comments

* display top rows message

* fix messages/table ordering and query 100 rows

* add missing escape import

* set default max rows to 5000

* add undefined check to updateResultSet

* change gridDataConversionComplete return type
This commit is contained in:
Lucy Zhang
2020-09-10 13:31:40 -07:00
committed by GitHub
parent 1528c642d1
commit e3ec6bf9c5
20 changed files with 400 additions and 132 deletions

View File

@@ -177,7 +177,7 @@ suite('CellToolbarActions', function (): void {
});
});
async function createandLoadNotebookModel(codeContent?: nb.INotebookContents): Promise<NotebookModel> {
export async function createandLoadNotebookModel(codeContent?: nb.INotebookContents): Promise<NotebookModel> {
let defaultCodeContent: nb.INotebookContents = {
cells: [{
cell_type: CellTypes.Code,

View File

@@ -21,6 +21,8 @@ import { SerializationService } from 'sql/platform/serialization/common/serializ
import { SaveFormat, ResultSerializer } from 'sql/workbench/services/query/common/resultSerializer';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { URI } from 'vs/base/common/uri';
import { CellModel } from 'sql/workbench/services/notebook/browser/models/cell';
import { createandLoadNotebookModel } from 'sql/workbench/contrib/notebook/test/browser/cellToolbarActions.test';
export class TestSerializationProvider implements azdata.SerializationProvider {
providerId: string;
@@ -63,8 +65,9 @@ suite('Data Resource Data Provider', function () {
id: 0,
rowCount: 2
};
let documentUri = 'untitled:Notebook-0';
let cellModel = TypeMoq.Mock.ofType(CellModel);
let notebookModel = await createandLoadNotebookModel();
cellModel.setup(x => x.notebookModel).returns(() => notebookModel);
tempFolderPath = path.join(os.tmpdir(), `TestDataResourceDataProvider_${uuid.v4()}`);
await fs.mkdir(tempFolderPath);
@@ -88,11 +91,13 @@ suite('Data Resource Data Provider', function () {
let _instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Strict);
_instantiationService.setup(x => x.createInstance(TypeMoq.It.isValue(ResultSerializer)))
.returns(() => serializer);
dataResourceDataProvider = new DataResourceDataProvider(
0, // batchId
0, // id
undefined, // QueryRunner
source,
resultSet,
documentUri,
cellModel.object,
_notificationService,
undefined, // IClipboardService
undefined, // IConfigurationService

View File

@@ -57,6 +57,8 @@ import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/u
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
import { IProductService } from 'vs/platform/product/common/productService';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { INotificationService } from 'vs/platform/notification/common/notification';
class NotebookModelStub extends stubs.NotebookModelStub {
private _cells: Array<ICellModel> = [new CellModel(undefined, undefined)];
@@ -97,10 +99,11 @@ suite.skip('Test class NotebookEditor:', () => {
let queryTextEditor: QueryTextEditor;
let untitledNotebookInput: UntitledNotebookInput;
let notebookEditorStub: NotebookEditorStub;
let notificationService: TypeMoq.Mock<INotificationService>;
setup(async () => {
// setup services
({ instantiationService, workbenchThemeService, notebookService, testTitle, extensionService, cellTextEditorGuid, queryTextEditor, untitledNotebookInput, notebookEditorStub } = setupServices({ instantiationService, workbenchThemeService }));
({ instantiationService, workbenchThemeService, notebookService, testTitle, extensionService, cellTextEditorGuid, queryTextEditor, untitledNotebookInput, notebookEditorStub, notificationService } = setupServices({ instantiationService, workbenchThemeService }));
// Create notebookEditor
notebookEditor = createNotebookEditor(instantiationService, workbenchThemeService, notebookService);
});
@@ -121,7 +124,7 @@ suite.skip('Test class NotebookEditor:', () => {
const untitledTextInput = instantiationService.createInstance(UntitledTextEditorInput, untitledTextEditorService.create({ associatedResource: untitledUri }));
const untitledNotebookInput = new UntitledNotebookInput(
testTitle, untitledUri, untitledTextInput,
undefined, instantiationService, notebookService, extensionService
undefined, instantiationService, notebookService, extensionService, notificationService.object
);
const testNotebookEditor = new NotebookEditorStub({ cellGuid: cellTextEditorGuid, editor: queryTextEditor, model: notebookModel, notebookParams: <INotebookParams>{ notebookUri: untitledNotebookInput.notebookUri } });
notebookService.addNotebookEditor(testNotebookEditor);
@@ -669,6 +672,7 @@ function setupServices(arg: { workbenchThemeService?: WorkbenchThemeService, ins
const uninstallEvent = new Emitter<IExtensionIdentifier>();
const didUninstallEvent = new Emitter<DidUninstallExtensionEvent>();
const notificationService = TypeMoq.Mock.ofType(TestNotificationService, TypeMoq.MockBehavior.Loose);
const instantiationService = arg.instantiationService ?? <TestInstantiationService>workbenchInstantiationService();
const workbenchThemeService = arg.workbenchThemeService ?? instantiationService.createInstance(WorkbenchThemeService);
instantiationService.stub(IWorkbenchThemeService, workbenchThemeService);
@@ -705,7 +709,7 @@ function setupServices(arg: { workbenchThemeService?: WorkbenchThemeService, ins
const untitledTextInput = instantiationService.createInstance(UntitledTextEditorInput, untitledTextEditorService.create({ associatedResource: untitledUri }));
const untitledNotebookInput = new UntitledNotebookInput(
testTitle, untitledUri, untitledTextInput,
undefined, instantiationService, notebookService, extensionService
undefined, instantiationService, notebookService, extensionService, notificationService.object
);
const cellTextEditorGuid = generateUuid();
@@ -720,7 +724,7 @@ function setupServices(arg: { workbenchThemeService?: WorkbenchThemeService, ins
);
const notebookEditorStub = new NotebookEditorStub({ cellGuid: cellTextEditorGuid, editor: queryTextEditor, model: new NotebookModelStub(), notebookParams: <INotebookParams>{ notebookUri: untitledNotebookInput.notebookUri } });
notebookService.addNotebookEditor(notebookEditorStub);
return { instantiationService, workbenchThemeService, notebookService, testTitle, extensionService, cellTextEditorGuid, queryTextEditor, untitledNotebookInput, notebookEditorStub };
return { instantiationService, workbenchThemeService, notebookService, testTitle, extensionService, cellTextEditorGuid, queryTextEditor, untitledNotebookInput, notebookEditorStub, notificationService };
}
function createNotebookEditor(instantiationService: TestInstantiationService, workbenchThemeService: WorkbenchThemeService, notebookService: NotebookService) {

View File

@@ -20,6 +20,7 @@ import { IExtensionService, NullExtensionService } from 'vs/workbench/services/e
import { INotebookService, IProviderInfo } from 'sql/workbench/services/notebook/browser/notebookService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
suite('Notebook Input', function (): void {
const instantiationService = workbenchInstantiationService();
@@ -44,6 +45,8 @@ suite('Notebook Input', function (): void {
(instantiationService as TestInstantiationService).stub(INotebookService, mockNotebookService.object);
const mockNotificationService = TypeMoq.Mock.ofType(TestNotificationService);
let untitledTextInput: UntitledTextEditorInput;
let untitledNotebookInput: UntitledNotebookInput;
@@ -53,14 +56,14 @@ suite('Notebook Input', function (): void {
untitledTextInput = instantiationService.createInstance(UntitledTextEditorInput, service.create({ associatedResource: untitledUri }));
untitledNotebookInput = new UntitledNotebookInput(
testTitle, untitledUri, untitledTextInput,
undefined, instantiationService, mockNotebookService.object, mockExtensionService.object);
undefined, instantiationService, mockNotebookService.object, mockExtensionService.object, mockNotificationService.object);
});
test('File Notebook Input', async function (): Promise<void> {
let fileUri = URI.from({ scheme: Schemas.file, path: 'TestPath' });
let fileNotebookInput = new FileNotebookInput(
testTitle, fileUri, undefined,
undefined, instantiationService, mockNotebookService.object, mockExtensionService.object);
undefined, instantiationService, mockNotebookService.object, mockExtensionService.object, mockNotificationService.object);
let inputId = fileNotebookInput.getTypeId();
assert.strictEqual(inputId, FileNotebookInput.ID);

View File

@@ -43,6 +43,9 @@ export class NotebookModelStub implements INotebookModel {
get sessionLoadFinished(): Promise<void> {
throw new Error('method not implemented.');
}
get gridDataConversionComplete(): Promise<any[]> {
throw new Error('method not implemented.');
}
get notebookManagers(): INotebookManager[] {
throw new Error('method not implemented.');
}