Skip to content

Commit 0636f60

Browse files
authored
Azure Web PubSub service: MapWebPubSubHub extension with custom hub name (Azure#47022)
* MapWebPubSubHub extension with hub name * updates to Readme * adding mising api file from Export-API.ps1 * more tests * cleaned up code duplication * fixed assert
1 parent 3d20049 commit 0636f60

File tree

8 files changed

+118
-5
lines changed

8 files changed

+118
-5
lines changed

sdk/webpubsub/Microsoft.Azure.WebPubSub.AspNetCore/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ public void ConfigureServices(IServiceCollection services)
6161

6262
### Map `WebPubSubHub` to endpoint routing
6363

64+
The name of the hub has to match the class name e.g. `SampleHub`.
65+
6466
```C# Snippet:WebPubSubMapHub
6567
public void Configure(IApplicationBuilder app)
6668
{
@@ -71,6 +73,18 @@ public void Configure(IApplicationBuilder app)
7173
}
7274
```
7375

76+
Hub name can be overriden by using extension method.
77+
78+
```C# Snippet:WebPubSubMapHubCustom
79+
public void Configure(IApplicationBuilder app)
80+
{
81+
app.UseEndpoints(endpoint =>
82+
{
83+
endpoint.MapWebPubSubHub<SampleHub>("/eventhandler", "customHub");
84+
});
85+
}
86+
```
87+
7488
## Key concepts
7589

7690
For information about general Web PubSub concepts [Concepts in Azure Web PubSub](https://docs.microsoft.com/azure/azure-web-pubsub/key-concepts)

sdk/webpubsub/Microsoft.Azure.WebPubSub.AspNetCore/api/Microsoft.Azure.WebPubSub.AspNetCore.netcoreapp3.1.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ namespace Microsoft.AspNetCore.Builder
33
public static partial class WebPubSubEndpointRouteBuilderExtensions
44
{
55
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapWebPubSubHub<THub>(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string path) where THub : Microsoft.Azure.WebPubSub.AspNetCore.WebPubSubHub { throw null; }
6+
public static Microsoft.AspNetCore.Builder.IEndpointConventionBuilder MapWebPubSubHub<THub>(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string path, string hubName) where THub : Microsoft.Azure.WebPubSub.AspNetCore.WebPubSubHub { throw null; }
67
}
78
}
89
namespace Microsoft.Azure.WebPubSub.AspNetCore

sdk/webpubsub/Microsoft.Azure.WebPubSub.AspNetCore/src/Extensions/WebPubSubEndpointRouteBuilderExtensions.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,43 @@ public static class WebPubSubEndpointRouteBuilderExtensions
1616
/// <summary>
1717
/// Maps the <see cref="WebPubSubHub"/> to the path <paramref name="path"/>.
1818
/// </summary>
19-
/// <typeparam name="THub">User implemented <see cref="WebPubSubHub"/>.</typeparam>
19+
/// <typeparam name="THub">User implemented <see cref="WebPubSubHub"/>.
20+
/// Name of the class has to match the name of the hub in the Azure portal.</typeparam>
2021
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/>.</param>
2122
/// <param name="path">The path to map the <see cref="WebPubSubHub"/>.</param>
2223
/// <returns>The <see cref="IEndpointConventionBuilder"/>.</returns>
2324
public static IEndpointConventionBuilder MapWebPubSubHub<THub>(
2425
this IEndpointRouteBuilder endpoints,
2526
string path) where THub: WebPubSubHub
27+
=> MapWebPubSubHub<THub>(endpoints, path, typeof(THub).Name);
28+
29+
/// <summary>
30+
/// Maps the <see cref="WebPubSubHub"/> to the path "/client" with the specified hub name.
31+
/// </summary>
32+
/// <typeparam name="THub">User implemented <see cref="WebPubSubHub"/>.</typeparam>
33+
/// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/>.</param>
34+
/// <param name="path">The path to map the <see cref="WebPubSubHub"/>.</param>
35+
/// <param name="hubName">The name of the hub to connect to.</param>
36+
/// <returns>The <see cref="IEndpointConventionBuilder"/>.</returns>
37+
public static IEndpointConventionBuilder MapWebPubSubHub<THub>(
38+
this IEndpointRouteBuilder endpoints, string path,
39+
string hubName) where THub : WebPubSubHub
2640
{
2741
if (endpoints == null)
2842
{
2943
throw new ArgumentNullException(nameof(endpoints));
3044
}
45+
if (string.IsNullOrEmpty(hubName))
46+
{
47+
throw new ArgumentNullException(nameof(hubName));
48+
}
3149

3250
var marker = endpoints.ServiceProvider.GetService<WebPubSubMarkerService>() ?? throw new InvalidOperationException(
3351
"Unable to find the required services. Please add all the required services by calling " +
3452
"'IServiceCollection.AddWebPubSub' inside the call to 'ConfigureServices(...)' in the application startup code.");
3553

3654
var adaptor = endpoints.ServiceProvider.GetService<ServiceRequestHandlerAdapter>();
37-
adaptor.RegisterHub<THub>();
55+
adaptor.RegisterHub<THub>(hubName);
3856

3957
var app = endpoints.CreateApplicationBuilder();
4058
app.UseMiddleware<WebPubSubMiddleware>();

sdk/webpubsub/Microsoft.Azure.WebPubSub.AspNetCore/src/Internal/ServiceRequestHandlerAdapter.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ public void RegisterHub<THub>() where THub : WebPubSubHub
3636
RegisterHub(hub.GetType().Name, hub);
3737
}
3838

39+
public void RegisterHub<THub>(string hubName) where THub : WebPubSubHub
40+
{
41+
var hub = Create<THub>();
42+
RegisterHub(hubName, hub);
43+
}
44+
3945
// For test only
4046
internal void RegisterHub(string hubName, WebPubSubHub hub)
4147
{

sdk/webpubsub/Microsoft.Azure.WebPubSub.AspNetCore/tests/AddWebPubSubTests.cs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License.
33

44
#if NETCOREAPP
5+
using Microsoft.AspNetCore.Builder;
56
using Microsoft.Extensions.DependencyInjection;
67
using Microsoft.Extensions.Options;
78
using NUnit.Framework;
@@ -59,6 +60,64 @@ public void TestWebPubSubConfigureEmptyOptionsAndAddHubThrows()
5960
Assert.Throws<ArgumentException>(() => clientFactory.Create<TestHub>());
6061
}
6162

63+
[Test]
64+
public void TestMapWebPubSubHubConfigureNormal()
65+
{
66+
var testHost = "webpubsub.azure.net";
67+
WebApplicationBuilder builder = WebApplication.CreateBuilder();
68+
builder.Services
69+
.AddWebPubSub(o => o.ServiceEndpoint = new WebPubSubServiceEndpoint($"Endpoint=https://{testHost};AccessKey=7aab239577fd4f24bc919802fb629f5f;Version=1.0;"));
70+
71+
using var app = builder.Build();
72+
app.MapWebPubSubHub<TestHub>("/testhub");
73+
74+
var validator = app.Services.GetRequiredService<RequestValidator>();
75+
76+
Assert.NotNull(validator);
77+
Assert.True(validator.IsValidOrigin(new List<string> { testHost }));
78+
}
79+
80+
[Test]
81+
public void TestMapWebPubSubHubConfigureCustomHub()
82+
{
83+
var testHost = "webpubsub.azure.net";
84+
var customHub = "customhub";
85+
WebApplicationBuilder builder = WebApplication.CreateBuilder();
86+
builder.Services
87+
.AddWebPubSub(o => o.ServiceEndpoint = new WebPubSubServiceEndpoint($"Endpoint=https://{testHost};AccessKey=7aab239577fd4f24bc919802fb629f5f;Version=1.0;"));
88+
89+
using var app = builder.Build();
90+
app.MapWebPubSubHub<TestHub>("/testhub", customHub);
91+
92+
var validator = app.Services.GetRequiredService<RequestValidator>();
93+
var adaptor = app.Services.GetRequiredService<ServiceRequestHandlerAdapter>();
94+
95+
Assert.NotNull(validator);
96+
Assert.NotNull(adaptor);
97+
Assert.True(validator.IsValidOrigin(new List<string> { testHost }));
98+
Assert.NotNull(adaptor.GetHub(customHub), "Custom hub should be registered");
99+
}
100+
101+
[Test]
102+
public void TestMapWebPubSubHubConfigureHubByTypeName()
103+
{
104+
var testHost = "webpubsub.azure.net";
105+
WebApplicationBuilder builder = WebApplication.CreateBuilder();
106+
builder.Services
107+
.AddWebPubSub(o => o.ServiceEndpoint = new WebPubSubServiceEndpoint($"Endpoint=https://{testHost};AccessKey=7aab239577fd4f24bc919802fb629f5f;Version=1.0;"));
108+
109+
using var app = builder.Build();
110+
app.MapWebPubSubHub<TestHub>("/testhub");
111+
112+
var validator = app.Services.GetRequiredService<RequestValidator>();
113+
var adaptor = app.Services.GetRequiredService<ServiceRequestHandlerAdapter>();
114+
115+
Assert.NotNull(validator);
116+
Assert.NotNull(adaptor);
117+
Assert.True(validator.IsValidOrigin(new List<string> { testHost }));
118+
Assert.NotNull(adaptor.GetHub(nameof(TestHub)), "Hub with the name that matches the class name should be registered");
119+
}
120+
62121
private sealed class TestHub : WebPubSubHub
63122
{ }
64123

sdk/webpubsub/Microsoft.Azure.WebPubSub.AspNetCore/tests/Microsoft.Azure.WebPubSub.AspNetCore.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
</ItemGroup>
1919

2020
<ItemGroup>
21-
<PackageReference Include="Azure.Identity"/>
21+
<PackageReference Include="Azure.Identity" />
2222
<PackageReference Include="Microsoft.NET.Test.Sdk" />
2323
<PackageReference Include="Moq" />
2424
<PackageReference Include="NUnit" />

sdk/webpubsub/Microsoft.Azure.WebPubSub.AspNetCore/tests/Samples/WebPubSubSampleCreateWithAzureIdentity.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ public void ConfigureServices(IServiceCollection services)
2626
}
2727
#endregion
2828

