feat(api): add deprecated /api/v2/resources alias for unified resources

This commit is contained in:
rcourtman
2026-02-16 16:27:44 +00:00
parent c772bd18c2
commit 80d09fd0df
4 changed files with 57 additions and 3 deletions

View File

@@ -84,8 +84,8 @@ This release is the Unified Resources + unified navigation foundation, plus opt-
## Breaking Changes
- Unified Resources is now the canonical resource model.
- `/api/v2/resources` has been renamed to `/api/resources`.
- Legacy resource plumbing and compatibility shims were removed as unified-only consumers landed (AI runtime, alerts, frontend selectors).
- Canonical API is now `/api/resources`.
- Deprecated alias: `/api/v2/resources` (temporary compatibility shim for older clients; will be removed after the migration window).
- Frontend routing is now canonicalized around unified navigation.
- Legacy routes (for example `/services` and `/kubernetes`) are transitional aliases and will be removed after the migration window.
- Storage/Backups naming and routing were de-V2'ed.
@@ -101,7 +101,7 @@ This release is the Unified Resources + unified navigation foundation, plus opt-
### API Clients and Integrations
- Replace calls to `/api/v2/resources` with `/api/resources`.
- Replace calls to `/api/v2/resources` with `/api/resources` (recommended).
- If an integration depended on legacy resource arrays or legacy resource endpoints, migrate to unified resource types and unified resource IDs.
### Multi-Tenant Organizations (Enterprise, Opt-In)

View File

@@ -354,6 +354,9 @@ var allRouteAllowlist = []string{
"/api/resources",
"/api/resources/stats",
"/api/resources/",
"/api/v2/resources",
"/api/v2/resources/stats",
"/api/v2/resources/",
"/api/guests/metadata",
"/api/guests/metadata/",
"/api/docker/metadata",

View File

@@ -7,6 +7,20 @@ import (
"github.com/rcourtman/pulse-go-rewrite/internal/config"
)
func deprecatedV2ResourceHandler(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// Back-compat shim for older clients. Canonical unified resources API is /api/resources.
w.Header().Set("Deprecation", "true")
// Rewrite /api/v2/resources/... -> /api/resources/...
if strings.HasPrefix(r.URL.Path, "/api/v2/") {
r.URL.Path = "/api/" + strings.TrimPrefix(r.URL.Path, "/api/v2/")
}
next(w, r)
}
}
func (r *Router) registerMonitoringResourceRoutes(
guestMetadataHandler *GuestMetadataHandler,
dockerMetadataHandler *DockerMetadataHandler,
@@ -34,6 +48,10 @@ func (r *Router) registerMonitoringResourceRoutes(
r.mux.HandleFunc("/api/resources", RequireAuth(r.config, RequireScope(config.ScopeMonitoringRead, r.resourceHandlers.HandleListResources)))
r.mux.HandleFunc("/api/resources/stats", RequireAuth(r.config, RequireScope(config.ScopeMonitoringRead, r.resourceHandlers.HandleStats)))
r.mux.HandleFunc("/api/resources/", RequireAuth(r.config, RequireScope(config.ScopeMonitoringRead, r.resourceHandlers.HandleResourceRoutes)))
// Deprecated v2 alias for unified resources (temporary compatibility shim).
r.mux.HandleFunc("/api/v2/resources", RequireAuth(r.config, RequireScope(config.ScopeMonitoringRead, deprecatedV2ResourceHandler(r.resourceHandlers.HandleListResources))))
r.mux.HandleFunc("/api/v2/resources/stats", RequireAuth(r.config, RequireScope(config.ScopeMonitoringRead, deprecatedV2ResourceHandler(r.resourceHandlers.HandleStats))))
r.mux.HandleFunc("/api/v2/resources/", RequireAuth(r.config, RequireScope(config.ScopeMonitoringRead, deprecatedV2ResourceHandler(r.resourceHandlers.HandleResourceRoutes))))
// Guest metadata routes
r.mux.HandleFunc("/api/guests/metadata", RequireAuth(r.config, RequireScope(config.ScopeMonitoringRead, guestMetadataHandler.HandleGetMetadata)))
r.mux.HandleFunc("/api/guests/metadata/", RequireAuth(r.config, func(w http.ResponseWriter, req *http.Request) {

View File

@@ -0,0 +1,33 @@
package api
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestDeprecatedV2ResourceHandler_RewritesPathAndSetsHeader(t *testing.T) {
rec := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/api/v2/resources/host-1/children", nil)
called := false
next := func(w http.ResponseWriter, r *http.Request) {
called = true
if r.URL.Path != "/api/resources/host-1/children" {
t.Fatalf("rewritten path = %q, want %q", r.URL.Path, "/api/resources/host-1/children")
}
w.WriteHeader(http.StatusOK)
}
deprecatedV2ResourceHandler(next)(rec, req)
if !called {
t.Fatalf("expected next handler to be called")
}
if got := rec.Header().Get("Deprecation"); got != "true" {
t.Fatalf("Deprecation header = %q, want %q", got, "true")
}
if rec.Code != http.StatusOK {
t.Fatalf("status = %d, want %d", rec.Code, http.StatusOK)
}
}