Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@Ren0503
Copy link
Contributor

@Ren0503 Ren0503 commented Sep 17, 2025

No description provided.

@Ren0503 Ren0503 added this to the Microservices v1.2.0 milestone Sep 17, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 17, 2025

Summary by CodeRabbit

  • New Features

    • Transport-aware microservices: specify one or more transports during handler and store setup for cross-transport subscriptions.
    • Enhanced provider behavior for dynamic, multi-transport registration.
  • Refactor

    • Simplified handler construction by removing the options object.
    • Store registration now accepts optional transports for targeted setup.
  • Tests

    • Updated test suite to reflect the simplified setup and transport-aware behavior.
  • Chores

    • Added a Makefile target to run tests with coverage via a single command.

Walkthrough

Updated the microservices handler API to remove ProviderOptions and add variadic transport support. Handler registration now resolves STORE and transport-specific stores via module refs and registers subscribers directly. Store registration optionally exports transport-specific providers. Tests updated to call the new constructor.

Changes

Cohort / File(s) Summary of change
Handler core & behavior
microservices/handler.go
NewHandler now accepts optional transports (transports ...string) and returns *Handler embedding core.DynamicProvider. Handler records transport providers and, in OnResponse/OnEvent, resolves STORE plus transport refs via module.Ref and appends SubscribeHandler entries into each Store's Subscribers. Local middlewares are cleared after registration.
Store registration & helpers
microservices/store.go
Register now func Register(transports ...string) core.Modules and can export transport-specific STORE providers. Added helper ToTransport(transport string) core.Provide to derive transport provider names.
Tests & examples updated to new API
microservices/*_test.go, microservices/tcp/tenant_test.go
Updated tests to use NewHandler(module) or NewHandler(module, microservices.TCP) where appropriate; replaced prior NewHandler(..., core.ProviderOptions{}) usages. Register() calls updated to Register(microservices.TCP) in TCP tests.
Build tooling
microservices/Makefile
Added test phony target that runs go test -cover ./....

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev as Developer
  participant H as Handler
  participant M as Module
  participant S as Store(STORE)
  participant T1 as Store(transport-A)
  participant T2 as Store(transport-B)

  Dev->>H: NewHandler(module, "transport-A", "transport-B")
  Note right of H #d6f5d6: Handler records transports via ToTransport

  Dev->>H: OnResponse(name, factory) / OnEvent(...)
  H->>M: Ref(STORE)
  alt STORE resolved
    H->>S: add Subscribers[name] = {factory, middlewares}
  else STORE missing
    Note right of H: skip STORE
  end
  loop for each transport
    H->>M: Ref(ToTransport(transport))
    alt transport store resolved
      H->>T1: add Subscribers[name] = {factory, middlewares}
      H->>T2: add Subscribers[name] = {factory, middlewares}
    else not found
      Note right of H: skip transport
    end
  end
  Note over H: clear local middlewares
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I twitched my ears at changing lanes,
From options gone to transport trains;
I hop between the stores in line,
And pin my subs where refs align.
Carrot-typed, I bind and cheer—new routes for messages draw near! 🥕🚚

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description Check ❓ Inconclusive No pull request description was provided, so the intent, rationale, and migration implications are not documented and reviewers cannot rely on the PR body to understand scope or upgrade impact. The raw_summary shows substantive public API changes (NewHandler and Register signatures, Handler type/behavior changes, and test updates), making the absence of a description material to the review. Request the author add a brief PR description summarizing purpose and high-level changes (notably the NewHandler/Register signature changes, multi-transport behavior, Handler structural changes), any migration or compatibility notes for callers, and a short summary of test updates so reviewers can evaluate impact.
✅ Passed checks (1 passed)
Check name Status Explanation
Title Check ✅ Passed The title "feat(microservices): add multi listen handler" concisely and accurately describes the primary change: introducing multi-listen / multi-transport support in the microservices handler. The diffs show NewHandler and Register signatures were changed to accept transports and the handler now registers subscribers across multiple Store transports, so the title aligns with the main intent and is clear for a teammate scanning history.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/ren/330-add-multi-listen-handler

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Ren0503 Ren0503 linked an issue Sep 17, 2025 that may be closed by this pull request
@codecov
Copy link

codecov bot commented Sep 17, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (7)
microservices/tcp/tenant_test.go (1)

156-172: Avoid unsafe type assertions on ctx.Get("tenant").

Direct .(string) will panic if the header is absent. Guard it to keep the test resilient.

Apply:

-      tenantID := ctx.Get("tenant").(string)
+      tenantID, _ := ctx.Get("tenant").(string)
+      if tenantID == "" {
+        return nil
+      }

And similarly in the event handler:

-      tenantID := ctx.Get("tenant").(string)
+      tenantID, _ := ctx.Get("tenant").(string)
+      if tenantID == "" {
+        return nil
+      }

Also applies to: 174-188

microservices/client_test.go (1)

67-67: Constructor update is correct.

Minor consistency nit: across tests, some use a local TCP_SERVICE and others microservices.TCP. Consider standardizing to one to reduce confusion.

microservices/store.go (1)

44-46: Make transport provider names unambiguous; handle empty input.
Without a delimiter, names can collide (e.g., "STOREab" vs "STOREa"+"b"). Also treat empty transport as base STORE.

Apply:

-func ToTransport(transport string) core.Provide {
-    return STORE + core.Provide(transport)
-}
+func ToTransport(transport string) core.Provide {
+    if transport == "" {
+        return STORE
+    }
+    return core.Provide(string(STORE) + ":" + transport)
+}
microservices/handler.go (4)

7-14: Skip empty transports and deduplicate provider refs.
Prevents redundant refs and accidental base STORE duplication.

Apply:

 func NewHandler(module core.Module, transports ...string) *Handler {
     provider := &Handler{}
     provider.module = module
-
-    for _, transport := range transports {
-        provider.transports = append(provider.transports, ToTransport(transport))
-    }
+    seen := map[core.Provide]struct{}{}
+    for _, transport := range transports {
+        if transport == "" {
+            continue
+        }
+        ref := ToTransport(transport)
+        if _, ok := seen[ref]; ok {
+            continue
+        }
+        seen[ref] = struct{}{}
+        provider.transports = append(provider.transports, ref)
+    }
 
     return provider
 }

38-46: Init slice is unnecessary; append handles nil.
Small cleanup.

Apply:

-        if store.Subscribers[RPC] == nil {
-            store.Subscribers[RPC] = []*SubscribeHandler{}
-        }
         store.Subscribers[RPC] = append(store.Subscribers[RPC], &SubscribeHandler{
             Name:        name,
             Factory:     fnc,
             Middlewares: append(h.globalMiddlewares, h.middlewares...),
         })

63-71: Unnecessary pre-init; rely on append to nil slice.
Keeps code tight and consistent with OnResponse fix.

Apply:

-        if store.Subscribers[PubSub] == nil {
-            store.Subscribers[PubSub] = []*SubscribeHandler{}
-        }
         store.Subscribers[PubSub] = append(store.Subscribers[PubSub], &SubscribeHandler{
             Name:        name,
             Factory:     fnc,
             Middlewares: append(h.globalMiddlewares, h.middlewares...),
         })

26-29: DRY: factor shared registration logic.
Both methods differ only by EventType. Extract helper: register(event EventType, name string, fnc FactoryFunc).

Happy to provide a small refactor patch if you want it in this PR.

Also applies to: 51-54

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d773ccc and 93b2438.

📒 Files selected for processing (9)
  • microservices/client_test.go (1 hunks)
  • microservices/exception_test.go (1 hunks)
  • microservices/guard_test.go (1 hunks)
  • microservices/handler.go (2 hunks)
  • microservices/handler_test.go (1 hunks)
  • microservices/middleware_test.go (1 hunks)
  • microservices/pipe_test.go (1 hunks)
  • microservices/store.go (2 hunks)
  • microservices/tcp/tenant_test.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (9)
microservices/middleware_test.go (1)
microservices/handler.go (1)
  • NewHandler (7-16)
microservices/tcp/tenant_test.go (1)
microservices/handler.go (1)
  • NewHandler (7-16)
microservices/client_test.go (1)
microservices/handler.go (1)
  • NewHandler (7-16)
microservices/pipe_test.go (1)
microservices/handler.go (1)
  • NewHandler (7-16)
microservices/exception_test.go (1)
microservices/handler.go (1)
  • NewHandler (7-16)
microservices/handler_test.go (1)
microservices/handler.go (1)
  • NewHandler (7-16)
microservices/guard_test.go (1)
microservices/handler.go (1)
  • NewHandler (7-16)
microservices/store.go (4)
core/module.go (1)
  • Modules (60-60)
core/provider.go (2)
  • ProviderOptions (103-117)
  • Provide (10-10)
microservices/message.go (1)
  • EventType (8-8)
microservices/subscriber.go (1)
  • SubscribeHandler (9-13)
microservices/handler.go (4)
microservices/store.go (3)
  • ToTransport (44-46)
  • STORE (5-5)
  • Store (7-9)
core/provider.go (2)
  • Provide (10-10)
  • Factory (21-21)
microservices/subscriber.go (3)
  • FactoryFunc (15-15)
  • SubscribeHandler (9-13)
  • Factory (5-7)
microservices/message.go (2)
  • RPC (11-11)
  • PubSub (12-12)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)
🔇 Additional comments (6)
microservices/exception_test.go (1)

18-18: API migration to NewHandler(module) looks correct.

Constructor usage matches the new signature; no transports provided is fine for default store registration.

microservices/guard_test.go (1)

22-22: Constructor update LGTM.

Matches the variadic-transport API; behavior of Guard/OnResponse remains unchanged.

microservices/middleware_test.go (1)

27-27: NewHandler(module) migration is correct.

No issues spotted with middleware chaining after the change.

microservices/tcp/tenant_test.go (1)

152-152: Constructor migration LGTM; verify transport expectations if you plan multi-listen.

If this handler should attach to transport-specific stores as well, ensure those stores are exported via microservices.Register(...transports) in this module and pass the matching transport names to NewHandler(module, ...).

microservices/pipe_test.go (1)

53-53: LGTM on the NewHandler(module) migration.

Pipe + OnResponse chaining remains intact.

microservices/store.go (1)

11-20: Base STORE provider registration looks good.
Exports a singleton Store with initialized map.

func appServer(addr string) microservices.Service {
appService := func(module core.Module) core.Provider {
handler := microservices.NewHandler(module, core.ProviderOptions{})
handler := microservices.NewHandler(module)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

Signature change adopted correctly; consider adding a multi-transport coverage test.

Current tests use default store only. To exercise the new multi-listen wiring, add one test that registers transport-specific stores and passes transports to NewHandler.

Run this quick check to ensure no stale call sites still pass core.ProviderOptions:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -nP --type=go -C1 $'NewHandler\\s*\\(\\s*[^,()]+\\s*,\\s*core\\.ProviderOptions'

Length of output: 1986


Signature change adopted; update remaining tests & add multi-transport coverage test.

  • Stale call sites still passing core.ProviderOptions{} — update to the new NewHandler signature:
    microservices/redis/connect_test.go:49,161
    microservices/mqtt/connect_test.go:48,159
    microservices/nats/connect_test.go:43,179
    microservices/kafka/connect_test.go:43
    microservices/amqlib/connect_test.go:42
  • Add one test that registers transport-specific stores and passes transports into NewHandler to exercise the multi-listen wiring.
🤖 Prompt for AI Agents
In microservices/handler_test.go around line 65, update the test to use the new
NewHandler signature (which now accepts transport registrations) instead of the
old call that passes core.ProviderOptions{}; change other stale call sites
listed (microservices/redis/connect_test.go:49,161;
microservices/mqtt/connect_test.go:48,159;
microservices/nats/connect_test.go:43,179;
microservices/kafka/connect_test.go:43; microservices/amqlib/connect_test.go:42)
to call NewHandler with the appropriate transport arguments or nil when not
needed, and add a new unit test that registers transport-specific stores and
invokes NewHandler with multiple transports to verify multi-listen wiring
(register mocks for at least two transports, pass them into NewHandler, assert
handlers/listeners are created).

Comment on lines +33 to +37
for _, refName := range refNames {
store, ok := h.module.Ref(refName).(*Store)
if !ok {
return
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Don’t abort registration on one missing store; continue.
Returning here prevents registration in remaining stores.

Apply:

-        if !ok {
-            return
-        }
+        if !ok {
+            continue
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for _, refName := range refNames {
store, ok := h.module.Ref(refName).(*Store)
if !ok {
return
}
for _, refName := range refNames {
store, ok := h.module.Ref(refName).(*Store)
if !ok {
continue
}
🤖 Prompt for AI Agents
In microservices/handler.go around lines 33 to 37, the loop currently returns
when a module.Ref(refName) cast to *Store fails, aborting registration for all
remaining refNames; instead, skip that refName and continue the loop. Change the
behavior to log or ignore the missing/non-*Store ref and use continue so the
loop proceeds to attempt registration on the other stores rather than exiting
early.

Comment on lines +58 to +62
for _, refName := range refNames {
store, ok := h.module.Ref(refName).(*Store)
if !ok {
return
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Same early-return bug in OnEvent; use continue.
Mirror fix from OnResponse.

Apply:

-        if !ok {
-            return
-        }
+        if !ok {
+            continue
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for _, refName := range refNames {
store, ok := h.module.Ref(refName).(*Store)
if !ok {
return
}
for _, refName := range refNames {
store, ok := h.module.Ref(refName).(*Store)
if !ok {
continue
}
🤖 Prompt for AI Agents
In microservices/handler.go around lines 58 to 62, the loop over refNames
currently returns early when h.module.Ref(refName) is not a *Store, which
prematurely ends processing of remaining refs; change the early return to
continue so the loop skips the non-matching ref and proceeds to the next one
(mirror the fix used in OnResponse). Ensure no other logic after the loop relies
on that return path.

Comment on lines +21 to +31
if len(transports) > 0 {
for _, transport := range transports {
name := ToTransport(transport)
handlerModule.NewProvider(core.ProviderOptions{
Name: name,
Value: &Store{Subscribers: make(map[EventType][]*SubscribeHandler)},
})
handlerModule.Export(name)
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Guard against empty/duplicate transports to avoid provider-name collisions.
Empty string yields STORE again; duplicates register/export twice. Dedup and skip empty.

Apply:

-        if len(transports) > 0 {
-            for _, transport := range transports {
-                name := ToTransport(transport)
-                handlerModule.NewProvider(core.ProviderOptions{
-                    Name:  name,
-                    Value: &Store{Subscribers: make(map[EventType][]*SubscribeHandler)},
-                })
-                handlerModule.Export(name)
-            }
-        }
+        if len(transports) > 0 {
+            seen := map[core.Provide]struct{}{}
+            for _, t := range transports {
+                if t == "" {
+                    continue
+                }
+                name := ToTransport(t)
+                if _, ok := seen[name]; ok {
+                    continue
+                }
+                seen[name] = struct{}{}
+                handlerModule.NewProvider(core.ProviderOptions{
+                    Name:  name,
+                    Value: &Store{Subscribers: make(map[EventType][]*SubscribeHandler)},
+                })
+                handlerModule.Export(name)
+            }
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if len(transports) > 0 {
for _, transport := range transports {
name := ToTransport(transport)
handlerModule.NewProvider(core.ProviderOptions{
Name: name,
Value: &Store{Subscribers: make(map[EventType][]*SubscribeHandler)},
})
handlerModule.Export(name)
}
}
if len(transports) > 0 {
seen := map[core.Provide]struct{}{}
for _, t := range transports {
if t == "" {
continue
}
name := ToTransport(t)
if _, ok := seen[name]; ok {
continue
}
seen[name] = struct{}{}
handlerModule.NewProvider(core.ProviderOptions{
Name: name,
Value: &Store{Subscribers: make(map[EventType][]*SubscribeHandler)},
})
handlerModule.Export(name)
}
}
🤖 Prompt for AI Agents
In microservices/store.go around lines 21 to 31, the loop registering
providers/exporters can register empty or duplicate transport names causing
collisions; update the code to skip empty transport names and deduplicate
transports before creating a provider/exporting: normalize/trim each transport,
maintain a seen map[string]bool to skip duplicates, continue on empty after
normalization, and only call handlerModule.NewProvider and handlerModule.Export
for the first occurrence of each unique non-empty transport.

@Ren0503 Ren0503 force-pushed the feat/ren/330-add-multi-listen-handler branch from 93b2438 to 1b9ba1f Compare September 18, 2025 13:27
@Ren0503 Ren0503 merged commit 52d1be9 into master Sep 18, 2025
7 of 8 checks passed
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
microservices/Makefile (1)

19-21: Add race detector and shuffle to harden tests

Enable -race and -shuffle=on to catch data races and order‑dependent tests during local/CI runs.

 test:
-	go test -cover ./...
+	go test -race -shuffle=on -cover ./...
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 93b2438 and 1b9ba1f.

📒 Files selected for processing (10)
  • microservices/Makefile (1 hunks)
  • microservices/client_test.go (1 hunks)
  • microservices/exception_test.go (1 hunks)
  • microservices/guard_test.go (1 hunks)
  • microservices/handler.go (2 hunks)
  • microservices/handler_test.go (1 hunks)
  • microservices/middleware_test.go (1 hunks)
  • microservices/pipe_test.go (1 hunks)
  • microservices/store.go (2 hunks)
  • microservices/tcp/tenant_test.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • microservices/middleware_test.go
  • microservices/handler_test.go
  • microservices/guard_test.go
  • microservices/exception_test.go
  • microservices/pipe_test.go
  • microservices/client_test.go
  • microservices/store.go
  • microservices/handler.go
🧰 Additional context used
🧬 Code graph analysis (1)
microservices/tcp/tenant_test.go (3)
microservices/handler.go (1)
  • NewHandler (7-16)
microservices/constants.go (1)
  • TCP (9-9)
microservices/store.go (1)
  • Register (11-34)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (go)
🔇 Additional comments (2)
microservices/tcp/tenant_test.go (2)

214-214: LGTM: Transport-scoped store registration

Imports: []core.Modules{microservices.Register(microservices.TCP)} correctly exports both the default store and the TCP-scoped store for handler resolution.


152-152: LGTM — manual verification required: no multi-transport registrations found

The ripgrep commands returned no matches, so I can't confirm multi-transport handler/store registrations are exercised elsewhere. Run these locally and confirm any NewHandler/Register calls that pass multiple transports:

rg -nP --type=go '\bNewHandler\s*\('
rg -nP --type=go '\bRegister\s*\('

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Multi Listen in Microservice

2 participants