Skip to content

Commit 87ebeb4

Browse files
committed
Deduplicate SignalR connection bar per hub in plugin
Ensure the SignalR connection bar is rendered only once per hub, even when multiple tags map to the same hub path. Track the primary tag for each hub and render the connection bar only for that tag. Add integration test to verify deduplication logic in the plugin JavaScript.
1 parent acebc3d commit 87ebeb4

2 files changed

Lines changed: 40 additions & 0 deletions

File tree

src/SignalR.OpenApi.SwaggerUi/Resources/signalr-openapi-plugin.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,10 @@ var SignalROpenApiPlugin = function (system) {
441441
// operations in the spec. Each operation's tags are associated with
442442
// the hub path from its x-signalr extension. This handles custom
443443
// [Tags] attributes where the tag name differs from the hub name.
444+
// Also tracks the primary (first-seen) tag for each hub path so the
445+
// connection bar is rendered only once per hub.
444446
var _cachedTagHubMap = null;
447+
var _cachedHubPrimaryTag = null;
445448
var _cachedTagHubMapVersion = null;
446449

447450
var _getTagHubMap = function () {
@@ -451,6 +454,7 @@ var SignalROpenApiPlugin = function (system) {
451454
}
452455

453456
var tagMap = {};
457+
var hubPrimaryTag = {};
454458
var pathsIm = specIm.get("paths");
455459
if (pathsIm) {
456460
pathsIm.keySeq().forEach(function (path) {
@@ -479,6 +483,9 @@ var SignalROpenApiPlugin = function (system) {
479483
var tagStr = typeof tag === "string" ? tag : (tag.get ? tag.get("name") || tag : tag);
480484
if (typeof tagStr === "string" && !tagMap[tagStr]) {
481485
tagMap[tagStr] = hubPath;
486+
if (!hubPrimaryTag[hubPath]) {
487+
hubPrimaryTag[hubPath] = tagStr;
488+
}
482489
}
483490
});
484491
}
@@ -488,6 +495,7 @@ var SignalROpenApiPlugin = function (system) {
488495

489496
_cachedTagHubMapVersion = specIm;
490497
_cachedTagHubMap = tagMap;
498+
_cachedHubPrimaryTag = hubPrimaryTag;
491499
return tagMap;
492500
};
493501

@@ -499,6 +507,14 @@ var SignalROpenApiPlugin = function (system) {
499507
return tagMap[tagName] || null;
500508
};
501509

510+
// Check if a tag is the primary (first-seen) tag for its hub.
511+
// Used to render the connection bar only once per hub when
512+
// multiple tags map to the same hub path.
513+
var _isPrimaryTagForHub = function (tagName, hubPath) {
514+
_getTagHubMap();
515+
return _cachedHubPrimaryTag && _cachedHubPrimaryTag[hubPath] === tagName;
516+
};
517+
502518
// Parse request body from the SwaggerUI OAS3 state.
503519
// The executeRequest wrapper intercepts before the original action reads
504520
// the request body, so we must read it from the OAS3 selectors directly.
@@ -991,6 +1007,12 @@ var SignalROpenApiPlugin = function (system) {
9911007
return result;
9921008
}
9931009

1010+
// Only render one connection bar per hub. When multiple tags
1011+
// map to the same hub, show the bar on the primary tag only.
1012+
if (!_isPrimaryTagForHub(tagName, hubPath)) {
1013+
return result;
1014+
}
1015+
9941016
return React.createElement("div", null,
9951017
React.createElement(SignalRHubConnectionBar, { hubPath: hubPath }),
9961018
result

test/SignalR.OpenApi.Tests/SwaggerUiIntegrationTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,24 @@ public async Task PluginJs_ContainsTagHubMapFunction()
548548
Assert.IsTrue(content.Contains("_getTagHubMap"), "Plugin JS should contain _getTagHubMap function for custom [Tags] support");
549549
}
550550

551+
/// <summary>
552+
/// Verifies that the plugin JS deduplicates connection bars per hub
553+
/// by only rendering on the primary tag via _isPrimaryTagForHub.
554+
/// </summary>
555+
/// <returns>A <see cref="Task"/> representing the asynchronous test.</returns>
556+
[TestMethod]
557+
public async Task PluginJs_ContainsPrimaryTagDeduplication()
558+
{
559+
using var host = await CreateTestHost();
560+
using var client = host.GetTestClient();
561+
562+
using var response = await client.GetAsync("/signalr-swagger/_resources/signalr-openapi-plugin.js");
563+
var content = await response.Content.ReadAsStringAsync();
564+
565+
Assert.IsTrue(content.Contains("_isPrimaryTagForHub"), "Plugin JS should contain _isPrimaryTagForHub for connection bar deduplication");
566+
Assert.IsTrue(content.Contains("_cachedHubPrimaryTag"), "Plugin JS should cache the primary tag per hub path");
567+
}
568+
551569
/// <summary>
552570
/// Verifies that the CSS contains connection bar styles.
553571
/// </summary>

0 commit comments

Comments
 (0)