fix(#3828): dedup duplicate publications and subscriptions by (channel, action)#4137
Conversation
…l, action) Previously the producer registry and SupplementalPublications could declare the same topic and end up in the document as two send operations (send_foo + send_foo_2). Subscriptions had the same flaw on the receive side. Assembly scanning was the only path that correctly skipped duplicates. Move the dedup into ProcessSourceAsync so all three sources go through the same gate: if (channel, action) is already covered, drop the duplicate. First source wins (producer registry beats SupplementalPublications; DI publications beat assembly scanning). Same channel with a different action (e.g. a subscription and a publication on one routing key) still produces both operations as intended. GetUniqueOperationId and the OperationKey record become dead code with true dedup in place — both removed. The redundant CoveredChannelActions check in AddFromAssemblyScanningAsync is also dropped. Tests: - It_Should_Dedup_Duplicate_Subscriptions_On_The_Same_Topic (was It_Should_Produce_One_Channel_And_Two_Operations_For_Duplicate_Subscriptions, which asserted the buggy behaviour). - It_Should_Dedup_Producer_Registry_Over_Supplemental_Publications now asserts one operation rather than two. - It_Should_Collapse_Duplicate_DI_And_Scanned_Publications_To_One_Operation replaces the "_Uniqueified_Operation_Id" test that codified the old suffix behaviour. 46/46 AsyncAPI tests pass on net9.0 and net10.0. Co-Authored-By: Claude (claude-opus-4-7) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Fixes a bug where duplicate publications/subscriptions on the same (channel, action) produced misleading "_2"-suffixed operations in the generated AsyncAPI document. Deduplication is now centralized in ProcessSourceAsync so producer-registry, supplemental publications, and assembly scanning all go through the same first-wins gate.
Changes:
- Move (channel, action) dedup into
ProcessSourceAsync; remove now-deadGetUniqueOperationIdandOperationKey. - Drop the redundant early-skip in
AddFromAssemblyScanningAsync. - Update tests to assert single operations for duplicate subscriptions, producer-registry-vs-supplemental, and DI-vs-scanned cases.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/Paramore.Brighter.AsyncAPI/AsyncApiDocumentGenerator.cs | Centralizes dedup in ProcessSourceAsync, removes unique-id suffix logic and OperationKey. |
| tests/Paramore.Brighter.AsyncAPI.Tests/When_Deduplicating_Channels_And_Messages.cs | Updates duplicate-subscription and producer-registry-vs-supplemental tests to expect a single operation. |
| tests/Paramore.Brighter.AsyncAPI.Tests/When_Generating_Document_From_Assembly_Scanning.cs | Renames and updates the DI-vs-scanned test to expect a single collapsed operation. |
Review: PR #4137 — dedup duplicate publications and subscriptions by (channel, action)Thanks for the focused fix. The diagnosis is right: prior to this PR, A few items to consider: 🐛 Formatting regression in
|
|
|
Have done. I am alright with merging with some flaky tests, it's just a release we need to review/fix them for |
Copilot review catch: the indentation on `EmptyObjectSchema` was lost when `GetUniqueOperationId` was removed in the parent commit. Re-indent to 8 spaces to match the other private methods in the class. Co-Authored-By: Claude (claude-opus-4-7) <noreply@anthropic.com>
There was a problem hiding this comment.
Gates Passed
4 Quality Gates Passed
See analysis details in CodeScene
Quality Gate Profile: Clean Code Collective
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.
Review: PR #4137 — dedup duplicate publications and subscriptions by (channel, action)The diagnosis is correct and the fix is well-scoped. The pre-existing Things I like
Things worth considering1. Test gap: mismatched Without this, a future refactor that re-orders the dedup or changes the wins-policy can pass all current tests while silently swapping which schema appears in the document. 2. 3. The first-wins precedence is now an implicit public-facing contract 4. Minor: stray double blank line at (Note: I checked the indentation of VerdictSolid, targeted bug fix. The behaviour change is in the right direction and the test updates correctly capture the new semantics. The mixed- Review by Claude (claude-opus-4-7) — automated PR review |
|
I've just resolved the outstanding Copilot comment so I'll keep and eye and merge when it's ready. |
Previously the producer registry and SupplementalPublications could declare the same topic and end up in the document as two send operations (send_foo + send_foo_2). Subscriptions had the same flaw on the receive side. Assembly scanning was the only path that correctly skipped duplicates.
Move the dedup into ProcessSourceAsync so all three sources go through the same gate: if (channel, action) is already covered, drop the duplicate. First source wins (producer registry beats SupplementalPublications; DI publications beat assembly scanning). Same channel with a different action (e.g. a subscription and a publication on one routing key) still produces both operations as intended.
GetUniqueOperationId and the OperationKey record become dead code with true dedup in place — both removed. The redundant CoveredChannelActions check in AddFromAssemblyScanningAsync is also dropped.
Tests:
46/46 AsyncAPI tests pass on net9.0 and net10.0.
Description
Related Issues
Type of Change
Checklist
Additional Notes