From d5a93453d7add28e5098a921c2d452c33bfa3dfa Mon Sep 17 00:00:00 2001 From: Gagik Amaryan Date: Fri, 8 Nov 2024 18:21:35 +0100 Subject: [PATCH] feat(chat): show errors if there are issues when getting databases & collections to pick VSCODE-610 (#864) --- src/participant/participant.ts | 45 ++++++---- .../suite/participant/participant.test.ts | 82 ++++++++++++++++++- 2 files changed, 107 insertions(+), 20 deletions(-) diff --git a/src/participant/participant.ts b/src/participant/participant.ts index b0254c16f..5fe25c130 100644 --- a/src/participant/participant.ts +++ b/src/participant/participant.ts @@ -793,11 +793,10 @@ export default class ParticipantController { _id: string; name: string; }[] - | undefined > { const dataService = this._connectionController.getActiveDataService(); if (!dataService) { - return undefined; + throw new Error(vscode.l10n.t('Failed to get the data service')); } stream.push( @@ -811,7 +810,11 @@ export default class ParticipantController { } catch (error) { log.error('Unable to fetch databases:', error); - return undefined; + throw new Error( + vscode.l10n.t( + `Unable to fetch database names: ${formatError(error).message}.` + ) + ); } } @@ -821,11 +824,11 @@ export default class ParticipantController { }: { stream: vscode.ChatResponseStream; databaseName: string; - }): Promise | undefined> { + }): Promise> { const dataService = this._connectionController.getActiveDataService(); if (!dataService) { - return undefined; + throw new Error(vscode.l10n.t('Failed to get the data service')); } stream.push( @@ -835,9 +838,15 @@ export default class ParticipantController { try { return await dataService.listCollections(databaseName); } catch (error) { - log.error('Unable to fetch collections:', error); + log.error('Unable to fetch collection names:', error); - return undefined; + throw new Error( + vscode.l10n.t( + `Unable to fetch collection names from ${databaseName}: ${ + formatError(error).message + }.` + ) + ); } } @@ -856,9 +865,12 @@ export default class ParticipantController { }): Promise { const collections = await this._getCollections({ stream, databaseName }); - if (collections === undefined) { - log.error('No collections found'); - return undefined; + if (collections.length === 0) { + throw new Error( + vscode.l10n.t( + `No collections were found in the database ${databaseName}.` + ) + ); } if (collections.length === 1) { return collections[0].name; @@ -894,9 +906,8 @@ export default class ParticipantController { }): Promise { const databases = await this._getDatabases({ stream }); - if (databases === undefined || databases.length === 0) { - log.error('No databases found'); - return undefined; + if (databases.length === 0) { + throw new Error(vscode.l10n.t('No databases were found.')); } if (databases.length === 1) { @@ -908,9 +919,11 @@ export default class ParticipantController { // Users can then select a value by clicking on an item in the list // or typing the name manually. stream.markdown( - `Which database would you like ${ - command === '/query' ? 'this query to run against' : 'to use' - }? Select one by either clicking on an item in the list or typing the name manually in the chat.\n\n` + vscode.l10n.t( + `Which database would you like ${ + command === '/query' ? 'this query to run against' : 'to use' + }? Select one by either clicking on an item in the list or typing the name manually in the chat.\n\n` + ) ); this.renderDatabasesTree({ diff --git a/src/test/suite/participant/participant.test.ts b/src/test/suite/participant/participant.test.ts index 8b5916ecf..f5209b8a1 100644 --- a/src/test/suite/participant/participant.test.ts +++ b/src/test/suite/participant/participant.test.ts @@ -1,7 +1,7 @@ import * as vscode from 'vscode'; import { beforeEach, afterEach } from 'mocha'; import { expect } from 'chai'; -import type { SinonSpy } from 'sinon'; +import type { SinonSpy, SinonStub } from 'sinon'; import sinon from 'sinon'; import type { DataService } from 'mongodb-data-service'; import { ObjectId, Int32 } from 'bson'; @@ -404,9 +404,9 @@ suite('Participant Controller Test Suite', function () { }); suite('when connected', function () { - let sampleStub; - let listCollectionsStub; - let listDatabasesStub; + let sampleStub: SinonStub; + let listCollectionsStub: SinonStub; + let listDatabasesStub: SinonStub; beforeEach(function () { sampleStub = sinon.stub(); @@ -1790,6 +1790,43 @@ Schema: sinon.restore(); }); + test('shows an error if something goes wrong with getting databases', async function () { + listDatabasesStub.rejects(new Error('Something went wrong')); + + let caughtError: Error | undefined; + try { + await invokeChatHandler({ + prompt: 'find all docs by a name example', + command, + references: [], + }); + } catch (error) { + caughtError = error as Error; + } + + expect(caughtError?.message).equals( + 'Unable to fetch database names: Something went wrong.' + ); + }); + + test('shows an error if there are no databases found', async function () { + // No databases + listDatabasesStub.resolves([]); + + let caughtError: Error | undefined; + try { + await invokeChatHandler({ + prompt: 'find all docs by a name example', + command, + references: [], + }); + } catch (error) { + caughtError = error as Error; + } + + expect(caughtError?.message).equals('No databases were found.'); + }); + test('database name gets picked automatically if there is only 1', async function () { listDatabasesStub.resolves([{ name: 'onlyOneDb' }]); @@ -1859,6 +1896,43 @@ Schema: ); }); + test('shows an error if something goes wrong with getting collections', async function () { + listCollectionsStub.rejects(new Error('Something went wrong')); + + let caughtError: Error | undefined; + try { + await invokeChatHandler({ + prompt: 'find all docs by a name example', + command, + references: [], + }); + } catch (error) { + caughtError = error as Error; + } + + expect(caughtError?.message).equals( + 'Unable to fetch collection names from dbOne: Something went wrong.' + ); + }); + + test('shows an error if there are no collections found', async function () { + listCollectionsStub.resolves([]); + let caughtError: Error | undefined; + try { + await invokeChatHandler({ + prompt: 'find all docs by a name example', + command, + references: [], + }); + } catch (error) { + caughtError = error as Error; + } + + expect(caughtError?.message).equals( + 'No collections were found in the database dbOne.' + ); + }); + test('collection name gets picked automatically if there is only 1', async function () { listCollectionsStub.resolves([{ name: 'onlyOneColl' }]); const renderCollectionsTreeSpy = sinon.spy(