Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support CancellationToken in the last parameter of a receive method #232

Merged
merged 2 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/TypedSignalR.Client/SourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ private static void GenerateBinderSource(SourceProductionContext context, (Immut
{
var receiverTypes = ExtractReceiverTypesFromRegisterMethods(context, sourceSymbols, specialSymbols);

var template = new HubConnectionExtensionsBinderTemplate(receiverTypes);
var template = new HubConnectionExtensionsBinderTemplate(receiverTypes, specialSymbols);

var source = NormalizeNewLines(template.TransformText());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ namespace TypedSignalR.Client.Templates;
public sealed class HubConnectionExtensionsBinderTemplate
{
private readonly IReadOnlyList<TypeMetadata> _receiverTypes;
private readonly SpecialSymbols _specialSymbols;

public HubConnectionExtensionsBinderTemplate(IReadOnlyList<TypeMetadata> receiverTypes)
public HubConnectionExtensionsBinderTemplate(IReadOnlyList<TypeMetadata> receiverTypes, SpecialSymbols specialSymbols)
{
_receiverTypes = receiverTypes;
_specialSymbols = specialSymbols;
}

public string TransformText()
Expand Down Expand Up @@ -104,7 +106,7 @@ private string CreateRegistrationString(TypeMetadata receiverType)
private string CreateRegistrationStringCore(MethodMetadata method)
{
return $$"""
compositeDisposable.Add(global::Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.On(connection, nameof(receiver.{{method.MethodName}}), {{method.CreateParameterTypeArrayString()}}, HandlerConverter.Convert{{method.CreateTypeArgumentsStringForHandlerConverter()}}(receiver.{{method.MethodName}})));
compositeDisposable.Add(global::Microsoft.AspNetCore.SignalR.Client.HubConnectionExtensions.On(connection, nameof(receiver.{{method.MethodName}}), {{method.CreateParameterTypeArrayString(_specialSymbols)}}, HandlerConverter.Convert{{method.CreateTypeArgumentsStringForHandlerConverter(_specialSymbols)}}(receiver.{{method.MethodName}})));
""";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,25 @@ private static class HandlerConverter
""");
}

stringBuilder.AppendLine("""

public static global::System.Func<object?[], global::System.Threading.Tasks.Task> Convert(global::System.Func<global::System.Threading.CancellationToken, global::System.Threading.Tasks.Task> handler)
{
return args => handler(default);
}
""");

for (int i = 1; i <= 15; i++)
{
stringBuilder.AppendLine($$"""

public static global::System.Func<object?[], global::System.Threading.Tasks.Task> Convert<{{CreateTypeParametersString(i)}}>(global::System.Func<{{CreateTypeParametersString(i)}}, global::System.Threading.CancellationToken, global::System.Threading.Tasks.Task> handler)
{
return args => handler({{CreateHandlerArgumentsString(i)}}, default);
}
""");
}

stringBuilder.AppendLine("""

public static global::System.Func<object?[], global::System.Threading.Tasks.Task<TResult>> Convert<TResult>(global::System.Func<global::System.Threading.Tasks.Task<TResult>> handler)
Expand All @@ -272,6 +291,7 @@ private static class HandlerConverter
};
}
""");

for (int i = 1; i <= 16; i++)
{
stringBuilder.AppendLine($$"""
Expand All @@ -284,6 +304,33 @@ private static class HandlerConverter
return result;
};
}
""");
}

stringBuilder.AppendLine("""

public static global::System.Func<object?[], global::System.Threading.Tasks.Task<TResult>> Convert<TResult>(global::System.Func<global::System.Threading.CancellationToken, global::System.Threading.Tasks.Task<TResult>> handler)
{
return async args =>
{
var result = await handler(default).ConfigureAwait(false);
return result;
};
}
""");

for (int i = 1; i <= 15; i++)
{
stringBuilder.AppendLine($$"""

public static global::System.Func<object?[], global::System.Threading.Tasks.Task<TResult>> Convert<{{CreateTypeParametersString(i)}}, TResult>(global::System.Func<{{CreateTypeParametersString(i)}}, global::System.Threading.CancellationToken, global::System.Threading.Tasks.Task<TResult>> handler)
{
return async args =>
{
var result = await handler({{CreateHandlerArgumentsString(i)}}, default).ConfigureAwait(false);
return result;
};
}
""");
}
}
Expand Down
40 changes: 30 additions & 10 deletions src/TypedSignalR.Client/Templates/MethodMetadataExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,13 @@ public static string CreateArgumentsStringExceptCancellationToken(this MethodMet
return sb.ToString();
}

public static string CreateTypeArgumentsStringForHandlerConverter(this MethodMetadata methodMetadata)
public static string CreateTypeArgumentsStringForHandlerConverter(this MethodMetadata methodMetadata, SpecialSymbols specialSymbols)
{
if (methodMetadata.Parameters.Count == 0)
var parameters = methodMetadata.LastParameterEqual(specialSymbols.CancellationTokenSymbol)
? methodMetadata.Parameters.Take(methodMetadata.Parameters.Count - 1).ToArray()
: methodMetadata.Parameters;

if (parameters.Count == 0)
{
if (methodMetadata.IsGenericReturnType)
{
Expand All @@ -98,25 +102,25 @@ public static string CreateTypeArgumentsStringForHandlerConverter(this MethodMet
return string.Empty;
}

if (methodMetadata.Parameters.Count == 1)
if (parameters.Count == 1)
{
if (methodMetadata.IsGenericReturnType)
{
return $"<{methodMetadata.Parameters[0].TypeName}, {methodMetadata.GenericReturnTypeArgument}>";
return $"<{parameters[0].TypeName}, {methodMetadata.GenericReturnTypeArgument}>";
}

return $"<{methodMetadata.Parameters[0].TypeName}>";
return $"<{parameters[0].TypeName}>";
}

var sb = new StringBuilder();

sb.Append('<');
sb.Append(methodMetadata.Parameters[0].TypeName);
sb.Append(parameters[0].TypeName);

for (int i = 1; i < methodMetadata.Parameters.Count; i++)
for (int i = 1; i < parameters.Count; i++)
{
sb.Append(", ");
sb.Append(methodMetadata.Parameters[i].TypeName);
sb.Append(parameters[i].TypeName);
}

if (methodMetadata.IsGenericReturnType)
Expand All @@ -129,9 +133,10 @@ public static string CreateTypeArgumentsStringForHandlerConverter(this MethodMet
return sb.ToString();
}

public static string CreateParameterTypeArrayString(this MethodMetadata methodMetadata)
public static string CreateParameterTypeArrayString(this MethodMetadata methodMetadata, SpecialSymbols specialSymbols)
{
if (methodMetadata.Parameters.Count == 0)
if (methodMetadata.Parameters.Count == 0
|| (methodMetadata.Parameters.Count == 1 && methodMetadata.LastParameterEqual(specialSymbols.CancellationTokenSymbol)))
{
return "global::System.Type.EmptyTypes";
}
Expand All @@ -148,6 +153,13 @@ public static string CreateParameterTypeArrayString(this MethodMetadata methodMe

for (int i = 1; i < methodMetadata.Parameters.Count; i++)
{
if (IsLast(i, methodMetadata.Parameters.Count)
&& methodMetadata.LastParameterEqual(specialSymbols.CancellationTokenSymbol))
{
// break if the last parameter is CancellationToken.
break;
}

sb.Append($", typeof({methodMetadata.Parameters[i].FullyQualifiedTypeName})");
}

Expand Down Expand Up @@ -189,6 +201,14 @@ public static string CreateMethodString(this MethodMetadata methodMetadata, Spec
};
}

private static bool IsLast(int index, int length) => index == length - 1;

private static bool LastParameterEqual(this MethodMetadata methodMetadata, ITypeSymbol typeSymbol)
{
return methodMetadata.Parameters.Count > 0
&& SymbolEqualityComparer.Default.Equals(methodMetadata.Parameters[methodMetadata.Parameters.Count - 1].Type, typeSymbol);
}

private static HubMethodType GetHubMethodType(this MethodMetadata methodMetadata, SpecialSymbols specialSymbols)
{
var returnTypeSymbol = methodMetadata.ReturnTypeSymbol as INamedTypeSymbol;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using Microsoft.AspNetCore.SignalR;
using TypedSignalR.Client.Tests.Shared;

namespace TypedSignalR.Client.Tests.Server.Hubs;

public class ReceiverWithCancellationTokenTestHub : Hub<IReceiverWithCancellationToken>, IReceiverTestHub
{
private readonly string[] _message = new[] {
"b1f7cd73-13b8-49bd-9557-ffb38859d18b",
"3f5c3585-d01b-4f8f-8139-62a1241850e2",
"92021a22-5823-4501-8cbd-c20d4ca6e54c",
"5b134f73-2dc1-4271-8316-1a4250f42241",
"e73acd30-e034-4569-8f30-88ac34b99052",
"0d7531b5-0a36-4fe7-bbe5-8fee38c38c07",
"32915627-3df6-41dc-8d30-7c655c2f7e61",
"c875a6f9-9ddb-440b-a7e4-6e893f59ab9e",
};

private readonly string[] _guids = new[] {
"b2f626e5-b4d4-4713-891d-f6cb107e502e",
"22733524-2087-4701-a586-c6bf0ce36f74",
"b89324bf-daf2-422a-85f2-6843b9c09b6a",
"779769d1-0aee-4dba-82c7-9e1044836d75"
};

private readonly string[] _dateTimes = new[] {
"2017-04-17",
"2018-05-25",
"2019-03-31",
"2022-02-06",
};

private readonly ILogger<ReceiverWithCancellationTokenTestHub> _logger;

public ReceiverWithCancellationTokenTestHub(ILogger<ReceiverWithCancellationTokenTestHub> logger)
{
_logger = logger;
}

public async Task Start()
{
_logger.Log(LogLevel.Information, "ReceiverTestHub.Start");

for (int i = 0; i < 17; i++)
{
await Task.Delay(TimeSpan.FromMilliseconds(15));
await this.Clients.Caller.Notify(this.Context.ConnectionAborted);
}

for (int i = 0; i < _message.Length; i++)
{
await Task.Delay(TimeSpan.FromMilliseconds(15));
await this.Clients.Caller.ReceiveMessage(_message[i], i, this.Context.ConnectionAborted);
}

for (int i = 0; i < _guids.Length; i++)
{
await Task.Delay(TimeSpan.FromMilliseconds(15));
await this.Clients.Caller.ReceiveCustomMessage(new UserDefinedType() { Guid = Guid.Parse(_guids[i]), DateTime = DateTime.Parse(_dateTimes[i]) }, this.Context.ConnectionAborted);
}
}
}
1 change: 1 addition & 0 deletions tests/TypedSignalR.Client.Tests.Server/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
app.MapHub<UnaryHub>("/Hubs/UnaryHub");
app.MapHub<SideEffectHub>("/Hubs/SideEffectHub");
app.MapHub<ReceiverTestHub>("/Hubs/ReceiverTestHub");
app.MapHub<ReceiverWithCancellationTokenTestHub>("/Hubs/ReceiverWithCancellationTokenTestHub");
app.MapHub<StreamingHub>("/Hubs/StreamingHub");
app.MapHub<ClientResultsTestHub>("/Hubs/ClientResultsTestHub");
app.MapHub<InheritTestHub>("/Hubs/InheritTestHub");
Expand Down
8 changes: 8 additions & 0 deletions tests/TypedSignalR.Client.Tests.Shared/IReceiver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ public interface IReceiver
Task ReceiveCustomMessage(UserDefinedType userDefined);
}


public interface IReceiverWithCancellationToken
{
Task ReceiveMessage(string message, int value, CancellationToken cancellationToken);
Task Notify(CancellationToken cancellationToken);
Task ReceiveCustomMessage(UserDefinedType userDefined, CancellationToken cancellationToken);
}

public interface IReceiverTestHub
{
Task Start();
Expand Down
Loading