grpc / grpc-web

gRPC for Web Clients
https://grpc.io
Apache License 2.0
8.46k stars 762 forks source link

gRPC client streaming typescript not created. #1275

Closed kkuehl closed 1 year ago

kkuehl commented 1 year ago

Minimal protobuf example:

syntax = "proto3";

package filestream;

service FileService {
  rpc FileUploadCommand(stream FileUploadCommandRequest)
      returns (FileUploadCommandResponse) {}
  rpc FileDownloadCommand(FileDownloadCommandRequest)
      returns (stream FileDownloadCommandResponse) {}
}

enum FileCommand {
  FILE_UPLOAD = 0;
  FILE_DOWNLOAD = 1;
}

message FileContent {
  string filename = 1;
  bytes filecontent = 2;
}

message FileUploadCommandRequest { FileContent filecontent = 1; }

message FileUploadCommandResponse {
  string filename = 1;
  int64 filesize = 2;
}

message FileDownloadCommandRequest { string filename = 1; }

message FileDownloadCommandResponse { FileContent filecontent = 1; }

gRPC version: 1.48.1

protoc --version
libprotoc 3.19.4

protoc-gen-grpc-web version: 1.3.1-linux-x86_64

Command:

protoc -I=$DIR file.proto \
    --js_out=import_style=commonjs:$OUT_DIR \
    --grpc-web_out=import_style=typescript,mode=grpcwebtext:$OUT_DIR

Output:

/**
 * @fileoverview gRPC-Web generated client stub for filestream
 * @enhanceable
 * @public
 */

// GENERATED CODE -- DO NOT EDIT!

/* eslint-disable */
// @ts-nocheck

import * as grpcWeb from 'grpc-web';

import * as file_pb from './file_pb';

export class FileServiceClient {
  client_: grpcWeb.AbstractClientBase;
  hostname_: string;
  credentials_: null | { [index: string]: string; };
  options_: null | { [index: string]: any; };

  constructor (hostname: string,
               credentials?: null | { [index: string]: string; },
               options?: null | { [index: string]: any; }) {
    if (!options) options = {};
    if (!credentials) credentials = {};
    options['format'] = 'text';

    this.client_ = new grpcWeb.GrpcWebClientBase(options);
    this.hostname_ = hostname;
    this.credentials_ = credentials;
    this.options_ = options;
  }

  methodDescriptorFileDownloadCommand = new grpcWeb.MethodDescriptor(
    '/filestream.FileService/FileDownloadCommand',
    grpcWeb.MethodType.SERVER_STREAMING,
    file_pb.FileDownloadCommandRequest,
    file_pb.FileDownloadCommandResponse,
    (request: file_pb.FileDownloadCommandRequest) => {
      return request.serializeBinary();
    },
    file_pb.FileDownloadCommandResponse.deserializeBinary
  );

  fileDownloadCommand(
    request: file_pb.FileDownloadCommandRequest,
    metadata?: grpcWeb.Metadata): grpcWeb.ClientReadableStream<file_pb.FileDownloadCommandResponse> {
    return this.client_.serverStreaming(
      this.hostname_ +
        '/filestream.FileService/FileDownloadCommand',
      request,
      metadata || {},
      this.methodDescriptorFileDownloadCommand);
  }

}

Expectations: expected fileUploadCommand to be generated.

kkuehl commented 1 year ago

Similar results with

protoc -I=$DIR file.proto \
    --js_out=import_style=commonjs:$OUT_DIR \
    --grpc-web_out=import_style=commonjs+dts,mode=grpcwebtext:$OUT_DIR

Output:


import * as file_pb from './file_pb';

export class FileServiceClient {
  constructor (hostname: string,
               credentials?: null | { [index: string]: string; },
               options?: null | { [index: string]: any; });

  fileDownloadCommand(
    request: file_pb.FileDownloadCommandRequest,
    metadata?: grpcWeb.Metadata
  ): grpcWeb.ClientReadableStream<file_pb.FileDownloadCommandResponse>;

}

export class FileServicePromiseClient {
  constructor (hostname: string,
               credentials?: null | { [index: string]: string; },
               options?: null | { [index: string]: any; });

