From 68bf3ebf118e7f1398fc532454c9b9a6b3220f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernd-Ren=C3=A9=20Predota?= Date: Mon, 17 May 2021 16:10:02 +0200 Subject: [PATCH] requestID and version header middleware added --- api/v1/users/users.go | 13 ++++++++++--- cmd/root.go | 2 +- cmd/server/server.go | 9 ++++++--- lib/common/common.go | 25 +++++++++++++++++++++++++ lib/config/config.go | 1 + lib/middlewares/requestid.go | 26 ++++++++++++++++++++++++++ lib/middlewares/versionheader.go | 12 ++++++++++++ main.go | 1 + 8 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 lib/middlewares/requestid.go create mode 100644 lib/middlewares/versionheader.go diff --git a/api/v1/users/users.go b/api/v1/users/users.go index 1cf2dd1..1bf1c43 100644 --- a/api/v1/users/users.go +++ b/api/v1/users/users.go @@ -2,6 +2,7 @@ package users import ( "git.devices.local/mawas/golang-api-skeleton/lib/cache" + "git.devices.local/mawas/golang-api-skeleton/lib/common" "git.devices.local/mawas/golang-api-skeleton/repositories" "git.devices.local/mawas/golang-api-skeleton/services" "github.com/gin-gonic/gin" @@ -11,10 +12,16 @@ import ( func Read(c *gin.Context) { db := c.MustGet("db").(*gorm.DB) cc := c.MustGet("cache").(cache.Cache) + response := common.Envelope{ + RequestID: c.MustGet("requestID").(string), + } userRepo := repositories.NewUserRepository(db, "01F5FSJXDHWT4HK93B9NB8V5G4", "test", cc) userService := services.NewUserService(userRepo) username := c.Param("username") - user, _ := userService.ReadByID(username) - c.JSON(200, user) - + user, err := userService.ReadByID(username) + if err != nil { + c.AbortWithStatusJSON(500, response.AppendError(err)) + return + } + c.JSON(200, response.SetSuccess(user)) } diff --git a/cmd/root.go b/cmd/root.go index 80d64bb..eb545ee 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -47,7 +47,7 @@ func NewRootCmd(version string, appName string, appDescription string) *cobra.Co cmd.AddCommand(newSampleCfgCmd()) cfgFile, flagCfg := getFlags(cmd) cmd.SetHelpCommand(add.Command(appName, cfgFile, flagCfg)) - cmd.AddCommand(server.Command(appName, cfgFile, flagCfg)) + cmd.AddCommand(server.Command(version, appName, cfgFile, flagCfg)) return cmd } diff --git a/cmd/server/server.go b/cmd/server/server.go index 6990c94..6bd0e11 100644 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -8,6 +8,7 @@ import ( "git.devices.local/mawas/golang-api-skeleton/lib/common" "git.devices.local/mawas/golang-api-skeleton/lib/config" "git.devices.local/mawas/golang-api-skeleton/lib/database" + "git.devices.local/mawas/golang-api-skeleton/lib/middlewares" "git.devices.local/mawas/golang-api-skeleton/models" "git.devices.local/mawas/golang-api-skeleton/repositories" "git.devices.local/mawas/golang-api-skeleton/services" @@ -16,7 +17,7 @@ import ( "github.com/spf13/viper" ) -func Command(appName string, cfgFile string, flagCfg map[string]interface{}) *cobra.Command { +func Command(version string, appName string, cfgFile string, flagCfg map[string]interface{}) *cobra.Command { return &cobra.Command{ Use: "server", Short: "Starts application", @@ -25,12 +26,12 @@ func Command(appName string, cfgFile string, flagCfg map[string]interface{}) *co // SilenceErrors: true, DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - return startServer(appName, cfgFile, flagCfg) + return startServer(version, appName, cfgFile, flagCfg) }, } } -func startServer(appName string, cfgFile string, flagCfg map[string]interface{}) error { +func startServer(version string, appName string, cfgFile string, flagCfg map[string]interface{}) error { if err := config.Initialize(appName, cfgFile, flagCfg); err != nil { return fmt.Errorf("config:" + err.Error()) } @@ -83,6 +84,8 @@ func startServer(appName string, cfgFile string, flagCfg map[string]interface{}) } } } + app.Use(middlewares.RequestID()) + app.Use(middlewares.VersionHeader(version)) app.Use(inject("cache", c)) app.Use(inject("db", db)) // FIXME find out where todo database migration diff --git a/lib/common/common.go b/lib/common/common.go index 5cc29af..098694b 100644 --- a/lib/common/common.go +++ b/lib/common/common.go @@ -99,3 +99,28 @@ func (guidPK *ModelHiddenGUIDPK) BeforeCreate(tx *gorm.DB) error { } return err } + +// Envelope for response objects +type Envelope struct { + Success bool `json:"success"` + RequestID string `json:"request_id,omitempty"` + Warnings []string `json:"warnings,omitempty"` + Errors []string `json:"errors,omitempty"` + // PaginationInfo *pagination.Pagination `json:"pagination_info,omitempty"` + Result interface{} `json:"result,omitempty"` +} + +// AppendError to envelope +func (envelope *Envelope) AppendError(err error) *Envelope { + envelope.Success = false + envelope.Result = nil + envelope.Errors = append(envelope.Errors, err.Error()) + return envelope +} + +// SetSuccess to envelope +func (envelope *Envelope) SetSuccess(result interface{}) *Envelope { + envelope.Success = true + envelope.Result = result + return envelope +} diff --git a/lib/config/config.go b/lib/config/config.go index f10d7de..80592e3 100644 --- a/lib/config/config.go +++ b/lib/config/config.go @@ -23,6 +23,7 @@ var Sample = `application: environment: ` + PRODENV + `|` + TESTENV + `|` + DEVENV + ` listenAddress: 127.0.0.1 port: 5100 + requestIDHeaderName: X-Request-Id database: host: 127.0.0.1 port: 3306 diff --git a/lib/middlewares/requestid.go b/lib/middlewares/requestid.go new file mode 100644 index 0000000..b54d936 --- /dev/null +++ b/lib/middlewares/requestid.go @@ -0,0 +1,26 @@ +package middlewares + +import ( + "fmt" + + "git.devices.local/mawas/golang-api-skeleton/lib/common" + "github.com/gin-gonic/gin" + "github.com/spf13/viper" +) + +func RequestID() gin.HandlerFunc { + return func(c *gin.Context) { + requestID := c.Request.Header.Get(viper.GetString("application.requestIDHeaderName")) + if requestID == "" { + id, err := common.NewGUID() + if err != nil { + // FIXME log request id creation error + fmt.Println("ERROR request id creation in middleware") + } + requestID = id.String() + } + c.Set("requestID", requestID) + c.Writer.Header().Set("X-Request-Id", requestID) + c.Next() + } +} diff --git a/lib/middlewares/versionheader.go b/lib/middlewares/versionheader.go new file mode 100644 index 0000000..a03c3c8 --- /dev/null +++ b/lib/middlewares/versionheader.go @@ -0,0 +1,12 @@ +package middlewares + +import ( + "github.com/gin-gonic/gin" +) + +func VersionHeader(version string) gin.HandlerFunc { + return func(c *gin.Context) { + c.Writer.Header().Set("X-Api-Version", version) + c.Next() + } +} diff --git a/main.go b/main.go index c167847..05a63d7 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ const defaultHost = "unknown" // https://towardsdatascience.com/building-restful-apis-in-golang-e3fe6e3f8f95 // https://www.voile.com/voile-straps.html +// https://yusufs.medium.com/creating-distributed-kv-database-by-implementing-raft-consensus-using-golang-d0884eef2e28 RAFT to create badgerdb sync? // FIXME how to set unique index for username to make soft deletes possible but disallow duplicates... also how to cache a username which got delete and recerated... user ID would get overwritten, maybe pre/suffix deleted username? // TODO add command for db migrate? no auto migrate?