Skip to content

Commit f3d75ee

Browse files
committed
feat: update to 1.4.1
1 parent 5e2b1fe commit f3d75ee

8 files changed

+128
-33
lines changed

extensions/Sisk.JsonRPC/Documentation/DocumentationDescriptor.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,6 @@ internal static JsonRpcDocumentation GetDocumentationDescriptor ( JsonRpcHandler
3636
.ToArray () ) );
3737
}
3838

39-
return new JsonRpcDocumentation ( methods.ToArray (), metadata );
39+
return new JsonRpcDocumentation ( methods.OrderBy ( m => m.MethodName ).ToArray (), metadata );
4040
}
4141
}

extensions/Sisk.JsonRPC/Documentation/JsonRpcJsonExport.cs

+12-2
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,27 @@ public JsonRpcJsonExport ( JsonOptions options ) {
4444
/// <returns></returns>
4545
public JsonValue EncodeDocumentation ( JsonRpcDocumentation documentation ) {
4646
JsonArray arr = JsonOptions.CreateJsonArray ();
47+
48+
static string GetTypeName ( Type type ) {
49+
if (type.IsGenericType) {
50+
string genericDefinition = string.Join ( ", ", type.GetGenericArguments ().Select ( s => s.Name ) );
51+
return $"{type.Name}<{genericDefinition}>";
52+
}
53+
else {
54+
return type.Name;
55+
}
56+
}
4757

4858
foreach (var method in documentation.Methods) {
4959

5060
var item = new {
5161
Name = method.MethodName,
5262
Description = method.Description,
53-
Returns = method.ReturnType.Name,
63+
Returns = GetTypeName ( method.ReturnType ),
5464
Parameters = method.Parameters
5565
.Select ( p => new {
5666
Name = p.ParameterName,
57-
TypeName = p.ParameterType.Name,
67+
TypeName = GetTypeName ( p.ParameterType ),
5868
Description = p.Description,
5969
IsOptional = p.IsOptional
6070
} )

extensions/Sisk.JsonRPC/JsonRpcMethodCollection.cs

+8-7
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ public sealed class JsonRpcMethodCollection {
2525
/// <param name="method">The delegate representing the method to add.</param>
2626
public void AddMethod ( string name, Delegate method ) {
2727
lock ((methods as ICollection).SyncRoot) {
28-
methods.Add ( name, new RpcDelegate ( method.Method, method.Target ) );
28+
var del = MethodScanner.ParseDelegate ( method.Method, method.Target, name );
29+
methods.Add ( name, del );
2930
}
3031
}
3132

@@ -35,10 +36,10 @@ public void AddMethod ( string name, Delegate method ) {
3536
/// <typeparam name="T">The type from which to scan and add methods.</typeparam>
3637
/// <param name="target">The target object instance containing the methods.</param>
3738
/// <param name="prefixTypes">Indicates whether to prefix method names with the type name.</param>
38-
public void AddMethodsFromType<[DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes.PublicMethods )] T> ( T target, bool prefixTypes = false ) where T : notnull {
39+
public void AddMethodsFromType<[DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes.All )] T> ( T target, bool prefixTypes = false ) where T : notnull {
3940
lock ((methods as ICollection).SyncRoot) {
4041
foreach (var method in MethodScanner.ScanMethods ( typeof ( T ), prefixTypes, target )) {
41-
methods.Add ( method.Item1, method.Item2 );
42+
methods.Add ( method.WebMethodName, method );
4243
}
4344
}
4445
}
@@ -49,10 +50,10 @@ public void AddMethod ( string name, Delegate method ) {
4950
/// <param name="type">The type from which to scan and add methods.</param>
5051
/// <param name="target">The target object instance containing the methods.</param>
5152
/// <param name="prefixTypes">Indicates whether to prefix method names with the type name.</param>
52-
public void AddMethodsFromType ( [DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes.PublicMethods )] Type type, object? target, bool prefixTypes ) {
53+
public void AddMethodsFromType ( [DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes.All )] Type type, object? target, bool prefixTypes ) {
5354
lock ((methods as ICollection).SyncRoot) {
5455
foreach (var method in MethodScanner.ScanMethods ( type, prefixTypes, target )) {
55-
methods.Add ( method.Item1, method.Item2 );
56+
methods.Add ( method.WebMethodName, method );
5657
}
5758
}
5859
}
@@ -62,13 +63,13 @@ public void AddMethodsFromType ( [DynamicallyAccessedMembers ( DynamicallyAccess
6263
/// </summary>
6364
/// <param name="type">The type from which to scan and add methods.</param>
6465
/// <param name="target">The target object instance containing the methods.</param>
65-
public void AddMethodsFromType ( [DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes.PublicMethods )] Type type, object? target ) => AddMethodsFromType ( type, target, false );
66+
public void AddMethodsFromType ( [DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes.All )] Type type, object? target ) => AddMethodsFromType ( type, target, false );
6667

6768
/// <summary>
6869
/// Adds methods from the specified type to the collection without prefixing method names.
6970
/// </summary>
7071
/// <param name="type">The type from which to scan and add methods.</param>
71-
public void AddMethodsFromType ( [DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes.PublicMethods )] Type type ) => AddMethodsFromType ( type, null, false );
72+
public void AddMethodsFromType ( [DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes.All )] Type type ) => AddMethodsFromType ( type, null, false );
7273

7374
/// <summary>
7475
/// Removes a method from the collection by its name.

extensions/Sisk.JsonRPC/JsonRpcResponse.cs

+6-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@ public sealed class JsonRpcResponse {
3838
internal JsonRpcResponse ( JsonValue? result, JsonRpcError? error, JsonValue id ) {
3939
Result = result;
4040
Error = error;
41-
Id = id;
41+
if (id.Type == JsonValueType.Undefined) {
42+
Id = JsonValue.Null;
43+
}
44+
else {
45+
Id = id;
46+
}
4247
}
4348

4449
/// <summary>

extensions/Sisk.JsonRPC/JsonRpcTransportLayer.cs

+30-16
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// File name: JsonRpcTransportLayer.cs
88
// Repository: https://github.com/sisk-http/core
99

10-
using System.Diagnostics.CodeAnalysis;
10+
using System.Reflection;
1111
using System.Runtime.CompilerServices;
1212
using System.Text;
1313
using LightJson;
@@ -116,12 +116,17 @@ HttpResponse ImplTransportPostHttp ( HttpRequest request ) {
116116
response = HandleRpcRequest ( rpcRequest );
117117
}
118118
catch (Exception ex) {
119-
response = new JsonRpcResponse ( null,
120-
new JsonRpcError ( JsonErrorCode.InternalError, ex.Message, JsonValue.Null ), rpcRequest?.Id ?? "0" );
119+
if (_handler._server.ServerConfiguration.ThrowExceptions) {
120+
throw;
121+
}
122+
else {
123+
response = new JsonRpcResponse ( null,
124+
new JsonRpcError ( JsonErrorCode.InternalError, ex.Message, JsonValue.Null ), rpcRequest?.Id ?? "0" );
125+
}
121126
}
122127

123128
sendResponse:
124-
if (rpcRequest is not null && rpcRequest.Id.IsNull) {
129+
if (rpcRequest is not null && rpcRequest.Id.IsNull && response.Error is null) {
125130
return new HttpResponse () {
126131
Status = HttpStatusInformation.Accepted
127132
};
@@ -134,10 +139,8 @@ HttpResponse ImplTransportPostHttp ( HttpRequest request ) {
134139
}
135140
}
136141

137-
[DynamicDependency ( DynamicallyAccessedMemberTypes.PublicMethods, typeof ( Task<> ) )]
138-
[DynamicDependency ( DynamicallyAccessedMemberTypes.PublicMethods, typeof ( TaskAwaiter<> ) )]
139-
[SuppressMessage ( "Trimming", "IL2026:Using dynamic types might cause types or members to be removed by trimmer.", Justification = "<Pendente>" )]
140-
[SuppressMessage ( "Trimming", "IL2072:Target parameter argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The return value of the source method does not have matching annotations.", Justification = "<Pending>" )]
142+
[System.Diagnostics.CodeAnalysis.SuppressMessage ( "Trimming",
143+
"IL2072:Target parameter argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The return value of the source method does not have matching annotations.", Justification = "<Pending>" )]
141144
JsonRpcResponse HandleRpcRequest ( JsonRpcRequest request ) {
142145
JsonRpcResponse response;
143146
try {
@@ -146,7 +149,7 @@ JsonRpcResponse HandleRpcRequest ( JsonRpcRequest request ) {
146149

147150
if (method != null) {
148151
var methodInfo = method.Method;
149-
var methodParameters = methodInfo.GetParameters ();
152+
var methodParameters = method.Parameters;
150153

151154
object? [] methodInvokationParameters = new object? [ methodParameters.Length ];
152155

@@ -168,7 +171,7 @@ JsonRpcResponse HandleRpcRequest ( JsonRpcRequest request ) {
168171
}
169172

170173
for (int i = 0; i < jsonValueList.Count; i++) {
171-
methodInvokationParameters [ i ] = jsonValueList [ i ].MaybeNull ()?.Get ( methodParameters [ i ].ParameterType );
174+
methodInvokationParameters [ i ] = jsonValueList [ i ].MaybeNull ()?.Get ( methodParameters [ i ].ParameterType ) ?? methodParameters [ i ].DefaultValue;
172175
}
173176
}
174177
else {
@@ -194,18 +197,24 @@ JsonRpcResponse HandleRpcRequest ( JsonRpcRequest request ) {
194197
return response;
195198
}
196199

197-
methodInvokationParameters [ i ] = jsonParameter.MaybeNull ()?.Get ( param.ParameterType );
200+
methodInvokationParameters [ i ] = jsonParameter.MaybeNull ()?.Get ( param.ParameterType ) ?? param.DefaultValue;
198201
}
199202
}
200203

201204
object? result = methodInfo.Invoke ( method.Target, methodInvokationParameters );
202205

203-
if (result is Task task) {
204-
result = ((dynamic) task).GetAwaiter ().GetResult ();
206+
if (result is not null) {
207+
if (method.ReturnInformation.IsAsyncTask) {
208+
ref Task<object> actionTask = ref Unsafe.As<object, Task<object>> ( ref result );
209+
result = actionTask.GetAwaiter ().GetResult ();
210+
}
211+
else if (method.ReturnInformation.IsAsyncEnumerable) {
212+
ref IAsyncEnumerable<object> asyncEnumerable = ref Unsafe.As<object, IAsyncEnumerable<object>> ( ref result );
213+
result = asyncEnumerable.ToBlockingEnumerable ();
214+
}
205215
}
206216

207217
JsonValue resultEncoded = JsonValue.Serialize ( result, _handler._jsonOptions );
208-
209218
response = new JsonRpcResponse ( resultEncoded, null, request.Id );
210219
}
211220
else {
@@ -216,13 +225,18 @@ JsonRpcResponse HandleRpcRequest ( JsonRpcRequest request ) {
216225
catch (JsonRpcException jex) {
217226
response = new JsonRpcResponse ( null, jex.AsRpcError (), request.Id );
218227
}
219-
catch (Exception ex) {
228+
catch (Exception erx) {
229+
Exception ex = erx;
230+
if (erx is TargetInvocationException)
231+
ex = ex.InnerException ?? ex;
232+
220233
if (_handler._server.ServerConfiguration.ThrowExceptions) {
221234
throw;
222235
}
223236
else {
237+
_handler._server.ServerConfiguration.ErrorsLogsStream?.WriteException ( erx );
224238
response = new JsonRpcResponse ( null,
225-
new JsonRpcError ( JsonErrorCode.InternalError, ex.Message, JsonValue.Null ), request?.Id ?? "0" );
239+
new JsonRpcError ( JsonErrorCode.InternalError, ex.Message, JsonValue.Null ), request?.Id ?? "0" );
226240
}
227241
}
228242
return response;

extensions/Sisk.JsonRPC/MethodScanner.cs

+65-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
namespace Sisk.JsonRPC;
1515

1616
internal static class MethodScanner {
17-
public static IEnumerable<(string, RpcDelegate)> ScanMethods ( [DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes.PublicMethods )] Type type, bool prefixTypes, object? target ) {
17+
18+
public static IEnumerable<RpcDelegate> ScanMethods ( [DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes.All )] Type type, bool prefixTypes, object? target ) {
1819
const BindingFlags Flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
1920

2021
var customTypeName = type.GetCustomAttribute<WebNameAttribute> ()?.Name;
@@ -24,14 +25,76 @@ internal static class MethodScanner {
2425
WebMethodAttribute? methodName = method.GetCustomAttribute<WebMethodAttribute> ();
2526

2627
if (methodName is not null) {
28+
2729
string typeName = customTypeName ?? type.Name;
2830
string name = methodName.Name ?? method.Name;
2931

3032
if (prefixTypes)
3133
name = typeName + "." + name;
3234

33-
yield return (name, new RpcDelegate ( method, target ));
35+
yield return ParseDelegate ( method, target, name );
36+
}
37+
}
38+
}
39+
40+
public static RpcDelegate ParseDelegate ( MethodInfo method, object? target, string? name = null ) {
41+
CheckIfMethodIsEligible ( method );
42+
43+
name ??= method.Name;
44+
var parameters = GetParameters ( method ).ToArray ();
45+
46+
bool _isAsyncTask = false, _isAsyncEnumerable = false;
47+
var retType = method.ReturnType;
48+
if (retType.IsValueType) {
49+
throw new NotSupportedException ( "Defining web methods which their return type is an value type is not supported. Encapsulate it with ValueResult<T>." );
50+
}
51+
else if (retType.IsAssignableTo ( typeof ( Task ) )) {
52+
_isAsyncTask = true;
53+
if (CheckAsyncReturnParameters ( retType, method ) is Exception rex) {
54+
throw rex;
55+
}
56+
}
57+
else if (retType.IsGenericType && retType.GetGenericTypeDefinition () == typeof ( IAsyncEnumerable<> )) {
58+
_isAsyncEnumerable = true;
59+
if (CheckAsyncReturnParameters ( retType, method ) is Exception rex) {
60+
throw rex;
61+
}
62+
}
63+
64+
var returnInformation = new RpcDelegateMethodReturnInformation ( retType, _isAsyncEnumerable, _isAsyncTask );
65+
66+
return new RpcDelegate ( name, method, parameters, returnInformation, target );
67+
}
68+
69+
public static IEnumerable<RpcDelegateMethodParameter> GetParameters ( MethodInfo method ) {
70+
return method.GetParameters ()
71+
.Select ( p => new RpcDelegateMethodParameter ( p.ParameterType, p.Name, p.IsOptional, p.DefaultValue ) );
72+
}
73+
74+
static void CheckIfMethodIsEligible ( MethodInfo method ) {
75+
var retType = method.ReturnType;
76+
if (retType.IsValueType) {
77+
throw new NotSupportedException ( "Defining web methods which their return type is an value type is not supported. Encapsulate it with ValueResult<T>." );
78+
}
79+
else if (retType.IsAssignableTo ( typeof ( Task ) )) {
80+
if (CheckAsyncReturnParameters ( retType, method ) is Exception rex) {
81+
throw rex;
82+
}
83+
}
84+
else if (retType.IsGenericType && retType.GetGenericTypeDefinition () == typeof ( IAsyncEnumerable<> )) {
85+
if (CheckAsyncReturnParameters ( retType, method ) is Exception rex) {
86+
throw rex;
87+
}
88+
}
89+
}
90+
91+
static Exception? CheckAsyncReturnParameters ( Type asyncOutType, MethodInfo method ) {
92+
if (asyncOutType.GenericTypeArguments.Length == 1) {
93+
Type genericAssignType = asyncOutType.GenericTypeArguments [ 0 ];
94+
if (genericAssignType.IsValueType) {
95+
return new NotSupportedException ( "Defining web methods which their return type is an value type is not supported. Encapsulate it with ValueResult<T>." );
3496
}
3597
}
98+
return null;
3699
}
37100
}

extensions/Sisk.JsonRPC/RpcDelegate.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@
1111

1212
namespace Sisk.JsonRPC;
1313

14-
internal record class RpcDelegate ( MethodInfo Method, object? Target );
14+
internal record class RpcDelegate ( string WebMethodName, MethodInfo Method, RpcDelegateMethodParameter [] Parameters, RpcDelegateMethodReturnInformation ReturnInformation, object? Target );
15+
internal record class RpcDelegateMethodParameter ( Type ParameterType, string? Name, bool IsOptional, object? DefaultValue );
16+
internal record class RpcDelegateMethodReturnInformation ( Type ReturnType, bool IsAsyncEnumerable, bool IsAsyncTask );

extensions/Sisk.JsonRPC/Sisk.JsonRPC.csproj

+3-3
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
<PackageTags>http-server,http,web framework</PackageTags>
2525
<RepositoryType>git</RepositoryType>
2626

27-
<AssemblyVersion>1.3.2</AssemblyVersion>
28-
<FileVersion>1.3.2</FileVersion>
29-
<Version>1.3.2</Version>
27+
<AssemblyVersion>1.4.1</AssemblyVersion>
28+
<FileVersion>1.4.1</FileVersion>
29+
<Version>1.4.1</Version>
3030

3131
<NeutralLanguage>en</NeutralLanguage>
3232
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>

0 commit comments

Comments
 (0)