Skip to content

Commit 0e7662c

Browse files
Refine Logic for Handling Nested Types (#38)
* deal with nested types * fix unit tests * add unit tests * add unit tests cases * address comments * mix format * add unit test cases * bump version
1 parent fd48614 commit 0e7662c

File tree

9 files changed

+172
-21
lines changed

9 files changed

+172
-21
lines changed

lib/grpc_reflection/service/builder.ex

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,29 @@ defmodule GrpcReflection.Service.Builder do
8080
descriptor = mod.descriptor()
8181
name = symbol
8282

83-
referenced_types = Util.types_from_descriptor(descriptor)
84-
unencoded_payload = process_common(name, descriptor, Util.get_syntax(mod))
83+
nested_types = Util.get_nested_types(name, descriptor)
84+
85+
referenced_types =
86+
Util.types_from_descriptor(descriptor)
87+
|> Enum.uniq()
88+
|> Kernel.--(nested_types)
89+
90+
unencoded_payload = process_common(name, descriptor, Util.get_syntax(mod), nested_types)
8591
payload = FileDescriptorProto.encode(unencoded_payload)
8692
response = %{file_descriptor_proto: [payload]}
8793

8894
root_symbols = %{symbol => response}
95+
96+
root_symbols =
97+
Enum.reduce(nested_types, root_symbols, fn name, acc -> Map.put(acc, name, response) end)
98+
8999
root_files = %{(symbol <> ".proto") => response}
90100

101+
root_files =
102+
Enum.reduce(nested_types, root_files, fn name, acc ->
103+
Map.put(acc, name <> ".proto", response)
104+
end)
105+
91106
extension_file = symbol <> "Extension.proto"
92107

93108
{root_extensions, root_files} =
@@ -159,21 +174,20 @@ defmodule GrpcReflection.Service.Builder do
159174

160175
defp process_extensions(_, _, _, _), do: {:ignore, {nil, nil}}
161176

162-
defp process_common(name, descriptor, syntax) do
163-
package = Util.get_package(name)
164-
177+
defp process_common(name, descriptor, syntax, nested_types \\ []) do
165178
dependencies =
166179
descriptor
167180
|> Util.types_from_descriptor()
181+
|> Enum.uniq()
182+
|> Kernel.--(nested_types)
168183
|> Enum.map(fn name ->
169184
name <> ".proto"
170185
end)
171-
|> Enum.uniq()
172186

173187
response_stub =
174188
%FileDescriptorProto{
175189
name: name <> ".proto",
176-
package: package,
190+
package: Util.get_package(name),
177191
dependency: dependencies,
178192
syntax: syntax
179193
}

lib/grpc_reflection/service/builder/util.ex

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,22 @@ defmodule GrpcReflection.Service.Builder.Util do
77

88
@type_message Map.fetch!(Google.Protobuf.FieldDescriptorProto.Type.mapping(), :TYPE_MESSAGE)
99

10+
def get_nested_types(symbol, descriptor), do: get_nested_types(symbol, descriptor, [])
11+
12+
def get_nested_types(_symbol, %Google.Protobuf.DescriptorProto{nested_type: []}, acc) do
13+
acc
14+
end
15+
16+
def get_nested_types(symbol, %Google.Protobuf.DescriptorProto{nested_type: nested_types}, acc) do
17+
Enum.reduce(nested_types, acc, fn nested_type, acc ->
18+
new_symbol = symbol <> "." <> nested_type.name
19+
new_acc = [new_symbol | acc]
20+
get_nested_types(new_symbol, nested_type, new_acc)
21+
end)
22+
end
23+
24+
def get_nested_types(_, _, acc), do: acc
25+
1026
def get_package(symbol) do
1127
parent_symbol = symbol |> String.split(".") |> Enum.slice(0..-2//1) |> Enum.join(".")
1228

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
defmodule GrpcReflection.MixProject do
22
use Mix.Project
33

4-
@version "0.1.4"
4+
@version "0.1.5"
55
@source_url "https://github.com/elixir-grpc/grpc-reflection"
66
@description "gRPC reflection server for Elixir"
77

priv/protos/test_service_v3.proto

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ message TestRequest {
2929
}
3030

3131
Location location = 2;
32+
Token token = 3;
33+
}
34+
35+
message Token {
36+
string vaule = 1;
3237
}
3338

3439
Payload payload = 7;

test/builder/util_test.exs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,19 @@ defmodule GrpcReflection.Service.Builder.UtilTest do
3535
Util.convert_symbol_to_module("testservice.TestRequest.Payload.Location")
3636
end
3737
end
38+
39+
test "get all nested types" do
40+
assert [
41+
"testserviceV3.TestRequest.Token",
42+
"testserviceV3.TestRequest.Payload.Location",
43+
"testserviceV3.TestRequest.Payload",
44+
"testserviceV3.TestRequest.GEntry"
45+
] ==
46+
Util.get_nested_types(
47+
"testserviceV3.TestRequest",
48+
TestserviceV3.TestRequest.descriptor()
49+
)
50+
end
3851
end
3952

4053
describe "utils for dealing with proto2 only" do

test/builder_test.exs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ defmodule GrpcReflection.BuilderTest do
1919
"testserviceV3.TestRequest.GEntry.proto",
2020
"testserviceV3.TestRequest.Payload.Location.proto",
2121
"testserviceV3.TestRequest.Payload.proto",
22+
"testserviceV3.TestRequest.Token.proto",
2223
"testserviceV3.TestRequest.proto",
2324
"testserviceV3.TestService.proto"
2425
]
@@ -33,6 +34,7 @@ defmodule GrpcReflection.BuilderTest do
3334
"testserviceV3.TestRequest.GEntry",
3435
"testserviceV3.TestRequest.Payload",
3536
"testserviceV3.TestRequest.Payload.Location",
37+
"testserviceV3.TestRequest.Token",
3638
"testserviceV3.TestService",
3739
"testserviceV3.TestService.CallFunction"
3840
]

