Skip to content

Commit

Permalink
Merge pull request #4 from MohamedAlosaili/enhancement
Browse files Browse the repository at this point in the history
feat: enhance error handling and database transaction management
  • Loading branch information
malosayli authored Dec 6, 2024
2 parents 691a59d + 0d2dd2b commit 8119156
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 107 deletions.
69 changes: 24 additions & 45 deletions cmd/gog/new/_template/internal/config/config.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package config

import (
"os"
"reflect"
"strings"

"github.com/PROJECT_NAME/internal/validator"
"github.com/spf13/viper"
Expand Down Expand Up @@ -45,44 +43,41 @@ type Config struct {
}

func Load() (*Config, error) {
viper.AddConfigPath(".")
viper.SetConfigFile(".env")
v := viper.New()

viper.SetEnvKeyReplacer(strings.NewReplacer(".", "__"))
v.SetConfigFile(".env")
v.AddConfigPath(".")

// err ignored to allow reading from os env
_ = viper.ReadInConfig()
v.AutomaticEnv()

// Load environment variables from the OS
viper.AutomaticEnv()
// err is ignored to allow reading from os env
_ = v.ReadInConfig()

// Set default values
viper.SetDefault("APP_NAME", "Project Name")
viper.SetDefault("ENV", "production")
viper.SetDefault("PORT", 3000)
viper.SetDefault("LOG_LEVEL", "info")
viper.SetDefault("READ_TIMEOUT", 60)
viper.SetDefault("WRITE_TIMEOUT", 60)
viper.SetDefault("DATABASE_MIGRATIONS_DIR", "migrations")
viper.SetDefault("DATABASE_DRIVER", "postgres")
viper.SetDefault("DATABASE_MIGRATE_TABLE", "schema_migrations")

// override values from env if present
// First, bind all possible environment variables based on struct tags
t := reflect.TypeOf(Config{})
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
envKey := strings.Split(field.Tag.Get("mapstructure"), ",")[0]
if envKey == "" {
continue
}

if val := getEnvCaseInsensitive(envKey); val != "" {
viper.Set(envKey, val)
if envKey := field.Tag.Get("mapstructure"); envKey != "" {
err := v.BindEnv(envKey)
if err != nil {
return nil, err
}
}
}

// Set default values
v.SetDefault("APP_NAME", "Project Name")
v.SetDefault("ENV", "production")
v.SetDefault("PORT", 3000)
v.SetDefault("LOG_LEVEL", "info")
v.SetDefault("READ_TIMEOUT", 60)
v.SetDefault("WRITE_TIMEOUT", 60)
v.SetDefault("DATABASE_MIGRATIONS_DIR", "migrations")
v.SetDefault("DATABASE_DRIVER", "postgres")
v.SetDefault("DATABASE_MIGRATE_TABLE", "schema_migrations")

var config Config
if err := viper.Unmarshal(&config); err != nil {
if err := v.Unmarshal(&config); err != nil {
return nil, err
}

Expand All @@ -98,19 +93,3 @@ func Load() (*Config, error) {

return &config, nil
}

func getEnvCaseInsensitive(key string) string {
if val := os.Getenv(key); val != "" {
return val
}

if val := os.Getenv(strings.ToUpper(key)); val != "" {
return val
}

if val := os.Getenv(strings.ToLower(key)); val != "" {
return val
}

return ""
}
2 changes: 1 addition & 1 deletion cmd/gog/new/_template/internal/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func (c *db) Transaction(fn func(tx *sqlx.Tx) error) error {
if rbErr := tx.Rollback(); rbErr != nil {
return fmt.Errorf("tx failed: %v, rollback failed: %v", err, rbErr)
}
return fmt.Errorf("tx failed: %w", err)
return err
}

if err := tx.Commit(); err != nil {
Expand Down
5 changes: 5 additions & 0 deletions cmd/gog/new/_template/internal/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ type (
func (e *AppError) Error() string {
return e.Message
}

func Is(err error, code ErrorCode) bool {
appErr, ok := err.(*AppError)
return ok && appErr.Code == code
}
12 changes: 5 additions & 7 deletions cmd/gog/new/_template/internal/errors/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,14 @@ func (h *Handler) errorResponseJSON(ctx *fiber.Ctx, err error) error {

// Map error codes to HTTP status codes
func (e *ErrorResponse) HttpStatus() int {
switch {
case e.ErrorCode == ErrUnauthorized:
switch e.ErrorCode {
case ErrUnauthorized:
return fiber.StatusUnauthorized
case e.ErrorCode == ErrForbidden:
case ErrForbidden:
return fiber.StatusForbidden
case e.ErrorCode >= 2000 && e.ErrorCode < 3000:
return fiber.StatusUnauthorized
case e.ErrorCode >= 3000 && e.ErrorCode < 4000:
case ErrBadRequest, ErrAccountAlreadyExists, ErrDuplicateEntry, ErrInvalidInput, ErrMissingField:
return fiber.StatusBadRequest
case e.ErrorCode >= 4000 && e.ErrorCode < 5000:
case ErrResourceNotFound:
return fiber.StatusNotFound
default:
return fiber.StatusInternalServerError
Expand Down
69 changes: 24 additions & 45 deletions scaffold_source/internal/config/config.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package config

import (
"os"
"reflect"
"strings"

"github.com/PROJECT_NAME/internal/validator"
"github.com/spf13/viper"
Expand Down Expand Up @@ -45,44 +43,41 @@ type Config struct {
}

func Load() (*Config, error) {
viper.AddConfigPath(".")
viper.SetConfigFile(".env")
v := viper.New()

viper.SetEnvKeyReplacer(strings.NewReplacer(".", "__"))
v.SetConfigFile(".env")
v.AddConfigPath(".")

// err ignored to allow reading from os env
_ = viper.ReadInConfig()
v.AutomaticEnv()

// Load environment variables from the OS
viper.AutomaticEnv()
// err is ignored to allow reading from os env
_ = v.ReadInConfig()

// Set default values
viper.SetDefault("APP_NAME", "Project Name")
viper.SetDefault("ENV", "production")
viper.SetDefault("PORT", 3000)
viper.SetDefault("LOG_LEVEL", "info")
viper.SetDefault("READ_TIMEOUT", 60)
viper.SetDefault("WRITE_TIMEOUT", 60)
viper.SetDefault("DATABASE_MIGRATIONS_DIR", "migrations")
viper.SetDefault("DATABASE_DRIVER", "postgres")
viper.SetDefault("DATABASE_MIGRATE_TABLE", "schema_migrations")

// override values from env if present
// First, bind all possible environment variables based on struct tags
t := reflect.TypeOf(Config{})
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
envKey := strings.Split(field.Tag.Get("mapstructure"), ",")[0]
if envKey == "" {
continue
}

if val := getEnvCaseInsensitive(envKey); val != "" {
viper.Set(envKey, val)
if envKey := field.Tag.Get("mapstructure"); envKey != "" {
err := v.BindEnv(envKey)
if err != nil {
return nil, err
}
}
}

// Set default values
v.SetDefault("APP_NAME", "Project Name")
v.SetDefault("ENV", "production")
v.SetDefault("PORT", 3000)
v.SetDefault("LOG_LEVEL", "info")
v.SetDefault("READ_TIMEOUT", 60)
v.SetDefault("WRITE_TIMEOUT", 60)
v.SetDefault("DATABASE_MIGRATIONS_DIR", "migrations")
v.SetDefault("DATABASE_DRIVER", "postgres")
v.SetDefault("DATABASE_MIGRATE_TABLE", "schema_migrations")

var config Config
if err := viper.Unmarshal(&config); err != nil {
if err := v.Unmarshal(&config); err != nil {
return nil, err
}

Expand All @@ -98,19 +93,3 @@ func Load() (*Config, error) {

return &config, nil
}

func getEnvCaseInsensitive(key string) string {
if val := os.Getenv(key); val != "" {
return val
}

if val := os.Getenv(strings.ToUpper(key)); val != "" {
return val
}

if val := os.Getenv(strings.ToLower(key)); val != "" {
return val
}

return ""
}
2 changes: 1 addition & 1 deletion scaffold_source/internal/db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func (c *db) Transaction(fn func(tx *sqlx.Tx) error) error {
if rbErr := tx.Rollback(); rbErr != nil {
return fmt.Errorf("tx failed: %v, rollback failed: %v", err, rbErr)
}
return fmt.Errorf("tx failed: %w", err)
return err
}

if err := tx.Commit(); err != nil {
Expand Down
5 changes: 5 additions & 0 deletions scaffold_source/internal/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ type (
func (e *AppError) Error() string {
return e.Message
}

func Is(err error, code ErrorCode) bool {
appErr, ok := err.(*AppError)
return ok && appErr.Code == code
}
12 changes: 5 additions & 7 deletions scaffold_source/internal/errors/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,14 @@ func (h *Handler) errorResponseJSON(ctx *fiber.Ctx, err error) error {

// Map error codes to HTTP status codes
func (e *ErrorResponse) HttpStatus() int {
switch {
case e.ErrorCode == ErrUnauthorized:
switch e.ErrorCode {
case ErrUnauthorized:
return fiber.StatusUnauthorized
case e.ErrorCode == ErrForbidden:
case ErrForbidden:
return fiber.StatusForbidden
case e.ErrorCode >= 2000 && e.ErrorCode < 3000:
return fiber.StatusUnauthorized
case e.ErrorCode >= 3000 && e.ErrorCode < 4000:
case ErrBadRequest, ErrAccountAlreadyExists, ErrDuplicateEntry, ErrInvalidInput, ErrMissingField:
return fiber.StatusBadRequest
case e.ErrorCode >= 4000 && e.ErrorCode < 5000:
case ErrResourceNotFound:
return fiber.StatusNotFound
default:
return fiber.StatusInternalServerError
Expand Down
2 changes: 1 addition & 1 deletion version.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package gog

const Version = "1.2.0"
const Version = "1.2.1"

0 comments on commit 8119156

Please sign in to comment.