-
Couldn't load subscription status.
- Fork 47
Add a bidirectional chat sample to demonstrate SignalR Trigger #101
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
Conversation
|
|
||
| [FunctionAuthorize] | ||
| [FunctionName("broadcast")] | ||
| public static async Task Broadcast([SignalRTrigger(Hub, Category.Messages, "broadcast", "message")]InvocationContext invocationContext, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
broadcast [](start = 84, length = 9)
How about enum messages event as well. the functionality is limited to our supported APIs.
| { | ||
| private const string AdminKey = "admin"; | ||
|
|
||
| public override Task OnExecutingAsync(FunctionExecutingContext executingContext, CancellationToken cancellationToken) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we extract some common logic into a base class so that developer only needs to focus on the claim validation part.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's good. I will use another PR to do that.
|
|
||
| [FunctionAuthorize] | ||
| [FunctionName("broadcast")] | ||
| public static async Task Broadcast([SignalRTrigger(Hub, Category.Messages, "broadcast", "message")]InvocationContext invocationContext, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible to get method name from FunctionName attribute?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function name need to be globally unique, but method name just need to be unique in hub level
|
The way I see it, we need to get as close to possible to a Hub as we can on with the Azure functions infrastructure and see what that looks like in multiple languages. I have some ideas of what a nice node and C# experience looks like. |
|
@davidfowl I've just updated the sample and applied class base on it. Briefly, when Functions are defined in under a class that derived from [FunctionName(nameof(SendToConnection))]
public async Task SendToConnection([SignalRTrigger]InvocationContext invocationContext,
[SignalRParameter]string connectionId,
[SignalRParameter]string message)
{
await Clients.Client(connectionId).SendAsync(NewMessageTarget, new NewMessage(invocationContext, message));
}Considered the balance of convenience and correction, However, all these simplify can only be applied in C#. As the limitation of Function architecture, we can do less in other language. I will then open another PR to demonstrate trigger in nodejs. |
src/SignalRServiceExtension/TriggerBindings/SignalRTriggerBindingProvider.cs
Show resolved
Hide resolved
src/SignalRServiceExtension/TriggerBindings/SignalRTriggerBindingProvider.cs
Outdated
Show resolved
Hide resolved
samples/bidirectional-chat/csharp/Authorize/FunctionAuthorizeAttribute.cs
Outdated
Show resolved
Hide resolved
|
Can we get rid of [SignalRParameter] and move InvocationContext to a Property on the base class? |
|
@davidfowl Getting rid of |
|
They want to bring everything that isn’t already marked. Mark the things that are non signalr explicitly. |
| private const string NewConnectionTarget = "newConnection"; | ||
|
|
||
| [FunctionName("negotiate")] | ||
| public static SignalRConnectionInfo GetSignalRInfo( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be moved into ServerlessHub?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's good. But it needs some investigation. I will put it into another RP.
| } | ||
|
|
||
| [FunctionName(nameof(Disconnect))] | ||
| public void Disconnect([SignalRTrigger]InvocationContext invocationContext) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you call these methods explicitly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's your "explicitly" means?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can these calls come from outside the signalr service?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can the client trigger a call to disconnect and not be disconnected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, as long as the message sent to SignalR trigger can follow our serverless spec.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If using SignalR Service, it won't happen. Because all message from client's invocation will be in the messages category. And connected/disconnected are connections category
| /// </summary> | ||
| [AttributeUsage(AttributeTargets.Parameter)] | ||
| public class SignalRParameterAttribute : Attribute | ||
| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also allow specify parameter name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's useless. The parameter name is used to match the actual parameter name in method. It should be exactly the name it decorates. We should avoid user using like
void Function([SignalRTrigger]InvocationContext context, [SignalRParameter("arg1")]arg0, [SignalRParameter("arg0")]arg1)It just cause confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like in JSON serialization, you can use JsonProperty to give property a different name (for example a short name). But seems SignalR itself doesn't support this as well. So it should be ok to not support it for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then do we really need this? A parameter is by default recognized as a SignalR parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only in class base. It prepared for non-class based function. It's still better than using ParameterNames directly
| $"{nameof(SignalRTriggerAttribute)}.{nameof(SignalRTriggerAttribute.ParameterNames)} and {nameof(SignalRParameterAttribute)} can not be set in the same Function."); | ||
| } | ||
|
|
||
| parameterNames = parameterNamesFromAttribute.Count != 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should check parameterNames first? parametersNames should have higher priority than attributes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't want parametersNames and attribute are set together. So, there's no priority issue.
| data.messages.unshift(message); | ||
| }; | ||
| function onNewConnection(message) { | ||
| data.myConnectionId = message.ConnectionId; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to do a camel case conversion for class members? (ConnectionId -> connectionId) I think SignalR by default does this conversion.
|
|
||
| private bool IsLegalClassBasedParameter(ParameterInfo parameter) | ||
| { | ||
| // In class based model, we treat all the parameters as a legal parameter exception the cases below |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exception [](start = 86, length = 9)
typo: except
| { | ||
| /// <summary> | ||
| /// In class based model, mark the parameter explicitly not to be a SignalR parameter. | ||
| /// That means it won't be bind to a InvocationMessage argument. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
be bind [](start = 28, length = 7)
typo: bind or be bound
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
![]()
| } | ||
|
|
||
| [FunctionName(nameof(Disconnect))] | ||
| public void Disconnect([SignalRTrigger]InvocationContext invocationContext) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be disconnected?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use another PR to do that change including onconnected
| UserGroups = hubContext.UserGroups; | ||
| } | ||
|
|
||
| public IHubClients Clients { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about also add them to InvocationContext?
| } | ||
|
|
||
| [FunctionName(nameof(Connected))] | ||
| public async Task Connected([SignalRTrigger]InvocationContext invocationContext, ILogger logger) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you still specify hub name in SignalRTrigger?
Here is the C# chat sample to demonstrate new feature: SignalR Trigger
The trigger can collaborate with SignalR Service's new upstream feature smoothly to empower customer to handle messages from service in serverless mode.
Now, client can call
connection.send()orconnection.invoke()just like what they usually do in default mode.Also, in C#, Function can design there own attribute to implement Authorization. In the sample, a very simple attribute that only allow the admin to call broadcast is demonstrated.
It's not the final design, please give some suggestions. And I will add a JavaScript sample later.