test/support/protos/test_service_v3.pb.ex

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,20 @@ defmodule TestserviceV3.TestRequest.Payload do
180180
json_name: "location",
181181
proto3_optional: nil,
182182
__unknown_fields__: []
183+
},
184+
%Google.Protobuf.FieldDescriptorProto{
185+
name: "token",
186+
extendee: nil,
187+
number: 3,
188+
label: :LABEL_OPTIONAL,
189+
type: :TYPE_MESSAGE,
190+
type_name: ".testserviceV3.TestRequest.Token",
191+
default_value: nil,
192+
options: nil,
193+
oneof_index: nil,
194+
json_name: "token",
195+
proto3_optional: nil,
196+
__unknown_fields__: []
183197
}
184198
],
185199
nested_type: [
@@ -239,6 +253,45 @@ defmodule TestserviceV3.TestRequest.Payload do
239253

240254
field :data, 1, type: Google.Protobuf.StringValue
241255
field :location, 2, type: TestserviceV3.TestRequest.Payload.Location
256+
field :token, 3, type: TestserviceV3.TestRequest.Token
257+
end
258+
259+
defmodule TestserviceV3.TestRequest.Token do
260+
use Protobuf, protoc_gen_elixir_version: "0.12.0", syntax: :proto3
261+
262+
def descriptor do
263+
# credo:disable-for-next-line
264+
%Google.Protobuf.DescriptorProto{
265+
name: "Token",
266+
field: [
267+
%Google.Protobuf.FieldDescriptorProto{
268+
name: "vaule",
269+
extendee: nil,
270+
number: 1,
271+
label: :LABEL_OPTIONAL,
272+
type: :TYPE_STRING,
273+
type_name: nil,
274+
default_value: nil,
275+
options: nil,
276+
oneof_index: nil,
277+
json_name: "vaule",
278+
proto3_optional: nil,
279+
__unknown_fields__: []
280+
}
281+
],
282+
nested_type: [],
283+
enum_type: [],
284+
extension_range: [],
285+
extension: [],
286+
options: nil,
287+
oneof_decl: [],
288+
reserved_range: [],
289+
reserved_name: [],
290+
__unknown_fields__: []
291+
}
292+
end
293+
294+
field :vaule, 1, type: :string
242295
end
243296

244297
defmodule TestserviceV3.TestRequest do
@@ -430,6 +483,20 @@ defmodule TestserviceV3.TestRequest do
430483
json_name: "location",
431484
proto3_optional: nil,
432485
__unknown_fields__: []
486+
},
487+
%Google.Protobuf.FieldDescriptorProto{
488+
name: "token",
489+
extendee: nil,
490+
number: 3,
491+
label: :LABEL_OPTIONAL,
492+
type: :TYPE_MESSAGE,
493+
type_name: ".testserviceV3.TestRequest.Token",
494+
default_value: nil,
495+
options: nil,
496+
oneof_index: nil,
497+
json_name: "token",
498+
proto3_optional: nil,
499+
__unknown_fields__: []
433500
}
434501
],
435502
nested_type: [
@@ -484,6 +551,34 @@ defmodule TestserviceV3.TestRequest do
484551
reserved_range: [],
485552
reserved_name: [],
486553
__unknown_fields__: []
554+
},
555+
%Google.Protobuf.DescriptorProto{
556+
name: "Token",
557+
field: [
558+
%Google.Protobuf.FieldDescriptorProto{
559+
name: "vaule",
560+
extendee: nil,
561+
number: 1,
562+
label: :LABEL_OPTIONAL,
563+
type: :TYPE_STRING,
564+
type_name: nil,
565+
default_value: nil,
566+
options: nil,
567+
oneof_index: nil,
568+
json_name: "vaule",
569+
proto3_optional: nil,
570+
__unknown_fields__: []
571+
}
572+
],
573+
nested_type: [],
574+
enum_type: [],
575+
extension_range: [],
576+
extension: [],
577+
options: nil,
578+
oneof_decl: [],
579+
reserved_range: [],
580+
reserved_name: [],
581+
__unknown_fields__: []
487582
}
488583
],
489584
enum_type: [],
@@ -616,4 +711,4 @@ end
616711

