diff --git a/web/server.go b/web/server.go index 8007bdf..083f542 100644 --- a/web/server.go +++ b/web/server.go @@ -1,179 +1,186 @@ package web import ( - "encoding/json" - "fmt" - "net/http" - "strings" + "encoding/json" + "fmt" + "net/http" + "strings" - "github.com/rs/zerolog/log" - "code.severnaya.net/kordophone-mock/v2/server" + "code.severnaya.net/kordophone-mock/v2/server" + "github.com/rs/zerolog/log" ) type MockHTTPServerConfiguration struct { - AuthEnabled bool + AuthEnabled bool } type MockHTTPServer struct { - Server server.Server - mux http.ServeMux - authEnabled bool + Server server.Server + mux http.ServeMux + authEnabled bool } type AuthError struct { - message string + message string } func (e *AuthError) Error() string { - return e.message + return e.message } func (m *MockHTTPServer) logRequest(r *http.Request, extras ...string) { - log.Debug().Msgf("%s %s %s", r.Method, r.URL.Path, strings.Join(extras, " ")) + log.Debug().Msgf("%s %s %s", r.Method, r.URL.Path, strings.Join(extras, " ")) } func (m *MockHTTPServer) checkAuthentication(r *http.Request) error { - if !m.authEnabled { - return nil - } + if !m.authEnabled { + return nil + } - // Check for Authorization header - authHeader := r.Header.Get("Authorization") - if authHeader == "" { - return &AuthError{"Missing Authorization header"} - } + // Check for Authorization header + authHeader := r.Header.Get("Authorization") + if authHeader == "" { + return &AuthError{"Missing Authorization header"} + } - // Check for "Bearer" prefix - if authHeader[:7] != "Bearer " { - return &AuthError{"Invalid Authorization header"} - } + // Check for "Bearer" prefix + if authHeader[:7] != "Bearer " { + return &AuthError{"Invalid Authorization header"} + } - // Check for valid token - token := authHeader[7:] - if !m.Server.CheckBearerToken(token) { - return &AuthError{"Invalid token"} - } + // Check for valid token + token := authHeader[7:] + if !m.Server.CheckBearerToken(token) { + return &AuthError{"Invalid token"} + } - return nil + return nil } func (m *MockHTTPServer) handleVersion(w http.ResponseWriter, r *http.Request) { - fmt.Fprintf(w, "%s", m.Server.Version()) + fmt.Fprintf(w, "%s", m.Server.Version()) } func (m *MockHTTPServer) handleStatus(w http.ResponseWriter, r *http.Request) { - if err := m.checkAuthentication(r); err != nil { - log.Error().Err(err).Msg("Status: Error checking authentication") - http.Error(w, err.Error(), http.StatusUnauthorized) - return - } + if err := m.checkAuthentication(r); err != nil { + log.Error().Err(err).Msg("Status: Error checking authentication") + http.Error(w, err.Error(), http.StatusUnauthorized) + return + } - fmt.Fprintf(w, "OK") + fmt.Fprintf(w, "OK") } func (m *MockHTTPServer) handleConversations(w http.ResponseWriter, r *http.Request) { - convos := m.Server.Conversations() + convos := m.Server.Conversations() - // Encode convos as JSON - jsonData, err := json.Marshal(convos) - if err != nil { - log.Error().Err(err).Msg("Error marshalling conversations") - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } + // Encode convos as JSON + jsonData, err := json.Marshal(convos) + if err != nil { + log.Error().Err(err).Msg("Error marshalling conversations") + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } - // Write JSON to response - w.Header().Set("Content-Type", "application/json") - w.Write(jsonData) + // Write JSON to response + w.Header().Set("Content-Type", "application/json") + w.Write(jsonData) } func (m *MockHTTPServer) handleMessages(w http.ResponseWriter, r *http.Request) { - // TODO handle optional "limit", "beforeDate", "beforeMessageGUID", and "afterMessageGUID" parameters + // TODO handle optional "limit", "beforeDate", "beforeMessageGUID", and "afterMessageGUID" parameters - guid := r.URL.Query().Get("guid") - if len(guid) == 0 { - log.Error().Msg("handleMessage: Got empty guid parameter") - http.Error(w, "no guid parameter specified", http.StatusBadRequest) - return - } + guid := r.URL.Query().Get("guid") + if len(guid) == 0 { + log.Error().Msg("handleMessage: Got empty guid parameter") + http.Error(w, "no guid parameter specified", http.StatusBadRequest) + return + } - conversation, err := m.Server.ConversationForGUID(guid) - if err != nil { - log.Error().Err(err).Msgf("handleMessage: Error getting conversation (%s)", guid) - http.Error(w, "conversation not found", http.StatusBadRequest) - return - } + conversation, err := m.Server.ConversationForGUID(guid) + if err != nil { + log.Error().Err(err).Msgf("handleMessage: Error getting conversation (%s)", guid) + http.Error(w, "conversation not found", http.StatusBadRequest) + return + } - messages := m.Server.MessagesForConversation(*conversation) + messages := m.Server.MessagesForConversation(*conversation) - jsonData, err := json.Marshal(messages) - if err != nil { - log.Error().Err(err).Msg("Error marshalling messages") - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } + jsonData, err := json.Marshal(messages) + if err != nil { + log.Error().Err(err).Msg("Error marshalling messages") + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } - // Write JSON to response - w.Header().Set("Content-Type", "application/json") - w.Write(jsonData) + // Write JSON to response + w.Header().Set("Content-Type", "application/json") + w.Write(jsonData) } func (m *MockHTTPServer) handleAuthenticate(w http.ResponseWriter, r *http.Request) { - // Decode request body as AuthenticationRequest - var authReq AuthenticationRequest - err := json.NewDecoder(r.Body).Decode(&authReq) - if err != nil { - log.Error().Err(err).Msg("Authenticate: Error decoding request body") - http.Error(w, err.Error(), http.StatusBadRequest) - return - } + // Decode request body as AuthenticationRequest + var authReq AuthenticationRequest + err := json.NewDecoder(r.Body).Decode(&authReq) + if err != nil { + log.Error().Err(err).Msg("Authenticate: Error decoding request body") + http.Error(w, err.Error(), http.StatusBadRequest) + return + } - // Authenticate - token, err := m.Server.Authenticate(authReq.Username, authReq.Password) - if err != nil { - log.Error().Err(err).Msg("Authenticate: Error authenticating") - http.Error(w, err.Error(), http.StatusUnauthorized) - return - } + // Authenticate + token, err := m.Server.Authenticate(authReq.Username, authReq.Password) + if err != nil { + log.Error().Err(err).Msg("Authenticate: Error authenticating") + http.Error(w, err.Error(), http.StatusUnauthorized) + return + } - // Write response - w.Header().Set("Content-Type", "application/json") + // Write response + w.Header().Set("Content-Type", "application/json") - // Encode token as JSON - jsonData, err := json.Marshal(token) - if err != nil { - log.Error().Err(err).Msg("Error marshalling token") - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } + // Encode token as JSON + jsonData, err := json.Marshal(token) + if err != nil { + log.Error().Err(err).Msg("Error marshalling token") + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } - // Write JSON to response - w.Write(jsonData) + // Write JSON to response + w.Write(jsonData) } func (m *MockHTTPServer) handleNotFound(w http.ResponseWriter, r *http.Request) { - log.Error().Msgf("Unimplemented API endpoint: %s %s", r.Method, r.URL.Path) - http.NotFound(w, r) + log.Error().Msgf("Unimplemented API endpoint: %s %s", r.Method, r.URL.Path) + http.NotFound(w, r) +} + +func (m *MockHTTPServer) handlePollUpdates(w http.ResponseWriter, r *http.Request) { + // Stub: return 205 (Nothing to report) + w.WriteHeader(http.StatusResetContent) } func (m *MockHTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { - m.logRequest(r, r.URL.Query().Encode()) - m.mux.ServeHTTP(w, r) + m.logRequest(r, r.URL.Query().Encode()) + m.mux.ServeHTTP(w, r) } func NewMockHTTPServer(config MockHTTPServerConfiguration) *MockHTTPServer { - this := MockHTTPServer{ - Server: *server.NewServer(), - mux: *http.NewServeMux(), - authEnabled: config.AuthEnabled, - } + this := MockHTTPServer{ + Server: *server.NewServer(), + mux: *http.NewServeMux(), + authEnabled: config.AuthEnabled, + } - this.mux.Handle("/version", http.HandlerFunc(this.handleVersion)) - this.mux.Handle("/conversations", http.HandlerFunc(this.handleConversations)) - this.mux.Handle("/status", http.HandlerFunc(this.handleStatus)) - this.mux.Handle("/authenticate", http.HandlerFunc(this.handleAuthenticate)) - this.mux.Handle("/messages", http.HandlerFunc(this.handleMessages)) - this.mux.Handle("/", http.HandlerFunc(this.handleNotFound)) + this.mux.Handle("/version", http.HandlerFunc(this.handleVersion)) + this.mux.Handle("/conversations", http.HandlerFunc(this.handleConversations)) + this.mux.Handle("/status", http.HandlerFunc(this.handleStatus)) + this.mux.Handle("/authenticate", http.HandlerFunc(this.handleAuthenticate)) + this.mux.Handle("/messages", http.HandlerFunc(this.handleMessages)) + this.mux.Handle("/pollUpdates", http.HandlerFunc(this.handlePollUpdates)) - return &this + this.mux.Handle("/", http.HandlerFunc(this.handleNotFound)) + + return &this }