mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2025-12-06 14:26:22 +00:00
More common handler interface
This commit is contained in:
parent
08050c960d
commit
9c5c7689e9
@ -47,8 +47,8 @@ func main() {
|
|||||||
handler.RegisterModel("public", modelNames[i], model)
|
handler.RegisterModel("public", modelNames[i], model)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup routes using new SetupMuxRoutes function
|
// Setup routes using new SetupMuxRoutes function (without authentication)
|
||||||
resolvespec.SetupMuxRoutes(r, handler)
|
resolvespec.SetupMuxRoutes(r, handler, nil)
|
||||||
|
|
||||||
// Start server
|
// Start server
|
||||||
logger.Info("Starting server on :8080")
|
logger.Info("Starting server on :8080")
|
||||||
|
|||||||
97
pkg/common/handler_example.go
Normal file
97
pkg/common/handler_example.go
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
// Example showing how to use the common handler interfaces
|
||||||
|
// This file demonstrates the handler interface hierarchy and usage patterns
|
||||||
|
|
||||||
|
// ProcessWithAnyHandler demonstrates using the base SpecHandler interface
|
||||||
|
// which works with any handler type (resolvespec, restheadspec, or funcspec)
|
||||||
|
func ProcessWithAnyHandler(handler SpecHandler) Database {
|
||||||
|
// All handlers expose GetDatabase() through the SpecHandler interface
|
||||||
|
return handler.GetDatabase()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessCRUDRequest demonstrates using the CRUDHandler interface
|
||||||
|
// which works with resolvespec.Handler and restheadspec.Handler
|
||||||
|
func ProcessCRUDRequest(handler CRUDHandler, w ResponseWriter, r Request, params map[string]string) {
|
||||||
|
// Both resolvespec and restheadspec handlers implement Handle()
|
||||||
|
handler.Handle(w, r, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessMetadataRequest demonstrates getting metadata from CRUD handlers
|
||||||
|
func ProcessMetadataRequest(handler CRUDHandler, w ResponseWriter, r Request, params map[string]string) {
|
||||||
|
// Both resolvespec and restheadspec handlers implement HandleGet()
|
||||||
|
handler.HandleGet(w, r, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example usage patterns (not executable, just for documentation):
|
||||||
|
/*
|
||||||
|
// Example 1: Using with resolvespec.Handler
|
||||||
|
func ExampleResolveSpec() {
|
||||||
|
db := // ... get database
|
||||||
|
registry := // ... get registry
|
||||||
|
|
||||||
|
handler := resolvespec.NewHandler(db, registry)
|
||||||
|
|
||||||
|
// Can be used as SpecHandler
|
||||||
|
var specHandler SpecHandler = handler
|
||||||
|
database := specHandler.GetDatabase()
|
||||||
|
|
||||||
|
// Can be used as CRUDHandler
|
||||||
|
var crudHandler CRUDHandler = handler
|
||||||
|
crudHandler.Handle(w, r, params)
|
||||||
|
crudHandler.HandleGet(w, r, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example 2: Using with restheadspec.Handler
|
||||||
|
func ExampleRestHeadSpec() {
|
||||||
|
db := // ... get database
|
||||||
|
registry := // ... get registry
|
||||||
|
|
||||||
|
handler := restheadspec.NewHandler(db, registry)
|
||||||
|
|
||||||
|
// Can be used as SpecHandler
|
||||||
|
var specHandler SpecHandler = handler
|
||||||
|
database := specHandler.GetDatabase()
|
||||||
|
|
||||||
|
// Can be used as CRUDHandler
|
||||||
|
var crudHandler CRUDHandler = handler
|
||||||
|
crudHandler.Handle(w, r, params)
|
||||||
|
crudHandler.HandleGet(w, r, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example 3: Using with funcspec.Handler
|
||||||
|
func ExampleFuncSpec() {
|
||||||
|
db := // ... get database
|
||||||
|
|
||||||
|
handler := funcspec.NewHandler(db)
|
||||||
|
|
||||||
|
// Can be used as SpecHandler
|
||||||
|
var specHandler SpecHandler = handler
|
||||||
|
database := specHandler.GetDatabase()
|
||||||
|
|
||||||
|
// Can be used as QueryHandler
|
||||||
|
var queryHandler QueryHandler = handler
|
||||||
|
// funcspec has different methods: SqlQueryList() and SqlQuery()
|
||||||
|
// which return HTTP handler functions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example 4: Polymorphic handler processing
|
||||||
|
func ProcessHandlers(handlers []SpecHandler) {
|
||||||
|
for _, handler := range handlers {
|
||||||
|
// All handlers expose the database
|
||||||
|
db := handler.GetDatabase()
|
||||||
|
|
||||||
|
// Type switch for specific handler types
|
||||||
|
switch h := handler.(type) {
|
||||||
|
case CRUDHandler:
|
||||||
|
// This is resolvespec or restheadspec
|
||||||
|
// Can call Handle() and HandleGet()
|
||||||
|
_ = h
|
||||||
|
case QueryHandler:
|
||||||
|
// This is funcspec
|
||||||
|
// Can call SqlQueryList() and SqlQuery()
|
||||||
|
_ = h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
@ -246,3 +246,39 @@ type PrimaryKeyNameProvider interface {
|
|||||||
type SchemaProvider interface {
|
type SchemaProvider interface {
|
||||||
SchemaName() string
|
SchemaName() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SpecHandler interface represents common functionality across all spec handlers
|
||||||
|
// This is the base interface implemented by:
|
||||||
|
// - resolvespec.Handler: Handles CRUD operations via request body with explicit operation field
|
||||||
|
// - restheadspec.Handler: Handles CRUD operations via HTTP methods (GET/POST/PUT/DELETE)
|
||||||
|
// - funcspec.Handler: Handles custom SQL query execution with dynamic parameters
|
||||||
|
//
|
||||||
|
// The interface hierarchy is:
|
||||||
|
//
|
||||||
|
// SpecHandler (base)
|
||||||
|
// ├── CRUDHandler (resolvespec, restheadspec)
|
||||||
|
// └── QueryHandler (funcspec)
|
||||||
|
type SpecHandler interface {
|
||||||
|
// GetDatabase returns the underlying database connection
|
||||||
|
GetDatabase() Database
|
||||||
|
}
|
||||||
|
|
||||||
|
// CRUDHandler interface for handlers that support CRUD operations
|
||||||
|
// This is implemented by resolvespec.Handler and restheadspec.Handler
|
||||||
|
type CRUDHandler interface {
|
||||||
|
SpecHandler
|
||||||
|
|
||||||
|
// Handle processes API requests through router-agnostic interface
|
||||||
|
Handle(w ResponseWriter, r Request, params map[string]string)
|
||||||
|
|
||||||
|
// HandleGet processes GET requests for metadata
|
||||||
|
HandleGet(w ResponseWriter, r Request, params map[string]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryHandler interface for handlers that execute SQL queries
|
||||||
|
// This is implemented by funcspec.Handler
|
||||||
|
// Note: funcspec uses standard http.ResponseWriter and *http.Request instead of common interfaces
|
||||||
|
type QueryHandler interface {
|
||||||
|
SpecHandler
|
||||||
|
// Methods are defined in funcspec package due to different function signature requirements
|
||||||
|
}
|
||||||
|
|||||||
@ -32,6 +32,12 @@ func NewHandler(db common.Database) *Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDatabase returns the underlying database connection
|
||||||
|
// Implements common.SpecHandler interface
|
||||||
|
func (h *Handler) GetDatabase() common.Database {
|
||||||
|
return h.db
|
||||||
|
}
|
||||||
|
|
||||||
// Hooks returns the hook registry for this handler
|
// Hooks returns the hook registry for this handler
|
||||||
// Use this to register custom hooks for operations
|
// Use this to register custom hooks for operations
|
||||||
func (h *Handler) Hooks() *HookRegistry {
|
func (h *Handler) Hooks() *HookRegistry {
|
||||||
|
|||||||
@ -34,6 +34,12 @@ func NewHandler(db common.Database, registry common.ModelRegistry) *Handler {
|
|||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDatabase returns the underlying database connection
|
||||||
|
// Implements common.SpecHandler interface
|
||||||
|
func (h *Handler) GetDatabase() common.Database {
|
||||||
|
return h.db
|
||||||
|
}
|
||||||
|
|
||||||
// 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()
|
||||||
|
|||||||
@ -37,28 +37,46 @@ func NewStandardBunRouter() *router.StandardBunRouterAdapter {
|
|||||||
return router.NewStandardBunRouterAdapter()
|
return router.NewStandardBunRouterAdapter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MiddlewareFunc is a function that wraps an http.Handler with additional functionality
|
||||||
|
type MiddlewareFunc func(http.Handler) http.Handler
|
||||||
|
|
||||||
// SetupMuxRoutes sets up routes for the ResolveSpec API with Mux
|
// SetupMuxRoutes sets up routes for the ResolveSpec API with Mux
|
||||||
func SetupMuxRoutes(muxRouter *mux.Router, handler *Handler) {
|
// authMiddleware is optional - if provided, routes will be protected with the middleware
|
||||||
muxRouter.HandleFunc("/{schema}/{entity}", func(w http.ResponseWriter, r *http.Request) {
|
// Example: SetupMuxRoutes(router, handler, func(h http.Handler) http.Handler { return security.NewAuthHandler(securityList, h) })
|
||||||
|
func SetupMuxRoutes(muxRouter *mux.Router, handler *Handler, authMiddleware MiddlewareFunc) {
|
||||||
|
// Create handler functions
|
||||||
|
postEntityHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
reqAdapter := router.NewHTTPRequest(r)
|
reqAdapter := router.NewHTTPRequest(r)
|
||||||
respAdapter := router.NewHTTPResponseWriter(w)
|
respAdapter := router.NewHTTPResponseWriter(w)
|
||||||
handler.Handle(respAdapter, reqAdapter, vars)
|
handler.Handle(respAdapter, reqAdapter, vars)
|
||||||
}).Methods("POST")
|
})
|
||||||
|
|
||||||
muxRouter.HandleFunc("/{schema}/{entity}/{id}", func(w http.ResponseWriter, r *http.Request) {
|
postEntityWithIDHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
reqAdapter := router.NewHTTPRequest(r)
|
reqAdapter := router.NewHTTPRequest(r)
|
||||||
respAdapter := router.NewHTTPResponseWriter(w)
|
respAdapter := router.NewHTTPResponseWriter(w)
|
||||||
handler.Handle(respAdapter, reqAdapter, vars)
|
handler.Handle(respAdapter, reqAdapter, vars)
|
||||||
}).Methods("POST")
|
})
|
||||||
|
|
||||||
muxRouter.HandleFunc("/{schema}/{entity}", func(w http.ResponseWriter, r *http.Request) {
|
getEntityHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
reqAdapter := router.NewHTTPRequest(r)
|
reqAdapter := router.NewHTTPRequest(r)
|
||||||
respAdapter := router.NewHTTPResponseWriter(w)
|
respAdapter := router.NewHTTPResponseWriter(w)
|
||||||
handler.HandleGet(respAdapter, reqAdapter, vars)
|
handler.HandleGet(respAdapter, reqAdapter, vars)
|
||||||
}).Methods("GET")
|
})
|
||||||
|
|
||||||
|
// Apply authentication middleware if provided
|
||||||
|
if authMiddleware != nil {
|
||||||
|
postEntityHandler = authMiddleware(postEntityHandler).(http.HandlerFunc)
|
||||||
|
postEntityWithIDHandler = authMiddleware(postEntityWithIDHandler).(http.HandlerFunc)
|
||||||
|
getEntityHandler = authMiddleware(getEntityHandler).(http.HandlerFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register routes
|
||||||
|
muxRouter.Handle("/{schema}/{entity}", postEntityHandler).Methods("POST")
|
||||||
|
muxRouter.Handle("/{schema}/{entity}/{id}", postEntityWithIDHandler).Methods("POST")
|
||||||
|
muxRouter.Handle("/{schema}/{entity}", getEntityHandler).Methods("GET")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Example usage functions for documentation:
|
// Example usage functions for documentation:
|
||||||
@ -68,12 +86,20 @@ func ExampleWithGORM(db *gorm.DB) {
|
|||||||
// Create handler using GORM
|
// Create handler using GORM
|
||||||
handler := NewHandlerWithGORM(db)
|
handler := NewHandlerWithGORM(db)
|
||||||
|
|
||||||
// Setup router
|
// Setup router without authentication
|
||||||
muxRouter := mux.NewRouter()
|
muxRouter := mux.NewRouter()
|
||||||
SetupMuxRoutes(muxRouter, handler)
|
SetupMuxRoutes(muxRouter, handler, nil)
|
||||||
|
|
||||||
// Register models
|
// Register models
|
||||||
// handler.RegisterModel("public", "users", &User{})
|
// handler.RegisterModel("public", "users", &User{})
|
||||||
|
|
||||||
|
// To add authentication, pass a middleware function:
|
||||||
|
// import "github.com/bitechdev/ResolveSpec/pkg/security"
|
||||||
|
// secList := security.NewSecurityList(myProvider)
|
||||||
|
// authMiddleware := func(h http.Handler) http.Handler {
|
||||||
|
// return security.NewAuthHandler(secList, h)
|
||||||
|
// }
|
||||||
|
// SetupMuxRoutes(muxRouter, handler, authMiddleware)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExampleWithBun shows how to switch to Bun ORM
|
// ExampleWithBun shows how to switch to Bun ORM
|
||||||
@ -88,9 +114,9 @@ func ExampleWithBun(bunDB *bun.DB) {
|
|||||||
// Create handler
|
// Create handler
|
||||||
handler := NewHandler(dbAdapter, registry)
|
handler := NewHandler(dbAdapter, registry)
|
||||||
|
|
||||||
// Setup routes
|
// Setup routes without authentication
|
||||||
muxRouter := mux.NewRouter()
|
muxRouter := mux.NewRouter()
|
||||||
SetupMuxRoutes(muxRouter, handler)
|
SetupMuxRoutes(muxRouter, handler, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupBunRouterRoutes sets up bunrouter routes for the ResolveSpec API
|
// SetupBunRouterRoutes sets up bunrouter routes for the ResolveSpec API
|
||||||
|
|||||||
@ -38,6 +38,12 @@ func NewHandler(db common.Database, registry common.ModelRegistry) *Handler {
|
|||||||
return handler
|
return handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDatabase returns the underlying database connection
|
||||||
|
// Implements common.SpecHandler interface
|
||||||
|
func (h *Handler) GetDatabase() common.Database {
|
||||||
|
return h.db
|
||||||
|
}
|
||||||
|
|
||||||
// Hooks returns the hook registry for this handler
|
// Hooks returns the hook registry for this handler
|
||||||
// Use this to register custom hooks for operations
|
// Use this to register custom hooks for operations
|
||||||
func (h *Handler) Hooks() *HookRegistry {
|
func (h *Handler) Hooks() *HookRegistry {
|
||||||
|
|||||||
@ -90,31 +90,51 @@ func NewStandardBunRouter() *router.StandardBunRouterAdapter {
|
|||||||
return router.NewStandardBunRouterAdapter()
|
return router.NewStandardBunRouterAdapter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MiddlewareFunc is a function that wraps an http.Handler with additional functionality
|
||||||
|
type MiddlewareFunc func(http.Handler) http.Handler
|
||||||
|
|
||||||
// SetupMuxRoutes sets up routes for the RestHeadSpec API with Mux
|
// SetupMuxRoutes sets up routes for the RestHeadSpec API with Mux
|
||||||
func SetupMuxRoutes(muxRouter *mux.Router, handler *Handler) {
|
// authMiddleware is optional - if provided, routes will be protected with the middleware
|
||||||
// GET, POST, PUT, PATCH, DELETE for /{schema}/{entity}
|
// Example: SetupMuxRoutes(router, handler, func(h http.Handler) http.Handler { return security.NewAuthHandler(securityList, h) })
|
||||||
muxRouter.HandleFunc("/{schema}/{entity}", func(w http.ResponseWriter, r *http.Request) {
|
func SetupMuxRoutes(muxRouter *mux.Router, handler *Handler, authMiddleware MiddlewareFunc) {
|
||||||
|
// Create handler functions
|
||||||
|
entityHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
reqAdapter := router.NewHTTPRequest(r)
|
reqAdapter := router.NewHTTPRequest(r)
|
||||||
respAdapter := router.NewHTTPResponseWriter(w)
|
respAdapter := router.NewHTTPResponseWriter(w)
|
||||||
handler.Handle(respAdapter, reqAdapter, vars)
|
handler.Handle(respAdapter, reqAdapter, vars)
|
||||||
}).Methods("GET", "POST")
|
})
|
||||||
|
|
||||||
// GET, PUT, PATCH, DELETE for /{schema}/{entity}/{id}
|
entityWithIDHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
muxRouter.HandleFunc("/{schema}/{entity}/{id}", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
reqAdapter := router.NewHTTPRequest(r)
|
reqAdapter := router.NewHTTPRequest(r)
|
||||||
respAdapter := router.NewHTTPResponseWriter(w)
|
respAdapter := router.NewHTTPResponseWriter(w)
|
||||||
handler.Handle(respAdapter, reqAdapter, vars)
|
handler.Handle(respAdapter, reqAdapter, vars)
|
||||||
}).Methods("GET", "PUT", "PATCH", "DELETE", "POST")
|
})
|
||||||
|
|
||||||
// GET for metadata (using HandleGet)
|
metadataHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
muxRouter.HandleFunc("/{schema}/{entity}/metadata", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
reqAdapter := router.NewHTTPRequest(r)
|
reqAdapter := router.NewHTTPRequest(r)
|
||||||
respAdapter := router.NewHTTPResponseWriter(w)
|
respAdapter := router.NewHTTPResponseWriter(w)
|
||||||
handler.HandleGet(respAdapter, reqAdapter, vars)
|
handler.HandleGet(respAdapter, reqAdapter, vars)
|
||||||
}).Methods("GET")
|
})
|
||||||
|
|
||||||
|
// Apply authentication middleware if provided
|
||||||
|
if authMiddleware != nil {
|
||||||
|
entityHandler = authMiddleware(entityHandler).(http.HandlerFunc)
|
||||||
|
entityWithIDHandler = authMiddleware(entityWithIDHandler).(http.HandlerFunc)
|
||||||
|
metadataHandler = authMiddleware(metadataHandler).(http.HandlerFunc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register routes
|
||||||
|
// GET, POST for /{schema}/{entity}
|
||||||
|
muxRouter.Handle("/{schema}/{entity}", entityHandler).Methods("GET", "POST")
|
||||||
|
|
||||||
|
// GET, PUT, PATCH, DELETE, POST for /{schema}/{entity}/{id}
|
||||||
|
muxRouter.Handle("/{schema}/{entity}/{id}", entityWithIDHandler).Methods("GET", "PUT", "PATCH", "DELETE", "POST")
|
||||||
|
|
||||||
|
// GET for metadata (using HandleGet)
|
||||||
|
muxRouter.Handle("/{schema}/{entity}/metadata", metadataHandler).Methods("GET")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Example usage functions for documentation:
|
// Example usage functions for documentation:
|
||||||
@ -124,12 +144,20 @@ func ExampleWithGORM(db *gorm.DB) {
|
|||||||
// Create handler using GORM
|
// Create handler using GORM
|
||||||
handler := NewHandlerWithGORM(db)
|
handler := NewHandlerWithGORM(db)
|
||||||
|
|
||||||
// Setup router
|
// Setup router without authentication
|
||||||
muxRouter := mux.NewRouter()
|
muxRouter := mux.NewRouter()
|
||||||
SetupMuxRoutes(muxRouter, handler)
|
SetupMuxRoutes(muxRouter, handler, nil)
|
||||||
|
|
||||||
// Register models
|
// Register models
|
||||||
// handler.registry.RegisterModel("public.users", &User{})
|
// handler.registry.RegisterModel("public.users", &User{})
|
||||||
|
|
||||||
|
// To add authentication, pass a middleware function:
|
||||||
|
// import "github.com/bitechdev/ResolveSpec/pkg/security"
|
||||||
|
// secList := security.NewSecurityList(myProvider)
|
||||||
|
// authMiddleware := func(h http.Handler) http.Handler {
|
||||||
|
// return security.NewAuthHandler(secList, h)
|
||||||
|
// }
|
||||||
|
// SetupMuxRoutes(muxRouter, handler, authMiddleware)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExampleWithBun shows how to switch to Bun ORM
|
// ExampleWithBun shows how to switch to Bun ORM
|
||||||
@ -144,9 +172,9 @@ func ExampleWithBun(bunDB *bun.DB) {
|
|||||||
// Create handler
|
// Create handler
|
||||||
handler := NewHandler(dbAdapter, registry)
|
handler := NewHandler(dbAdapter, registry)
|
||||||
|
|
||||||
// Setup routes
|
// Setup routes without authentication
|
||||||
muxRouter := mux.NewRouter()
|
muxRouter := mux.NewRouter()
|
||||||
SetupMuxRoutes(muxRouter, handler)
|
SetupMuxRoutes(muxRouter, handler, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetupBunRouterRoutes sets up bunrouter routes for the RestHeadSpec API
|
// SetupBunRouterRoutes sets up bunrouter routes for the RestHeadSpec API
|
||||||
|
|||||||
@ -243,3 +243,158 @@ func GetUserMeta(ctx context.Context) (map[string]any, bool) {
|
|||||||
meta, ok := ctx.Value(UserMetaKey).(map[string]any)
|
meta, ok := ctx.Value(UserMetaKey).(map[string]any)
|
||||||
return meta, ok
|
return meta, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// // Handler adapters for resolvespec/restheadspec compatibility
|
||||||
|
// // These functions allow using NewAuthHandler and NewOptionalAuthHandler with custom handler abstractions
|
||||||
|
|
||||||
|
// // SpecHandlerAdapter is an interface for handler adapters that need authentication
|
||||||
|
// // Implement this interface to create adapters for custom handler types
|
||||||
|
// type SpecHandlerAdapter interface {
|
||||||
|
// // AdaptToHTTPHandler converts the custom handler to a standard http.Handler
|
||||||
|
// AdaptToHTTPHandler() http.Handler
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // ResolveSpecHandlerAdapter adapts a resolvespec/restheadspec handler method to http.Handler
|
||||||
|
// type ResolveSpecHandlerAdapter struct {
|
||||||
|
// // HandlerMethod is the method to call (e.g., handler.Handle, handler.HandleGet)
|
||||||
|
// HandlerMethod func(w any, r any, params map[string]string)
|
||||||
|
// // Params are the route parameters (e.g., {"schema": "public", "entity": "users"})
|
||||||
|
// Params map[string]string
|
||||||
|
// // RequestAdapter converts *http.Request to the custom Request interface
|
||||||
|
// // Use router.NewHTTPRequest from pkg/common/adapters/router
|
||||||
|
// RequestAdapter func(*http.Request) any
|
||||||
|
// // ResponseAdapter converts http.ResponseWriter to the custom ResponseWriter interface
|
||||||
|
// // Use router.NewHTTPResponseWriter from pkg/common/adapters/router
|
||||||
|
// ResponseAdapter func(http.ResponseWriter) any
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // AdaptToHTTPHandler implements SpecHandlerAdapter
|
||||||
|
// func (a *ResolveSpecHandlerAdapter) AdaptToHTTPHandler() http.Handler {
|
||||||
|
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// req := a.RequestAdapter(r)
|
||||||
|
// resp := a.ResponseAdapter(w)
|
||||||
|
// a.HandlerMethod(resp, req, a.Params)
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // WrapSpecHandler wraps a spec handler adapter with authentication
|
||||||
|
// // Use this to apply NewAuthHandler or NewOptionalAuthHandler to resolvespec/restheadspec handlers
|
||||||
|
// //
|
||||||
|
// // Example with required auth:
|
||||||
|
// //
|
||||||
|
// // adapter := &security.ResolveSpecHandlerAdapter{
|
||||||
|
// // HandlerMethod: handler.Handle,
|
||||||
|
// // Params: map[string]string{"schema": "public", "entity": "users"},
|
||||||
|
// // RequestAdapter: func(r *http.Request) any { return router.NewHTTPRequest(r) },
|
||||||
|
// // ResponseAdapter: func(w http.ResponseWriter) any { return router.NewHTTPResponseWriter(w) },
|
||||||
|
// // }
|
||||||
|
// // authHandler := security.WrapSpecHandler(securityList, adapter, false)
|
||||||
|
// // muxRouter.Handle("/api/users", authHandler)
|
||||||
|
// func WrapSpecHandler(securityList *SecurityList, adapter SpecHandlerAdapter, optional bool) http.Handler {
|
||||||
|
// httpHandler := adapter.AdaptToHTTPHandler()
|
||||||
|
// if optional {
|
||||||
|
// return NewOptionalAuthHandler(securityList, httpHandler)
|
||||||
|
// }
|
||||||
|
// return NewAuthHandler(securityList, httpHandler)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // MuxRouteBuilder helps build authenticated routes with Gorilla Mux
|
||||||
|
// type MuxRouteBuilder struct {
|
||||||
|
// securityList *SecurityList
|
||||||
|
// requestAdapter func(*http.Request) any
|
||||||
|
// responseAdapter func(http.ResponseWriter) any
|
||||||
|
// paramExtractor func(*http.Request) map[string]string
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // NewMuxRouteBuilder creates a route builder for Gorilla Mux with standard router adapters
|
||||||
|
// // Usage:
|
||||||
|
// //
|
||||||
|
// // builder := security.NewMuxRouteBuilder(securityList, router.NewHTTPRequest, router.NewHTTPResponseWriter)
|
||||||
|
// func NewMuxRouteBuilder(
|
||||||
|
// securityList *SecurityList,
|
||||||
|
// requestAdapter func(*http.Request) any,
|
||||||
|
// responseAdapter func(http.ResponseWriter) any,
|
||||||
|
// ) *MuxRouteBuilder {
|
||||||
|
// return &MuxRouteBuilder{
|
||||||
|
// securityList: securityList,
|
||||||
|
// requestAdapter: requestAdapter,
|
||||||
|
// responseAdapter: responseAdapter,
|
||||||
|
// paramExtractor: nil, // Will be set per route using mux.Vars
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // HandleAuth creates an authenticated route handler
|
||||||
|
// // pattern: the route pattern (e.g., "/{schema}/{entity}")
|
||||||
|
// // handler: the handler method to call (e.g., handler.Handle)
|
||||||
|
// // optional: true for optional auth (guest fallback), false for required auth (401 on failure)
|
||||||
|
// // methods: HTTP methods (e.g., "GET", "POST")
|
||||||
|
// //
|
||||||
|
// // Usage:
|
||||||
|
// //
|
||||||
|
// // builder.HandleAuth(router, "/{schema}/{entity}", handler.Handle, false, "POST")
|
||||||
|
// func (b *MuxRouteBuilder) HandleAuth(
|
||||||
|
// router interface {
|
||||||
|
// HandleFunc(pattern string, f func(http.ResponseWriter, *http.Request)) interface{ Methods(...string) interface{} }
|
||||||
|
// },
|
||||||
|
// pattern string,
|
||||||
|
// handlerMethod func(w any, r any, params map[string]string),
|
||||||
|
// optional bool,
|
||||||
|
// methods ...string,
|
||||||
|
// ) {
|
||||||
|
// router.HandleFunc(pattern, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// // Extract params using the registered extractor or default to empty map
|
||||||
|
// var params map[string]string
|
||||||
|
// if b.paramExtractor != nil {
|
||||||
|
// params = b.paramExtractor(r)
|
||||||
|
// } else {
|
||||||
|
// params = make(map[string]string)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// adapter := &ResolveSpecHandlerAdapter{
|
||||||
|
// HandlerMethod: handlerMethod,
|
||||||
|
// Params: params,
|
||||||
|
// RequestAdapter: b.requestAdapter,
|
||||||
|
// ResponseAdapter: b.responseAdapter,
|
||||||
|
// }
|
||||||
|
// authHandler := WrapSpecHandler(b.securityList, adapter, optional)
|
||||||
|
// authHandler.ServeHTTP(w, r)
|
||||||
|
// }).Methods(methods...)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // SetParamExtractor sets a custom parameter extractor function
|
||||||
|
// // For Gorilla Mux, you would use: builder.SetParamExtractor(mux.Vars)
|
||||||
|
// func (b *MuxRouteBuilder) SetParamExtractor(extractor func(*http.Request) map[string]string) {
|
||||||
|
// b.paramExtractor = extractor
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // SetupAuthenticatedSpecRoutes sets up all standard resolvespec/restheadspec routes with authentication
|
||||||
|
// // This is a convenience function that sets up the common route patterns
|
||||||
|
// //
|
||||||
|
// // Usage:
|
||||||
|
// //
|
||||||
|
// // security.SetupAuthenticatedSpecRoutes(router, handler, securityList, router.NewHTTPRequest, router.NewHTTPResponseWriter, mux.Vars)
|
||||||
|
// func SetupAuthenticatedSpecRoutes(
|
||||||
|
// router interface {
|
||||||
|
// HandleFunc(pattern string, f func(http.ResponseWriter, *http.Request)) interface{ Methods(...string) interface{} }
|
||||||
|
// },
|
||||||
|
// handler interface {
|
||||||
|
// Handle(w any, r any, params map[string]string)
|
||||||
|
// HandleGet(w any, r any, params map[string]string)
|
||||||
|
// },
|
||||||
|
// securityList *SecurityList,
|
||||||
|
// requestAdapter func(*http.Request) any,
|
||||||
|
// responseAdapter func(http.ResponseWriter) any,
|
||||||
|
// paramExtractor func(*http.Request) map[string]string,
|
||||||
|
// ) {
|
||||||
|
// builder := NewMuxRouteBuilder(securityList, requestAdapter, responseAdapter)
|
||||||
|
// builder.SetParamExtractor(paramExtractor)
|
||||||
|
|
||||||
|
// // POST /{schema}/{entity}
|
||||||
|
// builder.HandleAuth(router, "/{schema}/{entity}", handler.Handle, false, "POST")
|
||||||
|
|
||||||
|
// // POST /{schema}/{entity}/{id}
|
||||||
|
// builder.HandleAuth(router, "/{schema}/{entity}/{id}", handler.Handle, false, "POST")
|
||||||
|
|
||||||
|
// // GET /{schema}/{entity}
|
||||||
|
// builder.HandleAuth(router, "/{schema}/{entity}", handler.HandleGet, false, "GET")
|
||||||
|
// }
|
||||||
|
|||||||
@ -68,12 +68,14 @@ func ExampleDatabaseSecurity(gormDB interface{}, sqlDB *sql.DB) (http.Handler, e
|
|||||||
// Step 5: Setup security
|
// Step 5: Setup security
|
||||||
securityList := SetupSecurityProvider(handler, provider)
|
securityList := SetupSecurityProvider(handler, provider)
|
||||||
|
|
||||||
// Step 6: Create router and setup routes
|
// Step 6: Create router and setup routes with authentication
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
restheadspec.SetupMuxRoutes(router, handler)
|
authMiddleware := func(h http.Handler) http.Handler {
|
||||||
|
return NewAuthHandler(securityList, h)
|
||||||
|
}
|
||||||
|
restheadspec.SetupMuxRoutes(router, handler, authMiddleware)
|
||||||
|
|
||||||
// Step 7: Apply middleware in correct order
|
// Step 7: Apply additional security middleware
|
||||||
router.Use(NewAuthMiddleware(securityList))
|
|
||||||
router.Use(SetSecurityMiddleware(securityList))
|
router.Use(SetSecurityMiddleware(securityList))
|
||||||
|
|
||||||
return router, nil
|
return router, nil
|
||||||
@ -95,9 +97,11 @@ func ExampleHeaderAuthentication(gormDB interface{}, sqlDB *sql.DB) (*mux.Router
|
|||||||
securityList := SetupSecurityProvider(handler, provider)
|
securityList := SetupSecurityProvider(handler, provider)
|
||||||
|
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
restheadspec.SetupMuxRoutes(router, handler)
|
authMiddleware := func(h http.Handler) http.Handler {
|
||||||
|
return NewAuthHandler(securityList, h)
|
||||||
|
}
|
||||||
|
restheadspec.SetupMuxRoutes(router, handler, authMiddleware)
|
||||||
|
|
||||||
router.Use(NewAuthMiddleware(securityList))
|
|
||||||
router.Use(SetSecurityMiddleware(securityList))
|
router.Use(SetSecurityMiddleware(securityList))
|
||||||
|
|
||||||
return router, nil
|
return router, nil
|
||||||
@ -150,9 +154,11 @@ func ExampleConfigSecurity(gormDB interface{}) (*mux.Router, error) {
|
|||||||
securityList := SetupSecurityProvider(handler, provider)
|
securityList := SetupSecurityProvider(handler, provider)
|
||||||
|
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
restheadspec.SetupMuxRoutes(router, handler)
|
authMiddleware := func(h http.Handler) http.Handler {
|
||||||
|
return NewAuthHandler(securityList, h)
|
||||||
|
}
|
||||||
|
restheadspec.SetupMuxRoutes(router, handler, authMiddleware)
|
||||||
|
|
||||||
router.Use(NewAuthMiddleware(securityList))
|
|
||||||
router.Use(SetSecurityMiddleware(securityList))
|
router.Use(SetSecurityMiddleware(securityList))
|
||||||
|
|
||||||
return router, nil
|
return router, nil
|
||||||
@ -282,10 +288,12 @@ func CompleteServerExample(gormDB interface{}, sqlDB *sql.DB) http.Handler {
|
|||||||
// Add auth routes (login/logout)
|
// Add auth routes (login/logout)
|
||||||
SetupAuthRoutes(router, securityList)
|
SetupAuthRoutes(router, securityList)
|
||||||
|
|
||||||
// Add API routes with security middleware
|
// Add API routes with authentication
|
||||||
apiRouter := router.PathPrefix("/api").Subrouter()
|
apiRouter := router.PathPrefix("/api").Subrouter()
|
||||||
restheadspec.SetupMuxRoutes(apiRouter, handler)
|
authMiddleware := func(h http.Handler) http.Handler {
|
||||||
apiRouter.Use(NewAuthMiddleware(securityList))
|
return NewAuthHandler(securityList, h)
|
||||||
|
}
|
||||||
|
restheadspec.SetupMuxRoutes(apiRouter, handler, authMiddleware)
|
||||||
apiRouter.Use(SetSecurityMiddleware(securityList))
|
apiRouter.Use(SetSecurityMiddleware(securityList))
|
||||||
|
|
||||||
return router
|
return router
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user