Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: Issue when generating ballerina client code with grpc containing oneof and google.profotbuf.Empty #7230

Open
maxwellb2 opened this issue Oct 3, 2024 · 16 comments · Fixed by ballerina-platform/protoc-tools#53
Assignees
Labels
module/grpc Reason/EngineeringMistake The issue occurred due to a mistake made in the past. Type/Bug

Comments

@maxwellb2
Copy link

Description

I am seeing a null pointer error when attempting to generate off of my GRPC spec. I wrote this demo GRPC spec similar to my internal set up to replicate the issue.

Steps to Reproduce

Create the service.proto file as follows:

syntax = "proto3";

package test;

import "google/protobuf/empty.proto";

service TestService {
    rpc TestRPC(RequestMessage) returns (ResponseMessage);
}

enum TestEnum {
    ENTRY_ONE = 0;
    ENTRY_TWO = 1;
}

message ComplexType {
    uint32 foo = 1;
    TestEnum bar = 2;
}

message OptionalComplexType {
    oneof value {
        ComplexType complex_value = 1;
        // A field to represent `None`. It's usually left as an empty field.
        google.protobuf.Empty none = 2; 
    }
}

message OptionalString {
    oneof value {
        string name = 1;
        // A field to represent `None`. It's usually left as an empty field.
        google.protobuf.Empty none = 2; 
    }
}

message RequestMessage {
    repeated OptionalComplexType req = 1;
}

message ResponseMessage {
    repeated OptionalString resp = 1;
}

When I test with:

$ bal grpc --mode client --input ../api/service.proto --output .

I get the following error:

Continuing with the existing protoc executor file at /var/folders/bt/5dmlqljj2rb1t5nfpkyg68zw0000gn/T/protoc-3.21.7-osx-aarch_64.exe
Successfully extracted the library files.
ballerina: Oh no, something really went wrong. Bad. Sad.

We appreciate it if you can report the code that broke Ballerina in
https://github.com/ballerina-platform/ballerina-lang/issues with the
log you get below and your sample code.

We thank you for helping make us better.

[2024-10-03 12:35:23,294] SEVERE {b7a.log.crash} - Cannot invoke "String.startsWith(String)" because "fieldType" is null
java.lang.NullPointerException: Cannot invoke "String.startsWith(String)" because "fieldType" is null
	at io.ballerina.protoc.builder.stub.Field$Builder.build(Field.java:113)
	at io.ballerina.protoc.builder.stub.Message$Builder.build(Message.java:159)
	at io.ballerina.protoc.builder.BallerinaFileBuilder.computeSourceContent(BallerinaFileBuilder.java:225)
	at io.ballerina.protoc.builder.BallerinaFileBuilder.build(BallerinaFileBuilder.java:129)
	at io.ballerina.protoc.protobuf.cmd.GrpcCmd.generateBalFile(GrpcCmd.java:317)
	at io.ballerina.protoc.protobuf.cmd.GrpcCmd.execute(GrpcCmd.java:165)
	at java.base/java.util.Optional.ifPresent(Optional.java:178)
	at io.ballerina.cli.launcher.Main.main(Main.java:59)

Affected Version(s)

No response

OS, DB, other environment details and versions

No response

Related area

-> gRPC Tool

Related issue(s) (optional)

No response

Suggested label(s) (optional)

No response

Suggested assignee(s) (optional)

No response

@ballerina-bot ballerina-bot transferred this issue from ballerina-platform/ballerina-lang Oct 3, 2024
@daneshk
Copy link
Member

daneshk commented Oct 4, 2024

@maxwellb2 Thanks for reporting the issue. It seems like an issue with the protoc-tool when handling the type google.protobuf.Empty as a message field. We will fix it soon and ship it in the next patch distribution. Can I know the Ballerina version you are using?

In the meantime, if you are stuck, you can proceed by making a slight change in your proto definition as shown below.

syntax = "proto3";

package test;

service TestService {
    rpc TestRPC(RequestMessage) returns (ResponseMessage);
}

enum TestEnum {
    ENTRY_ONE = 0;
    ENTRY_TWO = 1;
}