  fileDownloadCommand(
    request: file_pb.FileDownloadCommandRequest,
    metadata?: grpcWeb.Metadata
  ): grpcWeb.ClientReadableStream<file_pb.FileDownloadCommandResponse>;

}
kkuehl commented 1 year ago

Also attempted mode=grpcweb

protoc -I=$DIR file.proto \
    --js_out=import_style=commonjs:$OUT_DIR \
    --grpc-web_out=import_style=commonjs+dts,mode=grpcweb:$OUT_DIR

Output:

import * as grpcWeb from 'grpc-web';

import * as file_pb from './file_pb';

export class FileServiceClient {
  constructor (hostname: string,
               credentials?: null | { [index: string]: string; },
               options?: null | { [index: string]: any; });

  fileDownloadCommand(
    request: file_pb.FileDownloadCommandRequest,
    metadata?: grpcWeb.Metadata
  ): grpcWeb.ClientReadableStream<file_pb.FileDownloadCommandResponse>;

}

export class FileServicePromiseClient {
  constructor (hostname: string,
               credentials?: null | { [index: string]: string; },
               options?: null | { [index: string]: any; });

  fileDownloadCommand(
    request: file_pb.FileDownloadCommandRequest,
    metadata?: grpcWeb.Metadata
  ): grpcWeb.ClientReadableStream<file_pb.FileDownloadCommandResponse>;

}

And:

protoc -I=$DIR file.proto \
    --js_out=import_style=commonjs:$OUT_DIR \
    --grpc-web_out=import_style=typescript,mode=grpcweb:$OUT_DIR

Output:


import * as grpcWeb from 'grpc-web';

import * as file_pb from './file_pb';

export class FileServiceClient {
  client_: grpcWeb.AbstractClientBase;
  hostname_: string;
  credentials_: null | { [index: string]: string; };
  options_: null | { [index: string]: any; };

  constructor (hostname: string,
               credentials?: null | { [index: string]: string; },
               options?: null | { [index: string]: any; }) {
    if (!options) options = {};
    if (!credentials) credentials = {};
    options['format'] = 'binary';

    this.client_ = new grpcWeb.GrpcWebClientBase(options);
    this.hostname_ = hostname;
    this.credentials_ = credentials;
    this.options_ = options;
  }

  methodDescriptorFileDownloadCommand = new grpcWeb.MethodDescriptor(
    '/filestream.FileService/FileDownloadCommand',
    grpcWeb.MethodType.SERVER_STREAMING,
    file_pb.FileDownloadCommandRequest,
    file_pb.FileDownloadCommandResponse,
    (request: file_pb.FileDownloadCommandRequest) => {
      return request.serializeBinary();
    },
    file_pb.FileDownloadCommandResponse.deserializeBinary
  );

  fileDownloadCommand(
    request: file_pb.FileDownloadCommandRequest,
    metadata?: grpcWeb.Metadata): grpcWeb.ClientReadableStream<file_pb.FileDownloadCommandResponse> {
    return this.client_.serverStreaming(
      this.hostname_ +
        '/filestream.FileService/FileDownloadCommand',
      request,
      metadata || {},
      this.methodDescriptorFileDownloadCommand);
  }

}
kkuehl commented 1 year ago

This does not seem to be specifically a typescript problem:

protoc -I=$DIR file.proto \
    --js_out=import_style=commonjs:$OUT_DIR \
    --grpc-web_out=import_style=commonjs,mode=grpcwebtext:$OUT_DIR

Output:

/**
 * @fileoverview gRPC-Web generated client stub for filestream
 * @enhanceable
 * @public
 */

// GENERATED CODE -- DO NOT EDIT!

/* eslint-disable */
// @ts-nocheck

const grpc = {};
grpc.web = require('grpc-web');

const proto = {};
proto.filestream = require('./file_pb.js');

/**
 * @param {string} hostname
 * @param {?Object} credentials
 * @param {?grpc.web.ClientOptions} options
 * @constructor
 * @struct
 * @final
 */
proto.filestream.FileServiceClient =
    function(hostname, credentials, options) {
  if (!options) options = {};
  options.format = 'text';

  /**
   * @private @const {!grpc.web.GrpcWebClientBase} The client
   */
  this.client_ = new grpc.web.GrpcWebClientBase(options);

  /**
   * @private @const {string} The hostname
   */
  this.hostname_ = hostname;

};

