@ -3,33 +3,26 @@ package main
import (
"os"
"strings"
"time"
"git.devices.local/mawas/golang-api-skeleton/cmd"
"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/lib/config"
"git.devices.local/mawas/golang-api-skeleton/lib/database"
"git.devices.local/mawas/golang-api-skeleton/lib/utils"
"git.devices.local/mawas/golang-api-skeleton/models"
"git.devices.local/mawas/golang-api-skeleton/repositories"
"git.devices.local/mawas/golang-api-skeleton/services"
log "github.com/sirupsen/logrus"
"github.com/spf13/viper"
)
const defaultHost = "unknown"
// https://towardsdatascience.com/building-restful-apis-in-golang-e3fe6e3f8f95
// https://www.voile.com/voile-straps.html
// 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?
// TODO use badgerdb for permissions? path:[token1, token2...]
// TODO add database loadbalancing feature... so allow more hosts use first as default and other ones as fallback
// TODO usrid and username for cli user is hardcoded.. no need to live in database
// TODO get-stats cli command... which means unix socket support to communicate with server
// TODO data versioning which will be used as reference from the log, also with predictive analytics for restore which means versioning of reference objects needs to be considered
// TODO request body store as reference for log
// TODO because their is a badgerdb cache which tokens are existing or which userids are existing make an existence check at uuid generation just do be sure
// TODO because there is a badgerdb cache which tokens are existing or which userids are existing make an existence check at uuid generation just do be sure
// TODO add logger to gin
// TODO make explicit getDBConnection function
var (
Version string // allows to set version on build
APPName string // allows to overwrite app name on build
@ -41,7 +34,7 @@ func main() {
log . SetOutput ( os . Stdout )
hostname , err := os . Hostname ( )
if err != nil {
hostname = "unknown"
hostname = defaultHost
}
contextLogger := log . WithFields ( log . Fields {
"host" : hostname ,
@ -49,172 +42,204 @@ func main() {
"app" : APPName ,
} )
// cobra sets env if flag is given so that viper can pick it up
cfgFile , flagCfg , err := cmd . Initialize ( Version , APPName , APPDescription )
if err != nil {
os . Exit ( 1 )
}
// config priority: flags > env > config file
if err := config . Initialize ( APPName , cfgFile , flagCfg ) ; err != nil {
contextLogger . WithFields ( log . Fields {
"module" : "config" ,
} ) . Fatal ( err )
}
// bootstrap cache
c , err := cache . Bootstrap ( )
if err != nil {
contextLogger . WithFields ( log . Fields {
"module" : "cache" ,
} ) . Fatal ( err )
}
defer c . Close ( )
db , err := database . Connect ( database . Credentials {
Host : viper . GetString ( "database.host" ) ,
Port : viper . GetInt ( "database.port" ) ,
Dialect : viper . GetString ( "database.dialect" ) ,
Database : viper . GetString ( "database.database" ) ,
User : viper . GetString ( "database.user" ) ,
Password : viper . GetString ( "database.password" ) ,
MaxOpenConn : viper . GetInt ( "database.port" ) ,
MaxIdleConn : viper . GetInt ( "database.port" ) ,
MaxLifeTime : viper . GetInt ( "database.port" ) ,
Debug : true ,
} )
if err != nil {
contextLogger . WithFields ( log . Fields {
"module" : "database" ,
} ) . Fatal ( err )
}
db . AutoMigrate ( & models . User { } )
db . AutoMigrate ( & models . Token { } )
userRepo := repositories . NewUserRepository ( db , common . GUID { } , "" , nil )
userService := services . NewUserService ( userRepo )
command := cmd . NewRootCmd ( Version , APPName , APPDescription )
// Prefill cache
// TODO also add deleted users to cache for old references
if userData , err := userService . ReadAll ( ) ; err != nil {
contextLogger . WithFields ( log . Fields {
"module" : "userService" ,
} ) . Fatal ( err )
} else {
for i := range userData {
if err := c . Set ( "user:" + userData [ i ] . ID . String ( ) , userData [ i ] . Username ) ; err != nil { // userID -> username mapping
contextLogger . WithFields ( log . Fields {
"module" : "cache" ,
} ) . Fatal ( err )
}
if err := c . Set ( "user:" + userData [ i ] . Username , userData [ i ] . ID . String ( ) ) ; err != nil { // username -> userID mapping
contextLogger . WithFields ( log . Fields {
"module" : "cache" ,
} ) . Fatal ( err )
}
}
}
var userID common . GUID
firstname := strings . ToLower ( utils . RandString ( utils . RandInt ( 5 , 10 ) ) )
lastname := strings . ToLower ( utils . RandString ( utils . RandInt ( 6 , 14 ) ) )
username := string ( [ ] byte ( firstname ) [ 0 ] ) + lastname
user1 := & models . User {
Username : username ,
Firstname : strings . ToUpper ( string ( [ ] byte ( firstname ) [ 0 ] ) ) + string ( [ ] byte ( firstname ) [ 1 : ] ) ,
Lastname : strings . ToUpper ( string ( [ ] byte ( lastname ) [ 0 ] ) ) + string ( [ ] byte ( lastname ) [ 1 : ] ) ,
Email : username + "@acme.inc" ,
}
if result , err := userService . Create ( user1 ) ; err != nil {
panic ( err )
} else {
utils . PrettyPrintJSON ( result )
if err := c . Set ( "user:" + result . ID . String ( ) , result . Username ) ; err != nil { // userID -> username mapping
contextLogger . WithFields ( log . Fields {
"module" : "cache" ,
} ) . Fatal ( err )
}
if err := c . Set ( "user:" + result . Username , result . ID . String ( ) ) ; err != nil { // username -> userID mapping
if err := command . Execute ( ) ; err != nil {
msg := strings . SplitN ( err . Error ( ) , ":" , 1 )
if len ( msg ) == 2 {
contextLogger . WithFields ( log . Fields {
"module" : "cache" ,
} ) . Fatal ( err )
"module" : msg [ 0 ] ,
} ) . Fatal ( msg [ 1 ] )
}
userID = result . ID
}
userRepo = repositories . NewUserRepository ( db , userID , username , c )
userService = services . NewUserService ( userRepo )
// userID db context?
// k := common.UsernameContextKey("username")
// ctx := context.WithValue(context.Background(), "username", username)
// db = db.WithContext(ctx)
// db = db.Set("username", username)
// fmt.Println(db.Get("username"))
// create example token
tokenRepo := repositories . NewTokenRepository ( db , userID , username , c )
tokenService := services . NewTokenService ( tokenRepo )
token1 := & models . Token {
ExpiresAt : time . Now ( ) . Add ( 24 * time . Hour ) ,
UserID : userID ,
Active : true ,
}
if result1 , err := tokenService . Create ( token1 ) ; err != nil {
panic ( err )
} else {
utils . PrettyPrintJSON ( result1 )
}
result1 , err := tokenService . ReadAll ( )
if err != nil {
panic ( err )
}
utils . PrettyPrintJSON ( result1 )
result , err := userService . ReadAll ( )
if err != nil {
panic ( err )
}
utils . PrettyPrintJSON ( result )
firstname2 := strings . ToLower ( utils . RandString ( utils . RandInt ( 5 , 10 ) ) )
lastname2 := strings . ToLower ( utils . RandString ( utils . RandInt ( 6 , 14 ) ) )
username2 := string ( [ ] byte ( firstname2 ) [ 0 ] ) + lastname2
user2 := & models . User {
Username : username2 ,
Firstname : strings . ToUpper ( string ( [ ] byte ( firstname2 ) [ 0 ] ) ) + string ( [ ] byte ( firstname2 ) [ 1 : ] ) ,
Lastname : strings . ToUpper ( string ( [ ] byte ( lastname2 ) [ 0 ] ) ) + string ( [ ] byte ( lastname2 ) [ 1 : ] ) ,
Email : username2 + "@acme.inc" ,
}
if result , err := userService . Create ( user2 ) ; err != nil {
panic ( err )
} else {
utils . PrettyPrintJSON ( result )
result3 , err := userService . ReadByID ( result . Username )
if err != nil {
panic ( err )
}
utils . PrettyPrintJSON ( result3 )
}
// db, _ := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{})
// userRepo := user.NewRepository(db)
// userService := user.NewService(userRepo)
// user1 := &user.User{
// Username: "jdoe",
// Firstname: "Joe",
// Lastname: "Doe",
// Email: "jdoe@acme.inc",
// }
// result, err := userService.Create(user1)
// asd := flagCfg["application.environment"].(*string)
// fmt.Printf("SUPI %s\n", *asd)
// cfgFile, flagCfg, err := cmd.Initialize(Version, APPName, APPDescription)
// if err != nil {
// panic(err )
// os.Exit(1)
// }
// fmt.Println(result)
// fmt.Println("main.go --> Environment:", viper.GetString("application.environment"))
// fmt.Println("main.go --> Listen:", viper.GetString("application.listenaddress"))
// fmt.Println("main.go --> Database Port: ", viper.GetInt("database.port"))
// fmt.Println("main.go --> Application Port:", viper.GetInt("application.port"))
// contextLogger.WithFields(log.Fields{
// "module": "config",
// }).Fatal("ADSADSASd")
// config priority: flags > env > config file
// rootCMD.Execute()
// asd := flagCfg["application.environment"].(*string)
// fmt.Printf("SUPI1 %s\n", *asd)
// fmt.Println("SUPI2", cfgFile, flagCfg)
// if err := config.Initialize(APPName, cfgFile, flagCfg); err != nil {
// contextLogger.WithFields(log.Fields{
// "module": "config",
// }).Fatal(err)
// }
//
// // bootstrap cache
// c, err := cache.Bootstrap()
// if err != nil {
// contextLogger.WithFields(log.Fields{
// "module": "cache",
// }).Fatal(err)
// }
// defer c.Close()
// db, err := database.Connect(database.Credentials{
// Host: viper.GetString("database.host"),
// Port: viper.GetInt("database.port"),
// Dialect: viper.GetString("database.dialect"),
// Database: viper.GetString("database.database"),
// User: viper.GetString("database.user"),
// Password: viper.GetString("database.password"),
// MaxOpenConn: viper.GetInt("database.port"),
// MaxIdleConn: viper.GetInt("database.port"),
// MaxLifeTime: viper.GetInt("database.port"),
// Debug: true,
// })
// if err != nil {
// contextLogger.WithFields(log.Fields{
// "module": "database",
// }).Fatal(err)
// }
//
// // add cli user to cache
// if err := c.Set("user:"+CLIUserID, CLIUsername); err != nil { // userID -> username mapping
// contextLogger.WithFields(log.Fields{
// "module": "cache",
// }).Fatal(err)
// }
// if err := c.Set("user:"+CLIUsername, CLIUserID); err != nil { // username -> userID mapping
// contextLogger.WithFields(log.Fields{
// "module": "cache",
// }).Fatal(err)
// }
//
// db.AutoMigrate(&models.User{})
// db.AutoMigrate(&models.Token{})
// userRepo := repositories.NewUserRepository(db, CLIUserID, CLIUsername, nil)
// userService := services.NewUserService(userRepo)
//
// // Prefill cache
// // TODO also add deleted users to cache for old references
// if userData, err := userService.ReadAll(); err != nil {
// contextLogger.WithFields(log.Fields{
// "module": "userService",
// }).Fatal(err)
// } else {
//
// for i := range userData {
// if err := c.Set("user:"+userData[i].ID.String(), userData[i].Username); err != nil { // userID -> username mapping
// contextLogger.WithFields(log.Fields{
// "module": "cache",
// }).Fatal(err)
// }
// if err := c.Set("user:"+userData[i].Username, userData[i].ID.String()); err != nil { // username -> userID mapping
// contextLogger.WithFields(log.Fields{
// "module": "cache",
// }).Fatal(err)
// }
// }
// }
//
// var userID common.GUID
// firstname := strings.ToLower(utils.RandString(utils.RandInt(5, 10)))
// lastname := strings.ToLower(utils.RandString(utils.RandInt(6, 14)))
// username := string([]byte(firstname)[0]) + lastname
// user1 := &models.User{
// Username: username,
// Firstname: strings.ToUpper(string([]byte(firstname)[0])) + string([]byte(firstname)[1:]),
// Lastname: strings.ToUpper(string([]byte(lastname)[0])) + string([]byte(lastname)[1:]),
// Email: username + "@acme.inc",
// }
// if result, err := userService.Create(user1); err != nil {
// panic(err)
// } else {
// utils.PrettyPrintJSON(result)
// if err := c.Set("user:"+result.ID.String(), result.Username); err != nil { // userID -> username mapping
// contextLogger.WithFields(log.Fields{
// "module": "cache",
// }).Fatal(err)
// }
// if err := c.Set("user:"+result.Username, result.ID.String()); err != nil { // username -> userID mapping
// contextLogger.WithFields(log.Fields{
// "module": "cache",
// }).Fatal(err)
// }
// userID = result.ID
// }
//
// userRepo = repositories.NewUserRepository(db, userID.String(), username, c)
// userService = services.NewUserService(userRepo)
// // userID db context?
// // k := common.UsernameContextKey("username")
// // ctx := context.WithValue(context.Background(), "username", username)
// // db = db.WithContext(ctx)
// // db = db.Set("username", username)
// // fmt.Println(db.Get("username"))
//
// // create example token
// tokenRepo := repositories.NewTokenRepository(db, userID.String(), username, c)
// tokenService := services.NewTokenService(tokenRepo)
// token1 := &models.Token{
// ExpiresAt: time.Now().Add(24 * time.Hour),
// UserID: userID,
// Active: true,
// }
// if result1, err := tokenService.Create(token1); err != nil {
// panic(err)
// } else {
// utils.PrettyPrintJSON(result1)
// }
//
// result1, err := tokenService.ReadAll()
// if err != nil {
// panic(err)
// }
// utils.PrettyPrintJSON(result1)
//
// result, err := userService.ReadAll()
// if err != nil {
// panic(err)
// }
// utils.PrettyPrintJSON(result)
//
// firstname2 := strings.ToLower(utils.RandString(utils.RandInt(5, 10)))
// lastname2 := strings.ToLower(utils.RandString(utils.RandInt(6, 14)))
// username2 := string([]byte(firstname2)[0]) + lastname2
// user2 := &models.User{
// Username: username2,
// Firstname: strings.ToUpper(string([]byte(firstname2)[0])) + string([]byte(firstname2)[1:]),
// Lastname: strings.ToUpper(string([]byte(lastname2)[0])) + string([]byte(lastname2)[1:]),
// Email: username2 + "@acme.inc",
// }
// if result, err := userService.Create(user2); err != nil {
// panic(err)
// } else {
// utils.PrettyPrintJSON(result)
// result3, err := userService.ReadByID(result.Username)
// if err != nil {
// panic(err)
// }
// utils.PrettyPrintJSON(result3)
// }
//
// // db, _ := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{})
// // userRepo := user.NewRepository(db)
// // userService := user.NewService(userRepo)
// // user1 := &user.User{
// // Username: "jdoe",
// // Firstname: "Joe",
// // Lastname: "Doe",
// // Email: "jdoe@acme.inc",
// // }
// // result, err := userService.Create(user1)
// // if err != nil {
// // panic(err)
// // }
// // fmt.Println(result)
//
// // fmt.Println("main.go --> Environment:", viper.GetString("application.environment"))
// // fmt.Println("main.go --> Listen:", viper.GetString("application.listenaddress"))
// // fmt.Println("main.go --> Database Port: ", viper.GetInt("database.port"))
// // fmt.Println("main.go --> Application Port:", viper.GetInt("application.port"))
// // contextLogger.WithFields(log.Fields{
// // "module": "config",
// // }).Fatal("ADSADSASd")
//
}