fix: reduce cyclomatic complexity in admin handler routing
Refactor handleAdminRoutes to use a route matching helper instead of a large switch statement, reducing cyclomatic complexity from 16 to acceptable levels.
- commit
- 60706798a905c9a07a4f3e903ef7f3df1f39c558
- author
- brice · · 2026-01-17 15:30 UTC
- parent
- d4d1fa4
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +41 -23
M internal/adapters/in/http/admin/handler.go +41 -23
| @@ -122,30 +122,48 @@ func (h *Handler) handleAdminRoutes(w http.ResponseWriter, r *http.Request) { | |||
| 122 | 122 | path := strings.TrimPrefix(r.URL.Path, "/admin") | |
| 123 | 123 | ||
| 124 | 124 | // Route to appropriate handler | |
| 125 | - | switch { | |
| 126 | - | case path == "/attachments" || strings.HasPrefix(path, "/attachments/"): | |
| 127 | - | h.handleAttachmentsConfig(w, r, path) | |
| 128 | - | case path == "/routes" || strings.HasPrefix(path, "/routes/"): | |
| 129 | - | h.handleRoutes(w, r, path) | |
| 130 | - | case path == "/networks": | |
| 131 | - | h.handleNetworks(w, r) | |
| 132 | - | case path == "/secrets" || strings.HasPrefix(path, "/secrets/"): | |
| 133 | - | h.handleSecrets(w, r, path) | |
| 134 | - | case path == "/deploy" || strings.HasPrefix(path, "/deploy/"): | |
| 135 | - | h.handleDeploy(w, r, path) | |
| 136 | - | case path == "/logs" || strings.HasPrefix(path, "/logs/"): | |
| 137 | - | h.handleLogs(w, r, path) | |
| 138 | - | case path == "/status": | |
| 139 | - | h.handleStatus(w, r) | |
| 140 | - | case path == "/health": | |
| 141 | - | h.handleHealth(w, r) | |
| 142 | - | case path == "/reload": | |
| 143 | - | h.handleReload(w, r) | |
| 144 | - | case path == "/config": | |
| 145 | - | h.handleConfig(w, r) | |
| 146 | - | default: | |
| 147 | - | h.sendError(w, http.StatusNotFound, "route not found") | |
| 125 | + | if handler, ok := h.matchRoute(path); ok { | |
| 126 | + | handler(w, r, path) | |
| 127 | + | return | |
| 128 | + | } | |
| 129 | + | h.sendError(w, http.StatusNotFound, "route not found") | |
| 130 | + | } | |
| 131 | + | ||
| 132 | + | // routeHandler is the signature for path-based route handlers. | |
| 133 | + | type routeHandler func(w http.ResponseWriter, r *http.Request, path string) | |
| 134 | + | ||
| 135 | + | // matchRoute returns the handler for a given path, or false if not found. | |
| 136 | + | func (h *Handler) matchRoute(path string) (routeHandler, bool) { | |
| 137 | + | // Exact match routes | |
| 138 | + | exactRoutes := map[string]routeHandler{ | |
| 139 | + | "/networks": func(w http.ResponseWriter, r *http.Request, _ string) { h.handleNetworks(w, r) }, | |
| 140 | + | "/status": func(w http.ResponseWriter, r *http.Request, _ string) { h.handleStatus(w, r) }, | |
| 141 | + | "/health": func(w http.ResponseWriter, r *http.Request, _ string) { h.handleHealth(w, r) }, | |
| 142 | + | "/reload": func(w http.ResponseWriter, r *http.Request, _ string) { h.handleReload(w, r) }, | |
| 143 | + | "/config": func(w http.ResponseWriter, r *http.Request, _ string) { h.handleConfig(w, r) }, | |
| 144 | + | } | |
| 145 | + | if handler, ok := exactRoutes[path]; ok { | |
| 146 | + | return handler, true | |
| 147 | + | } | |
| 148 | + | ||
| 149 | + | // Prefix match routes | |
| 150 | + | prefixRoutes := []struct { | |
| 151 | + | prefix string | |
| 152 | + | handler routeHandler | |
| 153 | + | }{ | |
| 154 | + | {"/attachments", h.handleAttachmentsConfig}, | |
| 155 | + | {"/routes", h.handleRoutes}, | |
| 156 | + | {"/secrets", h.handleSecrets}, | |
| 157 | + | {"/deploy", h.handleDeploy}, | |
| 158 | + | {"/logs", h.handleLogs}, | |
| 159 | + | } | |
| 160 | + | for _, route := range prefixRoutes { | |
| 161 | + | if path == route.prefix || strings.HasPrefix(path, route.prefix+"/") { | |
| 162 | + | return route.handler, true | |
| 163 | + | } | |
| 148 | 164 | } | |
| 165 | + | ||
| 166 | + | return nil, false | |
| 149 | 167 | } | |
| 150 | 168 | ||
| 151 | 169 | // sendJSON sends a JSON response. | |
| @@ -122,30 +122,48 @@ func (h *Handler) handleAdminRoutes(w http.ResponseWriter, r *http.Request) { | |||||
| 122 | path := strings.TrimPrefix(r.URL.Path, "/admin") | 122 | path := strings.TrimPrefix(r.URL.Path, "/admin") | ||
| 123 | 123 | ||||
| 124 | // Route to appropriate handler | 124 | // Route to appropriate handler | ||
| 125 | - | switch { | 125 | + | if handler, ok := h.matchRoute(path); ok { |
| 126 | - | case path == "/attachments" || strings.HasPrefix(path, "/attachments/"): | 126 | + | handler(w, r, path) |
| 127 | - | h.handleAttachmentsConfig(w, r, path) | 127 | + | return |
| 128 | - | case path == "/routes" || strings.HasPrefix(path, "/routes/"): | 128 | + | } |
| 129 | - | h.handleRoutes(w, r, path) | 129 | + | h.sendError(w, http.StatusNotFound, "route not found") |
| 130 | - | case path == "/networks": | 130 | + | } |
| 131 | - | h.handleNetworks(w, r) | 131 | + | |
| 132 | - | case path == "/secrets" || strings.HasPrefix(path, "/secrets/"): | 132 | + | // routeHandler is the signature for path-based route handlers. |
| 133 | - | h.handleSecrets(w, r, path) | 133 | + | type routeHandler func(w http.ResponseWriter, r *http.Request, path string) |
| 134 | - | case path == "/deploy" || strings.HasPrefix(path, "/deploy/"): | 134 | + | |
| 135 | - | h.handleDeploy(w, r, path) | 135 | + | // matchRoute returns the handler for a given path, or false if not found. |
| 136 | - | case path == "/logs" || strings.HasPrefix(path, "/logs/"): | 136 | + | func (h *Handler) matchRoute(path string) (routeHandler, bool) { |
| 137 | - | h.handleLogs(w, r, path) | 137 | + | // Exact match routes |
| 138 | - | case path == "/status": | 138 | + | exactRoutes := map[string]routeHandler{ |
| 139 | - | h.handleStatus(w, r) | 139 | + | "/networks": func(w http.ResponseWriter, r *http.Request, _ string) { h.handleNetworks(w, r) }, |
| 140 | - | case path == "/health": | 140 | + | "/status": func(w http.ResponseWriter, r *http.Request, _ string) { h.handleStatus(w, r) }, |
| 141 | - | h.handleHealth(w, r) | 141 | + | "/health": func(w http.ResponseWriter, r *http.Request, _ string) { h.handleHealth(w, r) }, |
| 142 | - | case path == "/reload": | 142 | + | "/reload": func(w http.ResponseWriter, r *http.Request, _ string) { h.handleReload(w, r) }, |
| 143 | - | h.handleReload(w, r) | 143 | + | "/config": func(w http.ResponseWriter, r *http.Request, _ string) { h.handleConfig(w, r) }, |
| 144 | - | case path == "/config": | 144 | + | } |
| 145 | - | h.handleConfig(w, r) | 145 | + | if handler, ok := exactRoutes[path]; ok { |
| 146 | - | default: | 146 | + | return handler, true |
| 147 | - | h.sendError(w, http.StatusNotFound, "route not found") | 147 | + | } |
| 148 | + | ||||
| 149 | + | // Prefix match routes | |||
| 150 | + | prefixRoutes := []struct { | |||
| 151 | + | prefix string | |||
| 152 | + | handler routeHandler | |||
| 153 | + | }{ | |||
| 154 | + | {"/attachments", h.handleAttachmentsConfig}, | |||
| 155 | + | {"/routes", h.handleRoutes}, | |||
| 156 | + | {"/secrets", h.handleSecrets}, | |||
| 157 | + | {"/deploy", h.handleDeploy}, | |||
| 158 | + | {"/logs", h.handleLogs}, | |||
| 159 | + | } | |||
| 160 | + | for _, route := range prefixRoutes { | |||
| 161 | + | if path == route.prefix || strings.HasPrefix(path, route.prefix+"/") { | |||
| 162 | + | return route.handler, true | |||
| 163 | + | } | |||
| 148 | } | 164 | } | ||
| 165 | + | ||||
| 166 | + | return nil, false | |||
| 149 | } | 167 | } | ||
| 150 | 168 | ||||
| 151 | // sendJSON sends a JSON response. | 169 | // sendJSON sends a JSON response. | ||