From 5b3d371e36c2a37655dee7b67fb980772ad0f9b6 Mon Sep 17 00:00:00 2001 From: mjheilmann Date: Tue, 23 Sep 2025 21:47:11 -0400 Subject: [PATCH] Don't build symbols we already have in the state --- lib/grpc_reflection/service/builder.ex | 2 +- priv/protos/recursive_message.proto | 15 +++ test/service/builder_test.exs | 17 +++ test/support/protos/recursive_message.pb.ex | 120 ++++++++++++++++++++ 4 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 priv/protos/recursive_message.proto create mode 100644 test/support/protos/recursive_message.pb.ex diff --git a/lib/grpc_reflection/service/builder.ex b/lib/grpc_reflection/service/builder.ex index 7425970..41da12d 100644 --- a/lib/grpc_reflection/service/builder.ex +++ b/lib/grpc_reflection/service/builder.ex @@ -90,7 +90,7 @@ defmodule GrpcReflection.Service.Builder do symbol: Enum.find(fields, fn f -> f.name == name end).type_name } end) - |> Enum.reject(fn %{symbol: s} -> s == nil end) + |> Enum.reject(fn %{symbol: s} -> is_nil(s) or State.has_symbol?(state, s) end) |> Enum.reduce(state, fn %{mod: mod, symbol: symbol}, state -> symbol = Util.trim_symbol(symbol) diff --git a/priv/protos/recursive_message.proto b/priv/protos/recursive_message.proto new file mode 100644 index 0000000..e25f5ff --- /dev/null +++ b/priv/protos/recursive_message.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package recursive_message; + +service Service { + rpc call (Request) returns (Reply) {} +} + +message Request { + Reply reply = 1; +} + +message Reply { + Request request = 1; +} diff --git a/test/service/builder_test.exs b/test/service/builder_test.exs index da12e2d..834621f 100644 --- a/test/service/builder_test.exs +++ b/test/service/builder_test.exs @@ -167,4 +167,21 @@ defmodule GrpcReflection.Service.BuilderTest do assert {:ok, tree} = Builder.build_reflection_tree([WrappedService]) assert %State{services: [WrappedService]} = tree end + + test "handles a recursive message structure" do + assert {:ok, tree} = Builder.build_reflection_tree([RecursiveMessage.Service.Service]) + + assert tree.files |> Map.keys() |> Enum.sort() == [ + "recursive_message.Reply.proto", + "recursive_message.Request.proto", + "recursive_message.Service.proto" + ] + + assert tree.symbols |> Map.keys() |> Enum.sort() == [ + "recursive_message.Reply", + "recursive_message.Request", + "recursive_message.Service", + "recursive_message.Service.call" + ] + end end diff --git a/test/support/protos/recursive_message.pb.ex b/test/support/protos/recursive_message.pb.ex new file mode 100644 index 0000000..c71f370 --- /dev/null +++ b/test/support/protos/recursive_message.pb.ex @@ -0,0 +1,120 @@ +defmodule RecursiveMessage.Request do + @moduledoc false + + use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 + + def descriptor do + # credo:disable-for-next-line + %Google.Protobuf.DescriptorProto{ + name: "Request", + field: [ + %Google.Protobuf.FieldDescriptorProto{ + name: "reply", + extendee: nil, + number: 1, + label: :LABEL_OPTIONAL, + type: :TYPE_MESSAGE, + type_name: ".recursive_message.Reply", + default_value: nil, + options: nil, + oneof_index: nil, + json_name: "reply", + proto3_optional: nil, + __unknown_fields__: [] + } + ], + nested_type: [], + enum_type: [], + extension_range: [], + extension: [], + options: nil, + oneof_decl: [], + reserved_range: [], + reserved_name: [], + __unknown_fields__: [] + } + end + + field :reply, 1, type: RecursiveMessage.Reply +end + +defmodule RecursiveMessage.Reply do + @moduledoc false + + use Protobuf, protoc_gen_elixir_version: "0.14.1", syntax: :proto3 + + def descriptor do + # credo:disable-for-next-line + %Google.Protobuf.DescriptorProto{ + name: "Reply", + field: [ + %Google.Protobuf.FieldDescriptorProto{ + name: "request", + extendee: nil, + number: 1, + label: :LABEL_OPTIONAL, + type: :TYPE_MESSAGE, + type_name: ".recursive_message.Request", + default_value: nil, + options: nil, + oneof_index: nil, + json_name: "request", + proto3_optional: nil, + __unknown_fields__: [] + } + ], + nested_type: [], + enum_type: [], + extension_range: [], + extension: [], + options: nil, + oneof_decl: [], + reserved_range: [], + reserved_name: [], + __unknown_fields__: [] + } + end + + field :request, 1, type: RecursiveMessage.Request +end + +defmodule RecursiveMessage.Service.Service do + @moduledoc false + + use GRPC.Service, name: "recursive_message.Service", protoc_gen_elixir_version: "0.14.1" + + def descriptor do + # credo:disable-for-next-line + %Google.Protobuf.ServiceDescriptorProto{ + name: "Service", + method: [ + %Google.Protobuf.MethodDescriptorProto{ + name: "call", + input_type: ".recursive_message.Request", + output_type: ".recursive_message.Reply", + options: %Google.Protobuf.MethodOptions{ + deprecated: false, + idempotency_level: :IDEMPOTENCY_UNKNOWN, + features: nil, + uninterpreted_option: [], + __pb_extensions__: %{}, + __unknown_fields__: [] + }, + client_streaming: false, + server_streaming: false, + __unknown_fields__: [] + } + ], + options: nil, + __unknown_fields__: [] + } + end + + rpc :call, RecursiveMessage.Request, RecursiveMessage.Reply +end + +defmodule RecursiveMessage.Service.Stub do + @moduledoc false + + use GRPC.Stub, service: RecursiveMessage.Service.Service +end