617712
defmodule TestserviceV3.TestService.Stub do
618713
use GRPC.Stub, service: TestserviceV3.TestService.Service
619-
end
714+
end

test/v1_reflection_test.exs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,18 @@ defmodule GrpcReflection.V1ReflectionTest do
6565
assert {:error, _} = run_request(message, ctx)
6666
end
6767

68-
test "describing a type returns the type", ctx do
68+
test "describing a root type returns the type", ctx do
6969
message = {:file_containing_symbol, "helloworld.HelloRequest"}
7070
assert {:ok, response} = run_request(message, ctx)
7171
assert_response(response)
7272
end
7373

74+
test "describing a nested type returns the root type", ctx do
75+
message = {:file_containing_symbol, "testserviceV3.TestRequest.Payload"}
76+
assert {:ok, response} = run_request(message, ctx)
77+
assert response.name == "testserviceV3.TestRequest.proto"
78+
end
79+
7480
test "type with leading period still resolves", ctx do
7581
message = {:file_containing_symbol, ".helloworld.HelloRequest"}
7682
assert {:ok, response} = run_request(message, ctx)
@@ -173,7 +179,7 @@ defmodule GrpcReflection.V1ReflectionTest do
173179
]
174180
end
175181

176-
test "ensure inclusion of nested types in file descriptor dependencies", ctx do
182+
test "ensure exclusion of nested types in file descriptor dependencies", ctx do
177183
filename = "testserviceV3.TestRequest.proto"
178184
message = {:file_by_filename, filename}
179185
assert {:ok, response} = run_request(message, ctx)
@@ -182,11 +188,8 @@ defmodule GrpcReflection.V1ReflectionTest do
182188

183189
assert response.dependency == [
184190
"testserviceV3.Enum.proto",
185-
"testserviceV3.TestRequest.GEntry.proto",
186191
"google.protobuf.Any.proto",
187-
"testserviceV3.TestRequest.Payload.proto",
188-
"google.protobuf.StringValue.proto",
189-
"testserviceV3.TestRequest.Payload.Location.proto"
192+
"google.protobuf.StringValue.proto"
190193
]
191194
end
192195
end

test/v1alpha_reflection_test.exs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,18 @@ defmodule GrpcReflection.V1alphaReflectionTest do
6565
assert {:error, _} = run_request(message, ctx)
6666
end
6767

68-
test "describing a type returns the type", ctx do
68+
test "describing a root type returns the type", ctx do
6969
message = {:file_containing_symbol, "helloworld.HelloRequest"}
7070
assert {:ok, response} = run_request(message, ctx)
7171
assert_response(response)
7272
end
7373

74+
test "describing a nested type returns the root type", ctx do
75+
message = {:file_containing_symbol, "testserviceV3.TestRequest.Payload"}
76+
assert {:ok, response} = run_request(message, ctx)
77+
assert response.name == "testserviceV3.TestRequest.proto"
78+
end
79+
7480
test "type with leading period still resolves", ctx do
7581
message = {:file_containing_symbol, ".helloworld.HelloRequest"}
7682
assert {:ok, response} = run_request(message, ctx)
@@ -173,7 +179,7 @@ defmodule GrpcReflection.V1alphaReflectionTest do
173179
]
174180
end
175181

176-
test "ensure inclusion of nested types in file descriptor dependencies", ctx do
182+
test "ensure exclusion of nested types in file descriptor dependencies", ctx do
177183
filename = "testserviceV3.TestRequest.proto"
178184
message = {:file_by_filename, filename}
179185
assert {:ok, response} = run_request(message, ctx)
@@ -182,11 +188,8 @@ defmodule GrpcReflection.V1alphaReflectionTest do
182188

183189
assert response.dependency == [
184190
"testserviceV3.Enum.proto",
185-
"testserviceV3.TestRequest.GEntry.proto",
186191
"google.protobuf.Any.proto",
187-
"testserviceV3.TestRequest.Payload.proto",
188-
"google.protobuf.StringValue.proto",
189-
"testserviceV3.TestRequest.Payload.Location.proto"
192+
"google.protobuf.StringValue.proto"
190193
]
191194
end
192195
end

0 commit comments

Comments
 (0)