Skip to content

Commit 3161165

Browse files
committed
Merge branch 'main' into assistant-errors
2 parents 5aa85d4 + bb7cdac commit 3161165

File tree

15 files changed

+264
-49
lines changed

15 files changed

+264
-49
lines changed

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/compass-assistant/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@
5353
"@mongodb-js/atlas-service": "^0.56.0",
5454
"@mongodb-js/compass-app-registry": "^9.4.20",
5555
"@mongodb-js/compass-components": "^1.49.0",
56+
"@mongodb-js/connection-info": "^0.17.1",
57+
"mongodb-connection-string-url": "^3.0.1",
5658
"ai": "^5.0.26",
5759
"compass-preferences-model": "^2.51.0",
5860
"react": "^17.0.2",

packages/compass-assistant/src/compass-assistant-drawer.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export const CompassAssistantDrawer: React.FunctionComponent<{
3333
'The current chat will be cleared, and chat history will not be retrievable.',
3434
buttonText: 'Clear chat',
3535
variant: 'danger',
36+
'data-testid': 'assistant-confirm-clear-chat-modal',
3637
});
3738
if (confirmed) {
3839
clearChat();

packages/compass-assistant/src/compass-assistant-provider.spec.tsx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -202,19 +202,20 @@ describe('AssistantProvider', function () {
202202
userEvent.click(clearButton);
203203

204204
await waitFor(() => {
205-
expect(screen.getByTestId('confirmation-modal')).to.exist;
205+
expect(screen.getByTestId('assistant-confirm-clear-chat-modal')).to
206+
.exist;
206207
});
207208

208209
// There should be messages in the chat
209210
expect(screen.getByTestId('assistant-message-1')).to.exist;
210211
expect(screen.getByTestId('assistant-message-2')).to.exist;
211212

212-
const modal = screen.getByTestId('confirmation-modal');
213+
const modal = screen.getByTestId('assistant-confirm-clear-chat-modal');
213214
const confirmButton = within(modal).getByText('Clear chat');
214215
userEvent.click(confirmButton);
215216

216217
await waitForElementToBeRemoved(() =>
217-
screen.getByTestId('confirmation-modal')
218+
screen.getByTestId('assistant-confirm-clear-chat-modal')
218219
);
219220

220221
expect(mockChat.messages).to.be.empty;
@@ -231,19 +232,20 @@ describe('AssistantProvider', function () {
231232
userEvent.click(clearButton);
232233

233234
await waitFor(() => {
234-
expect(screen.getByTestId('confirmation-modal')).to.exist;
235+
expect(screen.getByTestId('assistant-confirm-clear-chat-modal')).to
236+
.exist;
235237
});
236238

237239
// There should be messages in the chat
238240
expect(screen.getByTestId('assistant-message-1')).to.exist;
239241
expect(screen.getByTestId('assistant-message-2')).to.exist;
240242

241-
const modal = screen.getByTestId('confirmation-modal');
243+
const modal = screen.getByTestId('assistant-confirm-clear-chat-modal');
242244
const cancelButton = within(modal).getByText('Cancel');
243245
userEvent.click(cancelButton);
244246

245247
await waitForElementToBeRemoved(() =>
246-
screen.getByTestId('confirmation-modal')
248+
screen.getByTestId('assistant-confirm-clear-chat-modal')
247249
);
248250

249251
expect(mockChat.messages).to.deep.equal(mockMessages);

packages/compass-assistant/src/compass-assistant-provider.tsx

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@ import React, { type PropsWithChildren, useRef } from 'react';
22
import { type UIMessage } from './@ai-sdk/react/use-chat';
33
import { Chat } from './@ai-sdk/react/chat-react';
44
import { createContext, useContext } from 'react';
5-
import { registerCompassPlugin } from '@mongodb-js/compass-app-registry';
5+
import {
6+
createServiceLocator,
7+
registerCompassPlugin,
8+
} from '@mongodb-js/compass-app-registry';
69
import { atlasServiceLocator } from '@mongodb-js/atlas-service/provider';
710
import { DocsProviderTransport } from './docs-provider-transport';
811
import { useDrawerActions } from '@mongodb-js/compass-components';
9-
import { buildExplainPlanPrompt } from './prompts';
12+
import { buildConnectionErrorPrompt, buildExplainPlanPrompt } from './prompts';
1013
import { usePreference } from 'compass-preferences-model/provider';
1114
import { createLoggerLocator } from '@mongodb-js/compass-logging/provider';
15+
import type { ConnectionInfo } from '@mongodb-js/connection-info';
16+
import { redactConnectionString } from 'mongodb-connection-string-url';
1217

1318
export const ASSISTANT_DRAWER_ID = 'compass-assistant-drawer';
1419

@@ -33,11 +38,19 @@ type AssistantActionsContextType = {
3338
namespace: string;
3439
explainPlan: string;
3540
}) => void;
41+
interpretConnectionError: ({
42+
connectionInfo,
43+
error,
44+
}: {
45+
connectionInfo: ConnectionInfo;
46+
error: Error;
47+
}) => void;
3648
clearChat: () => void;
3749
};
3850
export const AssistantActionsContext =
3951
createContext<AssistantActionsContextType>({
4052
interpretExplainPlan: () => {},
53+
interpretConnectionError: () => {},
4154
clearChat: () => {},
4255
});
4356

@@ -52,6 +65,24 @@ export function useAssistantActions(): AssistantActionsContextType & {
5265
};
5366
}
5467

68+
export const compassAssistantServiceLocator = createServiceLocator(function () {
69+
const { isAssistantEnabled, ...actions } = useAssistantActions();
70+
71+
const assistantEnabledRef = useRef(isAssistantEnabled);
72+
assistantEnabledRef.current = isAssistantEnabled;
73+
74+
return {
75+
...actions,
76+
getIsAssistantEnabled() {
77+
return assistantEnabledRef.current;
78+
},
79+
};
80+
}, 'compassAssistantLocator');
81+
82+
export type CompassAssistantService = ReturnType<
83+
typeof compassAssistantServiceLocator
84+
>;
85+
5586
export const AssistantProvider: React.FunctionComponent<
5687
PropsWithChildren<{
5788
chat: Chat<AssistantMessage>;
@@ -73,6 +104,25 @@ export const AssistantProvider: React.FunctionComponent<
73104
{}
74105
);
75106
},
107+
interpretConnectionError: ({ connectionInfo, error }) => {
108+
openDrawer(ASSISTANT_DRAWER_ID);
109+
110+
const connectionString = redactConnectionString(
111+
connectionInfo.connectionOptions.connectionString
112+
);
113+
const connectionError = error.toString();
114+
115+
const { prompt } = buildConnectionErrorPrompt({
116+
connectionString,
117+
connectionError,
118+
});
119+
void chat.sendMessage(
120+
{
121+
text: prompt,
122+
},
123+
{}
124+
);
125+
},
76126
clearChat: () => {
77127
chat.messages = [];
78128
},
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
export { CompassAssistantProvider } from './compass-assistant-provider';
22
export { CompassAssistantDrawer } from './compass-assistant-drawer';
3-
export { useAssistantActions } from './compass-assistant-provider';
3+
export {
4+
useAssistantActions,
5+
compassAssistantServiceLocator,
6+
} from './compass-assistant-provider';
7+
export type { CompassAssistantService } from './compass-assistant-provider';

packages/compass-assistant/src/prompts.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,21 @@ ${explainPlan}`,
1111
displayText: 'Provide an explanation of this explain plan.',
1212
};
1313
};
14+
15+
export const buildConnectionErrorPrompt = ({
16+
connectionString,
17+
connectionError,
18+
}: {
19+
connectionString: string;
20+
connectionError: string;
21+
}) => {
22+
return {
23+
prompt: `Given the error message below, please provide clear instructions to guide the user to debug their connection attempt from MongoDB Compass. If no auth mechanism is specified in the connection string, the default (username/password) is being used:
24+
25+
Connection string (password redacted):
26+
${connectionString}
27+
28+
Error message:
29+
${connectionError}`,
30+
};
31+
};

packages/compass-connections/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
},
5353
"dependencies": {
5454
"@mongodb-js/compass-app-registry": "^9.4.20",
55+
"@mongodb-js/compass-assistant": "^1.2.0",
5556
"@mongodb-js/compass-components": "^1.49.0",
5657
"@mongodb-js/compass-logging": "^1.7.12",
5758
"@mongodb-js/compass-telemetry": "^1.14.0",

packages/compass-connections/src/components/connection-status-notifications.tsx

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,33 @@ function ConnectionErrorToastBody({
8383
);
8484
}
8585

86+
type ConnectionDebugToastBodyProps = {
87+
onDebug: () => void;
88+
};
89+
90+
function ConnectionDebugToastBody({
91+
onDebug,
92+
}: ConnectionDebugToastBodyProps): React.ReactElement {
93+
return (
94+
<span className={connectionErrorToastBodyStyles}>
95+
<span
96+
data-testid="connection-debug-text"
97+
className={connectionErrorTextStyles}
98+
>
99+
Diagnose the issue and explore solutions with the assistant
100+
</span>
101+
<Link
102+
className={connectionErrorToastActionMessageStyles}
103+
hideExternalIcon={true}
104+
onClick={onDebug}
105+
data-testid="connection-error-debug"
106+
>
107+
DEBUG FOR ME
108+
</Link>
109+
</span>
110+
);
111+
}
112+
86113
const deviceAuthModalContentStyles = css({
87114
textAlign: 'center',
88115
'& > *:not(:last-child)': {
@@ -134,6 +161,8 @@ const openConnectionFailedToast = (
134161
) => {
135162
const failedToastId = connectionInfo?.id ?? 'failed';
136163

164+
// TODO(COMPASS-9746): close the existing connection toast and make a new one
165+
// for the failure so that the debug toast will appear below the failure one
137166
openToast(`connection-status--${failedToastId}`, {
138167
title: error.message,
139168
description: (
@@ -150,6 +179,25 @@ const openConnectionFailedToast = (
150179
});
151180
};
152181

182+
const openDebugConnectionErrorToast = (
183+
connectionInfo: ConnectionInfo,
184+
error: Error,
185+
onDebugClick: () => void
186+
) => {
187+
openToast(`debug-connection-error--${connectionInfo.id}`, {
188+
title: 'Need help debugging your connection error?',
189+
description: (
190+
<ConnectionDebugToastBody
191+
onDebug={() => {
192+
closeToast(`debug-connection-error--${connectionInfo.id}`);
193+
onDebugClick();
194+
}}
195+
/>
196+
),
197+
variant: 'note',
198+
});
199+
};
200+
153201
const openMaximumConnectionsReachedToast = (
154202
maxConcurrentConnections: number
155203
) => {
@@ -214,6 +262,7 @@ export function getNotificationTriggers() {
214262
openConnectionStartedToast,
215263
openConnectionSucceededToast,
216264
openConnectionFailedToast,
265+
openDebugConnectionErrorToast,
217266
openMaximumConnectionsReachedToast,
218267
closeConnectionStatusToast: (connectionId: string) => {
219268
return closeToast(`connection-status--${connectionId}`);

packages/compass-connections/src/index.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
} from './stores/store-context';
2525
export type { ConnectionFeature } from './utils/connection-supports';
2626
export { connectionSupports, connectable } from './utils/connection-supports';
27+
import { compassAssistantServiceLocator } from '@mongodb-js/compass-assistant';
2728

2829
const ConnectionsComponent: React.FunctionComponent<{
2930
/**
@@ -82,7 +83,14 @@ const CompassConnectionsPlugin = registerCompassPlugin(
8283
component: ConnectionsComponent,
8384
activate(
8485
initialProps,
85-
{ logger, preferences, connectionStorage, track, globalAppRegistry },
86+
{
87+
logger,
88+
preferences,
89+
connectionStorage,
90+
track,
91+
globalAppRegistry,
92+
compassAssistant,
93+
},
8694
{ addCleanup, cleanup }
8795
) {
8896
const store = configureStore(initialProps.preloadStorageConnectionInfos, {
@@ -95,6 +103,7 @@ const CompassConnectionsPlugin = registerCompassPlugin(
95103
connectFn: initialProps.connectFn,
96104
globalAppRegistry,
97105
onFailToLoadConnections: initialProps.onFailToLoadConnections,
106+
compassAssistant,
98107
});
99108

100109
setTimeout(() => {
@@ -128,6 +137,7 @@ const CompassConnectionsPlugin = registerCompassPlugin(
128137
preferences: preferencesLocator,
129138
connectionStorage: connectionStorageLocator,
130139
track: telemetryLocator,
140+
compassAssistant: compassAssistantServiceLocator,
131141
}
132142
);
133143

0 commit comments

Comments
 (0)