Skip to content

Commit

Permalink
Handle network disconnection (#107)
Browse files Browse the repository at this point in the history
This PR closes #43. It handles the network error for the DOTCOM
instance. It handles the server disconnection due to network issues by
adding an appropriate message. Also, add a reload button to return when
the user is back in the network.

# Test plan

All tests have passed.


https://github.com/sourcegraph/cody/assets/44617923/6becbe05-3d24-456a-9a1e-caf7f4fa04c6
  • Loading branch information
deepak2431 committed Jul 27, 2023
1 parent d07fa9b commit 0714046
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 3 deletions.
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 @@ -87,6 +87,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 rootUri = this.editor.getWorkspaceRootUri()
if (!rootUri) {
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 @@ -90,6 +91,7 @@ export interface AuthStatus {
siteHasCodyEnabled: boolean
siteVersion: string
configOverwrites?: CodyLLMSiteConfiguration
showNetworkError?: boolean
}

export const defaultAuthStatus = {
Expand All @@ -114,6 +116,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>
<VSCodeButton className={styles.button} type="button" onClick={handleReload}>
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;
}
}

0 comments on commit 0714046

Please sign in to comment.