message ComplexType {
    uint32 foo = 1;
    TestEnum bar = 2;
}

message OptionalComplexType {
    oneof value {
        ComplexType complex_value = 1;
        // A field to represent `None`. It's usually left as an empty field.
        Empty none = 2; 
    }
}

message OptionalString {
    oneof value {
        string name = 1;
        // A field to represent `None`. It's usually left as an empty field.
        Empty none = 2; 
    }
}

message RequestMessage {
    repeated OptionalComplexType req = 1;
}

message ResponseMessage {
    repeated OptionalString resp = 1;
}

message Empty {}

Instead of using the google.protobuf.Empty type[1], you can define the type Empty in your service schema definition file. Both are the same.

  1. https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/empty.proto

@rukmal
Copy link

rukmal commented Oct 4, 2024

Thanks for the workaround @daneshk! We will use our own Empty message.

@rukmal
Copy link

rukmal commented Oct 4, 2024

We are using version 2201.10.1 (the current latest version)

@daneshk
Copy link
Member

daneshk commented Oct 5, 2024

The issue will remain open till we do a distribution patch release.

@rukmal
Copy link

rukmal commented Oct 5, 2024

Hi @daneshk I just tried the example proto spec here (with the custom Empty type):

In the meantime, if you are stuck, you can proceed by making a slight change in your proto definition as shown below.

syntax = "proto3";

package test;

service TestService {
    rpc TestRPC(RequestMessage) returns (ResponseMessage);
}

enum TestEnum {
    ENTRY_ONE = 0;
    ENTRY_TWO = 1;
}

message ComplexType {
    uint32 foo = 1;
    TestEnum bar = 2;
}

message OptionalComplexType {
    oneof value {
        ComplexType complex_value = 1;
        // A field to represent `None`. It's usually left as an empty field.
        Empty none = 2; 
    }
}

message OptionalString {
    oneof value {
        string name = 1;
        // A field to represent `None`. It's usually left as an empty field.
        Empty none = 2; 
    }
}

message RequestMessage {
    repeated OptionalComplexType req = 1;
}

message ResponseMessage {
    repeated OptionalString resp = 1;
}

message Empty {}

Instead of using the google.protobuf.Empty type[1], you can define the type Empty in your service schema definition file. Both are the same.

1. https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/empty.proto

Unfortunately, I'm still not able to build the client and am seeing the following error:

Continuing with the existing protoc executor file at /tmp/protoc-3.21.7-linux-x86_64.exe
Successfully extracted the library files.
Oct 05, 2024 7:25:17 PM io.ballerina.protoc.protobuf.cmd.GrpcCmd generateBalFile
SEVERE: An error occurred when generating the proto descriptor.
io.ballerina.protoc.protobuf.exception.CodeGeneratorException: Invalid command syntax. Stream closed
	at io.ballerina.protoc.protobuf.utils.BalFileGenerationUtils.handleProcessExecutionErrors(BalFileGenerationUtils.java:110)
	at io.ballerina.protoc.protobuf.utils.BalFileGenerationUtils.generateDescriptor(BalFileGenerationUtils.java:83)
	at io.ballerina.protoc.protobuf.cmd.DescriptorsGenerator.generateRootDescriptor(DescriptorsGenerator.java:148)
	at io.ballerina.protoc.protobuf.cmd.GrpcCmd.generateBalFile(GrpcCmd.java:251)
	at io.ballerina.protoc.protobuf.cmd.GrpcCmd.execute(GrpcCmd.java:165)
	at java.base/java.util.Optional.ifPresent(Optional.java:178)
	at io.ballerina.cli.launcher.Main.main(Main.java:59)
