From b70819e976b4e439b9e32afcbe98b0539ba6b36a Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 5 Jul 2025 15:04:39 +0300 Subject: [PATCH] Add GeneralServer implementation for API request routing based on versioning --- .../{hanle_multi.go => handle_multi.go} | 55 +++++++++++++++---- 1 file changed, 45 insertions(+), 10 deletions(-) rename core/general_server/{hanle_multi.go => handle_multi.go} (54%) diff --git a/core/general_server/hanle_multi.go b/core/general_server/handle_multi.go similarity index 54% rename from core/general_server/hanle_multi.go rename to core/general_server/handle_multi.go index aac1417..ddf3381 100644 --- a/core/general_server/hanle_multi.go +++ b/core/general_server/handle_multi.go @@ -1,3 +1,16 @@ +// Package general_server provides an API request router based on versioning and custom layers. +// +// The GeneralServer distributes incoming HTTP requests to specific registered servers +// depending on the API version or defined logical layer. To operate properly, additional +// servers must be registered using the InitGeneral function or AppendToArray method. +// +// All registered servers must implement the GeneralServerApiContract interface to ensure +// correct interaction. The GeneralServer itself implements this interface and can be +// passed as an HTTP handler. +// +// If the requested version is not explicitly registered but matches a configured logical +// layer, the server will fallback to the latest registered version for that layer. +// Otherwise, an HTTP 400 error is returned. package general_server import ( @@ -12,52 +25,68 @@ import ( "github.com/go-chi/chi/v5" ) +// serversApiVer is a type alias for string, used to represent API version strings in the GeneralServer. type serversApiVer string +// GeneralServerApiContract defines the interface for servers that can be registered type GeneralServerApiContract interface { + // GetVersion returns the API version of the server. GetVersion() string + // Handle and HandleList methods are used to forward requests. Handle(w http.ResponseWriter, r *http.Request) HandleList(w http.ResponseWriter, r *http.Request) } +// GeneralServerContarct extends the GeneralServerApiContract with a method to append new servers. +// This interface is only for general server initialization and does not need to be implemented by individual servers. type GeneralServerContarct interface { GeneralServerApiContract + // AppendToArray adds a new server to the GeneralServer's internal map. AppendToArray(GeneralServerApiContract) error } +// GeneralServer implements the GeneralServerApiContract and serves as a router for different API versions. type GeneralServer struct { w http.ResponseWriter r *http.Request + // servers holds the registered servers by their API version. + // The key is the version string, and the value is the server implementing GeneralServerApi servers map[serversApiVer]GeneralServerApiContract log slog.Logger cfg *config.ConfigConf } -// structure only for initialization +// GeneralServerInit structure only for initialization general server. type GeneralServerInit struct { Log slog.Logger Config *config.ConfigConf } +// InitGeneral initializes a new GeneralServer with the provided configuration and registered servers. func InitGeneral(o *GeneralServerInit, servers ...GeneralServerApiContract) *GeneralServer { general := &GeneralServer{ servers: make(map[serversApiVer]GeneralServerApiContract), cfg: o.Config, log: o.Log, } + + // register the provided servers + // s is each server implementing GeneralServerApiContract, this is not a general server for _, s := range servers { general.servers[serversApiVer(s.GetVersion())] = s } return general } +// GetVersion returns the API version of the GeneralServer, which is "general". func (s *GeneralServer) GetVersion() string { return "general" } +// AppendToArray adds a new server to the GeneralServer's internal map. func (s *GeneralServer) AppendToArray(server GeneralServerApiContract) error { if _, exist := s.servers[serversApiVer(server.GetVersion())]; !exist { s.servers[serversApiVer(server.GetVersion())] = server @@ -66,6 +95,8 @@ func (s *GeneralServer) AppendToArray(server GeneralServerApiContract) error { return errors.New("server with this version is already exist") } +// Handle processes incoming HTTP requests, routing them to the appropriate server based on the API version. +// It checks if the requested version is registered and handles the request accordingly. func (s *GeneralServer) Handle(w http.ResponseWriter, r *http.Request) { s.w = w s.r = r @@ -83,17 +114,25 @@ func (s *GeneralServer) Handle(w http.ResponseWriter, r *http.Request) { s.log.Debug("Received request") + // transfer control to the server if srv, ok := s.servers[serversApiVer(serverReqApiVer)]; ok { srv.Handle(w, r) return } + // if the requested version is not registered, check if it matches a logical layer + // and use the latest version for that layer if available + // this allows for custom layers to be defined in the configuration + // and used as a fallback for unsupported versions + // this is useful for cases where the API version is not explicitly registered + // but the logical layer is defined in the configuration if slices.Contains(s.cfg.Layers, serverReqApiVer) { if srv, ok := s.servers[serversApiVer(s.cfg.LatestVer)]; ok { s.log.Debug("Using latest version under custom layer", slog.String("layer", serverReqApiVer), slog.String("fallback-version", s.cfg.LatestVer), ) + // transfer control to the latest version server under the custom layer srv.Handle(w, r) return } @@ -104,6 +143,7 @@ func (s *GeneralServer) Handle(w http.ResponseWriter, r *http.Request) { s.writeJSONError(http.StatusBadRequest, "unsupported API version") } +// HandleList processes incoming HTTP requests for listing commands, routing them to the appropriate server based on the API version. func (s *GeneralServer) HandleList(w http.ResponseWriter, r *http.Request) { s.w = w s.r = r @@ -122,6 +162,7 @@ func (s *GeneralServer) HandleList(w http.ResponseWriter, r *http.Request) { log.Debug("Received request") + // transfer control to the server if srv, ok := s.servers[serversApiVer(serverReqApiVer)]; ok { srv.HandleList(w, r) return @@ -133,6 +174,7 @@ func (s *GeneralServer) HandleList(w http.ResponseWriter, r *http.Request) { slog.String("layer", serverReqApiVer), slog.String("fallback-version", s.cfg.LatestVer), ) + // transfer control to the latest version server under the custom layer srv.HandleList(w, r) return } @@ -143,15 +185,8 @@ func (s *GeneralServer) HandleList(w http.ResponseWriter, r *http.Request) { s.writeJSONError(http.StatusBadRequest, "unsupported API version") } -// func (s *GeneralServer) _errNotFound() { -// s.writeJSONError(http.StatusBadRequest, "invalid request") -// s.log.Error("HTTP request error", -// slog.String("remote", s.r.RemoteAddr), -// slog.String("method", s.r.Method), -// slog.String("url", s.r.URL.String()), -// slog.Int("status", http.StatusBadRequest)) -// } - +// writeJSONError writes a JSON error response to the HTTP response writer. +// It sets the Content-Type to application/json, writes the specified HTTP status code, func (s *GeneralServer) writeJSONError(status int, msg string) { s.w.Header().Set("Content-Type", "application/json") s.w.WriteHeader(status)