elixir-grpc / grpc-reflection

elixir graph reflection support
Apache License 2.0
9 stars 6 forks source link

Failed to resolve symbol #35

Closed sleipnir closed 2 months ago

sleipnir commented 3 months ago

Hi guys. I have the following .proto file:

syntax = "proto3";

option objc_class_prefix = "HLW";

import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
import "eigr/functions/protocol/actors/extensions.proto";

package pinger;

// The ping-pong state of PingPongActor
message PingPongState {
  string actual_name = 1;
  string previous_name = 2;
  google.protobuf.Timestamp updated_at = 3;
}

// The request message containing the actor name.
message PingRequest {
  string name = 1 [(.eigr.functions.protocol.actors.actor_id) = true];
}

// The response message containing the pong
message PongReply {
  string message = 1;
  google.protobuf.Timestamp today = 2;
}

// The PingPong actor service definition.
service PingPongActor {

  rpc Ping(PingRequest) returns(PongReply) {
    option(google.api.http) = {post : "/v1/ping/{name}" body : "*"};
  }

  // Get Pong Message
  rpc Pong(google.protobuf.Empty) returns(PingPongState) {
    option(google.api.http) = {get : "/v1/pong/{name}"};
  }
}

When I tried to make grpcurl request below:

grpcurl -vv -plaintext localhost:9980 describe pinger.PingPongActor

I receive "Failed to resolve symbol "pinger.PingPongActor": rpc error: code = Unknown desc = Internal Server Error" message.

Follow the application logs below:

2024-06-04 18:57:37.065 [spawn_a3@127.0.0.1]:[pid=<0.1365.0> ]:[info]:Handled by Sidecar.GRPC.Reflection.Server.V1.server_reflection_info
2024-06-04 18:57:37.065 [spawn_a3@127.0.0.1]:[pid=<0.1365.0> ]:[info]:Received v1 reflection request: {:file_containing_symbol, "pinger.PingPongActor"}
2024-06-04 18:57:37.068 [spawn_a3@127.0.0.1]:[pid=<0.1365.0> ]:[error]:** (CaseClauseError) no case clause matching: {:error, {:function_clause, [{GrpcReflection.Service.Builder.Util, :types_from_descriptor, [%Google.Protobuf.FileDescriptorProto{name: "pinger.proto", package: "pinger", dependency: ["google/api/annotations.proto", "google/protobuf/empty.proto", "google/protobuf/timestamp.proto", "eigr/functions/protocol/actors/extensions.proto"], message_type: [%Google.Protobuf.DescriptorProto{name: "PingPongState", field: [%Google.Protobuf.FieldDescriptorProto{name: "actual_name", extendee: nil, number: 1, label: :LABEL_OPTIONAL, type: :TYPE_STRING, type_name: nil, default_value: nil, options: nil, oneof_index: nil, json_name: "actualName", proto3_optional: nil, __unknown_fields__: []}, %Google.Protobuf.FieldDescriptorProto{name: "previous_name", extendee: nil, number: 2, label: :LABEL_OPTIONAL, type: :TYPE_STRING, type_name: nil, default_value: nil, options: nil, oneof_index: nil, json_name: "previousName", proto3_optional: nil, __unknown_fields__: []}, %Google.Protobuf.FieldDescriptorProto{name: "updated_at", extendee: nil, number: 3, label: :LABEL_OPTIONAL, type: :TYPE_MESSAGE, type_name: ".google.protobuf.Timestamp", default_value: nil, options: nil, oneof_index: nil, json_name: "updatedAt", proto3_optional: nil, __unknown_fields__: []}], nested_type: [], enum_type: [], extension_range: [], extension: [], options: nil, oneof_decl: [], reserved_range: [], reserved_name: [], __unknown_fields__: []}, %Google.Protobuf.DescriptorProto{name: "PingRequest", field: [%Google.Protobuf.FieldDescriptorProto{name: "name", extendee: nil, number: 1, label: :LABEL_OPTIONAL, type: :TYPE_STRING, type_name: nil, default_value: nil, options: %Google.Protobuf.FieldOptions{ctype: :STRING, packed: nil, deprecated: false, lazy: false, jstype: :JS_NORMAL, weak: false, unverified_lazy: false, debug_redact: false, retention: nil, targets: [], edition_defaults: [], features: nil, uninterpreted_option: [], __pb_extensions__: %{{Eigr.Functions.Protocol.Actors.PbExtension, :actor_id} => true}, __unknown_fields__: []}, oneof_index: nil, json_name: "name", proto3_optional: nil, __unknown_fields__: []}], nested_type: [], enum_type: [], extension_range: [], extension: [], options: nil, oneof_decl: [], reserved_range: [], reserved_name: [], __unknown_fields__: []}, %Google.Protobuf.DescriptorProto{name: "PongReply", field: [%Google.Protobuf.FieldDescriptorProto{name: "message", extendee: nil, number: 1, label: :LABEL_OPTIONAL, type: :TYPE_STRING, type_name: nil, default_value: nil, options: nil, oneof_index: nil, json_name: "message", proto3_optional: nil, __unknown_fields__: []}, %Google.Protobuf.FieldDescriptorProto{name: "today", extendee: nil, number: 2, label: :LABEL_OPTIONAL, type: :TYPE_MESSAGE, type_name: ".google.protobuf.Timestamp", default_value: nil, options: nil, oneof_index: nil, json_name: "today", proto3_optional: nil, __unknown_fields__: []}], nested_type: [], enum_type: [], extension_range: [], extension: [], options: nil, oneof_decl: [], reserved_range: [], reserved_name: [], __unknown_fields__: []}], enum_type: [], service: [%Google.Protobuf.ServiceDescriptorProto{name: "PingPongActor", method: [%Google.Protobuf.MethodDescriptorProto{name: "Ping", input_type: ".pinger.PingRequest", output_type: ".pinger.PongReply", options: %Google.Protobuf.MethodOptions{deprecated: false, idempotency_level: :IDEMPOTENCY_UNKNOWN, features: nil, uninterpreted_option: [], __pb_extensions__: %{{Google.Api.PbExtension, :http} => %Google.Api.HttpRule{selector: "", body: "*", additional_bindings: [], response_body: "", pattern: {:post, "/v1/ping/{name}"}, __unknown_fields__: []}}, __unknown_fields__: []}, client_streaming: false, server_streaming: false, __unknown_fields__: []}, %Google.Protobuf.MethodDescriptorProto{name: "Pong", input_type: ".google.protobuf.Empty", output_type: ".pinger.PingPongState", options: %Google.Protobuf.MethodOptions{deprecated: false, idempotency_level: :IDEMPOTENCY_UNKNOWN, features: nil, uninterpreted_option: [], __pb_extensions__: %{{Google.Api.PbExtension, :http} => %Google.Api.HttpRule{selector: "", body: "", additional_bindings: [], response_body: "", pattern: {:get, "/v1/pong/{name}"}, __unknown_fields__: []}}, __unknown_fields__: []}, client_streaming: false, server_streaming: false, __unknown_fields__: []}], options: nil, __unknown_fields__: []}], extension: [], options: %Google.Protobuf.FileOptions{java_package: nil, java_outer_classname: nil, optimize_for: :SPEED, java_multiple_files: false, go_package: nil, cc_generic_services: false, java_generic_services: false, py_generic_services: false, java_generate_equals_and_hash: nil, deprecated: false, java_string_check_utf8: false, cc_enable_arenas: true, objc_class_prefix: "HLW", csharp_namespace: nil, swift_prefix: nil, php_class_prefix: nil, php_namespace: nil, php_metadata_namespace: nil, ruby_package: nil, features: nil, uninterpreted_option: [], __pb_extensions__: %{}, __unknown_fields__: []}, source_code_info: %Google.Protobuf.SourceCodeInfo{location: [%Google.Protobuf.SourceCodeInfo.Location{path: [], span: [0, 0, 40, 1], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: ~c"\f", span: [0, 0, 18], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: ~c"\b", span: [2, 0, 33], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: ~c"\b$", span: [2, 0, 33], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [3, 0], span: [4, 0, 38], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [3, 1], span: [5, 0, 37], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [3, 2], span: [6, 0, 41], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [3, 3], span: [7, 0, 57], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [2], span: [9, 0, 15], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 0], span: [12, 0, 16, 1], leading_comments: " The ping-pong state of PingPongActor\n", trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 0, 1], span: [12, 8, 21], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 0, 2, 0], span: [13, 2, 25], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 0, 2, 0, 5], span: [13, 2, 8], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 0, 2, 0, 1], span: [13, 9, 20], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 0, 2, 0, 3], span: [13, 23, 24], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 0, 2, 1], span: [14, 2, 27], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 0, 2, 1, 5], span: [14, 2, 8], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 0, 2, 1, 1], span: [14, 9, 22], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 0, 2, 1, 3], span: [14, 25, 26], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 0, 2, 2], span: [15, 2, 43], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 0, 2, 2, 6], span: [15, 2, 27], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 0, 2, 2, 1], span: [15, 28, 38], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 0, 2, 2, 3], span: [15, 41, 42], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 1], span: [19, 0, 21, 1], leading_comments: " The request message containing the actor name.\n", trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 1, 1], span: [19, 8, 19], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], __unknown_fields__: []}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 1, 2, 0], span: [20, 2, 70], leading_comments: nil, trailing_comments: nil, leading_detached_comments: [], ...}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 1, 2, ...], span: [20, 2, ...], leading_comments: nil, trailing_comments: nil, ...}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, 1, ...], span: [20, ...], leading_comments: nil, ...}, %Google.Protobuf.SourceCodeInfo.Location{path: [4, ...], span: [...], ...}, %Google.Protobuf.SourceCodeInfo.Location{path: [...], ...}, %Google.Protobuf.SourceCodeInfo.Location{...}, ...], __unknown_fields__: []}, public_dependency: [], weak_dependency: [], syntax: "proto3", edition: nil, __unknown_fields__: []}], [file: ~c"lib/grpc_reflection/service/builder/util.ex", line: 141]}, {GrpcReflection.Service.Builder, :process_service, 1, [file: ~c"lib/grpc_reflection/service/builder.ex", line: 38]}, {GrpcReflection.Service.Builder, :"-process_services/1-fun-0-", 2, [file: ~c"lib/grpc_reflection/service/builder.ex", line: 31]}, {Enum, :"-reduce/3-lists^foldl/2-0-", 3, [file: ~c"lib/enum.ex", line: 2510]}, {GrpcReflection.Service.Builder, :build_reflection_tree, 1, [file: ~c"lib/grpc_reflection/service/builder.ex", line: 11]}, {GrpcReflection.Service.Agent, :start_link, 2, [file: ~c"lib/grpc_reflection/service/agent.ex", line: 17]}, {DynamicSupervisor, :start_child, 3, [file: ~c"lib/dynamic_supervisor.ex", line: 795]}, {DynamicSupervisor, :handle_start_child, 2, [file: ~c"lib/dynamic_supervisor.ex", line: 781]}]}}
    (grpc_reflection 0.1.0) lib/grpc_reflection/service/agent.ex:66: GrpcReflection.Service.Agent.start_agent_on_first_call/1
    (grpc_reflection 0.1.0) lib/grpc_reflection/service/agent.ex:35: GrpcReflection.Service.Agent.get_by_symbol/2
    (grpc_reflection 0.1.0) lib/grpc_reflection/server/v1.ex:64: GrpcReflection.Server.V1.reflection_request/2
    (grpc_reflection 0.1.0) lib/grpc_reflection/server/v1.ex:15: anonymous fn/3 in GrpcReflection.Server.V1.server_reflection_info/3
    (elixir 1.15.7) lib/enum.ex:1701: anonymous fn/3 in Enum.map/2
    (elixir 1.15.7) lib/enum.ex:4387: anonymous fn/3 in Enum.map/2
    (elixir 1.15.7) lib/stream.ex:1816: anonymous fn/3 in Enumerable.Stream.reduce/3
    (elixir 1.15.7) lib/stream.ex:1759: Stream.do_unfold/4
