google/protobuf.dart

Missing annotation import on gRPC service definition

CarlosNihelton opened this issue · 12 comments

Release 21.0.0 of protoc_plugin generates pbgrpc.dart files in which the annotation @$pb.GrcpServiceName is used to wrap the class implementing the RPC service, but that annotation is not imported in any way, resulting in code that cannot compile.

//
//  Generated code. Do not modify.
//  source: hello.proto
//
// @dart = 2.12

// ignore_for_file: annotate_overrides, camel_case_types
// ignore_for_file: constant_identifier_names, library_prefixes
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import

import 'dart:async' as $async;
import 'dart:core' as $core;

import 'package:grpc/service_api.dart' as $grpc;
// missing `import 'package:protobuf/protobuf.dart';`
import 'hello.pb.dart' as $0;

export 'hello.pb.dart';

@$pb.GrpcServiceName('hello.Hello') // <- this is defined in protobuf/lib/src/protobuf/annotations.dart, but it's not imported.
class HelloClient extends $grpc.Client {
...

To reproduce the issue:

hello/
- hello.proto
- lib/
- pubspec.yaml

hello.proto

syntax = "proto3";
package hello;

message Empty {}

service Hello {
    rpc Ping(Empty) returns (Empty) {}
}

pubspec.yaml

name: hello
description: A gRPC Tutorial.
version: 0.0.1

environment:
  sdk: '>=2.19.0 <4.0.0'

dependencies:
  grpc: ^3.2.0
  protobuf: ^3.0.0
dart pub global activate protoc_plugin
cd hello/
protoc --proto_path=. --dart_out=grpc:lib/ ./hello.proto 

The output hello.pbgrpc.dart file will look like:

//
//  Generated code. Do not modify.
//  source: hello.proto
//
// @dart = 2.12

// ignore_for_file: annotate_overrides, camel_case_types
// ignore_for_file: constant_identifier_names, library_prefixes
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import

import 'dart:async' as $async;
import 'dart:core' as $core;

import 'package:grpc/service_api.dart' as $grpc;

import 'hello.pb.dart' as $0;

export 'hello.pb.dart';

@$pb.GrpcServiceName('hello.Hello')
class HelloClient extends $grpc.Client {
  static final _$ping = $grpc.ClientMethod<$0.Empty, $0.Empty>(
      '/hello.Hello/Ping',
      ($0.Empty value) => value.writeToBuffer(),
      ($core.List<$core.int> value) => $0.Empty.fromBuffer(value));

  HelloClient($grpc.ClientChannel channel,
      {$grpc.CallOptions? options,
      $core.Iterable<$grpc.ClientInterceptor>? interceptors})
      : super(channel, options: options,
        interceptors: interceptors);

  $grpc.ResponseFuture<$0.Empty> ping($0.Empty request, {$grpc.CallOptions? options}) {
    return $createUnaryCall(_$ping, request, options: options);
  }
}

@$pb.GrpcServiceName('hello.Hello')
abstract class HelloServiceBase extends $grpc.Service {
  $core.String get $name => 'hello.Hello';

  HelloServiceBase() {
    $addMethod($grpc.ServiceMethod<$0.Empty, $0.Empty>(
        'Ping',
        ping_Pre,
        false,
        false,
        ($core.List<$core.int> value) => $0.Empty.fromBuffer(value),
        ($0.Empty value) => value.writeToBuffer()));
  }

  $async.Future<$0.Empty> ping_Pre($grpc.ServiceCall call, $async.Future<$0.Empty> request) async {
    return ping(call, await request);
  }

  $async.Future<$0.Empty> ping($grpc.ServiceCall call, $0.Empty request);
}

The import statement is also missing in the golden file as shown in the snippet below:

//
// Generated code. Do not modify.
// source: test
//
// @dart = 2.12
// ignore_for_file: annotate_overrides, camel_case_types
// ignore_for_file: constant_identifier_names, library_prefixes
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
import 'dart:async' as $async;
import 'dart:core' as $core;
import 'package:grpc/service_api.dart' as $grpc;
import 'test.pb.dart' as $0;
export 'test.pb.dart';
@$pb.GrpcServiceName('Test')
class TestClient extends $grpc.Client {

Facing the same issue, while following observable flutter: grpc with @craiglabenz and @gianfrancopapa awesome content btw https://www.youtube.com/live/jCbclWBV32o?feature=share.

To finish coding along I commented out the problem area as shown below as I noticed that piece of code didn't show up while @gianfrancopapa was coding in the protos/lib/src/generated/todo.pbgrpc.dart file.

//  Generated code. Do not modify.
//  source: todo.proto
//
// @dart = 2.12

// ignore_for_file: annotate_overrides, camel_case_types
// ignore_for_file: constant_identifier_names, library_prefixes
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import

import 'dart:async' as $async;
import 'dart:core' as $core;

import 'package:grpc/service_api.dart' as $grpc;

import 'todo.pb.dart' as $0;

export 'todo.pb.dart';

//@$pb.GrpcServiceName('TodoService')       **<- problem area 1**
class TodoServiceClient extends $grpc.Client {
  static final _$getTodo = $grpc.ClientMethod<$0.GetTodoByIdRequest, $0.Todo>(
      '/TodoService/getTodo',
      ($0.GetTodoByIdRequest value) => value.writeToBuffer(),
      ($core.List<$core.int> value) => $0.Todo.fromBuffer(value));
  static final _$getTodoStream =
      $grpc.ClientMethod<$0.GetTodoByIdRequest, $0.Todo>(
          '/TodoService/getTodoStream',
          ($0.GetTodoByIdRequest value) => value.writeToBuffer(),
          ($core.List<$core.int> value) => $0.Todo.fromBuffer(value));

