Added openapi spec

This commit is contained in:
Hein
2025-12-09 12:01:21 +02:00
parent 0f05202438
commit d188f49126
9 changed files with 2428 additions and 16 deletions

View File

@@ -22,11 +22,12 @@ type FallbackHandler func(w common.ResponseWriter, r common.Request, params map[
// Handler handles API requests using database and model abstractions
type Handler struct {
db common.Database
registry common.ModelRegistry
nestedProcessor *common.NestedCUDProcessor
hooks *HookRegistry
fallbackHandler FallbackHandler
db common.Database
registry common.ModelRegistry
nestedProcessor *common.NestedCUDProcessor
hooks *HookRegistry
fallbackHandler FallbackHandler
openAPIGenerator func() (string, error)
}
// NewHandler creates a new API handler with database and registry abstractions
@@ -75,6 +76,12 @@ func (h *Handler) Handle(w common.ResponseWriter, r common.Request, params map[s
}
}()
// Check for ?openapi query parameter
if r.UnderlyingRequest().URL.Query().Get("openapi") != "" {
h.HandleOpenAPI(w, r)
return
}
ctx := r.UnderlyingRequest().Context()
body, err := r.Body()
@@ -156,6 +163,12 @@ func (h *Handler) HandleGet(w common.ResponseWriter, r common.Request, params ma
}
}()
// Check for ?openapi query parameter
if r.UnderlyingRequest().URL.Query().Get("openapi") != "" {
h.HandleOpenAPI(w, r)
return
}
schema := params["schema"]
entity := params["entity"]
@@ -1433,3 +1446,28 @@ func toSnakeCase(s string) string {
}
return strings.ToLower(result.String())
}
// HandleOpenAPI generates and returns the OpenAPI specification
func (h *Handler) HandleOpenAPI(w common.ResponseWriter, r common.Request) {
if h.openAPIGenerator == nil {
logger.Error("OpenAPI generator not configured")
h.sendError(w, http.StatusInternalServerError, "openapi_not_configured", "OpenAPI generation not configured", nil)
return
}
spec, err := h.openAPIGenerator()
if err != nil {
logger.Error("Failed to generate OpenAPI spec: %v", err)
h.sendError(w, http.StatusInternalServerError, "openapi_generation_error", "Failed to generate OpenAPI specification", err)
return
}
w.SetHeader("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(spec))
}
// SetOpenAPIGenerator sets the OpenAPI generator function
func (h *Handler) SetOpenAPIGenerator(generator func() (string, error)) {
h.openAPIGenerator = generator
}

View File

@@ -46,6 +46,16 @@ type MiddlewareFunc func(http.Handler) http.Handler
// authMiddleware is optional - if provided, routes will be protected with the middleware
// Example: SetupMuxRoutes(router, handler, func(h http.Handler) http.Handler { return security.NewAuthHandler(securityList, h) })
func SetupMuxRoutes(muxRouter *mux.Router, handler *Handler, authMiddleware MiddlewareFunc) {
// Add global /openapi route
openAPIHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
corsConfig := common.DefaultCORSConfig()
respAdapter := router.NewHTTPResponseWriter(w)
common.SetCORSHeaders(respAdapter, corsConfig)
reqAdapter := router.NewHTTPRequest(r)
handler.HandleOpenAPI(respAdapter, reqAdapter)
})
muxRouter.Handle("/openapi", openAPIHandler).Methods("GET", "OPTIONS")
// Get all registered models from the registry
allModels := handler.registry.GetAllModels()
@@ -201,12 +211,27 @@ func ExampleWithBun(bunDB *bun.DB) {
func SetupBunRouterRoutes(bunRouter *router.StandardBunRouterAdapter, handler *Handler) {
r := bunRouter.GetBunRouter()
// Get all registered models from the registry
allModels := handler.registry.GetAllModels()
// CORS config
corsConfig := common.DefaultCORSConfig()
// Add global /openapi route
r.Handle("GET", "/openapi", func(w http.ResponseWriter, req bunrouter.Request) error {
respAdapter := router.NewHTTPResponseWriter(w)
common.SetCORSHeaders(respAdapter, corsConfig)
reqAdapter := router.NewHTTPRequest(req.Request)
handler.HandleOpenAPI(respAdapter, reqAdapter)
return nil
})
r.Handle("OPTIONS", "/openapi", func(w http.ResponseWriter, req bunrouter.Request) error {
respAdapter := router.NewHTTPResponseWriter(w)
common.SetCORSHeaders(respAdapter, corsConfig)
return nil
})
// Get all registered models from the registry
allModels := handler.registry.GetAllModels()
// Loop through each registered model and create explicit routes
for fullName := range allModels {
// Parse the full name (e.g., "public.users" or just "users")