github.com/quickfeed/quickfeed@v0.0.0-20240507093252-ed8ca812a09c/public/src/client.ts (about)

     1  import { CallOptions, ConnectError, Transport, makeAnyClient } from "@bufbuild/connect";
     2  import { createAsyncIterable } from "@bufbuild/connect/protocol";
     3  import {
     4      MethodInfo,
     5      MethodInfoServerStreaming,
     6      MethodInfoUnary,
     7      PartialMessage,
     8      ServiceType,
     9      Message,
    10      MethodKind,
    11      AnyMessage,
    12  } from "@bufbuild/protobuf";
    13  
    14  export type Response<T extends AnyMessage> = {
    15      error: ConnectError | null
    16      message: T
    17  }
    18  
    19  /**
    20   * ResponseClient is a simple client that supports unary and server-streaming
    21   * methods. Methods will produce a promise for the response message,
    22   * or an asynchronous iterable of response messages.
    23   */
    24  export type ResponseClient<T extends ServiceType> = {
    25      [P in keyof T["methods"]]:
    26      T["methods"][P] extends MethodInfoUnary<infer I, infer O> ? (request: PartialMessage<I>, options?: CallOptions) => Promise<Response<O>>
    27      : T["methods"][P] extends MethodInfoServerStreaming<infer I, infer O> ? (request: PartialMessage<I>, options?: CallOptions) => AsyncIterable<O>
    28      : never;
    29  };
    30  
    31  /**
    32   * Create a ResponseClient for the given service, invoking RPCs through the
    33   * given transport.
    34   */
    35  export function createResponseClient<T extends ServiceType>(
    36      service: T,
    37      transport: Transport,
    38      errorHandler: (payload?: { method: string; error: ConnectError; } | undefined) => void
    39  ) {
    40      return makeAnyClient(service, (method) => {
    41          switch (method.kind) {
    42              case MethodKind.Unary:
    43                  return createUnaryFn(transport, service, method, errorHandler);
    44              case MethodKind.ServerStreaming:
    45                  return createServerStreamingFn(transport, service, method);
    46              default:
    47                  return null;
    48          }
    49      }) as ResponseClient<T>;
    50  }
    51  
    52  /**
    53   * UnaryFn is the method signature for a unary method of a ResponseClient.
    54   */
    55  type UnaryFn<I extends Message<I>, O extends Message<O>> = (
    56      request: PartialMessage<I>,
    57      options?: CallOptions
    58  ) => Promise<Response<O>>;
    59  
    60  function createUnaryFn<I extends Message<I>, O extends Message<O>>(
    61      transport: Transport,
    62      service: ServiceType,
    63      method: MethodInfo<I, O>,
    64      errorHandler: (payload?: { method: string; error: ConnectError; } | undefined) => void
    65  ): UnaryFn<I, O> {
    66      return async function (input, options) {
    67          try {
    68              const response = await transport.unary(
    69                  service,
    70                  method,
    71                  options?.signal,
    72                  options?.timeoutMs,
    73                  options?.headers,
    74                  input
    75              );
    76              options?.onHeader?.(response.header);
    77              options?.onTrailer?.(response.trailer);
    78              return {
    79                  error: null,
    80                  message: response.message
    81              } as Response<O>;
    82          }
    83          catch (error) {
    84              errorHandler({ method: method.name, error });
    85              return {
    86                  error,
    87              } as Response<O>;
    88          }
    89      }
    90  }
    91  
    92  /**
    93   * ServerStreamingFn is the method signature for a server-streaming method of
    94   * a ResponseClient.
    95   */
    96  type ServerStreamingFn<I extends Message<I>, O extends Message<O>> = (
    97      request: PartialMessage<I>,
    98      options?: CallOptions
    99  ) => AsyncIterable<O>;
   100  
   101  export function createServerStreamingFn<
   102      I extends Message<I>,
   103      O extends Message<O>
   104  >(
   105      transport: Transport,
   106      service: ServiceType,
   107      method: MethodInfo<I, O>
   108  ): ServerStreamingFn<I, O> {
   109      return async function* (input, options): AsyncIterable<O> {
   110          const inputMessage =
   111              input instanceof method.I ? input : new method.I(input);
   112          const response = await transport.stream<I, O>(
   113              service,
   114              method,
   115              options?.signal,
   116              options?.timeoutMs,
   117              options?.headers,
   118              createAsyncIterable([inputMessage])
   119          );
   120          options?.onHeader?.(response.header);
   121          yield* response.message;
   122          options?.onTrailer?.(response.trailer);
   123      }
   124  }