module license import { ModuleRpcCommon } from '../common' ; import { ClientProtocolError , ClientRpcError } from './errors' ; import { BackoffOptions } from './backoff' ; import { StreamProducer , Stream , transformStream } from './stream' ; import { mapValuesWithStringKeys } from '../utils/utils' ; import * as events from 'events' ; import { retryStream } from './stream_retrier' ; /**
 * Default list of [[ModuleRpcCommon.RpcErrorType]] error types that
 * should lead to an immediate failure and not to a retry.
 */ export const ERROR_TYPES_TO_FAIL_IMMEDIATELY = [ ModuleRpcCommon . RpcErrorType . invalidArgument , ModuleRpcCommon . RpcErrorType . notFound , ModuleRpcCommon . RpcErrorType . alreadyExists , ModuleRpcCommon . RpcErrorType . permissionDenied , ModuleRpcCommon . RpcErrorType . failedPrecondition , ModuleRpcCommon . RpcErrorType . unimplemented , ModuleRpcCommon . RpcErrorType . internal , ModuleRpcCommon . RpcErrorType . unauthenticated , ] ; see export const DEFAULT_SHOULD_RETRY = ( err ) : boolean => { if ( err instanceof ClientProtocolError ) { return false ; } else if ( err instanceof ClientRpcError && ERROR_TYPES_TO_FAIL_IMMEDIATELY . includes ( err . errorType ) ) { return false ; } return true ; } ; typeparam typeparam export class Service < serviceDefinition extends ModuleRpcCommon . ServiceDefinition , ResponseContext > { param serviceDefinition param streamProducer constructor ( readonly serviceDefinition , private readonly streamProducer ) { } return stream < method extends ModuleRpcCommon . MethodsFor < serviceDefinition > > ( method , request ) { return this . streamProducer < ModuleRpcCommon . RequestFor < serviceDefinition , method > , { response : ModuleRpcCommon . ResponseFor < serviceDefinition , method > ; responseContext : ResponseContext ; } > ( method , request ) ; } return throws throws see call < method extends ModuleRpcCommon . UnaryMethodsFor < serviceDefinition > > ( method , request ) { return new Promise < ResponseWithContext < serviceDefinition , method , ResponseContext > > ( ( accept , reject ) => { let message ; this . stream ( method , request ) . on ( 'message' , msg => { if ( message ) { reject ( new ClientProtocolError ( method , 'expected unary method, but got more than one message from server' ) ) ; } else { message = msg ; } } ) . on ( 'error' , err => { reject ( err ) ; } ) . on ( 'complete' , ( ) => { if ( ! message ) { reject ( new ClientProtocolError ( method , 'expected unary method, but got no message from server' ) ) ; } else { accept ( message ) ; } } ) . on ( 'canceled' , ( ) => { reject ( new ClientRpcError ( ModuleRpcCommon . RpcErrorType . canceled ) ) ; } ) . start ( ) ; } ) ; } param backoffOptions param shouldRetry withRetry ( backoffOptions = { } , shouldRetry = DEFAULT_SHOULD_RETRY ) { return new ServiceRetrierImpl ( this , backoffOptions , shouldRetry ) ; } example methodMap ( ) { const methods = mapValuesWithStringKeys ( this . serviceDefinition , ( methodDefinition , method ) => { if ( methodDefinition . type === ModuleRpcCommon . ServiceMethodType . serverStream ) { return ( request ) => transformStream ( this . stream ( method , request ) , ( { response } ) => response ) ; } else { return async ( request ) => { const { response } = await this . call ( method as any , request ) ; return response ; } ; } } ) as any ; return { ... methods , [ serviceKey ] : this , } ; } } see export interface ResponseWithContext < serviceDefinition extends ModuleRpcCommon . ServiceDefinition , method extends ModuleRpcCommon . MethodsFor < serviceDefinition > , ResponseContext > { response : ModuleRpcCommon . ResponseFor < serviceDefinition , method > ; responseContext : ResponseContext ; } /**
 * Symbol used as a property name on a [[ServiceMethodMap]] to access the
 * full service interface from a "method map" service interface.
 *
 * This symbol is private to this module as [[serviceInstance]] should
 * be used externally instead of directly accessing the service instance
 * using the symbol.
 */ const serviceKey = Symbol ( 'serviceKey' ) ; see export type ServiceMethodMap < serviceDefinition extends ModuleRpcCommon . ServiceDefinition , ResponseContext = any > = { [ method in ModuleRpcCommon . MethodsFor < serviceDefinition > ] : serviceDefinition [ method ] extends { type : typeof ModuleRpcCommon . ServiceMethodType . serverStream ; } ? ServerStreamMethod < serviceDefinition , method > : UnaryMethod < serviceDefinition , method > ; } & { [ serviceKey ] : Service < serviceDefinition , ResponseContext > ; } ; example export function serviceInstance < serviceDefinition extends ModuleRpcCommon . ServiceDefinition , ResponseContext = any > ( methodMap ) { return methodMap [ serviceKey ] ; } /** A unary method typed as part of a "method map" service interface. */ export interface UnaryMethod < serviceDefinition extends ModuleRpcCommon . ServiceDefinition , method extends ModuleRpcCommon . MethodsFor < serviceDefinition > > { ( request ) : Promise < ModuleRpcCommon . ResponseFor < serviceDefinition , method > > ; } /** A server-stream method typed as part of a "method map" service interface. */ export interface ServerStreamMethod < serviceDefinition extends ModuleRpcCommon . ServiceDefinition , method extends ModuleRpcCommon . MethodsFor < serviceDefinition > > { ( request ) : Stream < ModuleRpcCommon . ResponseFor < serviceDefinition , method > > ; } /**
 * Wrapper that retries RPCs when they fail and emit service-wide events.
 */ export interface ServiceRetrier < serviceDefinition extends ModuleRpcCommon . ServiceDefinition , ResponseContext > { see ModuleRpcClient . Service service ( ) : Service < serviceDefinition , ResponseContext > ; /**
     * Register listeners.
     *
     * Events are:
     *
     * - `serviceReady`: When a method is ready to process a request.
     * - `serviceComplete`: When a method has completed after successfully
     * processing a request.
     * - `serviceError`: When a method has errored.  Parameters:
     *   - `err`: The error.
     *   - `retriesSinceLastReady`: The number of retries made since the method
     * last emitted a 'ready' event.
     *   - `abandoned`: Whether as a result of this error event the retrying was
     * abandoned (as a consequence, the error `err` has been forwarded to the client).
     *   - `method`: The method that errored.
     *   - `request`: The request the method errored on.
     */ on ( event , callback ) : this ; on ( event , callback ) : this ; } class ServiceRetrierImpl < serviceDefinition extends ModuleRpcCommon . ServiceDefinition , ResponseContext > extends events . EventEmitter implements ServiceRetrier < serviceDefinition , ResponseContext > { constructor ( private readonly baseService , private readonly backoffOptions , private readonly shouldRetry ) { super ( ) ; } service ( ) { return new Service < serviceDefinition , ResponseContext > ( this . baseService . serviceDefinition , this . stream ) ; } private stream : StreamProducer = < method extends ModuleRpcCommon . MethodsFor < serviceDefinition > > ( method , request ) => { return retryStream < ModuleRpcCommon . ResponseFor < serviceDefinition , method > > ( ( ) => this . baseService . stream ( method , request ) , this . backoffOptions , this . shouldRetry ) . on ( 'ready' , ( ) => { this . emit ( 'serviceReady' , method , request ) ; } ) . on ( 'retryingError' , ( err , retries , abandoned ) => { this . emit ( 'serviceError' , err , retries , abandoned , method , request ) ; } ) . on ( 'complete' , ( ) => { this . emit ( 'serviceComplete' , method , request ) ; } ) ; } ; }