diff --git a/_example/cleanarch/Makefile b/_example/cleanarch/Makefile index ed8dc23..988d391 100644 --- a/_example/cleanarch/Makefile +++ b/_example/cleanarch/Makefile @@ -12,14 +12,13 @@ help: .PHONY: build build: ## Build go build -o $(BINARY) $(LDFLAGS) - - +{{ if .docker }} .PHONY: docker docker: ## Build the docker image docker build -t $(BINARY):latest -t $(BINARY):$(VERSION) \ --build-arg build=$(BUILD) --build-arg version=$(VERSION) \ -f Dockerfile --no-cache . - +{{ end }} .PHONY: test test: ## Run the test suite go test ./... diff --git a/_example/cleanarch/cmd/root.go b/_example/cleanarch/cmd/root.go index 0d7fc4f..d4bce49 100644 --- a/_example/cleanarch/cmd/root.go +++ b/_example/cleanarch/cmd/root.go @@ -1,6 +1,8 @@ package cmd import ( + "strings" + "github.com/onrik/logrus/filename" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -21,10 +23,11 @@ func AddLoggerFlags(c *cobra.Command) { func AddServerFlags(c *cobra.Command) { c.PersistentFlags().String("server.host", "127.0.0.1", "host on which the server should listen") c.PersistentFlags().Int("server.port", 8080, "port on which the server should listen") - c.PersistentFlags().Bool("server.debug", false, "debug mode for the server") + c.PersistentFlags().String("server.mode", "release", "server mode can be either 'debug', 'test' or 'release'") c.PersistentFlags().String("server.token", "", "authorization token to use if any") + c.PersistentFlags().String("server.allowedOrigins", "*", "allowed origins for the server") if err := viper.BindPFlags(c.PersistentFlags()); err != nil { - logrus.WithError(err).WithField("step", "AddLoggerFlags").Fatal("Couldn't bind flags") + logrus.WithError(err).WithField("step", "AddServerFlags").Fatal("Couldn't bind flags") } } @@ -33,7 +36,7 @@ func AddServerFlags(c *cobra.Command) { func AddConfigurationFlag(c *cobra.Command) { c.PersistentFlags().String("conf", "", "configuration file to use") if err := viper.BindPFlags(c.PersistentFlags()); err != nil { - logrus.WithError(err).Fatal("Couldn't bind flags") + logrus.WithError(err).WithField("step", "AddConfigurationFlag").Fatal("Couldn't bind flags") } } @@ -41,6 +44,7 @@ func AddConfigurationFlag(c *cobra.Command) { func Initialize() { // Environment variables viper.AutomaticEnv() + viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) // Configuration file if viper.GetString("conf") != "" { @@ -54,19 +58,24 @@ func Initialize() { logrus.Debug("No configuration file found") } + // Set log level lvl := viper.GetString("log.level") l, err := logrus.ParseLevel(lvl) if err != nil { - logrus.WithField("level", lvl).Warn("Invalid log level, fallback to 'info'") + logrus.WithFields(logrus.Fields{"level": lvl, "fallback": "info"}).Warn("Invalid log level") } else { logrus.SetLevel(l) } + + // Set log format switch viper.GetString("log.format") { case "json": logrus.SetFormatter(&logrus.JSONFormatter{}) default: logrus.SetFormatter(&logrus.TextFormatter{}) } + + // Defines if logrus should display filenames and line where the log occured if viper.GetBool("log.line") { logrus.AddHook(filename.NewHook()) } diff --git a/_example/cleanarch/implem/router.gin/router.go b/_example/cleanarch/implem/router.gin/router.go new file mode 100644 index 0000000..12e00ac --- /dev/null +++ b/_example/cleanarch/implem/router.gin/router.go @@ -0,0 +1,31 @@ +package router + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +// Interface is the interface that has to be implemented in order to handle +// every functionality of the router +type Interface interface { + Health(c *gin.Context) +} + +// New returns a new router +func New() Interface { + return Impl{} +} + +// Impl is the implementation +type Impl struct{} + +// Health is the implementation of health check +func (r Impl) Health(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{"alive": true}) +} + +//SetRoutes update router with routes +func SetRoutes(router *gin.Engine, r Interface) { + router.GET("/health", r.Health) +} diff --git a/_example/cleanarch/infra/gin.go b/_example/cleanarch/infra/gin.go new file mode 100644 index 0000000..f1060e9 --- /dev/null +++ b/_example/cleanarch/infra/gin.go @@ -0,0 +1,65 @@ +package infra + +import ( + "fmt" + "time" + + "github.com/gin-gonic/gin" + "github.com/itsjamie/gin-cors" + "github.com/sirupsen/logrus" +) + +// GinServer is the struct gathering all the server details +type GinServer struct { + host string + port int + Router *gin.Engine +} + +// NewServer creates the gin Server +func NewServer(host string, port int, mode, allowedOrigins string) GinServer { + s := GinServer{} + s.port = port + s.host = host + + s.Router = gin.New() + s.setMode(mode) + s.Router.Use(gin.Recovery()) + s.setCORS(allowedOrigins) + + return s +} + +func (s *GinServer) setMode(mode string) { + switch mode { + case "debug": + gin.SetMode(gin.DebugMode) + case "test": + gin.SetMode(gin.TestMode) + case "release": + gin.SetMode(gin.ReleaseMode) + default: + logrus.WithField("mode", mode).Warn("Unknown gin mode, fallback to release") + gin.SetMode(gin.ReleaseMode) + } +} + +// setCORS is a helper to set current engine CORS +func (s *GinServer) setCORS(allowedOrigins string) { + s.Router.Use(cors.Middleware(cors.Config{ + Origins: allowedOrigins, + Methods: "GET, PUT, POST, DELETE, OPTION, PATCH", + RequestHeaders: "Origin, Authorization, Content-Type", + ExposedHeaders: "", + MaxAge: 50 * time.Second, + Credentials: true, + ValidateHeaders: false, + })) +} + +// Start tells the router to start listening +func (s GinServer) Start() { + if err := s.Router.Run(fmt.Sprintf("%s:%d", s.host, s.port)); err != nil { + logrus.WithError(err).Fatal("Couldn't start router") + } +}