Skip to content

Commit acd8a2f

Browse files
author
Meir Kriheli
committed
support extracting methods from base interfaces of the main service-interface being reflected.
Add test to prove it works also with base interfaces using Generics. Generated schema will include also the methods defined in the base interfaces recursively.
1 parent d2980e1 commit acd8a2f

File tree

2 files changed

+75
-1
lines changed

2 files changed

+75
-1
lines changed

src/protobuf-net.Grpc.Reflection/SchemaGenerator.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using ProtoBuf.Grpc.Internal;
44
using ProtoBuf.Meta;
55
using System;
6+
using System.Linq;
67
using System.Reflection;
78

89
namespace ProtoBuf.Grpc.Reflection
@@ -47,7 +48,8 @@ public string GetSchema(Type contractType)
4748
{
4849
Name = name
4950
};
50-
var ops = contractType.GetMethods(BindingFlags.Public | BindingFlags.Instance);
51+
52+
var ops = GetMethodsRecursively(contractType);
5153
foreach (var method in ops)
5254
{
5355
if (method.DeclaringType == typeof(object))
@@ -100,5 +102,16 @@ static Type ApplySubstitutes(Type type)
100102
return type;
101103
}
102104
}
105+
106+
private static MethodInfo[] GetMethodsRecursively(Type contractType)
107+
{
108+
var methods = contractType.GetMethods(BindingFlags.Public | BindingFlags.Instance);
109+
var baseInterfaces = contractType.GetInterfaces();
110+
var inheritedMethods = baseInterfaces.SelectMany(iface => GetMethodsRecursively(iface)).ToArray();
111+
if (inheritedMethods.Any())
112+
return inheritedMethods.Concat(methods).ToArray();
113+
114+
return methods;
115+
}
103116
}
104117
}

tests/protobuf-net.Grpc.Reflection.Test/SchemaGeneration.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,49 @@ service MyService {
5252
rpc SyncEmpty (.google.protobuf.Empty) returns (.google.protobuf.Empty);
5353
rpc Unary (MyRequest) returns (MyResponse);
5454
}
55+
", schema, ignoreLineEndingDifferences: true);
56+
}
57+
58+
[Fact]
59+
public void CheckIngeritedInterfaceSchema()
60+
{
61+
var generator = new SchemaGenerator();
62+
var schema = generator.GetSchema<IMyInheritedService>();
63+
Log(schema);
64+
Assert.Equal(@"syntax = ""proto3"";
65+
package protobuf_net.Grpc.Reflection.Test;
66+
import ""google/protobuf/empty.proto"";
67+
import ""google/protobuf/timestamp.proto"";
68+
69+
enum Category {
70+
Default = 0;
71+
Foo = 1;
72+
Bar = 2;
73+
}
74+
message MyRequest {
75+
int32 Id = 1;
76+
.google.protobuf.Timestamp When = 2;
77+
}
78+
message MyResponse {
79+
string Value = 1;
80+
Category Category = 2;
81+
string RefId = 3; // default value could not be applied: 00000000-0000-0000-0000-000000000000
82+
}
83+
service MyInheritedService {
84+
rpc AsyncEmpty (.google.protobuf.Empty) returns (.google.protobuf.Empty);
85+
rpc ClientStreaming (stream MyRequest) returns (MyResponse);
86+
rpc FullDuplex (stream MyRequest) returns (stream MyResponse);
87+
rpc GenericUnary (MyRequest) returns (MyResponse);
88+
rpc InheritedAsyncEmpty (.google.protobuf.Empty) returns (.google.protobuf.Empty);
89+
rpc InheritedClientStreaming (stream MyRequest) returns (MyResponse);
90+
rpc InheritedFullDuplex (stream MyRequest) returns (stream MyResponse);
91+
rpc InheritedServerStreaming (MyRequest) returns (stream MyResponse);
92+
rpc InheritedSyncEmpty (.google.protobuf.Empty) returns (.google.protobuf.Empty);
93+
rpc InheritedUnary (MyRequest) returns (MyResponse);
94+
rpc ServerStreaming (MyRequest) returns (stream MyResponse);
95+
rpc SyncEmpty (.google.protobuf.Empty) returns (.google.protobuf.Empty);
96+
rpc Unary (MyRequest) returns (MyResponse);
97+
}
5598
", schema, ignoreLineEndingDifferences: true);
5699
}
57100

@@ -67,6 +110,24 @@ public interface IMyService
67110
void SyncEmpty();
68111
}
69112

113+
[Service]
114+
public interface ISomeGenericService<TGenericRequest, TGenericResult>
115+
{
116+
ValueTask<TGenericResult> GenericUnary(TGenericRequest request, CallContext callContext = default);
117+
}
118+
119+
[Service]
120+
public interface IMyInheritedService : IMyService, ISomeGenericService<MyRequest, MyResponse>
121+
{
122+
ValueTask<MyResponse> InheritedUnary(MyRequest request, CallContext callContext = default);
123+
ValueTask<MyResponse> InheritedClientStreaming(IAsyncEnumerable<MyRequest> request, CallContext callContext = default);
124+
IAsyncEnumerable<MyResponse> InheritedServerStreaming(MyRequest request, CallContext callContext = default);
125+
IAsyncEnumerable<MyResponse> InheritedFullDuplex(IAsyncEnumerable<MyRequest> request, CallContext callContext = default);
126+
127+
ValueTask InheritedAsyncEmpty();
128+
void InheritedSyncEmpty();
129+
}
130+
70131
[DataContract]
71132
public class MyRequest
72133
{

0 commit comments

Comments
 (0)