Caused by: java.io.IOException: Stream closed
	at java.base/java.io.BufferedInputStream.ensureOpen(BufferedInputStream.java:206)
	at java.base/java.io.BufferedInputStream.implRead(BufferedInputStream.java:411)
	at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:405)
	at java.base/sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:350)
	at java.base/sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:393)
	at java.base/sun.nio.cs.StreamDecoder.lockedRead(StreamDecoder.java:217)
	at java.base/sun.nio.cs.StreamDecoder.read(StreamDecoder.java:171)
	at java.base/java.io.InputStreamReader.read(InputStreamReader.java:188)
	at java.base/java.io.BufferedReader.fill(BufferedReader.java:160)
	at java.base/java.io.BufferedReader.implReadLine(BufferedReader.java:370)
	at java.base/java.io.BufferedReader.readLine(BufferedReader.java:347)
	at java.base/java.io.BufferedReader.readLine(BufferedReader.java:436)
	at io.ballerina.protoc.protobuf.utils.BalFileGenerationUtils.handleProcessExecutionErrors(BalFileGenerationUtils.java:105)
	... 6 more

An error occurred when generating the proto descriptor. Invalid command syntax. Stream closed

I also tried changing our internal google.protobuf.Empty usage to a custom Empty type, and am seeing the same issue. I know there is no problem with the Protobuf syntax or spec itself, as I have a Rust server that implements the specification working as expected.

I'm testing this with Ballerina 2201.10.1 on Linux.

@daneshk
Copy link
Member

daneshk commented Oct 7, 2024

@rukmal Based on the stack trace, it seems like there is an error when trying to compile the proto file with the protoc executor(protoc-3.21.7-linux-x86_64.exe)[1]. But we couldn't read the exact error here because the stream was closed. Could you please execute your modified schema file against the protoc executor and share the output?

Please execute the following command, providing the directory where the proto file resides and the proto file path.

/tmp/protoc-3.21.7-linux-x86_64.exe --proto_path="<absolute directory path where proto files resides>" "<absolute filepath of the root proto file>" --descriptor_set_out="schema-descriptor.desc"

For example,

  • protoc executor file path - /tmp/protoc-3.21.7-linux-x86_64.exe. This is already downloaded in your tmp directory
  • the root proto file path - /User/danesh/grpc_project/schema.proto
  • The directory where proto files are resides - /User/danesh/grpc_project

Execute the below command,

/tmp/protoc-3.21.7-linux-x86_64.exe --proto_path="/User/danesh/grpc_project" "/User/danesh/grpc_project/schema.proto" --descriptor_set_out="schema-descriptor.desc"
  1. https://repo1.maven.org/maven2/com/google/protobuf/protoc/3.21.7/

@rukmal
Copy link

rukmal commented Oct 7, 2024

Hi @daneshk,

I can't directly execute the /tmp/protoc-3.21.7... file as I'm running NixOS (there are OS protections against running dynamically linked binaries). However, I was able to install and run protoc directly, with the command:

protoc --proto_path="/home/rukmal/tmp/protoc_test" "/home/rukmal/tmp/protoc_test/service.proto" --descriptor_set_out="schema-descriptor.desc"

This is building the descriptor set correctly, and I see the output:
(With cat):

�
service.prototest"A