mjheilmann commented 3 months ago

I can't seem to locate eigr/functions/protocol/actors/extensions.proto to attempt to reproduce this locally.

I tried removing import eigr/functions/protocol/actors/extensions.proto and changing the line string name = 1 [(.eigr.functions.protocol.actors.actor_id) = true]; to string name = 1; to see if I can see the error without that file, and it reflected properly.

Can you direct me to the project that contains the extensions.proto file?

Noting here that your error is a mismatch on the builder trying to derive types from a FileDescriptorProto, which is unusual as FileDescriptorProto is normally the payload you get from a reflecting server and not a descriptor output from protoc, at least as far as I know.

sleipnir commented 3 months ago

Noting here that your error is a mismatch on the builder trying to derive types from a FileDescriptorProto, which is unusual as FileDescriptorProto is normally the payload you get from a reflecting server and not a descriptor output from protoc, at least as far as I know.

Hi @mjheilmann, thanks for responding. This occurs because the program in question generates gRPC contracts and servers on the fly.

Here is the project https://github.com/eigr/spawn

And here you find the .proto in question. https://github.com/eigr/spawn/blob/feat/embedded-grpc/priv/protos/eigr/functions/protocol/actors/extensions.proto

To run and reproduce locally use the make run-proxy-local-3 task on the branch feat/embedded-grpc.

