55
66import { GitpodClient , GitpodServer , GitpodServiceImpl } from '@gitpod/gitpod-protocol/lib/gitpod-service' ;
77import { JsonRpcProxyFactory } from '@gitpod/gitpod-protocol/lib/messaging/proxy-factory' ;
8- import { listen as doListen } from 'vscode-ws-jsonrpc' ;
8+ import { listen as doListen , ResponseError } from 'vscode-ws-jsonrpc' ;
99import WebSocket , { ErrorEvent } from 'ws' ;
1010import ReconnectingWebSocket from 'reconnecting-websocket' ;
1111import * as vscode from 'vscode' ;
1212import { ILogService } from './services/logService' ;
13+ import { Code } from '@bufbuild/connect' ;
14+ import { WrapError } from './common/utils' ;
1315
1416type UsedGitpodFunction = [ 'getLoggedInUser' , 'getWorkspace' , 'getOwnerToken' , 'getSSHPublicKeys' , 'sendHeartBeat' ] ;
1517type Union < Tuple extends any [ ] , Union = never > = Tuple [ number ] | Union ;
@@ -58,6 +60,11 @@ class GitpodServerApi extends vscode.Disposable {
5860 } ) ;
5961 webSocket . onerror = ( e : ErrorEvent ) => {
6062 if ( webSocket . retryCount >= maxRetries ) {
63+ // https://github.com/gitpod-io/gitpod/blob/d41a38ba83939856e5292e30912f52e749787db1/components/server/src/server.ts#L193-L195
64+ if ( e . error . message === 'Unexpected server response: 401' ) {
65+ this . onErrorEmitter . fire ( new WrapError ( 'Failed to call server API' , e . error , 'ServerAPI:' + Code [ Code . Unauthenticated ] ) ) ;
66+ return ;
67+ }
6168 this . onErrorEmitter . fire ( e . error ) ;
6269 }
6370 } ;
@@ -84,9 +91,46 @@ class GitpodServerApi extends vscode.Disposable {
8491export function withServerApi < T > ( accessToken : string , serviceUrl : string , cb : ( service : GitpodConnection ) => Promise < T > , logger : ILogService ) : Promise < T > {
8592 const api = new GitpodServerApi ( accessToken , serviceUrl , logger ) ;
8693 return Promise . race ( [
87- cb ( api . service ) ,
94+ new Promise < T > ( ( resolve , reject ) => cb ( api . service ) . then ( resolve ) . catch ( err => {
95+ if ( err instanceof ResponseError ) {
96+ const code = categorizeRPCError ( err ) ;
97+ const codeStr = code ? Code [ code ] : 'Unknown' ;
98+ reject ( new WrapError ( 'Failed to call server API' , err , 'ServerAPI:' + codeStr ) ) ;
99+ return ;
100+ }
101+ reject ( err ) ;
102+ } ) ) ,
88103 new Promise < T > ( ( _ , reject ) => api . onError ( error => {
89104 reject ( error ) ;
90105 } ) )
91106 ] ) . finally ( ( ) => api . dispose ( ) ) ;
92107}
108+
109+ // Should align with https://github.com/gitpod-io/gitpod/blob/d41a38ba83939856e5292e30912f52e749787db1/components/public-api-server/pkg/proxy/errors.go#LL25C1-L26C1
110+ function categorizeRPCError ( err ?: ResponseError < any > ) : Code | undefined {
111+ if ( ! err ) {
112+ return ;
113+ }
114+ switch ( err . code ) {
115+ case 400 :
116+ return Code . InvalidArgument ;
117+ case 401 :
118+ return Code . Unauthenticated ;
119+ case 403 :
120+ return Code . PermissionDenied ;
121+ case 404 :
122+ return Code . NotFound ;
123+ case 409 :
124+ return Code . AlreadyExists ;
125+ case 429 :
126+ return Code . ResourceExhausted ;
127+ case 470 :
128+ return Code . PermissionDenied ;
129+ case - 32603 :
130+ return Code . Internal ;
131+ }
132+ if ( err . code >= 400 && err . code < 500 ) {
133+ return Code . InvalidArgument ;
134+ }
135+ return Code . Internal ;
136+ }
0 commit comments