Initial bootrap of application

- Creates scaffold for router and server
- Main program set up for environment, OS signal listener
- Makefile generic commands
This commit is contained in:
2021-06-17 17:06:21 -04:00
parent 61e39b6c2f
commit 336b21a835
14 changed files with 307 additions and 2 deletions
+8
View File
@@ -0,0 +1,8 @@
package http
import "errors"
var (
ErrUnknownError = errors.New("unknown error occurred")
ErrUnrecognizedCode = errors.New("unrecognized status code")
)
+13
View File
@@ -0,0 +1,13 @@
package http
import "net/http"
func (s *server) handleHealth(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
return
}
func (s *server) handleReadiness(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
return
}
+80
View File
@@ -0,0 +1,80 @@
package http
import (
"context"
"fmt"
"net/http"
"time"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/sirupsen/logrus"
)
// server holds configurations and services used by the HTTP server and handlers.
type server struct {
port int
logger *logrus.Logger
environment string
server *http.Server
}
// NewServer initializes a new server with the required configurations.
func NewServer(port int, logger *logrus.Logger, environment string) *server {
return &server{
port: port,
logger: logger,
environment: environment,
server: &http.Server{
Addr: fmt.Sprintf(":%d", port),
ReadTimeout: 60 * time.Second,
WriteTimeout: 60 * time.Second,
},
}
}
// Run builds the routes and starts the server listening on the configured port.
func (s *server) Run() error {
s.server.Handler = s.buildRoutes()
s.logger.Infof("starting server on port: %d", s.port)
return s.server.ListenAndServe()
}
// Shutdown calls Shutdown on the http.Server which attempts to gracefully shutdown. If the context deadline is exceeded any cotnext errors are returned.
func (s *server) Shutdown(ctx context.Context) error {
return s.server.Shutdown(ctx)
}
// buildRoutes configures a router with middleware, headers, and routes.
func (s *server) buildRoutes() http.Handler {
r := s.configureRouter()
r.Get("/health", s.handleHealth)
r.Get("/ready", s.handleReadiness)
r.Group(func(r chi.Router) {
r.Route("/v1", func(r chi.Router) {
// r.Group(s.produceGroup)
})
})
return r
}
// configureRouter returns a configured chi.Router with sane defaults.
func (s *server) configureRouter() chi.Router {
r := chi.NewRouter()
r.Use(middleware.RequestID)
r.Use(middleware.RealIP)
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(setResponseHeaders(map[string]string{
"Content-Type": "application/json",
"Allow-Access-Control-Origin": "*",
"Allow-Access-Control-Method": "OPTIONS, GET, POST, DELETE",
"Allow-Access-Control-Headers": "Origin, Content-Type, Accept",
"Access-Control-Max-Age": "600",
}))
return r
}
+16
View File
@@ -0,0 +1,16 @@
package http
import "net/http"
// setResponseHeaders is a middleware that accepts a map[string]string of headers:values and sets them for all responses.
func setResponseHeaders(headers map[string]string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
for header, value := range headers {
w.Header().Set(header, value)
}
next.ServeHTTP(w, r)
})
}
}
+43
View File
@@ -0,0 +1,43 @@
package http
import (
"context"
"encoding/json"
"net/http"
)
func (s *server) writeSuccess(ctx context.Context, w http.ResponseWriter, data interface{}, status int) error {
if status != 0 {
w.WriteHeader(status)
}
if data != nil {
err := json.NewEncoder(w).Encode(data)
if err != nil {
return err
}
}
return nil
}
func (s *server) writeError(ctx context.Context, w http.ResponseWriter, err error, code int) error {
if http.StatusText(code) == "" {
return ErrUnrecognizedCode
}
if err == nil {
err = ErrUnknownError
}
s.logger.Errorf("request failed: %v", err.Error())
w.WriteHeader(code)
res := struct {
Message string `json:"message"`
}{
Message: http.StatusText(code),
}
return json.NewEncoder(w).Encode(res)
}