Skip to content

Refactors router - #96

Merged
jlpdeveloper merged 10 commits into
mainfrom
refactor-router
Jul 6, 2026
Merged

Refactors router#96
jlpdeveloper merged 10 commits into
mainfrom
refactor-router

Conversation

@jlpdeveloper

@jlpdeveloper jlpdeveloper commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Description

Code Rabbit Summary

Summary by CodeRabbit

  • New Features
    • Centralized HTTP route registration for platforms, products, flows, flow steps, and capabilities.
    • Exposed endpoints for creating, listing, fetching, updating, and deleting platforms/products/flows/capabilities, plus flow steps and flow-path retrieval.
  • Bug Fixes
    • Improved “not found” handling for missing flows/capabilities.
    • List endpoints now return empty arrays (not null) when no records exist.
  • Tests
    • Added/updated router tests to verify the expected set of routes and that nested endpoints are reachable.

Fixes

Closes #95

Post Deployment Tasks?

…up, and add unit tests for route registration
… and add unit tests for flow route registration.
…n to dedicated file, and update unit tests accordingly.
…ng, extract route registration to a dedicated file, and add unit tests for route registration.
…t, flow, and capability modules into dedicated files. Simplify `SetupRouter` and update unit tests accordingly.
@jlpdeveloper jlpdeveloper added this to the Phase 2: API Endpoints milestone Jul 2, 2026
@jlpdeveloper jlpdeveloper self-assigned this Jul 2, 2026
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Handlers and services in capability, flow, platform, and product are rewired to unexported constructors and concrete handler types. Each package now exposes RegisterRoutes, and the top-level router plus main.go use the renamed router initializer. Dependency versions are also updated.

Changes

Package-level RegisterRoutes refactor

Layer / File(s) Summary
Capability handler, service, and routes
internal/capability/handler.go, internal/capability/service.go, internal/capability/service_capability.go, internal/capability/router.go, internal/capability/handler_test.go, internal/capability/router_test.go
Capability switches from exported handler construction to an internal handler/newHandler setup, splits service wiring and capability methods, and adds route registration plus tests.
Flow handler, service, and routes
internal/flow/handler.go, internal/flow/service.go, internal/flow/serivice_flow.go, internal/flow/service_flow_steps.go, internal/flow/router.go, internal/flow/handler_test.go, internal/flow/router_test.go
Flow switches to an internal handler/newHandler setup, splits flow and flow-step logic into service files, and adds route registration plus tests.
Platform handler, service, and routes
internal/platform/handler.go, internal/platform/service.go, internal/platform/router.go, internal/platform/handler_test.go, internal/platform/router_test.go
Platform switches to an internal handler/newHandler setup, adds service construction wiring, and adds route registration plus tests.
Product handler, service, and routes
internal/product/handler.go, internal/product/service.go, internal/product/router.go, internal/product/handler_test.go, internal/product/router_test.go
Product switches to an internal handler/newHandler setup, adds service construction wiring, and adds route registration plus tests.
Top-level router and entrypoint
router/router.go, router/router_test.go, main.go, go.mod
The router initializer is renamed, route wiring is delegated to package-level registration functions, tests are rewritten for the new initializer, main.go uses the renamed entrypoint, and module versions are bumped.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested labels: chore

Poem

I hopped through routes from stem to stack,
With handlers tucked in, and no turning back.
RegisterRoutes hums soft and neat,
While the burrow’s wiring finds its seat. 🐰

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning go.mod dependency version bumps appear unrelated to the routing refactor and are outside the linked issue scope. Split the dependency updates into a separate PR or remove them from this routing refactor.
Title check ❓ Inconclusive The title is related to the change but too vague to describe the main refactor clearly. Use a more specific title such as "Move route registration into package-level RegisterRoutes functions".
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The refactor matches issue #95: route registration moved into package-level functions, app routing delegates to them, and tests cover the new wiring.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor-router

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
router/router_test.go (1)

25-70: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Test only validates static route registration, not runtime dispatch.