ComplexType
Rfoo 
bar (2.test.TestEnumRbar"{
OptionalComplexType8
complex_value (
               2.test.ComplexTypeHR
                                   complexValue!
none (
      2
       .test.EmptyHRnoneB
value"R
OptionalString
name (	HRname!
none (
      2
       .test.EmptyHRnoneB
value"=
RequestMessage+
req (
     2.test.OptionalComplexTypeRreq";
ResponseMessage(
resp (
      2.test.OptionalStringRresp"
Empty*(
TestEnum
	ENTRY_ONE
	ENTRY_TWO2E

TestService6
TestRPC.test.RequestMessage.test.ResponseMessagebproto3%  

Again, I don't think the issue is with the protobuf schema, or my installation of Ballerina. This schema is working as expected with both tonic in Rust and grpcio in Python.

For reference, I'm using the following shell.nix file on my system to create the Ballerina dev environment:

{ pkgs ? import <nixpkgs> {} }:

let
  version = "2201.10.1"; # Define the version here
  codeName = "swan-lake"; # Define the codeName here
  ballerina = pkgs.stdenv.mkDerivation {
    pname = "ballerina";
    inherit version;

    src = pkgs.fetchzip {
      url = "https://dist.ballerina.io/downloads/${version}/ballerina-${version}-${codeName}.zip";
      hash = "sha256-gKxJnoNWYE3ozQ0JvMgHgrg/DCkvFnJqZAecgqvJGq8=";
    };

    nativeBuildInputs = [ pkgs.makeWrapper ];

    installPhase = ''
      runHook preInstall
      cp -rv distributions/ballerina-${version} $out
      runHook postInstall
    '';

    preFixup = ''
      wrapProgram $out/bin/bal --set JAVA_HOME ${pkgs.openjdk}
    '';

    meta = with pkgs.lib; {
      description = "Open-source programming language for the cloud";
      license = licenses.asl20;
      platforms = pkgs.openjdk.meta.platforms;
    };
  };
in
pkgs.mkShell {
  buildInputs = [
    ballerina
    pkgs.openjdk
  ];

  shellHook = ''
    echo "Ballerina ${version} is ready to use!"
    export JAVA_HOME=${pkgs.openjdk}
  '';
}

We tested this on a MacOS machine and are seeing the same error too.

This is the protoc version I'm using:

$ protoc --version
libprotoc 24.4

@daneshk
Copy link
Member

daneshk commented Oct 7, 2024

@rukmal, is it possible for you to share your modified proto schema or something similar that I can use to reproduce the issue, please? It would be really helpful to find out the problem with the Ballerina tool.

@rukmal
Copy link

rukmal commented Oct 7, 2024

I'm using the test schema we discussed above:

syntax = "proto3";

package test;

service TestService {
    rpc TestRPC(RequestMessage) returns (ResponseMessage);
}

enum TestEnum {
    ENTRY_ONE = 0;
    ENTRY_TWO = 1;
}

message ComplexType {
    uint32 foo = 1;
    TestEnum bar = 2;
}

message OptionalComplexType {
    oneof value {
        ComplexType complex_value = 1;
        // A field to represent `None`. It's usually left as an empty field.
        Empty none = 2; 
    }
}

message OptionalString {
    oneof value {
        string name = 1;
        // A field to represent `None`. It's usually left as an empty field.
        Empty none = 2; 
    }
}

message RequestMessage {
    repeated OptionalComplexType req = 1;
}

message ResponseMessage {
    repeated OptionalString resp = 1;
}

message Empty {}

The OptionalComplexType and OptionalString are triggering the same error we're seeing with our internal protobuf spec. I unfortunately can't share the full spec, but I created the OptionalComplexType, OptionalString, RequestMessage and ResponseMessage in the example above to replicate the functionality we have in our spec.

@rukmal
Copy link

rukmal commented Oct 7, 2024

Hi @daneshk please disregard the previous comment about not sharing our spec.

I've created and have attached a subset of our specification here for testing purposes by the team. Please note that some of these messages are distributed across different files in our setup, and are imported with import "foo.proto";; they are collapsed here into the same file for brevity.

The imports are not an issue, as we have been using the bal grpc tool until now; what broke it was either the use of oneOf in a repeated field, or the Empty type like we've been discussing thus far.

syntax = "proto3";

package usm;

import "google/protobuf/duration.proto";
import "google/protobuf/empty.proto";

service USMDataService {
    // Get unicode interface nodes given a unicode string
    rpc GetUnicodeInterfaceNodeIdentifiers(UnicodeInterfaceNodeIdentifiersRequest) returns (UnicodeInterfaceNodeIdentifiersResponse);
    // Get a synthesized route from the graph
    rpc GetSynthesizedRoute(SynthesisRequest) returns (SynthesisResponse);
}

// Synthesis interface node request
message UnicodeInterfaceNodeIdentifiersRequest {
    // Unicode string to fetch interface nodes for
    string unicode_string = 1;
}

// Synthesis interface node response
message UnicodeInterfaceNodeIdentifiersResponse {
    // Names used from the string to fetch the interface nodes
    repeated string names = 1;
    // Interface nodes for the unicode string (corresponds to the names above)
    repeated OptionalInterfaceNodeIdentifier node_ids = 2;
}

// Synthesis request message
message SynthesisRequest {
    // Interface nodes for the synthesis operation
    repeated NodeIdentifier interface_nodes = 1;
    // (Optional) VoyagerIdentifier of the graph to use for synthesis
    optional VoyagerIdentifier voyager_id = 2;
    // (Optional) Bidirectional step limit for the synthesis operation
    optional uint32 bidirectional_step_limit = 3;
}

// Synthesis response message
message SynthesisResponse {
    // Response routes from the synthesis process
    repeated RouteSynthesisResponse responses = 1;
    // Number of bidirectional steps taken for the synthesis operation
    uint32 bidirectional_steps_taken = 2;
    // Number of forward nodes scanned
    uint32 forward_scanned_count = 3;
    // Number of reverse nodes scanned
    uint32 reverse_scanned_count = 4;
    // Duration of the synthesis operation
    google.protobuf.Duration duration = 5;
}

// Optional interface node identifier
message OptionalInterfaceNodeIdentifier {
    oneof value {
        // ID of the Interface Node
        NodeIdentifier node_id = 1;
        // A field to represent `None`. It's usually left as an empty field.
        google.protobuf.Empty none = 2; 
    }
}

// Optional interface node name
message OptionalInterfaceNodeName {
    oneof value {
        // Name of the interface node
        string name = 1;
        // A field to represent `None`. It's usually left as an empty field.
        google.protobuf.Empty none = 2; 
    }
}

// Route Synthesis response
message RouteSynthesisResponse {
    // Route interface nodes
    repeated OptionalInterfaceNodeIdentifier interface_nodes = 1;
    // Route interface node names
    repeated OptionalInterfaceNodeName interface_node_names = 2;
    // Number of non-null interface nodes in the route
    uint32 interface_node_count = 3;
    // Hidden nodes in the route
    repeated LongNodeIdentifier hidden_node_long_ids = 4;
    // Boolean indicating whether the route is novel, or was present in the loaded data
    bool is_novel = 5;
}


// Node type
enum NodeType {
    // Error (nodes always have a node type)
    UNSPECIFIED = 0;
    // Interface node
    INTERFACE = 1;
    // Hidden node
    HIDDEN = 2;
}

// Interface device
enum InterfaceDevice {
    // Null device (never this for interface nodes)
    NULL = 0;
    // Reference device
    REFERENCE = 1;
    // Unicode device
    UNICODE = 2;
}

// Node implementation
enum NodeImplementation {
    // Error implementation (LongeNodeIdentifier always has one)
    ERROR = 0;
    // Base Node
    BASE_NODE = 1;
    // Collided Node
    COLLIDED_NODE = 2;
    // Projected Node
    PROJECTED_NODE = 3;
}

// Voyager Identifier
message VoyagerIdentifier {
    // Run ID of the Voyager Identifier
    uint32 run_id = 1;
    // Iteration ID of the Voyager Identifier
    uint32 iteration_id = 2;
}

// Node Identifier
message NodeIdentifier {
    // Type of the node
    NodeType node_type = 1;
    // Numeric index of the node
    uint32 node_idx = 2;
    // (Optional) interface device; necessary if node_type is INTERFACE
    optional InterfaceDevice device = 3;
}

// Long Node Identifier
message LongNodeIdentifier {
    // Node IDs of the Node
    repeated NodeIdentifier node_ids = 1;
    // Node implementation
    NodeImplementation node_impl = 2;
    // Voyager ID of the Node (Base graph if this is None)
    optional VoyagerIdentifier voyager_id = 3;
}

Please note that I already tried replacing google.protobuf.Empty with a custom Empty type but was running into the same error. I did not merge that update into the main trunk as that's still causing an issue.

I tried creating the schema descriptor file with protoc for this subset of our specification too, and it works as expected.

This is the output of running the ballerina GRPC tool against this spec (I deleted the /tmp/protoc..exe to make sure there wasn't an issue with me running a mismatched version of that):

$ bal grpc --mode client --input ./service.proto --output ./ 

Downloading the protoc executor file - protoc-3.21.7-linux-x86_64.exe
Download successfully completed. Executor file path - /tmp/protoc-3.21.7-linux-x86_64.exe
Successfully extracted the library files.
Oct 07, 2024 2:37:21 PM io.ballerina.protoc.protobuf.cmd.GrpcCmd generateBalFile
SEVERE: An error occurred when generating the proto descriptor.
io.ballerina.protoc.protobuf.exception.CodeGeneratorException: Invalid command syntax. Stream closed
	at io.ballerina.protoc.protobuf.utils.BalFileGenerationUtils.handleProcessExecutionErrors(BalFileGenerationUtils.java:110)
	at io.ballerina.protoc.protobuf.utils.BalFileGenerationUtils.generateDescriptor(BalFileGenerationUtils.java:83)
	at io.ballerina.protoc.protobuf.cmd.DescriptorsGenerator.generateRootDescriptor(DescriptorsGenerator.java:148)
	at io.ballerina.protoc.protobuf.cmd.GrpcCmd.generateBalFile(GrpcCmd.java:251)
	at io.ballerina.protoc.protobuf.cmd.GrpcCmd.execute(GrpcCmd.java:165)
	at java.base/java.util.Optional.ifPresent(Optional.java:178)
	at io.ballerina.cli.launcher.Main.main(Main.java:58)
Caused by: java.io.IOException: Stream closed
	at java.base/java.io.BufferedInputStream.getBufIfOpen(BufferedInputStream.java:168)
	at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:334)
	at java.base/sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:270)
	at java.base/sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:313)
	at java.base/sun.nio.cs.StreamDecoder.read(StreamDecoder.java:188)
	at java.base/java.io.InputStreamReader.read(InputStreamReader.java:177)
	at java.base/java.io.BufferedReader.fill(BufferedReader.java:162)
	at java.base/java.io.BufferedReader.readLine(BufferedReader.java:329)
	at java.base/java.io.BufferedReader.readLine(BufferedReader.java:396)
	at io.ballerina.protoc.protobuf.utils.BalFileGenerationUtils.handleProcessExecutionErrors(BalFileGenerationUtils.java:105)
	... 6 more

An error occurred when generating the proto descriptor. Invalid command syntax. Stream closed

@daneshk
Copy link
Member

daneshk commented Oct 7, 2024

Thanks alot @rukmal. I just replaced google.protobuf.Empty type with an custom Empty type and removed the import. unfortunately I am able to generate the client stub file without any errors in my machine. My modified version is,

syntax = "proto3";

package usm;

import "google/protobuf/duration.proto";

service USMDataService {
    // Get unicode interface nodes given a unicode string
    rpc GetUnicodeInterfaceNodeIdentifiers(UnicodeInterfaceNodeIdentifiersRequest) returns (UnicodeInterfaceNodeIdentifiersResponse);
    // Get a synthesized route from the graph
    rpc GetSynthesizedRoute(SynthesisRequest) returns (SynthesisResponse);
}

// Synthesis interface node request
message UnicodeInterfaceNodeIdentifiersRequest {
    // Unicode string to fetch interface nodes for
    string unicode_string = 1;
}

// Synthesis interface node response
message UnicodeInterfaceNodeIdentifiersResponse {
    // Names used from the string to fetch the interface nodes
    repeated string names = 1;
    // Interface nodes for the unicode string (corresponds to the names above)
    repeated OptionalInterfaceNodeIdentifier node_ids = 2;
}

// Synthesis request message
message SynthesisRequest {
    // Interface nodes for the synthesis operation
    repeated NodeIdentifier interface_nodes = 1;
    // (Optional) VoyagerIdentifier of the graph to use for synthesis
    optional VoyagerIdentifier voyager_id = 2;
    // (Optional) Bidirectional step limit for the synthesis operation
    optional uint32 bidirectional_step_limit = 3;
}

// Synthesis response message
message SynthesisResponse {
    // Response routes from the synthesis process
    repeated RouteSynthesisResponse responses = 1;
    // Number of bidirectional steps taken for the synthesis operation
    uint32 bidirectional_steps_taken = 2;
    // Number of forward nodes scanned
    uint32 forward_scanned_count = 3;
    // Number of reverse nodes scanned
    uint32 reverse_scanned_count = 4;
    // Duration of the synthesis operation
    google.protobuf.Duration duration = 5;
}

// Optional interface node identifier
message OptionalInterfaceNodeIdentifier {
    oneof value {
        // ID of the Interface Node
        NodeIdentifier node_id = 1;
        // A field to represent `None`. It's usually left as an empty field.
        Empty none = 2; 
    }
}

// Optional interface node name
message OptionalInterfaceNodeName {
    oneof value {
        // Name of the interface node
        string name = 1;
        // A field to represent `None`. It's usually left as an empty field.
        Empty none = 2; 
    }
}

// Route Synthesis response
message RouteSynthesisResponse {
    // Route interface nodes
    repeated OptionalInterfaceNodeIdentifier interface_nodes = 1;
    // Route interface node names
    repeated OptionalInterfaceNodeName interface_node_names = 2;
    // Number of non-null interface nodes in the route
    uint32 interface_node_count = 3;
    // Hidden nodes in the route
    repeated LongNodeIdentifier hidden_node_long_ids = 4;
    // Boolean indicating whether the route is novel, or was present in the loaded data
    bool is_novel = 5;
}


// Node type
enum NodeType {
    // Error (nodes always have a node type)
    UNSPECIFIED = 0;
    // Interface node
    INTERFACE = 1;
    // Hidden node
    HIDDEN = 2;
}

// Interface device
enum InterfaceDevice {
    // Null device (never this for interface nodes)
    NULL = 0;
    // Reference device
    REFERENCE = 1;
    // Unicode device
    UNICODE = 2;
}

// Node implementation
enum NodeImplementation {
    // Error implementation (LongeNodeIdentifier always has one)
    ERROR = 0;
    // Base Node
    BASE_NODE = 1;
    // Collided Node
    COLLIDED_NODE = 2;
    // Projected Node
    PROJECTED_NODE = 3;
}

// Voyager Identifier
message VoyagerIdentifier {
    // Run ID of the Voyager Identifier
    uint32 run_id = 1;
    // Iteration ID of the Voyager Identifier
    uint32 iteration_id = 2;
}

// Node Identifier
message NodeIdentifier {
    // Type of the node
    NodeType node_type = 1;
    // Numeric index of the node
    uint32 node_idx = 2;
    // (Optional) interface device; necessary if node_type is INTERFACE
    optional InterfaceDevice device = 3;
}

// Long Node Identifier
message LongNodeIdentifier {
    // Node IDs of the Node
    repeated NodeIdentifier node_ids = 1;
    // Node implementation
    NodeImplementation node_impl = 2;
    // Voyager ID of the Node (Base graph if this is None)
    optional VoyagerIdentifier voyager_id = 3;
}

message Empty {}

I will try to reproduce the issue in a different machine(linux machine) and get back to you soon.

@rukmal
Copy link

rukmal commented Oct 7, 2024

I just tried the above spec on my machine, and it's not working unfortunately... I attached the nix configuration I'm using to build on linux here, so please let me know if there are any other questions I can help with.

The Ballerina work (handled by @maxwellb2) is luckily enough happening on a Mac, so we will try to build the updated spec (replacing google.protobuf.Empty with a custom Empty message) and get back to you on that too.

This is the system I'm using:

$ uname -a
Linux mercury 6.11.0 ballerina-platform/ballerina-release#1-NixOS SMP PREEMPT_DYNAMIC Sun Sep 15 14:57:56 UTC 2024 x86_64 GNU/Linux

@daneshk
Copy link
Member

daneshk commented Oct 8, 2024

@rukmal I couldn't reproduce the issue even in a Linux environment. So, we are planning to fix both this original issue and #7238 and trigger a new patch release for update 10 soon so we can capture the cause of the issue we are facing. I will keep update you progress.

@maxwellb2
Copy link
Author

@daneshk I was able to generate the ballerina client code without error on MacOS using the custom Empty
message fix.

@AzeemMuzammil
Copy link
Member

This fix will be released with 2201.10.4 patch
ballerina-platform/ballerina-distribution#5771

@rukmal
Copy link

rukmal commented Oct 14, 2024

Thanks @daneshk, I'll work on getting a reproduction example in a dockerfile for you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
module/grpc Reason/EngineeringMistake The issue occurred due to a mistake made in the past. Type/Bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants