Make web server optional and allow to use a random port

This commit is contained in:
Miguel de la Cruz 2023-07-11 15:47:01 +02:00
parent 1cc687395c
commit eaca9c1691
8 changed files with 56 additions and 25 deletions

View file

@ -74,8 +74,8 @@ $ make run
- [X] Health endpoint - [X] Health endpoint
- [X] Next birthdays endpoint - [X] Next birthdays endpoint
- [ ] Birthday list endpoint - [ ] Birthday list endpoint
- [ ] Allow to use a random port in web tests - [X] Allow to use a random port in web tests
- [ ] Web server should be optional - [X] Web server should be optional
- [ ] Create different message systems to use with the bot - [ ] Create different message systems to use with the bot
- [X] Telegram - [X] Telegram
- [ ] Email - [ ] Email

View file

@ -1,5 +1,6 @@
--- ---
web: web:
enabled: true
port: 8080 port: 8080
birthdays: birthdays:

View file

@ -111,14 +111,11 @@ func (lc *LoggerConfig) IsValid() error {
} }
type WebConfig struct { type WebConfig struct {
Enabled bool `yaml:"enabled"`
Port int `yaml:"port"` Port int `yaml:"port"`
} }
func (wc *WebConfig) SetDefaults() { func (wc *WebConfig) SetDefaults() {}
if wc.Port == 0 {
wc.Port = 8080
}
}
func (wc *WebConfig) IsValid() error { func (wc *WebConfig) IsValid() error {
return nil return nil

View file

@ -28,9 +28,8 @@ func testConfig(t *testing.T) *model.Config {
require.NoError(t, f.Close()) require.NoError(t, f.Close())
require.NoError(t, os.Remove(f.Name())) require.NoError(t, os.Remove(f.Name()))
// ToDo: allow for a random port to be used
return &model.Config{ return &model.Config{
Web: &model.WebConfig{Port: 9090}, Web: &model.WebConfig{Enabled: true, Port: 0},
Birthdays: &model.BirthdaysConfig{File: f.Name()}, Birthdays: &model.BirthdaysConfig{File: f.Name()},
} }
} }

View file

@ -108,10 +108,15 @@ func New(options ...Option) (*Server, error) {
} }
} }
if srv.WebServer == nil { if srv.WebServer == nil && srv.Config.Web.Enabled {
srv.Logger.Debug("creating web server") srv.Logger.Debug("creating web server")
srv.WebServer = NewWebServer(srv) ws, err := NewWebServer(srv)
if err != nil {
return nil, fmt.Errorf("cannot create web server: %w", err)
}
srv.WebServer = ws
} }
return srv, nil return srv, nil
@ -120,9 +125,11 @@ func New(options ...Option) (*Server, error) {
func (s *Server) Start() error { func (s *Server) Start() error {
s.Logger.Info("starting server") s.Logger.Info("starting server")
if s.WebServer != nil {
if err := s.WebServer.Start(); err != nil { if err := s.WebServer.Start(); err != nil {
return fmt.Errorf("cannot start web server: %w", err) return fmt.Errorf("cannot start web server: %w", err)
} }
}
for _, worker := range s.workers { for _, worker := range s.workers {
worker.Start() worker.Start()
@ -136,9 +143,11 @@ func (s *Server) Start() error {
func (s *Server) Stop() error { func (s *Server) Stop() error {
s.Logger.Info("stopping server") s.Logger.Info("stopping server")
if s.WebServer != nil {
if err := s.WebServer.Stop(); err != nil { if err := s.WebServer.Stop(); err != nil {
return fmt.Errorf("cannot stop web server: %w", err) return fmt.Errorf("cannot stop web server: %w", err)
} }
}
for _, worker := range s.workers { for _, worker := range s.workers {
worker.Stop() worker.Stop()

View file

@ -15,6 +15,7 @@ import (
func TestNotify(t *testing.T) { func TestNotify(t *testing.T) {
th := SetupTestHelper(t) th := SetupTestHelper(t)
defer th.TearDown() defer th.TearDown()
t.Run("should correctly use the notification services to notify", func(t *testing.T) { t.Run("should correctly use the notification services to notify", func(t *testing.T) {
birthday := th.srv.birthdays[0] birthday := th.srv.birthdays[0]
th.mockNotificationService. th.mockNotificationService.

View file

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"net"
"net/http" "net/http"
"github.com/charmbracelet/log" "github.com/charmbracelet/log"
@ -11,33 +12,37 @@ import (
type WebServer struct { type WebServer struct {
server *Server server *Server
listener net.Listener
logger *log.Logger logger *log.Logger
httpServer *http.Server httpServer *http.Server
} }
func NewWebServer(server *Server) *WebServer { func NewWebServer(server *Server) (*WebServer, error) {
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", server.Config.Web.Port))
if err != nil {
return nil, fmt.Errorf("cannot create listener: %w", err)
}
ws := &WebServer{ ws := &WebServer{
server: server, server: server,
listener: listener,
logger: server.Logger, logger: server.Logger,
httpServer: &http.Server{
Addr: fmt.Sprintf(":%d", server.Config.Web.Port),
},
} }
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("/health", ws.healthHandler) mux.HandleFunc("/health", ws.healthHandler)
mux.HandleFunc("/next_birthdays", ws.nextBirthdayHandler) mux.HandleFunc("/next_birthdays", ws.nextBirthdayHandler)
ws.httpServer.Handler = mux ws.httpServer = &http.Server{Handler: mux}
return ws return ws, nil
} }
func (ws *WebServer) Start() error { func (ws *WebServer) Start() error {
ws.logger.Debug("starting web server") ws.logger.Debug("starting web server")
go func() { go func() {
if err := ws.httpServer.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { if err := ws.httpServer.Serve(ws.listener); err != nil && !errors.Is(err, http.ErrServerClosed) {
ws.logger.Fatal("cannot start web server", "error", err) ws.logger.Fatal("cannot start web server", "error", err)
} }
}() }()
@ -55,6 +60,10 @@ func (ws *WebServer) Stop() error {
return nil return nil
} }
func (ws *WebServer) Port() int {
return ws.listener.Addr().(*net.TCPAddr).Port
}
func (ws *WebServer) healthHandler(w http.ResponseWriter, r *http.Request) { func (ws *WebServer) healthHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "OK") fmt.Fprint(w, "OK")
} }

15
server/web_test.go Normal file
View file

@ -0,0 +1,15 @@
package server
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestPort(t *testing.T) {
th := SetupTestHelper(t)
defer th.TearDown()
port := th.srv.WebServer.Port()
require.NotEmpty(t, port)
}