This PR will give more context https://github.com/eigr/spawn/pull/302

sleipnir commented 3 months ago

@mjheilmann any workaround?

mjheilmann commented 3 months ago

@sleipnir from make run-proxy-local-3

** (Mix) Could not start application proxy: exited in: Proxy.Application.start(:normal, [])
    ** (EXIT) an exception was raised:
        ** (RuntimeError) Failed to start Proxy Application: {:error, {:shutdown, {:failed_to_start_child, Proxy.Supervisor, {:shutdown, {:failed_to_start_child, Sidecar.Supervisor, {:shutdown, {:failed_to_start_child, Sidecar.ProcessSupervisor, {:shutdown, {:failed_to_start_child, Statestores.Supervisor, {%DBConnection.ConnectionError{message: "connection not available and request was dropped from queue after 20997ms. This means requests are coming in and your connection pool cannot serve them fast enough. You can address this by:\n\n  1. Ensuring your database is available and that you can connect to it\n  2. Tracking down slow queries and making sure they are running fast enough\n  3. Increasing the pool_size (although this increases resource consumption)\n  4. Allowing requests to wait longer by increasing :queue_target and :queue_interval\n\nSee DBConnection.start_link/2 for more information\n", severity: :error, reason: :queue_timeout}, [{Ecto.Adapters.SQL, :raise_sql_call_error, 1, [file: ~c"lib/ecto/adapters/sql.ex", line: 1054, error_info: %{module: Exception}]}, {Enum, :"-map/2-lists^map/1-1-", 2, [file: ~c"lib/enum.ex", line: 1693]}, {Ecto.Adapters.SQL, :execute_ddl, 4, [file: ~c"lib/ecto/adapters/sql.ex", line: 1161]}, {Ecto.Migrator, :verbose_schema_migration, 3, [file: ~c"lib/ecto/migrator.ex", line: 755]}, {Ecto.Migrator, :lock_for_migrations, 4, [file: ~c"lib/ecto/migrator.ex", line: 563]}, {Ecto.Migrator, :run, 4, [file: ~c"lib/ecto/migrator.ex", line: 432]}, {Ecto.Migrator, :with_repo, 3, [file: ~c"lib/ecto/migrator.ex", line: 170]}, {Statestores.Migrator, :migrate, 1, [file: ~c"lib/statestores/migrator/migrator.ex", line: 14]}]}}}}}}}}}}
            (proxy 0.0.0-local.dev) lib/proxy/application.ex:39: Proxy.Application.do_start/2
            (kernel 8.5.4.2) application_master.erl:293: :application_master.start_it_old/4
