-
Notifications
You must be signed in to change notification settings - Fork 2
feat(microservices): add multi listen handler #331
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Summary by CodeRabbit
WalkthroughUpdated 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests
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.
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. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this 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_SERVICEand othersmicroservices.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
📒 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 toNewHandler(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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 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).
| for _, refName := range refNames { | ||
| store, ok := h.module.Ref(refName).(*Store) | ||
| if !ok { | ||
| return | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| for _, refName := range refNames { | ||
| store, ok := h.module.Ref(refName).(*Store) | ||
| if !ok { | ||
| return | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
| 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) | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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.
93b2438 to
1b9ba1f
Compare
There was a problem hiding this 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 testsEnable
-raceand-shuffle=onto 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
📒 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 foundThe 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*\('
No description provided.