  TodoServiceClient($grpc.ClientChannel channel,
      {$grpc.CallOptions? options,
      $core.Iterable<$grpc.ClientInterceptor>? interceptors})
      : super(channel, options: options, interceptors: interceptors);

  $grpc.ResponseFuture<$0.Todo> getTodo($0.GetTodoByIdRequest request,
      {$grpc.CallOptions? options}) {
    return $createUnaryCall(_$getTodo, request, options: options);
  }

  $grpc.ResponseStream<$0.Todo> getTodoStream($0.GetTodoByIdRequest request,
      {$grpc.CallOptions? options}) {
    return $createStreamingCall(
        _$getTodoStream, $async.Stream.fromIterable([request]),
        options: options);
  }
}

//@$pb.GrpcServiceName('TodoService')    **<- problem area 2**
abstract class TodoServiceBase extends $grpc.Service {
  $core.String get $name => 'TodoService';

  TodoServiceBase() {
    $addMethod($grpc.ServiceMethod<$0.GetTodoByIdRequest, $0.Todo>(
        'getTodo',
        getTodo_Pre,
        false,
        false,
        ($core.List<$core.int> value) =>
            $0.GetTodoByIdRequest.fromBuffer(value),
        ($0.Todo value) => value.writeToBuffer()));
    $addMethod($grpc.ServiceMethod<$0.GetTodoByIdRequest, $0.Todo>(
        'getTodoStream',
        getTodoStream_Pre,
        false,
        true,
        ($core.List<$core.int> value) =>
            $0.GetTodoByIdRequest.fromBuffer(value),
        ($0.Todo value) => value.writeToBuffer()));
  }

  $async.Future<$0.Todo> getTodo_Pre($grpc.ServiceCall call,
      $async.Future<$0.GetTodoByIdRequest> request) async {
    return getTodo(call, await request);
  }

  $async.Stream<$0.Todo> getTodoStream_Pre($grpc.ServiceCall call,
      $async.Future<$0.GetTodoByIdRequest> request) async* {
    yield* getTodoStream(call, await request);
  }

  $async.Future<$0.Todo> getTodo(
      $grpc.ServiceCall call, $0.GetTodoByIdRequest request);
  $async.Stream<$0.Todo> getTodoStream(
      $grpc.ServiceCall call, $0.GetTodoByIdRequest request);
}

Below is my protos/protos/todo.proto file.

syntax = "proto3";

message Todo {
    int32 id = 1;
    string title = 2;
    bool completed = 3;
}

message GetTodoByIdRequest {
    int32 id = 1;
}

service TodoService {
    rpc getTodo(GetTodoByIdRequest) returns (Todo);
    rpc getTodoStream(GetTodoByIdRequest) returns (stream Todo);
}

Below is my protos/pubspec.yaml file.


name: proto
description: A starting point for Dart libraries or applications.
version: 1.0.0

environment:
  sdk: ^3.0.0

dependencies:
  grpc: ^3.2.0
  protobuf: ^2.1.0
  

dev_dependencies:
  lints: ^2.0.0
  test: ^1.21.0

Despite this, I was able to complete and test the rpc on windows and it works! The only issue is I couldn't access Todo parameters while implementing the rpc methods in the server/lib/todo_service.dart file below.

import 'package:proto/proto.dart';

class TodoService extends TodoServiceBase {
  @override
  Future<Todo> getTodo(ServiceCall call, GetTodoByIdRequest request) async {
    final id = request.id;
    final todo =
        Todo();           **// <- cannot access Todo required parameters id, title, and completed**
    return todo;
  }

  @override
  Stream<Todo> getTodoStream(
      ServiceCall call, GetTodoByIdRequest request) async* {
    final id = request.id;
    final todo =
        Todo();             **// <- cannot access Todo required parameters id, title, and completed**
    while (true) {
      yield todo;
    }
  }
}

osa1 commented

Thanks for reporting this. We've released protoc_plugin-21.0.1 with the fix.

I still do encounter that issue. I am not too deep into that topic but wasn't 21.0.1 already released? Will that then override the existing package? Or do you need to publish 21.0.2?

osa1 commented

I just released 21.0.1 an hour ago. If you're using 21.0.1 and the issue persists could you share your proto/grpc files so that I can take a look?

I installed 21.0.1 but it doesn't help, because GrpcServiceName is nowhere to be found in the protobuf 2.1.0 package.

import 'dart:async' as $async;
import 'dart:core' as $core;

import 'package:grpc/service_api.dart' as $grpc;
import 'package:protobuf/protobuf.dart' as $pb;

import 'service.pb.dart' as $0;

export 'service.pb.dart';

@$pb.GrpcServiceName('unary.Unary')
class UnaryClient extends $grpc.Client {
  static final _$utilKnockKnock = $grpc.ClientMethod<$0.Empty, $0.Empty>(
      '/unary.Unary/utilKnockKnock',
      ($0.Empty value) => value.writeToBuffer(),
      ($core.List<$core.int> value) => $0.Empty.fromBuffer(value));
Exception: Because management_unary_server depends on grpc ^3.2.0 which depends on protobuf ^2.0.0, protobuf ^2.0.0 is required.
So, because management_unary_server depends on protobuf ^3.0.0, version solving failed.

Cannot use protobuf 3.0.0 until 'grpc' is updated.

osa1 commented

We will release a new grpc soon with protobuf-3.0.0 support. (cc @sigurdm)

OK. By soon you mean in the next few hours (in which case I will just take a break)? Or in the next few days, weeks, ... in which case I will revert to previous versions.

osa1 commented

Probably in the next few days.