internal/apiserver: Server.Handle(version, pattern, h) registriert Routen
unter /api/{version}/... (Akzeptanzkriterium 1) — verschiedene Versionen
sind unabhaengige Pfade im ServeMux, eine neue Version beeintraechtigt
bestehende nicht. HandleV1 ist die Kurzform fuer die aktuelle Hauptversion.
Einheitliches Fehlerschema {"error":{"code","message"}} ueber WriteError
(Akzeptanzkriterium 2) — bewusst NICHT auth.RequireAuth aus IAM-02
wiederverwendet, da dessen Klartext-Fehlerantworten nicht zum einheitlichen
JSON-Schema passen wuerden; stattdessen authAndTenantContext nutzt
auth.TokenIssuer.Verify direkt (dieselbe Kryptographie, keine Duplikation)
und antwortet im API-01-Schema, auch bei 401.
authAndTenantContext ist die Middleware, die JEDEM ueber Handle registrierten
Endpunkt Tenant-/Benutzerkontext bereitstellt (Akzeptanzkriterium 3, ueber
apiserver.FromContext abrufbar) — kein Handler prueft Auth selbst.
loggingMiddleware protokolliert jede Anfrage strukturiert.
Pruefungen (ausgefuehrt auf root@192.168.1.131, go build/vet/test PASS):
1. Fehlerhafte Anfrage liefert ueber mehrere Endpunkte hinweg dasselbe
Fehlerschema (Stichprobe) — TestErrorFormat_ConsistentAcrossEndpoints:
zwei unabhaengige Endpunkte, beide liefern 401 im identischen
{"error":{"code","message"}}-Schema. PASS.
2. Middleware-Kette nachweislich von jedem Endpunkt durchlaufen —
TestMiddleware_SetsRequestContextForEveryEndpoint: zwei Endpunkte lesen
RequestContext, beide erhalten korrekten UserID/TenantSlug aus dem Token. PASS.
3. Versionswechsel (fiktive v2-Route) ohne v1 zu beeintraechtigen —
TestVersioning_V2DoesNotAffectV1: v1 vor und nach Anlage von v2 liefert
unveraendert dieselbe Antwort. PASS.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
38 lines
1.1 KiB
Go
38 lines
1.1 KiB
Go
package apiserver
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"gitea.perlbach24.de/scripte/nexarch/internal/auth"
|
|
)
|
|
|
|
// Server registriert versionierte API-Routen (Akzeptanzkriterium 1: unter
|
|
// /api/{version}/...) und verdrahtet fuer jede Route dieselbe Middleware-
|
|
// Kette (Logging -> Auth+Tenantkontext -> Handler).
|
|
type Server struct {
|
|
mux *http.ServeMux
|
|
issuer *auth.TokenIssuer
|
|
}
|
|
|
|
func NewServer(issuer *auth.TokenIssuer) *Server {
|
|
return &Server{mux: http.NewServeMux(), issuer: issuer}
|
|
}
|
|
|
|
// Handle registriert pattern unter der angegebenen Version, z.B.
|
|
// Handle("v1", "/things", h) -> erreichbar unter /api/v1/things. Verschiedene
|
|
// Versionen sind unabhaengige Pfade — eine neue Version beeintraechtigt
|
|
// bestehende nicht (Akzeptanzkriterium 1 / Pruefung 3).
|
|
func (s *Server) Handle(version, pattern string, h http.HandlerFunc) {
|
|
full := "/api/" + version + pattern
|
|
s.mux.HandleFunc(full, loggingMiddleware(authAndTenantContext(s.issuer, h)))
|
|
}
|
|
|
|
// HandleV1 ist die Kurzform fuer die aktuelle Hauptversion.
|
|
func (s *Server) HandleV1(pattern string, h http.HandlerFunc) {
|
|
s.Handle("v1", pattern, h)
|
|
}
|
|
|
|
func (s *Server) Handler() http.Handler {
|
|
return s.mux
|
|
}
|