3
3
using ProtoBuf . Grpc . Internal ;
4
4
using ProtoBuf . Meta ;
5
5
using System ;
6
+ using System . Collections . Generic ;
6
7
using System . Linq ;
7
8
using System . Reflection ;
8
9
@@ -26,68 +27,101 @@ public sealed class SchemaGenerator
26
27
/// <summary>
27
28
/// Get the .proto schema associated with a service contract
28
29
/// </summary>
30
+ /// <typeparam name="TService">The service type to generate schema for.</typeparam>
29
31
/// <remarks>This API is considered experimental and may change slightly</remarks>
30
32
public string GetSchema < TService > ( )
31
33
=> GetSchema ( typeof ( TService ) ) ;
32
34
33
35
/// <summary>
34
36
/// Get the .proto schema associated with a service contract
35
37
/// </summary>
36
- /// <remarks>This API is considered experimental and may change slightly</remarks>
38
+ /// <param name="contractType">The service type to generate schema for.</param>
39
+ /// <remarks>This API is considered experimental and may change slightly.
40
+ /// ATTENTION! although the 'GetSchema(params Type[] contractTypes)' covers also a case of 'GetSchema(Type contractType)',
41
+ /// this method need to remain for backward compatibility for client which will get this updated version, without recompilation.
42
+ /// Thus, this method mustn't be deleted.</remarks>
37
43
public string GetSchema ( Type contractType )
44
+ => GetSchema ( new [ ] { contractType } ) ;
45
+
46
+ /// <summary>
47
+ /// Get the .proto schema associated with multiple service contracts
48
+ /// </summary>
49
+ /// <param name="contractTypes">Array (or params syntax) of service types to generate schema for.</param>
50
+ /// <remarks>This API is considered experimental and may change slightly
51
+ /// All types will be generated into single schema.
52
+ /// All the shared classes the services use will be generated only once for all of them.</remarks>
53
+ public string GetSchema ( params Type [ ] contractTypes )
38
54
{
55
+ string globalPackage = "" ;
56
+ List < Service > services = new List < Service > ( ) ;
39
57
var binderConfiguration = BinderConfiguration ?? BinderConfiguration . Default ;
40
58
var binder = binderConfiguration . Binder ;
41
- if ( ! binder . IsServiceContract ( contractType , out var name ) )
59
+ foreach ( var contractType in contractTypes )
42
60
{
43
- throw new ArgumentException ( $ "Type '{ contractType . Name } ' is not a service contract", nameof ( contractType ) ) ;
44
- }
61
+ if ( ! binder . IsServiceContract ( contractType , out var name ) )
62
+ {
63
+ throw new ArgumentException ( $ "Type '{ contractType . Name } ' is not a service contract",
64
+ nameof ( contractTypes ) ) ;
65
+ }
45
66
46
- name = ServiceBinder . GetNameParts ( name , contractType , out var package ) ;
47
- var service = new Service
48
- {
49
- Name = name
50
- } ;
67
+ name = ServiceBinder . GetNameParts ( name , contractType , out var package ) ;
68
+ // currently we allow only services from same package, to be output to single proto file
69
+ if ( ! string . IsNullOrEmpty ( globalPackage )
70
+ && package != globalPackage )
71
+ {
72
+ throw new ArgumentException (
73
+ $ "All services must be of the same package! '{ contractType . Name } ' is from package '{ package } ' while previous package: { globalPackage } ",
74
+ nameof ( contractTypes ) ) ;
75
+ }
76
+ globalPackage = package ;
77
+
78
+ var service = new Service
79
+ {
80
+ Name = name
81
+ } ;
51
82
52
- var ops = GetMethodsRecursively ( binder , contractType ) ;
53
- foreach ( var method in ops )
54
- {
55
- if ( method . DeclaringType == typeof ( object ) )
56
- { /* skip */ }
57
- else if ( ContractOperation . TryIdentifySignature ( method , binderConfiguration , out var op , null ) )
83
+ var ops = GetMethodsRecursively ( binder , contractType ) ;
84
+ foreach ( var method in ops )
58
85
{
59
- service . Methods . Add (
60
- new ServiceMethod
61
- {
62
- Name = op . Name ,
63
- InputType = ApplySubstitutes ( op . From ) ,
64
- OutputType = ApplySubstitutes ( op . To ) ,
65
- ClientStreaming = op . MethodType switch
66
- {
67
- MethodType . ClientStreaming => true ,
68
- MethodType . DuplexStreaming => true ,
69
- _ => false ,
70
- } ,
71
- ServerStreaming = op . MethodType switch
86
+ if ( method . DeclaringType == typeof ( object ) )
87
+ {
88
+ /* skip */
89
+ }
90
+ else if ( ContractOperation . TryIdentifySignature ( method , binderConfiguration , out var op , null ) )
91
+ {
92
+ service . Methods . Add (
93
+ new ServiceMethod
72
94
{
73
- MethodType . ServerStreaming => true ,
74
- MethodType . DuplexStreaming => true ,
75
- _ => false ,
76
- } ,
77
- }
78
- ) ;
95
+ Name = op . Name ,
96
+ InputType = ApplySubstitutes ( op . From ) ,
97
+ OutputType = ApplySubstitutes ( op . To ) ,
98
+ ClientStreaming = op . MethodType switch
99
+ {
100
+ MethodType . ClientStreaming => true ,
101
+ MethodType . DuplexStreaming => true ,
102
+ _ => false ,
103
+ } ,
104
+ ServerStreaming = op . MethodType switch
105
+ {
106
+ MethodType . ServerStreaming => true ,
107
+ MethodType . DuplexStreaming => true ,
108
+ _ => false ,
109
+ } ,
110
+ }
111
+ ) ;
112
+ }
79
113
}
114
+
115
+ service . Methods . Sort ( ( x , y ) => string . Compare ( x . Name , y . Name ) ) ; // make it predictable
116
+ services . Add ( service ) ;
80
117
}
81
- service . Methods . Sort ( ( x , y ) => string . Compare ( x . Name , y . Name ) ) ; // make it predictable
118
+
82
119
var options = new SchemaGenerationOptions
83
120
{
84
121
Syntax = ProtoSyntax ,
85
- Package = package ,
86
- Services =
87
- {
88
- service
89
- }
122
+ Package = globalPackage ,
90
123
} ;
124
+ options . Services . AddRange ( services ) ;
91
125
92
126
var model = binderConfiguration . MarshallerCache . TryGetFactory < ProtoBufMarshallerFactory > ( ) ? . Model ?? RuntimeTypeModel . Default ;
93
127
return model . GetSchema ( options ) ;
0 commit comments