diff --git a/src/editors/playgroundController.ts b/src/editors/playgroundController.ts index 84e7c2dbb..49f0c6adb 100644 --- a/src/editors/playgroundController.ts +++ b/src/editors/playgroundController.ts @@ -1,11 +1,10 @@ import * as vscode from 'vscode'; import path from 'path'; -import type { OutputChannel, TextEditor } from 'vscode'; +import type { TextEditor } from 'vscode'; import { ProgressLocation } from 'vscode'; import vm from 'vm'; import os from 'os'; import transpiler from 'bson-transpilers'; -import util from 'util'; import type ActiveConnectionCodeLensProvider from './activeConnectionCodeLensProvider'; import type PlaygroundSelectedCodeActionProvider from './playgroundSelectedCodeActionProvider'; @@ -111,7 +110,6 @@ export default class PlaygroundController { _isPartialRun = false; - _outputChannel: OutputChannel; private _activeConnectionCodeLensProvider: ActiveConnectionCodeLensProvider; private _playgroundResultViewColumn?: vscode.ViewColumn; private _playgroundResultTextDocument?: vscode.TextDocument; @@ -145,8 +143,6 @@ export default class PlaygroundController { this._telemetryService = telemetryService; this._statusView = statusView; this._playgroundResultViewProvider = playgroundResultViewProvider; - this._outputChannel = - vscode.window.createOutputChannel('Playground output'); this._activeConnectionCodeLensProvider = activeConnectionCodeLensProvider; this._exportToLanguageCodeLensProvider = exportToLanguageCodeLensProvider; this._playgroundSelectedCodeActionProvider = @@ -467,7 +463,7 @@ export default class PlaygroundController { // If a user clicked the cancel button terminate all playground scripts. this._languageServerController.cancelAll(); - return { outputLines: undefined, result: undefined }; + return { result: undefined }; }); // Run all playground scripts. @@ -483,10 +479,7 @@ export default class PlaygroundController { } catch (error) { log.error('Evaluating playground with cancel modal failed', error); - return { - outputLines: undefined, - result: undefined, - }; + return { result: undefined }; } } @@ -567,27 +560,10 @@ export default class PlaygroundController { } } - this._outputChannel.clear(); - const evaluateResponse: ShellEvaluateResult = await this._evaluateWithCancelModal(); - if (evaluateResponse?.outputLines?.length) { - for (const line of evaluateResponse.outputLines) { - this._outputChannel.appendLine( - typeof line.content === 'string' - ? line.content - : util.inspect(line.content) - ); - } - - this._outputChannel.show(true); - } - - if ( - !evaluateResponse || - (!evaluateResponse.outputLines && !evaluateResponse.result) - ) { + if (!evaluateResponse || !evaluateResponse.result) { return false; } diff --git a/src/language/languageServerController.ts b/src/language/languageServerController.ts index eb828ddf9..4525b0014 100644 --- a/src/language/languageServerController.ts +++ b/src/language/languageServerController.ts @@ -12,6 +12,7 @@ import { } from 'vscode-languageclient/node'; import type { ExtensionContext } from 'vscode'; import { workspace } from 'vscode'; +import util from 'util'; import { createLogger } from '../logging'; import type { @@ -38,6 +39,9 @@ export default class LanguageServerController { _currentConnectionString?: string; _currentConnectionOptions?: MongoClientOptions; + _consoleOutputChannel = + vscode.window.createOutputChannel('Playground output'); + constructor(context: ExtensionContext) { this._context = context; @@ -151,6 +155,19 @@ export default class LanguageServerController { void vscode.window.showErrorMessage(messsage); } ); + + this._client.onNotification( + ServerCommands.SHOW_CONSOLE_OUTPUT, + (outputs) => { + for (const line of outputs) { + this._consoleOutputChannel.appendLine( + typeof line === 'string' ? line : util.inspect(line) + ); + } + + this._consoleOutputChannel.show(true); + } + ); } deactivate(): Thenable | undefined { @@ -173,6 +190,8 @@ export default class LanguageServerController { }); this._isExecutingInProgress = true; + this._consoleOutputChannel.clear(); + // Instantiate a new CancellationTokenSource object // that generates a cancellation token for each run of a playground. this._source = new CancellationTokenSource(); diff --git a/src/language/mongoDBService.ts b/src/language/mongoDBService.ts index 3b0f6a365..3e5d967d0 100644 --- a/src/language/mongoDBService.ts +++ b/src/language/mongoDBService.ts @@ -21,6 +21,7 @@ import parseSchema from 'mongodb-schema'; import path from 'path'; import { signatures } from '@mongosh/shell-api'; import translator from '@mongosh/i18n'; +import { isAtlasStream } from 'mongodb-build-info'; import { Worker as WorkerThreads } from 'worker_threads'; import { ExportToLanguageMode } from '../types/playgroundType'; @@ -65,6 +66,7 @@ export default class MongoDBService { _currentConnectionOptions?: MongoClientOptions; _databaseCompletionItems: CompletionItem[] = []; + _streamProcessorCompletionItems: CompletionItem[] = []; _shellSymbolCompletionItems: { [symbol: string]: CompletionItem[] } = {}; _globalSymbolCompletionItems: CompletionItem[] = []; _collections: { [database: string]: string[] } = {}; @@ -147,6 +149,7 @@ export default class MongoDBService { databases: true, collections: true, fields: true, + streamProcessors: true, }); await this._closeCurrentConnection(); } @@ -174,6 +177,22 @@ export default class MongoDBService { ); } + if (isAtlasStream(connectionString || '')) { + await this._getAndCacheStreamProcessors(); + } else { + await this._getAndCacheDatabases(); + } + + this._connection.console.log( + `CliServiceProvider active connection has changed: { connectionId: ${connectionId} }` + ); + return { + successfullyConnected: true, + connectionId, + }; + } + + async _getAndCacheDatabases() { try { // Get database names for the current connection. const databases = await this._getDatabases(); @@ -184,14 +203,17 @@ export default class MongoDBService { `LS get databases error: ${util.inspect(error)}` ); } + } - this._connection.console.log( - `CliServiceProvider active connection has changed: { connectionId: ${connectionId} }` - ); - return { - successfullyConnected: true, - connectionId, - }; + async _getAndCacheStreamProcessors() { + try { + const processors = await this._getStreamProcessors(); + this._cacheStreamProcessorCompletionItems(processors); + } catch (error) { + this._connection.console.error( + `LS get stream processors error: ${util.inspect(error)}` + ); + } } /** @@ -245,9 +267,16 @@ export default class MongoDBService { ) ); - worker?.on( - 'message', - ({ error, data }: { data?: ShellEvaluateResult; error?: any }) => { + worker?.on('message', ({ name, payload }) => { + if (name === ServerCommands.SHOW_CONSOLE_OUTPUT) { + void this._connection.sendNotification(name, payload); + } + + if (name === ServerCommands.CODE_EXECUTION_RESULT) { + const { error, data } = payload as { + data?: ShellEvaluateResult; + error?: any; + }; if (error) { this._connection.console.error( `WORKER error: ${util.inspect(error)}` @@ -261,7 +290,7 @@ export default class MongoDBService { resolve(data); }); } - ); + }); worker.postMessage({ name: ServerCommands.EXECUTE_CODE_FROM_PLAYGROUND, @@ -294,6 +323,24 @@ export default class MongoDBService { }); } + /** + * Get stream processors names for the current connection. + */ + async _getStreamProcessors(): Promise { + if (this._serviceProvider) { + try { + const cmd = { listStreamProcessors: 1 }; + const result = await this._serviceProvider.runCommand('admin', cmd); + return result.streamProcessors ?? []; + } catch (error) { + this._connection.console.error( + `LS get stream processors error: ${error}` + ); + } + } + return []; + } + /** * Get database names for the current connection. */ @@ -377,7 +424,7 @@ export default class MongoDBService { } /** - * Return 'db' and 'use' completion items. + * Return 'db', 'sp' and 'use' completion items. */ _cacheGlobalSymbolCompletionItems() { this._globalSymbolCompletionItems = [ @@ -386,6 +433,11 @@ export default class MongoDBService { kind: CompletionItemKind.Method, preselect: true, }, + { + label: 'sp', + kind: CompletionItemKind.Method, + preselect: true, + }, { label: 'use', kind: CompletionItemKind.Function, @@ -783,6 +835,18 @@ export default class MongoDBService { } } + /** + * If the current node is 'sp.processor.' or 'sp["processor"].'. + */ + _provideStreamProcessorSymbolCompletionItems(state: CompletionState) { + if (state.isStreamProcessorSymbol) { + this._connection.console.log( + 'VISITOR found stream processor symbol completions' + ); + return this._shellSymbolCompletionItems.StreamProcessor; + } + } + /** * If the current node is 'db.collection.find().'. */ @@ -895,6 +959,37 @@ export default class MongoDBService { } } + /** + * If the current node is 'sp.'. + */ + _provideSpSymbolCompletionItems(state: CompletionState) { + if (state.isSpSymbol) { + if (state.isStreamProcessorName) { + this._connection.console.log( + 'VISITOR found sp symbol and stream processor name completions' + ); + return this._shellSymbolCompletionItems.Streams.concat( + this._streamProcessorCompletionItems + ); + } + + this._connection.console.log('VISITOR found sp symbol completions'); + return this._shellSymbolCompletionItems.Streams; + } + } + + /** + * If the current node is 'sp.get()'. + */ + _provideStreamProcessorNameCompletionItems(state: CompletionState) { + if (state.isStreamProcessorName) { + this._connection.console.log( + 'VISITOR found stream processor name completions' + ); + return this._streamProcessorCompletionItems; + } + } + /** * If the current node can be used as a collection name * e.g. 'db..find()' or 'let a = db.'. @@ -965,6 +1060,7 @@ export default class MongoDBService { this._provideIdentifierObjectValueCompletionItems.bind(this, state), this._provideTextObjectValueCompletionItems.bind(this, state), this._provideCollectionSymbolCompletionItems.bind(this, state), + this._provideStreamProcessorSymbolCompletionItems.bind(this, state), this._provideFindCursorCompletionItems.bind(this, state), this._provideAggregationCursorCompletionItems.bind(this, state), this._provideGlobalSymbolCompletionItems.bind(this, state), @@ -974,6 +1070,7 @@ export default class MongoDBService { currentLineText, position ), + this._provideSpSymbolCompletionItems.bind(this, state), this._provideCollectionNameCompletionItems.bind( this, state, @@ -981,6 +1078,7 @@ export default class MongoDBService { position ), this._provideDbNameCompletionItems.bind(this, state), + this._provideStreamProcessorNameCompletionItems.bind(this, state), ]; for (const func of completionOptions) { @@ -1117,6 +1215,18 @@ export default class MongoDBService { this._collections[database] = collections.map((item) => item.name); } + _cacheStreamProcessorCompletionItems(processors: Document[]): void { + this._streamProcessorCompletionItems = processors.map(({ name }) => ({ + kind: CompletionItemKind.Folder, + preselect: true, + label: name, + })); + } + + clearCachedStreamProcessors(): void { + this._streamProcessorCompletionItems = []; + } + clearCachedFields(): void { this._fields = {}; } @@ -1142,13 +1252,16 @@ export default class MongoDBService { clearCachedCompletions(clear: ClearCompletionsCache): void { if (clear.fields) { - this._fields = {}; + this.clearCachedFields(); } if (clear.databases) { - this._databaseCompletionItems = []; + this.clearCachedDatabases(); } if (clear.collections) { - this._collections = {}; + this.clearCachedCollections(); + } + if (clear.streamProcessors) { + this.clearCachedStreamProcessors(); } } } diff --git a/src/language/serverCommands.ts b/src/language/serverCommands.ts index 883229dc6..bc2aee9a9 100644 --- a/src/language/serverCommands.ts +++ b/src/language/serverCommands.ts @@ -10,6 +10,8 @@ export enum ServerCommands { CLEAR_CACHED_COMPLETIONS = 'CLEAR_CACHED_COMPLETIONS', MONGODB_SERVICE_CREATED = 'MONGODB_SERVICE_CREATED', INITIALIZE_MONGODB_SERVICE = 'INITIALIZE_MONGODB_SERVICE', + CODE_EXECUTION_RESULT = 'CODE_EXECUTION_RESULT', + SHOW_CONSOLE_OUTPUT = 'SHOW_CONSOLE_OUTPUT', } export type PlaygroundRunParameters = { diff --git a/src/language/visitor.ts b/src/language/visitor.ts index 75433e7fa..9cda0aa46 100644 --- a/src/language/visitor.ts +++ b/src/language/visitor.ts @@ -22,16 +22,20 @@ type ObjectKey = export interface CompletionState { databaseName: string | null; collectionName: string | null; + streamProcessorName: string | null; isObjectKey: boolean; isIdentifierObjectValue: boolean; isTextObjectValue: boolean; isStage: boolean; stageOperator: string | null; isCollectionSymbol: boolean; + isStreamProcessorSymbol: boolean; isUseCallExpression: boolean; isGlobalSymbol: boolean; isDbSymbol: boolean; + isSpSymbol: boolean; isCollectionName: boolean; + isStreamProcessorName: boolean; isAggregationCursor: boolean; isFindCursor: boolean; } @@ -66,6 +70,7 @@ export class Visitor { this._checkIsBSONSelection(path.node); this._checkIsUseCall(path.node); this._checkIsCollectionNameAsCallExpression(path.node); + this._checkIsStreamProcessorNameAsCallExpression(path.node); this._checkHasDatabaseName(path.node); } @@ -79,12 +84,16 @@ export class Visitor { this._checkIsCollectionSymbol(path.node); this._checkIsCollectionNameAsMemberExpression(path.node); this._checkHasCollectionName(path.node); + this._checkIsStreamProcessorSymbol(path.node); + this._checkIsStreamProcessorNameAsMemberExpression(path.node); + this._checkHasStreamProcessorName(path.node); } _visitExpressionStatement(path: babel.NodePath): void { if (path.node.type === 'ExpressionStatement') { this._checkIsGlobalSymbol(path.node); this._checkIsDbSymbol(path.node); + this._checkIsSpSymbol(path.node); } } @@ -199,6 +208,7 @@ export class Visitor { return { databaseName: null, collectionName: null, + streamProcessorName: null, isObjectSelection: false, isArraySelection: false, isObjectKey: false, @@ -207,10 +217,13 @@ export class Visitor { isStage: false, stageOperator: null, isCollectionSymbol: false, + isStreamProcessorSymbol: false, isUseCallExpression: false, isGlobalSymbol: false, isDbSymbol: false, + isSpSymbol: false, isCollectionName: false, + isStreamProcessorName: false, isAggregationCursor: false, isFindCursor: false, }; @@ -245,6 +258,8 @@ export class Visitor { _checkIsUseCallAsTemplate(node: babel.types.CallExpression): void { if ( + node.callee.type === 'Identifier' && + node.callee.name === 'use' && node.arguments && node.arguments.length === 1 && node.arguments[0].type === 'TemplateLiteral' && @@ -283,6 +298,17 @@ export class Visitor { } } + _checkIsSpSymbol(node: babel.types.ExpressionStatement): void { + if ( + node.expression.type === 'MemberExpression' && + node.expression.object.type === 'Identifier' && + node.expression.object.name === 'sp' && + 'isSpSymbol' in this._state + ) { + this._state.isSpSymbol = true; + } + } + _checkIsObjectKey(node: babel.types.ObjectExpression): void { node.properties.find((item: ObjectKey) => { if ( @@ -689,4 +715,143 @@ export class Visitor { this._checkIsCollectionMemberExpression(node); this._checkIsCollectionCallExpression(node); } + + _checkIsStreamProcessorNameAsMemberExpression( + node: babel.types.MemberExpression + ): void { + if ( + node.object.type === 'Identifier' && + node.object.name === 'sp' && + ((node.property.type === 'Identifier' && + node.property.name.includes(PLACEHOLDER)) || + (node.property.type === 'StringLiteral' && + node.property.value.includes(PLACEHOLDER))) && + 'isStreamProcessorName' in this._state + ) { + this._state.isSpSymbol = true; + this._state.isStreamProcessorName = true; + } + } + + _checkIsStreamProcessorNameAsCallExpression( + node: babel.types.CallExpression + ): void { + if ( + node.callee.type === 'MemberExpression' && + node.callee.object.type === 'Identifier' && + node.callee.object.name === 'sp' && + node.callee.property.type === 'Identifier' && + node.callee.property.name === 'getProcessor' && + node.arguments.length === 1 + ) { + this._checkGetStreamProcessorAsSimpleString(node); + this._checkGetStreamProcessorAsTemplate(node); + } + } + + _checkGetStreamProcessorAsSimpleString( + node: babel.types.CallExpression + ): void { + if ( + node.arguments[0].type === 'StringLiteral' && + node.arguments[0].value.includes(PLACEHOLDER) && + 'isStreamProcessorName' in this._state + ) { + this._state.isStreamProcessorName = true; + } + } + + _checkGetStreamProcessorAsTemplate(node: babel.types.CallExpression): void { + if ( + node.arguments[0].type === 'TemplateLiteral' && + node.arguments[0].quasis.length === 1 && + node.arguments[0].quasis[0].value.raw.includes(PLACEHOLDER) && + 'isStreamProcessorName' in this._state + ) { + this._state.isStreamProcessorName = true; + } + } + + _checkHasStreamProcessorName(node: babel.types.MemberExpression): void { + this._checkHasStreamProcessorNameMemberExpression(node); + this._checkHasStreamProcessorNameCallExpression(node); + } + + _checkHasStreamProcessorNameMemberExpression( + node: babel.types.MemberExpression + ): void { + if ( + node.object.type === 'MemberExpression' && + node.object.object.type === 'Identifier' && + node.object.object.name === 'sp' + ) { + if ( + node.object.property.type === 'Identifier' && + 'streamProcessorName' in this._state + ) { + this._state.streamProcessorName = node.object.property.name; + } else if ( + node.object.property.type === 'StringLiteral' && + 'streamProcessorName' in this._state + ) { + this._state.streamProcessorName = node.object.property.value; + } + } + } + + _checkHasStreamProcessorNameCallExpression( + node: babel.types.MemberExpression + ) { + if ( + node.object.type === 'CallExpression' && + node.object.callee.type === 'MemberExpression' && + node.object.callee.object.type === 'Identifier' && + node.object.callee.object.name === 'sp' && + node.object.callee.property.type === 'Identifier' && + node.object.callee.property.name === 'getProcessor' && + node.object.arguments.length === 1 && + node.object.arguments[0].type === 'StringLiteral' && + 'streamProcessorName' in this._state + ) { + this._state.streamProcessorName = node.object.arguments[0].value; + } + } + + _checkIsStreamProcessorSymbol(node: babel.types.MemberExpression): void { + this._checkIsStreamProcessorMemberExpression(node); + this._checkIsStreamProcessorCallExpression(node); + } + + _checkIsStreamProcessorMemberExpression( + node: babel.types.MemberExpression + ): void { + if ( + node.object.type === 'MemberExpression' && + node.object.object.type === 'Identifier' && + node.object.object.name === 'sp' && + node.property.type === 'Identifier' && + node.property.name.includes(PLACEHOLDER) && + 'isStreamProcessorSymbol' in this._state + ) { + this._state.isStreamProcessorSymbol = true; + } + } + + _checkIsStreamProcessorCallExpression( + node: babel.types.MemberExpression + ): void { + if ( + node.object.type === 'CallExpression' && + node.object.callee.type === 'MemberExpression' && + node.object.callee.object.type === 'Identifier' && + node.object.callee.object.name === 'sp' && + node.object.callee.property.type === 'Identifier' && + node.object.callee.property.name === 'getProcessor' && + node.property.type === 'Identifier' && + node.property.name.includes(PLACEHOLDER) && + 'isStreamProcessorSymbol' in this._state + ) { + this._state.isStreamProcessorSymbol = true; + } + } } diff --git a/src/language/worker.ts b/src/language/worker.ts index f0b31aaf4..31ee97049 100644 --- a/src/language/worker.ts +++ b/src/language/worker.ts @@ -6,7 +6,6 @@ import { ServerCommands } from './serverCommands'; import type { ShellEvaluateResult, - PlaygroundDebug, WorkerEvaluate, MongoClientOptions, } from '../types/playgroundType'; @@ -52,19 +51,14 @@ const execute = async ( try { // Create a new instance of the runtime for each playground evaluation. const runtime = new ElectronRuntime(serviceProvider); - const outputLines: PlaygroundDebug = []; // Collect console.log() output. runtime.setEvaluationListener({ onPrint(values: EvaluationResult[]) { - for (const { type, printable } of values) { - outputLines.push({ - type, - content: printable, - namespace: null, - language: null, - }); - } + parentPort?.postMessage({ + name: ServerCommands.SHOW_CONSOLE_OUTPUT, + payload: values.map((v) => v.printable), + }); }, }); @@ -83,7 +77,7 @@ const execute = async ( language: getLanguage({ type, printable }), }; - return { data: { outputLines, result } }; + return { data: { result } }; } catch (error) { return { error }; } finally { @@ -93,13 +87,14 @@ const execute = async ( const handleMessageFromParentPort = async ({ name, data }): Promise => { if (name === ServerCommands.EXECUTE_CODE_FROM_PLAYGROUND) { - parentPort?.postMessage( - await execute( + parentPort?.postMessage({ + name: ServerCommands.CODE_EXECUTION_RESULT, + payload: await execute( data.codeToEvaluate, data.connectionString, data.connectionOptions - ) - ); + ), + }); } }; diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index d244eb197..65305a710 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -317,6 +317,7 @@ export default class MDBExtensionController implements vscode.Disposable { databases: true, collections: true, fields: true, + streamProcessors: true, }); return true; diff --git a/src/test/suite/editors/playgroundController.test.ts b/src/test/suite/editors/playgroundController.test.ts index dfddee1ab..95ee609aa 100644 --- a/src/test/suite/editors/playgroundController.test.ts +++ b/src/test/suite/editors/playgroundController.test.ts @@ -332,10 +332,7 @@ suite('Playground Controller Test Suite', function () { const result = await testPlaygroundController._evaluateWithCancelModal(); - expect(result).to.deep.equal({ - outputLines: undefined, - result: undefined, - }); + expect(result).to.deep.equal({ result: undefined }); }); test('playground controller loads the active editor on start', () => { @@ -399,75 +396,12 @@ suite('Playground Controller Test Suite', function () { ); }); - suite('output channels', () => { - let outputChannelAppendLineStub: SinonStub; - let outputChannelClearStub: SinonStub; - let outputChannelShowStub: SinonStub; - - beforeEach(function () { - outputChannelAppendLineStub = sinon.stub(); - outputChannelClearStub = sinon.stub(); - outputChannelShowStub = sinon.stub(); - - const mockOutputChannel = { - appendLine: outputChannelAppendLineStub, - clear: outputChannelClearStub, - show: outputChannelShowStub, - } as Partial as unknown as vscode.OutputChannel; - sandbox.replace( - testPlaygroundController, - '_outputChannel', - mockOutputChannel - ); - showInformationMessageStub.resolves('Yes'); - }); - - test('show the output in the vscode output channel as a string', async () => { - const outputLines = [ - 'test', - { pineapple: 'yes' }, - ['porcupine', { anObject: true }], - ].map((content) => ({ content })); - sandbox.replace( - testPlaygroundController, - '_evaluateWithCancelModal', - sandbox.stub().resolves({ - outputLines, - result: '123', - }) - ); - - expect(outputChannelClearStub).to.not.be.called; - expect(outputChannelShowStub).to.not.be.called; - expect(outputChannelAppendLineStub).to.not.be.called; - - await testPlaygroundController.runAllPlaygroundBlocks(); - - expect(outputChannelClearStub).to.be.calledOnce; - expect(outputChannelShowStub).to.be.calledOnce; - expect(outputChannelAppendLineStub.calledThrice).to.be.true; - expect(outputChannelAppendLineStub.firstCall.args[0]).to.equal( - 'test' - ); - // Make sure we're not printing anything like [object Object]. - expect(outputChannelAppendLineStub.secondCall.args[0]).to.equal( - "{ pineapple: 'yes' }" - ); - expect(outputChannelAppendLineStub.thirdCall.args[0]).to.equal( - "[ 'porcupine', { anObject: true } ]" - ); - }); - }); - suite('confirmation modal', () => { beforeEach(function () { sandbox.replace( testPlaygroundController, '_evaluateWithCancelModal', - sandbox.stub().resolves({ - outputLines: [], - result: '123', - }) + sandbox.stub().resolves({ result: '123' }) ); sandbox.replace( testPlaygroundController, @@ -479,6 +413,10 @@ suite('Playground Controller Test Suite', function () { test('show a confirmation message if mdb.confirmRunAll is true', async () => { showInformationMessageStub.resolves('Yes'); + await vscode.workspace + .getConfiguration('mdb') + .update('confirmRunAll', true); + const result = await testPlaygroundController.runAllPlaygroundBlocks(); diff --git a/src/test/suite/language/languageServerController.test.ts b/src/test/suite/language/languageServerController.test.ts index 0e66629c1..b34944301 100644 --- a/src/test/suite/language/languageServerController.test.ts +++ b/src/test/suite/language/languageServerController.test.ts @@ -4,6 +4,7 @@ import chai from 'chai'; import fs from 'fs'; import path from 'path'; import sinon from 'sinon'; +import type { SinonStub } from 'sinon'; import type { DataService } from 'mongodb-data-service'; import chaiAsPromised from 'chai-as-promised'; @@ -146,4 +147,49 @@ suite('Language Server Controller Test Suite', () => { ); await fs.promises.stat(languageServerModuleBundlePath); }); + + suite('console output channels', () => { + let outputChannelAppendLineStub: SinonStub; + let outputChannelClearStub: SinonStub; + let outputChannelShowStub: SinonStub; + + beforeEach(function () { + outputChannelAppendLineStub = sandbox.stub(); + outputChannelClearStub = sandbox.stub(); + outputChannelShowStub = sandbox.stub(); + + const mockOutputChannel = { + appendLine: outputChannelAppendLineStub, + clear: outputChannelClearStub, + show: outputChannelShowStub, + } as Partial as unknown as vscode.OutputChannel; + sandbox.replace( + languageServerControllerStub, + '_consoleOutputChannel', + mockOutputChannel + ); + }); + + test('clear output channel when evaluating', async () => { + sandbox.replace( + testPlaygroundController, + '_evaluateWithCancelModal', + sandbox.stub().resolves({ + result: '123', + }) + ); + + expect(outputChannelClearStub).to.not.be.called; + + await languageServerControllerStub.evaluate({ + codeToEvaluate: ` + print('test'); + console.log({ pineapple: 'yes' }); + `, + connectionId: 'pineapple', + }); + + expect(outputChannelClearStub).to.be.calledOnce; + }); + }); }); diff --git a/src/test/suite/language/mongoDBService.test.ts b/src/test/suite/language/mongoDBService.test.ts index 87ab182ca..33f7dd41e 100644 --- a/src/test/suite/language/mongoDBService.test.ts +++ b/src/test/suite/language/mongoDBService.test.ts @@ -23,6 +23,7 @@ import { mdbTestExtension } from '../stubbableMdbExtension'; import { StreamStub } from '../stubs'; import READ_PREFERENCES from '../../../views/webview-app/legacy/connection-model/constants/read-preferences'; import DIAGNOSTIC_CODES from '../../../language/diagnosticCodes'; +import { ServerCommands } from '../../../language/serverCommands'; import LINKS from '../../../utils/links'; import Sinon from 'sinon'; @@ -131,6 +132,8 @@ suite('MongoDBService Test Suite', () => { Promise.resolve([]); testMongoDBService._getSchemaFields = (): Promise => Promise.resolve([]); + testMongoDBService._getStreamProcessors = (): Promise => + Promise.resolve([]); await testMongoDBService.activeConnectionChanged(params); }); @@ -894,6 +897,32 @@ suite('MongoDBService Test Suite', () => { expect(completion).to.have.property('kind', CompletionItemKind.Reference); }); + test('clear cached stream processors', async () => { + const content = 'sp.'; + const position = { line: 0, character: 3 }; + const document = TextDocument.create('init', 'javascript', 1, content); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'testProcessor' }, + ]); + + let result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + let completion = result.find((item) => item.label === 'testProcessor'); + expect(completion).to.have.property('kind', CompletionItemKind.Folder); + + testMongoDBService.clearCachedCompletions({ streamProcessors: true }); + + result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + completion = result.find((item) => item.label === 'testProcessor'); + expect(completion).to.be.undefined; + }); + test('clear cached databases', async () => { const content = 'use("m");'; const position = { line: 0, character: 6 }; @@ -1353,6 +1382,7 @@ suite('MongoDBService Test Suite', () => { databases: true, collections: true, fields: true, + streamProcessors: true, }); }); @@ -2026,6 +2056,509 @@ suite('MongoDBService Test Suite', () => { }); } ); + + suite('streams operations', function () { + const streamProcessorMethods = ['start', 'stop', 'drop', 'sample']; + const spMethods = [ + 'createStreamProcessor', + 'listStreamProcessors', + 'listConnections', + 'getProcessor', + 'process', + ]; + + test('provide shell sp methods completion with dot the same line', async () => { + const content = 'sp.'; + const position = { line: 0, character: 3 }; + const document = TextDocument.create('init', 'javascript', 1, content); + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + spMethods.every((m) => { + const completion = result.find((item) => item.label === m); + expect(completion?.kind).to.be.eql(CompletionItemKind.Method); + }); + }); + + test('provide shell sp methods completion with dot next line', async () => { + const content = ['sp', '.'].join('\n'); + const position = { line: 1, character: 1 }; + const document = TextDocument.create('init', 'javascript', 1, content); + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + spMethods.every((m) => { + const completion = result.find((item) => item.label === m); + expect(completion?.kind).to.be.eql(CompletionItemKind.Method); + }); + }); + + test('provide shell sp methods completion with dot after space', async () => { + const content = 'sp .'; + const position = { line: 0, character: 4 }; + const document = TextDocument.create('init', 'javascript', 1, content); + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + spMethods.every((m) => { + const completion = result.find((item) => item.label === m); + expect(completion?.kind).to.be.eql(CompletionItemKind.Method); + }); + }); + + test('provide shell stream processor methods completion if global scope', async () => { + const content = 'sp.test.'; + const position = { line: 0, character: 8 }; + const document = TextDocument.create('init', 'javascript', 1, content); + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + + streamProcessorMethods.every((m) => { + const completion = result.find((item) => item.label === m); + expect(completion?.kind).to.be.eql(CompletionItemKind.Method); + }); + }); + + test('provide shell stream processor methods completion if function scope', async () => { + const content = 'const name = () => { sp.test. }'; + const position = { line: 0, character: 29 }; + const document = TextDocument.create('init', 'javascript', 1, content); + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + streamProcessorMethods.every((m) => { + const completion = result.find((item) => item.label === m); + expect(completion?.kind).to.be.eql(CompletionItemKind.Method); + }); + }); + + test('provide shell stream processor methods completion for a processor name in a bracket notation', async () => { + const content = 'sp["test"].'; + const position = { line: 0, character: 11 }; + const document = TextDocument.create('init', 'javascript', 1, content); + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + streamProcessorMethods.every((m) => { + const completion = result.find((item) => item.label === m); + expect(completion?.kind).to.be.eql(CompletionItemKind.Method); + }); + }); + + test('provide shell stream processor methods completion for a processor name in getProcessor', async () => { + const content = 'sp.getProcessor("test").'; + const position = { line: 0, character: 24 }; + const document = TextDocument.create('init', 'javascript', 1, content); + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + streamProcessorMethods.every((m) => { + const completion = result.find((item) => item.label === m); + expect(completion?.kind).to.be.eql(CompletionItemKind.Method); + }); + }); + + test('provide shell stream processor methods completion if single quotes', async () => { + const content = "sp['test']."; + const position = { line: 0, character: 11 }; + const document = TextDocument.create('init', 'javascript', 1, content); + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + streamProcessorMethods.every((m) => { + const completion = result.find((item) => item.label === m); + expect(completion?.kind).to.be.eql(CompletionItemKind.Method); + }); + }); + + test('provide stream processor names completion for dot notation', async () => { + const content = 'sp.'; + const position = { line: 0, character: 3 }; + const document = TextDocument.create('init', 'javascript', 1, content); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'testProcessor' }, + ]); + + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + const completion = result.find( + (item) => item.label === 'testProcessor' + ); + expect(completion).to.have.property('kind', CompletionItemKind.Folder); + }); + + test('provide stream processor names completion for object names with dashes', async () => { + const content = 'sp.'; + const position = { line: 0, character: content.length }; + const document = TextDocument.create('init', 'javascript', 1, content); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'test-processor' }, + ]); + + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + const completion = result.find( + (item) => item.label === 'test-processor' + ); + expect(completion).to.have.property('kind', CompletionItemKind.Folder); + }); + + test('provide stream processor names completion for object names with dots', async () => { + const content = 'sp.'; + const position = { line: 0, character: content.length }; + const document = TextDocument.create('init', 'javascript', 1, content); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'test.processor' }, + ]); + + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + const completion = result.find( + (item) => item.label === 'test.processor' + ); + expect(completion).to.have.property('kind', CompletionItemKind.Folder); + }); + + test('provide stream processor names completion in variable declarations', async () => { + const content = 'let a = sp.'; + const position = { line: 0, character: content.length }; + const document = TextDocument.create('init', 'javascript', 1, content); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'testProcessor' }, + ]); + + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + const completion = result.find( + (item) => item.label === 'testProcessor' + ); + expect(completion).to.have.property('kind', CompletionItemKind.Folder); + }); + + test('provide stream processor names completion for sp symbol with bracket notation', async () => { + const content = "sp['']"; + const position = { line: 0, character: 4 }; + const document = TextDocument.create('init', 'javascript', 1, content); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'test-processor' }, + ]); + + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + const completion = result.find( + (item: CompletionItem) => item.label === 'test-processor' + ); + expect(completion).to.have.property('kind', CompletionItemKind.Folder); + }); + + test('provide stream processor names completion for getProcessor as a simple string', async () => { + const content = "sp.getProcessor('')"; + const position = { line: 0, character: content.length - 2 }; + const document = TextDocument.create('init', 'javascript', 1, content); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'test-processor' }, + ]); + + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + const findCollectionCompletion = result.find( + (item: CompletionItem) => item.label === 'test-processor' + ); + expect(findCollectionCompletion).to.have.property( + 'kind', + CompletionItemKind.Folder + ); + }); + + test('provide stream processor names completion for getProcessor as a string template', async () => { + const content = 'sp.getProcessor(``)'; + const position = { line: 0, character: content.length - 2 }; + const document = TextDocument.create('init', 'javascript', 1, content); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'test_processor' }, + ]); + + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + const findCollectionCompletion = result.find( + (item: CompletionItem) => item.label === 'test_processor' + ); + expect(findCollectionCompletion).to.have.property( + 'kind', + CompletionItemKind.Folder + ); + }); + + test('provide shell sp and stream processor names completion in the middle of expression', async () => { + const content = 'sp..stop()'; + const position = { line: 0, character: 3 }; + const document = TextDocument.create('init', 'javascript', 1, content); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'testProcessor' }, + ]); + + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + const nameCompletion = result.find( + (item: CompletionItem) => item.label === 'testProcessor' + ); + expect(nameCompletion).to.have.property( + 'kind', + CompletionItemKind.Folder + ); + const spShellCompletion = result.find( + (item: CompletionItem) => item.label === 'process' + ); + expect(spShellCompletion).to.have.property( + 'kind', + CompletionItemKind.Method + ); + }); + + test('provide stream processor names with dashes completion in the middle of expression', async () => { + const content = 'sp..stop()'; + const position = { line: 0, character: 3 }; + const document = TextDocument.create('init', 'javascript', 1, content); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'test-processor' }, + ]); + + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + const completion = result.find( + (item: CompletionItem) => item.label === 'test-processor' + ); + expect(completion).to.have.property('kind', CompletionItemKind.Folder); + }); + + test('provide stream processor names completion after single line comment', async () => { + const content = ['', '// Comment', 'sp.']; + const position = { line: content.length - 1, character: 3 }; + const document = TextDocument.create( + 'init', + 'javascript', + 1, + content.join('\n') + ); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'testProccessor' }, + ]); + + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + const completion = result.find( + (item: CompletionItem) => item.label === 'testProccessor' + ); + expect(completion).to.have.property('kind', CompletionItemKind.Folder); + }); + + test('provide stream processor names completion after single line comment with new line character', async () => { + const content = ['', '// Comment\\n', 'sp.']; + const position = { line: content.length - 1, character: 3 }; + const document = TextDocument.create( + 'init', + 'javascript', + 1, + content.join('\n') + ); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'testProcessor' }, + ]); + + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + const completion = result.find( + (item: CompletionItem) => item.label === 'testProcessor' + ); + expect(completion).to.have.property('kind', CompletionItemKind.Folder); + }); + + test('provide stream processor names completion after multi-line comment', async () => { + const content = ['', '/*', ' * Comment', '*/', 'sp.']; + const position = { line: content.length - 1, character: 3 }; + const document = TextDocument.create( + 'init', + 'javascript', + 1, + content.join('\n') + ); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'testProcessor' }, + ]); + + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + const completion = result.find( + (item: CompletionItem) => item.label === 'testProcessor' + ); + expect(completion).to.have.property('kind', CompletionItemKind.Folder); + }); + + test('provide stream processor names completion after end of line comment', async () => { + const content = [' // Comment', '', 'sp.']; + const position = { line: 2, character: 3 }; + const document = TextDocument.create( + 'init', + 'javascript', + 1, + content.join('\n') + ); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'testProcessor' }, + ]); + + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + const completion = result.find( + (item: CompletionItem) => item.label === 'testProcessor' + ); + expect(completion).to.have.property('kind', CompletionItemKind.Folder); + }); + + test('provide stream processor names completion at the same line block comment starts', async () => { + const content = ['', 'sp. /*', '* Comment', '*/']; + const position = { line: content.length - 3, character: 3 }; + const document = TextDocument.create( + 'init', + 'javascript', + 1, + content.join('\n') + ); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'testProcessor' }, + ]); + + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + const completion = result.find( + (item: CompletionItem) => item.label === 'testProcessor' + ); + expect(completion).to.have.property('kind', CompletionItemKind.Folder); + }); + + test('provide stream processor names completion at the same line block comment ends', async () => { + const content = ['', '/*', ' * Comment', '*/ sp.']; + const position = { line: content.length - 1, character: 6 }; + const document = TextDocument.create( + 'init', + 'javascript', + 1, + content.join('\n') + ); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'testProcessor' }, + ]); + + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + const completion = result.find( + (item: CompletionItem) => item.label === 'testProcessor' + ); + expect(completion).to.have.property('kind', CompletionItemKind.Folder); + }); + + test('provide stream processor names completion at the same line with end line comment', async () => { + const content = ['', 'sp. // Comment']; + const position = { line: content.length - 1, character: 3 }; + const document = TextDocument.create( + 'init', + 'javascript', + 1, + content.join('\n') + ); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'testProcessor' }, + ]); + + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + const completion = result.find( + (item: CompletionItem) => item.label === 'testProcessor' + ); + expect(completion).to.have.property('kind', CompletionItemKind.Folder); + }); + + test('provide stream processor names completion if code without a semicolon', async () => { + const content = ['', 'sp.']; + const position = { line: content.length - 1, character: 3 }; + const document = TextDocument.create( + 'init', + 'javascript', + 1, + content.join('\n') + ); + + testMongoDBService._cacheStreamProcessorCompletionItems([ + { name: 'testProcessor' }, + ]); + + const result = await testMongoDBService.provideCompletionItems({ + document, + position, + }); + const completion = result.find( + (item: CompletionItem) => item.label === 'testProcessor' + ); + expect(completion).to.have.property('kind', CompletionItemKind.Folder); + }); + }); }); suite('Evaluate', function () { @@ -2063,7 +2596,6 @@ suite('MongoDBService Test Suite', () => { source.token ); const expectedResult = { - outputLines: [], result: { namespace: null, type: 'number', @@ -2115,7 +2647,6 @@ suite('MongoDBService Test Suite', () => { source.token ); const expectedResult = { - outputLines: [], result: { namespace: `${dbName1}.${collectionName1}`, type: 'Document', @@ -2147,7 +2678,6 @@ suite('MongoDBService Test Suite', () => { source.token ); const expectedResult = { - outputLines: [], result: { namespace: `${dbName2}.${collectionName2}`, type: 'Document', @@ -2176,7 +2706,6 @@ suite('MongoDBService Test Suite', () => { source.token ); const expectedResult = { - outputLines: [], result: { namespace: `${dbName2}.${collectionName2}`, type: 'Document', @@ -2214,7 +2743,6 @@ suite('MongoDBService Test Suite', () => { source.token ); const expectedResult = { - outputLines: [], result: { namespace: null, type: 'number', @@ -2236,7 +2764,6 @@ suite('MongoDBService Test Suite', () => { source.token ); const firstRes = { - outputLines: [], result: { namespace: null, type: 'number', @@ -2255,7 +2782,6 @@ suite('MongoDBService Test Suite', () => { source.token ); const secondRes = { - outputLines: [], result: { namespace: null, type: 'number', @@ -2279,7 +2805,6 @@ suite('MongoDBService Test Suite', () => { source.token ); const expectedResult = { - outputLines: [], result: { namespace: null, type: 'object', @@ -2306,7 +2831,6 @@ suite('MongoDBService Test Suite', () => { source.token ); const expectedResult = { - outputLines: [], result: { namespace: null, type: 'object', @@ -2331,7 +2855,6 @@ suite('MongoDBService Test Suite', () => { source.token ); const expectedResult = { - outputLines: [], result: { namespace: null, type: 'object', @@ -2357,7 +2880,6 @@ suite('MongoDBService Test Suite', () => { source.token ); const expectedResult = { - outputLines: [], result: { namespace: null, type: 'undefined', @@ -2379,7 +2901,6 @@ suite('MongoDBService Test Suite', () => { source.token ); const expectedResult = { - outputLines: [], result: { namespace: null, type: 'object', @@ -2402,7 +2923,6 @@ suite('MongoDBService Test Suite', () => { source.token ); const expectedResult = { - outputLines: [], result: { namespace: null, type: 'string', @@ -2427,7 +2947,6 @@ suite('MongoDBService Test Suite', () => { source.token ); const expectedResult = { - outputLines: [], result: { namespace: null, type: 'string', @@ -2441,31 +2960,47 @@ suite('MongoDBService Test Suite', () => { expect(result).to.deep.equal(expectedResult); }); - test('includes results from print() and console.log()', async () => { - const source = new CancellationTokenSource(); - const result = await testMongoDBService.evaluate( - { - connectionId: 'pineapple', - codeToEvaluate: 'print("Hello"); console.log(1,2,3); 42', - }, - source.token - ); - const expectedResult = { - outputLines: [ - { namespace: null, type: null, content: 'Hello', language: null }, - { namespace: null, type: null, content: 1, language: null }, - { namespace: null, type: null, content: 2, language: null }, - { namespace: null, type: null, content: 3, language: null }, - ], - result: { - namespace: null, - type: 'number', - content: 42, - language: 'plaintext', - }, - }; + suite('continous console logging', function () { + let consoleOutputs: unknown[]; - expect(result).to.deep.equal(expectedResult); + beforeEach(function () { + consoleOutputs = []; + + Sinon.stub(connection, 'sendNotification') + .withArgs(ServerCommands.SHOW_CONSOLE_OUTPUT) + .callsFake((_, params) => + Promise.resolve(void consoleOutputs.push(...params)) + ); + }); + + afterEach(function () { + Sinon.restore(); + }); + + test('sends print() and console.log() output continuously', async () => { + const source = new CancellationTokenSource(); + + const result = await testMongoDBService.evaluate( + { + connectionId: 'pineapple', + codeToEvaluate: 'print("Hello"); console.log(1,2,3); 42', + }, + source.token + ); + + const expectedConsoleOutputs = ['Hello', 1, 2, 3]; + expect(consoleOutputs).to.deep.equal(expectedConsoleOutputs); + + const expectedResult = { + result: { + namespace: null, + type: 'number', + content: 42, + language: 'plaintext', + }, + }; + expect(result).to.deep.equal(expectedResult); + }); }); }); diff --git a/src/test/suite/stubs.ts b/src/test/suite/stubs.ts index 6a45566e3..41bfcd85b 100644 --- a/src/test/suite/stubs.ts +++ b/src/test/suite/stubs.ts @@ -248,6 +248,8 @@ class LanguageServerControllerStub { _isExecutingInProgress: boolean; _client: LanguageClient; _currentConnectionId: string | null = null; + _consoleOutputChannel = + vscode.window.createOutputChannel('Playground output'); constructor( context: ExtensionContextStub, @@ -322,7 +324,6 @@ class LanguageServerControllerStub { evaluate(/* codeToEvaluate: string */): Promise { return Promise.resolve({ - outputLines: [], result: { namespace: null, type: null, diff --git a/src/test/suite/telemetry/telemetryService.test.ts b/src/test/suite/telemetry/telemetryService.test.ts index 61d1d5934..ea48e2da5 100644 --- a/src/test/suite/telemetry/telemetryService.test.ts +++ b/src/test/suite/telemetry/telemetryService.test.ts @@ -370,7 +370,6 @@ suite('Telemetry Controller Test Suite', () => { suite('prepare playground result types', () => { test('convert AggregationCursor shellApiType to aggregation telemetry type', () => { const res = { - outputLines: [], result: { namespace: null, type: 'AggregationCursor', @@ -384,7 +383,6 @@ suite('Telemetry Controller Test Suite', () => { test('convert BulkWriteResult shellApiType to other telemetry type', () => { const res = { - outputLines: [], result: { namespace: null, type: 'BulkWriteResult', @@ -398,7 +396,6 @@ suite('Telemetry Controller Test Suite', () => { test('convert Collection shellApiType to other telemetry type', () => { const res = { - outputLines: [], result: { namespace: null, type: 'Collection', @@ -412,7 +409,6 @@ suite('Telemetry Controller Test Suite', () => { test('convert Cursor shellApiType to other telemetry type', () => { const res = { - outputLines: [], result: { namespace: null, type: 'Cursor', @@ -426,7 +422,6 @@ suite('Telemetry Controller Test Suite', () => { test('convert Database shellApiType to other telemetry type', () => { const res = { - outputLines: [], result: { namespace: null, type: 'Database', @@ -440,7 +435,6 @@ suite('Telemetry Controller Test Suite', () => { test('convert DeleteResult shellApiType to other telemetry type', () => { const res = { - outputLines: [], result: { namespace: null, type: 'DeleteResult', @@ -454,7 +448,6 @@ suite('Telemetry Controller Test Suite', () => { test('convert InsertManyResult shellApiType to other telemetry type', () => { const res = { - outputLines: [], result: { namespace: null, type: 'InsertManyResult', @@ -468,7 +461,6 @@ suite('Telemetry Controller Test Suite', () => { test('convert InsertOneResult shellApiType to other telemetry type', () => { const res = { - outputLines: [], result: { namespace: null, type: 'InsertOneResult', @@ -482,7 +474,6 @@ suite('Telemetry Controller Test Suite', () => { test('convert ReplicaSet shellApiType to other telemetry type', () => { const res = { - outputLines: [], result: { namespace: null, type: 'ReplicaSet', @@ -496,7 +487,6 @@ suite('Telemetry Controller Test Suite', () => { test('convert Shard shellApiType to other telemetry type', () => { const res = { - outputLines: [], result: { namespace: null, type: 'Shard', @@ -510,7 +500,6 @@ suite('Telemetry Controller Test Suite', () => { test('convert ShellApi shellApiType to other telemetry type', () => { const res = { - outputLines: [], result: { namespace: null, type: 'ShellApi', @@ -524,7 +513,6 @@ suite('Telemetry Controller Test Suite', () => { test('convert UpdateResult shellApiType to other telemetry type', () => { const res = { - outputLines: [], result: { namespace: null, type: 'UpdateResult', @@ -538,7 +526,6 @@ suite('Telemetry Controller Test Suite', () => { test('return other telemetry type if evaluation returns a string', () => { const res = { - outputLines: [], result: { namespace: null, type: null, diff --git a/src/types/completionsCache.ts b/src/types/completionsCache.ts index 50e739653..d9c1f2474 100644 --- a/src/types/completionsCache.ts +++ b/src/types/completionsCache.ts @@ -1,3 +1,7 @@ export type ClearCompletionsCache = { - [key in 'databases' | 'collections' | 'fields']?: boolean; + [key in + | 'databases' + | 'collections' + | 'fields' + | 'streamProcessors']?: boolean; }; diff --git a/src/types/playgroundType.ts b/src/types/playgroundType.ts index 962647f95..b2728c71b 100644 --- a/src/types/playgroundType.ts +++ b/src/types/playgroundType.ts @@ -14,7 +14,6 @@ export type PlaygroundResult = OutputItem | undefined; export type ShellEvaluateResult = | { - outputLines: PlaygroundDebug; result: PlaygroundResult; } | undefined;