29+
#region Snippet:WebPubSubMapHubCustom
2930
public void Configure(IApplicationBuilder app)
3031
{
3132
app.UseEndpoints(endpoint =>
3233
{
33-
endpoint.MapWebPubSubHub<SampleHub>("/eventhandler");
34+
endpoint.MapWebPubSubHub<SampleHub>("/eventhandler", "customHub");
3435
});
3536
}
37+
#endregion
3638

3739
private sealed class SampleHub : WebPubSubHub
3840
{

sdk/webpubsub/Microsoft.Azure.WebPubSub.AspNetCore/tests/ServiceRequestHandlerTests.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
using System.IO;
88
using System.Linq;
99
using System.Net;
10-
using System.Security.Authentication;
1110
using System.Text;
1211
using System.Text.Json;
1312
using System.Threading;
@@ -311,6 +310,20 @@ public async Task TestWrongTypeReturns()
311310
Assert.AreEqual("Invalid user", response);
312311
}
313312

313+
[Test]
314+
public async Task TestHubBaseReturnsWhenCustomHubName()
315+
{
316+
var hubName = "customHub";
317+
_adaptor.RegisterHub<TestDefaultHub>(hubName);
318+
var connectBody = "{\"claims\":{\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier\":[\"ddd\"],\"nbf\":[\"1629183374\"],\"exp\":[\"1629186974\"],\"iat\":[\"1629183374\"],\"aud\":[\"http://localhost:8080/client/hubs/chat\"],\"sub\":[\"ddd\"]},\"query\":{\"access_token\":[\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkZGQiLCJuYmYiOjE2MjkxODMzNzQsImV4cCI6MTYyOTE4Njk3NCwiaWF0IjoxNjI5MTgzMzc0LCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvY2xpZW50L2h1YnMvY2hhdCJ9.tqD8ykjv5NmYw6gzLKglUAv-c-AVWu-KNZOptRKkgMM\"]},\"subprotocols\":[\"protocol1\", \"protocol2\"],\"clientCertificates\":[],\"headers\":{}}";
319+
var context = PrepareHttpContext(httpMethod: HttpMethods.Post, eventName: "connect", body: connectBody, hub: hubName);
320+
321+
await _adaptor.HandleRequest(context);
322+
323+
Assert.AreEqual(StatusCodes.Status200OK, context.Response.StatusCode);
324+
Assert.Null(context.Response.ContentLength);
325+
}
326+
314327
private static HttpContext PrepareHttpContext(
315328
WebPubSubEventType type = WebPubSubEventType.System,
316329
string eventName = "connect",

0 commit comments

Comments
 (0)