/**
 * @param {string} hostname
 * @param {?Object} credentials
 * @param {?grpc.web.ClientOptions} options
 * @constructor
 * @struct
 * @final
 */
proto.filestream.FileServicePromiseClient =
    function(hostname, credentials, options) {
  if (!options) options = {};
  options.format = 'text';

  /**
   * @private @const {!grpc.web.GrpcWebClientBase} The client
   */
  this.client_ = new grpc.web.GrpcWebClientBase(options);

  /**
   * @private @const {string} The hostname
   */
  this.hostname_ = hostname;

};

/**
 * @const
 * @type {!grpc.web.MethodDescriptor<
 *   !proto.filestream.FileDownloadCommandRequest,
 *   !proto.filestream.FileDownloadCommandResponse>}
 */
const methodDescriptor_FileService_FileDownloadCommand = new grpc.web.MethodDescriptor(
  '/filestream.FileService/FileDownloadCommand',
  grpc.web.MethodType.SERVER_STREAMING,
  proto.filestream.FileDownloadCommandRequest,
  proto.filestream.FileDownloadCommandResponse,
  /**
   * @param {!proto.filestream.FileDownloadCommandRequest} request
   * @return {!Uint8Array}
   */
  function(request) {
    return request.serializeBinary();
  },
  proto.filestream.FileDownloadCommandResponse.deserializeBinary
);

/**
 * @param {!proto.filestream.FileDownloadCommandRequest} request The request proto
 * @param {?Object<string, string>=} metadata User defined
 *     call metadata
 * @return {!grpc.web.ClientReadableStream<!proto.filestream.FileDownloadCommandResponse>}
 *     The XHR Node Readable Stream
 */
proto.filestream.FileServiceClient.prototype.fileDownloadCommand =
    function(request, metadata) {
  return this.client_.serverStreaming(this.hostname_ +
      '/filestream.FileService/FileDownloadCommand',
      request,
      metadata || {},
      methodDescriptor_FileService_FileDownloadCommand);
};

/**
 * @param {!proto.filestream.FileDownloadCommandRequest} request The request proto
 * @param {?Object<string, string>=} metadata User defined
 *     call metadata
 * @return {!grpc.web.ClientReadableStream<!proto.filestream.FileDownloadCommandResponse>}
 *     The XHR Node Readable Stream
 */
proto.filestream.FileServicePromiseClient.prototype.fileDownloadCommand =
    function(request, metadata) {
  return this.client_.serverStreaming(this.hostname_ +
      '/filestream.FileService/FileDownloadCommand',
      request,
      metadata || {},
      methodDescriptor_FileService_FileDownloadCommand);
};

module.exports = proto.filestream;
kkuehl commented 1 year ago

Manually adding works.

methodDescriptorFileUploadloadCommand = new grpcWeb.MethodDescriptor(
    '/filestream.FileService/FileUploadCommand',
    grpcWeb.MethodType.SERVER_STREAMING,
    file_pb.FileUploadCommandRequest,
    file_pb.FileUploadCommandResponse,
    (request: file_pb.FileUploadCommandRequest) => {
      return request.serializeBinary();
    },
    file_pb.FileUploadCommandResponse.deserializeBinary
  );

  fileUploadCommand(
    request: file_pb.FileUploadCommandRequest,
    metadata?: grpcWeb.Metadata): grpcWeb.ClientReadableStream<file_pb.FileUploadCommandResponse> {
    return this.client_.serverStreaming(
      this.hostname_ +
        '/filestream.FileService/FileUploadCommand',
      request,
      metadata || {},
      this.methodDescriptorFileUploadCommand);
  }
opporancisis commented 1 year ago

Hi @kkuehl , I think that the client streaming is still not supported.
Look also: a ticket about the same topic and the roadmap.

sampajano commented 1 year ago

@opporancisis thanks for helping clarifying! That is right - Client streaming it not currently supported. Please follow the above mentioned thread if you're interested in this feature. Thanks!

kkuehl commented 1 year ago

Feel free to close ticket.

sampajano commented 1 year ago

thanks for reminder :)