make: *** [run-proxy-local-3] Error 1

There is extra setup and possible configuration, I don't think its meaningful for me to start running and debugging your application.

The stack trace you posted is in types_from_descriptor/1, which in grpc-reflection is called in 2 places:

These are the only places where we have an entry to our code that could crash as you posted. We expect that the return value from descriptor/0 on these modules is either a ServiceDescriptorProto or a DescriptorProto. Check your pb.ex or equivalent files like this to see what your compiled descriptors look like, I wouldn't expect any of them to be FileDescriptorProto. My guess is either you have a module whose descriptor is returning a bad type, or you have a module that collides with a proto symbol name and is being picked up by mistake.

sleipnir commented 3 months ago

There is extra setup and possible configuration, I don't think its meaningful for me to start running and debugging your application.

Yeah! Sorry for this!

I will try to debug this later But I expect a ServiceDescriptorProto type too.... Thanks for now @mjheilmann, I'll try to find the error with your valuable insights.

sleipnir commented 3 months ago

@mjheilmann It seems that I found the error and it is quite obvious in reality. The grpc-reflection library is based on the older grpc plugin while my proto was compiled using the protobuf-generate library. I did this because protobuf-generate is the only one that supports extensions and http transcoding. But it ends up generating an elixir file that is slightly different from the one expected by the reflection library.

I think the ideal would be to include support for both in the reflection library and the choice of compiler could be done in the module options. This should be relatively simple to implement and I'll try to submit a PR in the next few days to add this functionality if that's okay with you.

mjheilmann commented 3 months ago

Adding support for https://github.com/drowzy/protobuf_generate is likely going to be a bigger PR than you would think. A lot of the parsing and iteration that grpc-reflection does is to simulate a filesystem and generate a set of file descriptors, because we don't have access to the files to get file descriptors directly. However protobuf_generate is providing file descriptors directly. We should leverage this directly, and have an alternate builder to support it.

Instead of collecting service and field descriptors to assemble a file descriptor, we would take and reference the existing file descriptor and instead build the symbol lookup table directly from the contents of that descriptor. I think we could build a state object, which is the post-build runtime lookup structure.

mjheilmann commented 3 months ago

If you are up for solving that, a PR would be very welcome

sleipnir commented 3 months ago

If you are up for solving that, a PR would be very welcome

Okay. Let me think a little about how I could resolve this properly. Since I "depend" on it I'm likely to dedicate time to it. Thanks for detailing the process a little more.

mjheilmann commented 3 months ago

@sleipnir I played around with protobuf_generate, it looks like it only generates theFileDescriptorProto for the service definitions, the regular types expose the expected descriptors. The entry point to the builder module is the service. While it would not be elegant, I bet we can fiddle with the interface to the builder so that we can provide a package, name, and service descriptor instead of a service module; this would let us unwrap the ServiceDescriptor and pass it in to the normal builder logic.

I attempted this in https://github.com/elixir-grpc/grpc-reflection/pull/37

sleipnir commented 2 months ago

@sleipnir I played around with protobuf_generate, it looks like it only generates theFileDescriptorProto for the service definitions, the regular types expose the expected descriptors. The entry point to the builder module is the service. While it would not be elegant, I bet we can fiddle with the interface to the builder so that we can provide a package, name, and service descriptor instead of a service module; this would let us unwrap the ServiceDescriptor and pass it in to the normal builder logic.

I attempted this in #37

I agree that this approach is actually simpler. Good!

mjheilmann commented 2 months ago

I merged in those changes, @sleipnir. Please let me know if there are further issues around, if not we can close the issue

