Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle network disconnection #107

Merged
merged 10 commits into from
Jul 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions vscode/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ Starting from `0.2.0`, Cody is using `major.EVEN_NUMBER.patch` for release versi
- Add suggested recipes to the new chat welcome message. [pull/54277](https://github.com/sourcegraph/sourcegraph/pull/54277)
- Inline Chat: Added the option to collapse all inline chats from within the inline chat window. [pull/54675](https://github.com/sourcegraph/sourcegraph/pull/54675)
- Inline Chat: We now stream messages rather than waiting for the response to be fully complete. This means you can read Cody's response as it is being generated. [pull/54665](https://github.com/sourcegraph/sourcegraph/pull/54665)
- Show network error message when connection is lost and a reload button to get back when network is restored. [pull/107](https://github.com/sourcegraph/cody/pull/107)

### Fixed

Expand Down
3 changes: 3 additions & 0 deletions vscode/src/chat/ChatViewProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ export class ChatViewProvider extends MessageProvider implements vscode.WebviewV
case 'my-prompt':
await this.onCustomRecipeClicked(message.title, message.value)
break
case 'reload':
await this.authProvider.reloadAuthStatus()
break
case 'openFile': {
const rootPath = this.editor.getWorkspaceRootPath()
if (!rootPath) {
Expand Down
6 changes: 5 additions & 1 deletion vscode/src/chat/MessageProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { debug } from '../log'
import { CodyPromptType } from '../my-cody/types'
import { FixupTask } from '../non-stop/FixupTask'
import { IdleRecipeRunner } from '../non-stop/roles'
import { AuthProvider } from '../services/AuthProvider'
import { AuthProvider, isNetworkError } from '../services/AuthProvider'
import { LocalStorage } from '../services/LocalStorageProvider'
import { TestSupport } from '../test-support'

Expand Down Expand Up @@ -262,6 +262,10 @@ export abstract class MessageProvider extends MessageHandler implements vscode.D
.catch(error => console.error(error))
debug('ChatViewProvider:onError:unauthUser', err, { verbose: { statusCode } })
}

if (isNetworkError(err)) {
err = 'Cody could not respond due to network error.'
}
// Display error message as assistant response
this.transcript.addErrorAsAssistantResponse(err)
// We ignore embeddings errors in this instance because we're already showing an
Expand Down
13 changes: 13 additions & 0 deletions vscode/src/chat/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export type WebviewMessage =
| { command: 'chat-button'; action: string }
| { command: 'setEnabledPlugins'; plugins: string[] }
| { command: 'my-prompt'; title: string; value?: CodyPromptType }
| { command: 'reload' }

/**
* A message sent from the extension host to the webview.
Expand Down Expand Up @@ -91,6 +92,7 @@ export interface AuthStatus {
siteHasCodyEnabled: boolean
siteVersion: string
configOverwrites?: CodyLLMSiteConfiguration
showNetworkError?: boolean
}

export const defaultAuthStatus = {
Expand All @@ -115,6 +117,17 @@ export const unauthenticatedStatus = {
siteVersion: '',
}

export const networkErrorAuthStatus = {
showInvalidAccessTokenError: false,
authenticated: false,
isLoggedIn: false,
hasVerifiedEmail: false,
showNetworkError: true,
requiresVerifiedEmail: false,
siteHasCodyEnabled: false,
siteVersion: '',
}

/** The local environment of the editor. */
export interface LocalEnv {
// The operating system kind
Expand Down
31 changes: 31 additions & 0 deletions vscode/src/services/AuthProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
isLoggedIn as isAuthed,
isLocalApp,
LOCAL_APP_URL,
networkErrorAuthStatus,
unauthenticatedStatus,
} from '../chat/protocol'
import { newAuthStatus } from '../chat/utils'
Expand Down Expand Up @@ -191,6 +192,14 @@ export class AuthProvider {
if (!isDotComOrApp) {
const currentUserID = await this.client.getCurrentUserId()
const hasVerifiedEmail = false

// check first if it's a network error
if (isError(currentUserID)) {
if (isNetworkError(currentUserID.message)) {
return { ...networkErrorAuthStatus, endpoint }
}
}

return newAuthStatus(
endpoint,
isDotComOrApp,
Expand All @@ -203,6 +212,14 @@ export class AuthProvider {
}
const userInfo = await this.client.getCurrentUserIdAndVerifiedEmail()
const isCodyEnabled = true

// check first if it's a network error
if (isError(userInfo)) {
if (isNetworkError(userInfo.message)) {
return { ...networkErrorAuthStatus, endpoint }
}
}

return isError(userInfo)
? { ...unauthenticatedStatus, endpoint }
: newAuthStatus(
Expand Down Expand Up @@ -241,6 +258,11 @@ export class AuthProvider {
return { authStatus, isLoggedIn }
}

// Set auth status in case of reload
public async reloadAuthStatus(): Promise<void> {
await this.auth(this.config.serverEndpoint, this.config.accessToken, this.config.customHeaders)
}

// Set auth status and share it with chatview
private async syncAuthStatus(authStatus: AuthStatus): Promise<void> {
if (this.authStatus === authStatus) {
Expand Down Expand Up @@ -341,6 +363,15 @@ export class AuthProvider {
}
}

export function isNetworkError(error: string): boolean {
return (
error.includes('ENOTFOUND') ||
error.includes('ECONNREFUSED') ||
error.includes('ECONNRESET') ||
error.includes('EHOSTUNREACH')
)
}

function formatURL(uri: string): string | null {
if (!uri) {
return null
Expand Down
28 changes: 27 additions & 1 deletion vscode/webviews/Error.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { VSCodeButton } from '@vscode/webview-ui-toolkit/react'

import { AuthStatus, isLocalApp } from '../src/chat/protocol'

import { getVSCodeAPI } from './utils/VSCodeApi'

import styles from './Login.module.css'

const AUTH_ERRORS = {
Expand All @@ -10,6 +14,7 @@ const AUTH_ERRORS = {
EMAIL_NOT_VERIFIED: 'Email not verified. Please add a verified email to your Sourcegraph.com account.',
APP_NOT_RUNNING: 'Cody App is not running. Please open the Cody App to continue.',
INVALID_URL: 'Connection failed due to invalid URL. Please enter a valid Sourcegraph instance URL.',
NETWORK_ERROR: 'Connection failed due to a network problem. Please check your internet connection and try again.',
}
export const ErrorContainer: React.FunctionComponent<{
authStatus: AuthStatus
Expand All @@ -26,11 +31,12 @@ export const ErrorContainer: React.FunctionComponent<{
requiresVerifiedEmail,
hasVerifiedEmail,
siteVersion,
showNetworkError,
} = authStatus
// Version is compatible if this is an insider build or version is 5.1.0 or above
// Right now we assumes all insider builds have Cody enabled
// NOTE: Insider build includes App builds but we should seperate them in the future
if (!authenticated && !showInvalidAccessTokenError) {
if (!authenticated && !showInvalidAccessTokenError && !showNetworkError) {
return null
}
// Errors for app are handled in the ConnectApp component
Expand All @@ -45,6 +51,26 @@ export const ErrorContainer: React.FunctionComponent<{
const isVersionCompatible = isInsiderBuild || siteVersion >= '5.1.0'
const isVersionBeforeCody = !isVersionCompatible && siteVersion < '5.0.0'
const prefix = `Failed: ${isApp.isRunning ? 'Cody App' : endpoint}`

const handleReload = (): void => {
getVSCodeAPI().postMessage({ command: 'reload' })
}

// When there's a network error
if (showNetworkError) {
return (
<>
<div className={styles.error}>
<p>Current endpoint: {endpoint}</p>
<p>{AUTH_ERRORS.NETWORK_ERROR}</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this! Maybe we can show the endpoint address to let the user know which instance is having network issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be a good add on, I will update it.

<VSCodeButton className={styles.button} type="button" onClick={handleReload}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe update the button styles to have full width like the rest of the buttons in the login page? Will defer to @toolmantim who will be back next week from his PTO :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we can have that so it would be align to the style with all other buttons.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd love us to move away from the-full width buttons… but it's good to be consistent. But given it's an error state, I'm okay with just leaving this as a normal button w/ inherent width.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, thanks @toolmantim

Reload
</VSCodeButton>
</div>
</>
)
}

// When doesn't have a valid token
if (showInvalidAccessTokenError) {
return (
Expand Down
3 changes: 2 additions & 1 deletion vscode/webviews/Login.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
background-color: var(--vscode-inputValidation-errorBackground);
padding: 0.5rem;
margin-top: 1rem;
margin-bottom: 1rem;
}

.terms {
Expand All @@ -82,4 +83,4 @@

width: 100%;
gap: 1rem;
}
}
Loading