Compare commits

...

3 Commits

Author SHA1 Message Date
Hein
abd045493a mux UnderlyingRequest 2025-12-02 17:34:18 +02:00
Hein
a61556d857 Added FallbackHandler 2025-12-02 17:16:34 +02:00
Hein
eaf1133575 Fixed security rules not loading 2025-12-02 16:55:12 +02:00
4 changed files with 67 additions and 9 deletions

View File

@@ -137,6 +137,12 @@ func (h *HTTPRequest) AllHeaders() map[string]string {
return headers return headers
} }
// UnderlyingRequest returns the underlying *http.Request
// This is useful when you need to pass the request to other handlers
func (h *HTTPRequest) UnderlyingRequest() *http.Request {
return h.req
}
// HTTPResponseWriter adapts our ResponseWriter interface to standard http.ResponseWriter // HTTPResponseWriter adapts our ResponseWriter interface to standard http.ResponseWriter
type HTTPResponseWriter struct { type HTTPResponseWriter struct {
resp http.ResponseWriter resp http.ResponseWriter
@@ -166,6 +172,12 @@ func (h *HTTPResponseWriter) WriteJSON(data interface{}) error {
return json.NewEncoder(h.resp).Encode(data) return json.NewEncoder(h.resp).Encode(data)
} }
// UnderlyingResponseWriter returns the underlying http.ResponseWriter
// This is useful when you need to pass the response writer to other handlers
func (h *HTTPResponseWriter) UnderlyingResponseWriter() http.ResponseWriter {
return h.resp
}
// StandardMuxAdapter creates routes compatible with standard http.HandlerFunc // StandardMuxAdapter creates routes compatible with standard http.HandlerFunc
type StandardMuxAdapter struct { type StandardMuxAdapter struct {
*MuxAdapter *MuxAdapter

View File

@@ -16,12 +16,17 @@ import (
"github.com/bitechdev/ResolveSpec/pkg/reflection" "github.com/bitechdev/ResolveSpec/pkg/reflection"
) )
// FallbackHandler is a function that handles requests when no model is found
// It receives the same parameters as the Handle method
type FallbackHandler func(w common.ResponseWriter, r common.Request, params map[string]string)
// Handler handles API requests using database and model abstractions // Handler handles API requests using database and model abstractions
type Handler struct { type Handler struct {
db common.Database db common.Database
registry common.ModelRegistry registry common.ModelRegistry
nestedProcessor *common.NestedCUDProcessor nestedProcessor *common.NestedCUDProcessor
hooks *HookRegistry hooks *HookRegistry
fallbackHandler FallbackHandler
} }
// NewHandler creates a new API handler with database and registry abstractions // NewHandler creates a new API handler with database and registry abstractions
@@ -42,6 +47,12 @@ func (h *Handler) Hooks() *HookRegistry {
return h.hooks return h.hooks
} }
// SetFallbackHandler sets a fallback handler to be called when no model is found
// If not set, the handler will simply return (pass through to next route)
func (h *Handler) SetFallbackHandler(fallback FallbackHandler) {
h.fallbackHandler = fallback
}
// GetDatabase returns the underlying database connection // GetDatabase returns the underlying database connection
// Implements common.SpecHandler interface // Implements common.SpecHandler interface
func (h *Handler) GetDatabase() common.Database { func (h *Handler) GetDatabase() common.Database {
@@ -89,8 +100,14 @@ func (h *Handler) Handle(w common.ResponseWriter, r common.Request, params map[s
// Get model and populate context with request-scoped data // Get model and populate context with request-scoped data
model, err := h.registry.GetModelByEntity(schema, entity) model, err := h.registry.GetModelByEntity(schema, entity)
if err != nil { if err != nil {
// Model not found - pass through to next route without writing response // Model not found - call fallback handler if set, otherwise pass through
logger.Debug("Model not found for %s.%s, passing through to next route", schema, entity) logger.Debug("Model not found for %s.%s", schema, entity)
if h.fallbackHandler != nil {
logger.Debug("Calling fallback handler for %s.%s", schema, entity)
h.fallbackHandler(w, r, params)
} else {
logger.Debug("No fallback handler set, passing through to next route")
}
return return
} }
@@ -156,8 +173,14 @@ func (h *Handler) HandleGet(w common.ResponseWriter, r common.Request, params ma
model, err := h.registry.GetModelByEntity(schema, entity) model, err := h.registry.GetModelByEntity(schema, entity)
if err != nil { if err != nil {
// Model not found - pass through to next route without writing response // Model not found - call fallback handler if set, otherwise pass through
logger.Debug("Model not found for %s.%s, passing through to next route", schema, entity) logger.Debug("Model not found for %s.%s", schema, entity)
if h.fallbackHandler != nil {
logger.Debug("Calling fallback handler for %s.%s", schema, entity)
h.fallbackHandler(w, r, params)
} else {
logger.Debug("No fallback handler set, passing through to next route")
}
return return
} }

View File

@@ -17,6 +17,10 @@ import (
"github.com/bitechdev/ResolveSpec/pkg/reflection" "github.com/bitechdev/ResolveSpec/pkg/reflection"
) )
// FallbackHandler is a function that handles requests when no model is found
// It receives the same parameters as the Handle method
type FallbackHandler func(w common.ResponseWriter, r common.Request, params map[string]string)
// Handler handles API requests using database and model abstractions // Handler handles API requests using database and model abstractions
// This handler reads filters, columns, and options from HTTP headers // This handler reads filters, columns, and options from HTTP headers
type Handler struct { type Handler struct {
@@ -24,6 +28,7 @@ type Handler struct {
registry common.ModelRegistry registry common.ModelRegistry
hooks *HookRegistry hooks *HookRegistry
nestedProcessor *common.NestedCUDProcessor nestedProcessor *common.NestedCUDProcessor
fallbackHandler FallbackHandler
} }
// NewHandler creates a new API handler with database and registry abstractions // NewHandler creates a new API handler with database and registry abstractions
@@ -50,6 +55,12 @@ func (h *Handler) Hooks() *HookRegistry {
return h.hooks return h.hooks
} }
// SetFallbackHandler sets a fallback handler to be called when no model is found
// If not set, the handler will simply return (pass through to next route)
func (h *Handler) SetFallbackHandler(fallback FallbackHandler) {
h.fallbackHandler = fallback
}
// handlePanic is a helper function to handle panics with stack traces // handlePanic is a helper function to handle panics with stack traces
func (h *Handler) handlePanic(w common.ResponseWriter, method string, err interface{}) { func (h *Handler) handlePanic(w common.ResponseWriter, method string, err interface{}) {
stack := debug.Stack() stack := debug.Stack()
@@ -81,8 +92,14 @@ func (h *Handler) Handle(w common.ResponseWriter, r common.Request, params map[s
// Get model and populate context with request-scoped data // Get model and populate context with request-scoped data
model, err := h.registry.GetModelByEntity(schema, entity) model, err := h.registry.GetModelByEntity(schema, entity)
if err != nil { if err != nil {
// Model not found - pass through to next route without writing response // Model not found - call fallback handler if set, otherwise pass through
logger.Debug("Model not found for %s.%s, passing through to next route", schema, entity) logger.Debug("Model not found for %s.%s", schema, entity)
if h.fallbackHandler != nil {
logger.Debug("Calling fallback handler for %s.%s", schema, entity)
h.fallbackHandler(w, r, params)
} else {
logger.Debug("No fallback handler set, passing through to next route")
}
return return
} }
@@ -197,8 +214,14 @@ func (h *Handler) HandleGet(w common.ResponseWriter, r common.Request, params ma
model, err := h.registry.GetModelByEntity(schema, entity) model, err := h.registry.GetModelByEntity(schema, entity)
if err != nil { if err != nil {
// Model not found - pass through to next route without writing response // Model not found - call fallback handler if set, otherwise pass through
logger.Debug("Model not found for %s.%s, passing through to next route", schema, entity) logger.Debug("Model not found for %s.%s", schema, entity)
if h.fallbackHandler != nil {
logger.Debug("Calling fallback handler for %s.%s", schema, entity)
h.fallbackHandler(w, r, params)
} else {
logger.Debug("No fallback handler set, passing through to next route")
}
return return
} }

View File

@@ -28,7 +28,7 @@ func loadSecurityRules(secCtx SecurityContext, securityList *SecurityList) error
userID, ok := secCtx.GetUserID() userID, ok := secCtx.GetUserID()
if !ok { if !ok {
logger.Warn("No user ID in context for security check") logger.Warn("No user ID in context for security check")
return fmt.Errorf("authentication required") return nil
} }
schema := secCtx.GetSchema() schema := secCtx.GetSchema()