sleipnir commented 2 months ago

Thank you @mjheilmann I will test and get back with feedback

sleipnir commented 2 months ago

The reflection worked, but the call to the function did not work accordingly. I haven't identified the problem yet.

Follow logs:

🚀 ▶ grpcurl -v -plaintext 127.0.0.1:9980 describe io.eigr.spawn.example.Joe.Sum
io.eigr.spawn.example.Joe.Sum is a method:
rpc Sum ( .io.eigr.spawn.example.MyBusinessMessage ) returns ( .io.eigr.spawn.example.MyBusinessMessage ) {
  option (.google.api.http) = { post: "/v1/sum", body: "*" };
}

🚀 ▶ grpcurl -v -plaintext 127.0.0.1:9980 describe io.eigr.spawn.example.Joe
io.eigr.spawn.example.Joe is a service:
service Joe {
  rpc Ping ( .io.eigr.spawn.example.MyBusinessMessage ) returns ( .io.eigr.spawn.example.MyBusinessMessage ) {
    option (.google.api.http) = { get: "/v1/ping" };
  }
  rpc Sum ( .io.eigr.spawn.example.MyBusinessMessage ) returns ( .io.eigr.spawn.example.MyBusinessMessage ) {
    option (.google.api.http) = { post: "/v1/sum", body: "*" };
  }
}

🚀 ▶ grpcurl -v -plaintext 127.0.0.1:9980 describe .io.eigr.spawn.example.MyBusinessMessage
io.eigr.spawn.example.MyBusinessMessage is a message:
message MyBusinessMessage {
  int32 value = 1;
}

🚀 ▶ grpcurl -v -plaintext -format text -d 'value: 1' 127.0.0.1:9980 io.eigr.spawn.example.Joe.Sum

Resolved method descriptor:
rpc Sum ( .io.eigr.spawn.example.MyBusinessMessage ) returns ( .io.eigr.spawn.example.MyBusinessMessage ) {
  option (.google.api.http) = { post: "/v1/sum", body: "*" };
}

Request metadata to send:
(empty)

Response headers received:
content-type: application/grpc+proto
date: Tue, 11 Jun 2024 14:45:08 GMT
server: Cowboy

Response trailers received:
(empty)
Sent 1 request and received 0 responses
ERROR:
  Code: Unknown
  Message: Internal Server Error
sleipnir commented 2 months ago

@mjheilmann works as expect

grpcurl -v -plaintext -d '{"value": 1}' 127.0.0.1:9980 io.eigr.spawn.example.Joe/Sum

Resolved method descriptor:
rpc Sum ( .io.eigr.spawn.example.MyBusinessMessage ) returns ( .io.eigr.spawn.example.MyBusinessMessage ) {
  option (.google.api.http) = { post: "/v1/sum", body: "*" };
}

Request metadata to send:
(empty)

Response headers received:
content-type: application/grpc+proto
date: Tue, 11 Jun 2024 16:32:57 GMT
server: Cowboy

Response contents:
{
  "value": 75
}

Response trailers received:
(empty)
Sent 1 request and received 1 response

You can publish a new hex version?

mjheilmann commented 2 months ago

@sleipnir I had published a new version this morning for another bugfix, which included the merged fixes here. I think you might be good to go on version 0.1.4

sleipnir commented 2 months ago

@sleipnir I had published a new version this morning for another bugfix, which included the merged fixes here. I think you might be good to go on version 0.1.4

Cool @mjheilmann This here is the result of our collaboration https://twitter.com/sleipni_r/status/1800584524591743205

mjheilmann commented 2 months ago

Very nice!On Jun 12, 2024, at 12:14 AM, Adriano Santos @.***> wrote:

@sleipnir I had published a new version this morning for another bugfix, which included the merged fixes here. I think you might be good to go on version 0.1.4

Cool @mjheilmann This here is the result of our collaboration https://twitter.com/sleipni_r/status/1800584524591743205

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: @.***>