TestSetupRouter walks the tree with chi.Walk and asserts each expected method route string is present, which is good coverage for confirming each package's RegisterRoutes wired something into the mux. However, several expected entries are nested paths registered directly on the top-level router while a sibling package Mounts an overlapping prefix (see comment on router.go lines 36-39) — chi.Walk will list both entries regardless of which one actually wins at request time.

To give confidence that route behavior is truly unchanged (a stated PR acceptance criterion), consider adding a lightweight httptest-based check for at least the routes that sit under another package's mounted prefix (e.g., GET /platforms/{id}/products, GET /products/{id}/flows, GET /flows/{id}/capabilities) to confirm they reach the intended handler rather than a 404 from the mounted subrouter.

As per path instructions, this file should be assessed for "sufficient code coverage for the changes associated in the pull request."

🧪 Example addition to strengthen coverage
func TestSetupRouter_NestedRoutesReachHandlers(t *testing.T) {
	r := SetupRouter(&mockDBTX{})

	cases := []struct{ method, path string }{
		{http.MethodGet, "/platforms/1/products"},
		{http.MethodGet, "/products/1/flows"},
		{http.MethodGet, "/flows/1/capabilities"},
	}
	for _, c := range cases {
		req := httptest.NewRequest(c.method, c.path, nil)
		rec := httptest.NewRecorder()
		r.ServeHTTP(rec, req)
		if rec.Code == http.StatusNotFound {
			t.Errorf("%s %s: expected route to be reachable, got 404", c.method, c.path)
		}
	}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@router/router_test.go` around lines 25 - 70, `TestSetupRouter` only checks
that routes are registered via `chi.Walk`, but it does not verify that
overlapping mounted prefixes still dispatch to the intended handlers at runtime.
Keep the existing `SetupRouter` route coverage, and add a small `httptest`-based
test that exercises the nested paths under the mounted prefix, using
`SetupRouter`, `ServeHTTP`, and the affected routes like
`/platforms/{id}/products`, `/products/{id}/flows`, and
`/flows/{id}/capabilities`, to confirm they do not return 404.

Source: Path instructions

internal/flow/router_test.go (1)

57-90: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Exercise route dispatch, not just route presence.

chi.Walk confirms patterns exist, but it won’t catch wiring GET /flows/{id}/path to the wrong handler. The existing mockService.called map is ideal for asserting each route invokes the expected service method.

As per path instructions, **/*_test.go: "Assess the unit test code assessing sufficient code coverage for the changes associated in the pull request".

🧪 Suggested coverage extension
 import (
 	"context"
 	"net/http"
+	"net/http/httptest"
 	"products/internal/flow/db"
+	"strings"
 	"testing"
@@
 	for _, route := range want {
 		if !got[route] {
 			t.Errorf("expected route %q to be registered", route)
 		}
 	}
+
+	dispatchTests := []struct {
+		method   string
+		path     string
+		body     string
+		wantCall string
+	}{
+		{http.MethodPost, "/flows/1/steps", `{"current":"550e8400-e29b-41d4-a716-446655440000","next":"550e8400-e29b-41d4-a716-446655440000"}`, "CreateFlowStep"},
+		{http.MethodGet, "/flows/1/steps", "", "GetFlowSteps"},
+		{http.MethodGet, "/flows/1/path", "", "GetFlowPath"},
+		{http.MethodGet, "/flows/1/", "", "GetFlowById"},
+		{http.MethodPut, "/flows/1/", `{"name":"Updated Flow"}`, "UpdateFlow"},
+		{http.MethodDelete, "/flows/1/", "", "DeleteFlow"},
+		{http.MethodDelete, "/flow-steps/1", "", "DeleteFlowStep"},
+		{http.MethodPost, "/products/1/flows", `{"name":"Test Flow"}`, "CreateFlow"},
+		{http.MethodGet, "/products/1/flows", "", "GetFlowsByProduct"},
+	}
+
+	for _, tt := range dispatchTests {
+		t.Run(tt.method+" "+tt.path, func(t *testing.T) {
+			svc := newMockService()
+			r := chi.NewRouter()
+			registerRoutesWithHandler(r, newHandler(svc))
+
+			req := httptest.NewRequest(tt.method, tt.path, strings.NewReader(tt.body))
+			rr := httptest.NewRecorder()
+			r.ServeHTTP(rr, req)
+
+			if !svc.called[tt.wantCall] {
+				t.Fatalf("expected %s to be called", tt.wantCall)
+			}
+		})
+	}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/flow/router_test.go` around lines 57 - 90, The current
TestRegisterRoutesWithHandler only checks that routes are registered, not that
each route dispatches to the correct handler. Update the test to exercise actual
requests against the router from registerRoutesWithHandler and assert the
mockService.called map records the expected service method for each route, using
newHandler and newMockService to verify wiring such as GET /flows/{id}/path and
the other flow/product endpoints.

Source: Path instructions

internal/product/router_test.go (1)

16-38: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Same route-set completeness gap as the platform router test.

Only presence of want routes is checked; unexpected extra registrations in got would not be caught.

♻️ Suggested addition
 	for _, route := range want {
 		if !got[route] {
 			t.Errorf("expected route %q to be registered", route)
 		}
 	}
+
+	if len(got) != len(want) {
+		t.Errorf("expected %d routes, got %d: %v", len(want), len(got), got)
+	}

As per path instructions, "Assess sufficient code coverage for the changes associated in the pull request."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/product/router_test.go` around lines 16 - 38, The route coverage in
router_test.go only verifies that the expected entries in chi.Walk appear, so
extra unexpected registrations in got would still pass. Update the test around
chi.Walk to assert the route set is exact by checking that every route in got is
present in want and that the counts match, using the existing got and want
collections in the test.

Source: Path instructions

internal/platform/router_test.go (1)

16-38: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Test doesn't catch unexpected extra route registrations.

The assertions only check that each wanted route is present in got; they don't verify got has no additional/unexpected routes, so an accidental duplicate or mis-registered route pattern would silently pass.

♻️ Suggested addition
 	for _, route := range want {
 		if !got[route] {
 			t.Errorf("expected route %q to be registered", route)
 		}
 	}
+
+	if len(got) != len(want) {
+		t.Errorf("expected %d routes, got %d: %v", len(want), len(got), got)
+	}

As per path instructions, "Assess sufficient code coverage for the changes associated in the pull request."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/platform/router_test.go` around lines 16 - 38, The route
registration test in router_test.go only checks that expected entries from
chi.Walk are present, so extra or duplicate routes can still pass. Update the
assertions around the got map in TestRouter (the chi.Walk callback and want
list) to also verify the total registered routes match exactly, ensuring no
unexpected route patterns are registered alongside the expected /platforms and
/platforms/{id} routes.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/flow/service_flow_steps.go`:
- Around line 99-107: The traversal in the path-building loop can re-enqueue
nodes forever when the flow graph contains cycles, so update the logic in the
path construction routine to track visited nodes before appending `nexts` to
`queue`. Use the existing `queue`, `pathMap`, and `PathItem` processing in
`service_flow_steps.go` to skip already-seen nodes or edges so cyclic paths like
`A -> B -> C -> B` terminate cleanly.

---

Nitpick comments:
In `@internal/flow/router_test.go`:
- Around line 57-90: The current TestRegisterRoutesWithHandler only checks that
routes are registered, not that each route dispatches to the correct handler.
Update the test to exercise actual requests against the router from
registerRoutesWithHandler and assert the mockService.called map records the
expected service method for each route, using newHandler and newMockService to
verify wiring such as GET /flows/{id}/path and the other flow/product endpoints.

In `@internal/platform/router_test.go`:
- Around line 16-38: The route registration test in router_test.go only checks
that expected entries from chi.Walk are present, so extra or duplicate routes
can still pass. Update the assertions around the got map in TestRouter (the
chi.Walk callback and want list) to also verify the total registered routes
match exactly, ensuring no unexpected route patterns are registered alongside
the expected /platforms and /platforms/{id} routes.

In `@internal/product/router_test.go`:
- Around line 16-38: The route coverage in router_test.go only verifies that the
expected entries in chi.Walk appear, so extra unexpected registrations in got
would still pass. Update the test around chi.Walk to assert the route set is
exact by checking that every route in got is present in want and that the counts
match, using the existing got and want collections in the test.

In `@router/router_test.go`:
- Around line 25-70: `TestSetupRouter` only checks that routes are registered
via `chi.Walk`, but it does not verify that overlapping mounted prefixes still
dispatch to the intended handlers at runtime. Keep the existing `SetupRouter`
route coverage, and add a small `httptest`-based test that exercises the nested
paths under the mounted prefix, using `SetupRouter`, `ServeHTTP`, and the
affected routes like `/platforms/{id}/products`, `/products/{id}/flows`, and
`/flows/{id}/capabilities`, to confirm they do not return 404.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1252d53b-011f-44a6-ba8e-c0b55d7931fc

📥 Commits

Reviewing files that changed from the base of the PR and between 1b34414 and f53686a.

📒 Files selected for processing (25)
  • internal/capability/handler.go
  • internal/capability/handler_test.go
  • internal/capability/router.go
  • internal/capability/router_test.go
  • internal/capability/service.go
  • internal/capability/service_capability.go
  • internal/flow/handler.go
  • internal/flow/handler_test.go
  • internal/flow/router.go
  • internal/flow/router_test.go
  • internal/flow/serivice_flow.go
  • internal/flow/service.go
  • internal/flow/service_flow_steps.go
  • internal/platform/handler.go
  • internal/platform/handler_test.go
  • internal/platform/router.go
  • internal/platform/router_test.go
  • internal/platform/service.go
  • internal/product/handler.go
  • internal/product/handler_test.go
  • internal/product/router.go
  • internal/product/router_test.go
  • internal/product/service.go
  • router/router.go
  • router/router_test.go

Comment thread internal/flow/service_flow_steps.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
router/router_test.go (1)

14-24: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Return a non-nil pgx.Rows stub from mockDBTX.Query. The nested route handlers call GetProductsByPlatform, GetFlowsByProduct, GetCapabilitiesByFlow, and GetCapabilitiesByProduct, and each generated query path does defer rows.Close() after Query; returning nil here will panic as soon as one of those handlers runs. QueryRow isn’t exercised by these routes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@router/router_test.go` around lines 14 - 24, The mockDBTX.Query stub
currently returns a nil pgx.Rows, which will panic when the route handlers
invoke Query and then defer rows.Close() in GetProductsByPlatform,
GetFlowsByProduct, GetCapabilitiesByFlow, or GetCapabilitiesByProduct. Update
mockDBTX.Query to return a non-nil pgx.Rows test double that safely supports
Close and any iteration the handlers expect, and leave QueryRow unchanged since
it is not used by these routes.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@router/router_test.go`:
- Around line 14-24: The mockDBTX.Query stub currently returns a nil pgx.Rows,
which will panic when the route handlers invoke Query and then defer
rows.Close() in GetProductsByPlatform, GetFlowsByProduct, GetCapabilitiesByFlow,
or GetCapabilitiesByProduct. Update mockDBTX.Query to return a non-nil pgx.Rows
test double that safely supports Close and any iteration the handlers expect,
and leave QueryRow unchanged since it is not used by these routes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b6b0ac44-4b3b-4869-ba69-6f4759b34258

📥 Commits

Reviewing files that changed from the base of the PR and between f53686a and 5c5ca8a.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (4)
  • go.mod
  • main.go
  • router/router.go
  • router/router_test.go

@jlpdeveloper
jlpdeveloper merged commit 8f9c79c into main Jul 6, 2026
3 checks passed
@jlpdeveloper
jlpdeveloper deleted the refactor-router branch July 6, 2026 01:05
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.

Refactor HTTP route registration into package-level RegisterRoutes functions

1 participant