Skip to content

Commit df89121

Browse files
committed
Improve tag handling for client events with custom [Tags]
Previously, the default "{HubName} Events" tag was always added for client events, even when all events had custom [Tags] attributes. Now, only custom tags are added in such cases, and the default tag is used only if no custom tags are present. Added a test and supporting hub/interface to verify that the default tag is omitted when all client events have custom tags.
1 parent a1e27af commit df89121

4 files changed

Lines changed: 84 additions & 3 deletions

File tree

src/SignalR.OpenApi/Generation/SignalROpenApiDocumentGenerator.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -449,10 +449,25 @@ private void AddDocumentTags(OpenApiDocument document, IReadOnlyList<SignalRHubI
449449

450450
foreach (var clientEvent in hub.ClientEvents)
451451
{
452-
var eventTag = $"{hub.Name} Events";
453-
if (tagSet.Add(eventTag))
452+
if (clientEvent.Tags.Count > 0)
454453
{
455-
tagNames.Add(eventTag);
454+
// Client event has custom [Tags] — register those tags.
455+
foreach (var tag in clientEvent.Tags)
456+
{
457+
if (tagSet.Add(tag))
458+
{
459+
tagNames.Add(tag);
460+
}
461+
}
462+
}
463+
else
464+
{
465+
// No custom tags — use the default "{HubName} Events" tag.
466+
var eventTag = $"{hub.Name} Events";
467+
if (tagSet.Add(eventTag))
468+
{
469+
tagNames.Add(eventTag);
470+
}
456471
}
457472
}
458473
}

test/SignalR.OpenApi.Tests/SignalROpenApiDocumentGeneratorTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,6 +1260,24 @@ public void GenerateDocument_ClientEventWithoutTagsAttribute_UsesDefaultTag()
12601260
CollectionAssert.Contains(tagNames, "TypedChat Events");
12611261
}
12621262

1263+
/// <summary>
1264+
/// Verifies that the default "{HubName} Events" tag is not added to document tags
1265+
/// when all client events have custom [Tags] attributes.
1266+
/// </summary>
1267+
[TestMethod]
1268+
public void GenerateDocument_AllClientEventsHaveCustomTags_OmitsDefaultEventsTag()
1269+
{
1270+
var (discoverer, generator) = CreateServices();
1271+
var hubs = discoverer.DiscoverHubs();
1272+
var doc = generator.GenerateDocument(hubs);
1273+
1274+
Assert.IsNotNull(doc.Tags);
1275+
var tagNames = doc.Tags.Select(t => t.Name).ToList();
1276+
1277+
CollectionAssert.DoesNotContain(tagNames, "AllTaggedEvents Events");
1278+
CollectionAssert.Contains(tagNames, "Notifications");
1279+
}
1280+
12631281
/// <summary>
12641282
/// Verifies that document tags are deduplicated when multiple methods share the same tag.
12651283
/// </summary>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) SignalR.OpenApi Contributors. Licensed under the MIT License.
2+
3+
using Microsoft.AspNetCore.SignalR;
4+
5+
namespace SignalR.OpenApi.Tests.TestHubs;
6+
7+
/// <summary>
8+
/// A typed hub whose client events all have custom <see cref="Microsoft.AspNetCore.Http.TagsAttribute"/>.
9+
/// </summary>
10+
public class AllTaggedEventsHub : Hub<IAllTaggedClient>
11+
{
12+
/// <summary>
13+
/// Sends a ping to all clients.
14+
/// </summary>
15+
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
16+
public async Task Ping()
17+
{
18+
await this.Clients.All.UserConnected("system");
19+
}
20+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) SignalR.OpenApi Contributors. Licensed under the MIT License.
2+
3+
using Microsoft.AspNetCore.Http;
4+
5+
namespace SignalR.OpenApi.Tests.TestHubs;
6+
7+
/// <summary>
8+
/// Client interface where all events have custom <see cref="TagsAttribute"/>.
9+
/// Used to verify the default "{HubName} Events" tag is omitted.
10+
/// </summary>
11+
public interface IAllTaggedClient
12+
{
13+
/// <summary>
14+
/// Notifies that a user connected.
15+
/// </summary>
16+
/// <param name="user">The user who connected.</param>
17+
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
18+
[Tags("Notifications")]
19+
Task UserConnected(string user);
20+
21+
/// <summary>
22+
/// Notifies that a user disconnected.
23+
/// </summary>
24+
/// <param name="user">The user who disconnected.</param>
25+
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
26+
[Tags("Notifications")]
27+
Task UserDisconnected(string user);
28+
}

0 commit comments

Comments
 (0)