diff --git a/README.md b/README.md
index 9e55ca6..3767e80 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# golang API Skeleton
-[](https://drone.devices.local/mawas/golang-api-skeleton)
+[](https://drone.devices.local/mawas/golang-api-skeleton)
refined skeleton future apis should be based on
diff --git a/api/api.go b/api/api.go
new file mode 100644
index 0000000..44fea59
--- /dev/null
+++ b/api/api.go
@@ -0,0 +1,15 @@
+package api
+
+import (
+ v1 "git.devices.local/mawas/golang-api-skeleton/api/v1"
+ "github.com/gin-gonic/gin"
+)
+
+// ApplyRoutes applies router to gin Router
+func ApplyRoutes(r *gin.Engine) *gin.Engine {
+ api := r.Group("/api")
+ {
+ v1.ApplyRoutes(api)
+ }
+ return r
+}
diff --git a/api/v1/v1.go b/api/v1/v1.go
new file mode 100644
index 0000000..c552ff3
--- /dev/null
+++ b/api/v1/v1.go
@@ -0,0 +1,11 @@
+package v1
+
+import "github.com/gin-gonic/gin"
+
+func ApplyRoutes(r *gin.RouterGroup) *gin.RouterGroup {
+ v1 := r.Group("/v1")
+ {
+ v1.GET("/ping")
+ }
+ return v1
+}
diff --git a/cmd/add/add.go b/cmd/add/add.go
new file mode 100644
index 0000000..a325707
--- /dev/null
+++ b/cmd/add/add.go
@@ -0,0 +1,23 @@
+package add
+
+import (
+ "git.devices.local/mawas/golang-api-skeleton/cmd/add/admin"
+ "github.com/spf13/cobra"
+)
+
+func Command(appName string, cfgFile string, flagCfg map[string]interface{}) *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "add",
+ Short: "Allows to add items",
+ Long: "Allows to add items",
+ // SilenceUsage: true,
+ // SilenceErrors: true,
+ DisableAutoGenTag: true,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ cmd.Help()
+ return nil
+ },
+ }
+ cmd.AddCommand(admin.Command(appName, cfgFile, flagCfg))
+ return cmd
+}
diff --git a/cmd/add/admin/admin.go b/cmd/add/admin/admin.go
new file mode 100644
index 0000000..14428d9
--- /dev/null
+++ b/cmd/add/admin/admin.go
@@ -0,0 +1,68 @@
+package admin
+
+import (
+ "fmt"
+
+ "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/models"
+ "git.devices.local/mawas/golang-api-skeleton/repositories"
+ "git.devices.local/mawas/golang-api-skeleton/services"
+ "github.com/spf13/cobra"
+ "github.com/spf13/viper"
+)
+
+func Command(appName string, cfgFile string, flagCfg map[string]interface{}) *cobra.Command {
+ var username, firstname, lastname, email string
+ cmd := &cobra.Command{
+ Use: "admin",
+ Short: "Create admin user if non existent",
+ Long: "Create admin user if non existent",
+ // SilenceUsage: true,
+ // SilenceErrors: true,
+ DisableAutoGenTag: true,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return createAdminUser(appName, cfgFile, flagCfg, username, firstname, lastname, email)
+ },
+ }
+ cmd.Flags().StringVarP(&username, "username", "U", "admin", "desired admin user name")
+ cmd.Flags().StringVarP(&firstname, "firstname", "F", "", "admin user fistname")
+ if err := cmd.MarkFlagRequired("firstname"); err != nil {
+ // cmd.Usage()
+ }
+ cmd.Flags().StringVarP(&lastname, "lastname", "L", "", "admin user lastname")
+ cmd.MarkFlagRequired("lastname")
+ cmd.Flags().StringVarP(&email, "email", "M", "", "admin user email address")
+ cmd.MarkFlagRequired("email")
+ return cmd
+}
+
+func createAdminUser(appName, cfgFile string, flagCfg map[string]interface{}, username, firstname, lastname, email string) error {
+ if err := config.Initialize(appName, cfgFile, flagCfg); err != nil {
+ return fmt.Errorf("config:%v", err)
+ }
+ 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 {
+ return fmt.Errorf("database:%v", err)
+ }
+ if err := db.AutoMigrate(&models.User{}); err != nil {
+ return fmt.Errorf("database:%v", err)
+ }
+ userRepo := repositories.NewUserRepository(db, common.CLIUserID, common.CLIUsername, nil)
+ userService := services.NewUserService(userRepo)
+ adminUser := services.NewUser(username, firstname, lastname, email)
+ userService.Create(&adminUser)
+ return nil
+}
diff --git a/cmd/cmd.go b/cmd/cmd.go
deleted file mode 100644
index ec2d416..0000000
--- a/cmd/cmd.go
+++ /dev/null
@@ -1,132 +0,0 @@
-package cmd
-
-import (
- "os"
-
- "git.devices.local/mawas/golang-api-skeleton/lib/config"
- "github.com/spf13/cobra"
-)
-
-// TODO add shell completion
-
-func newRootCmd(appName string, appDescription string) *cobra.Command {
- return &cobra.Command{
- Use: appName,
- Short: appDescription,
- Long: appDescription,
- // SilenceUsage: true,
- // SilenceErrors: true,
- DisableAutoGenTag: true,
- Run: func(cmd *cobra.Command, args []string) {
- },
- }
-}
-
-func newVersionCmd(version string, appName string) *cobra.Command {
- return &cobra.Command{
- Use: "version",
- Aliases: []string{"Version"},
- Short: "Print the version number",
- Long: "Print the version number",
- DisableAutoGenTag: true,
- Run: func(cmd *cobra.Command, args []string) {
- cmd.Println(appName, "version", version)
- os.Exit(0)
- },
- }
-}
-
-func newSampleCfgCmd() *cobra.Command {
- return &cobra.Command{
- Use: "sample-config",
- Short: "Print sample config",
- Long: "Print an example configuration file content",
- Run: func(cmd *cobra.Command, args []string) {
- cmd.Println(config.Sample)
- os.Exit(0)
- },
- }
-}
-
-func newSampleCLICmd() *cobra.Command {
- return &cobra.Command{
- Use: "cli",
- Short: "Interactive CLI",
- Long: "Interactive CLI maybe",
- Run: func(cmd *cobra.Command, args []string) {
- cmd.Println("here might be an cli")
- os.Exit(0)
- },
- }
-}
-
-func Initialize(version string, appName string, appDescription string) (string, map[string]interface{}, error) {
- var cfgFile string
- rootCmd := newRootCmd(appName, appDescription)
- rootCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
- if err := cmd.Usage(); err != nil {
- os.Exit(1)
- }
- os.Exit(0)
- })
- rootCmd.AddCommand(newVersionCmd(version, appName))
- rootCmd.AddCommand(newSampleCfgCmd())
- // rootCmd.AddCommand(newSampleCLICmd())
-
- rootCmd.Flags().StringVarP(&cfgFile, "config", "c", "", "config file")
- flagCfg := map[string]interface{}{
- "application.environment": rootCmd.Flags().StringP("environment", "e", "", "application environment"),
- "application.listenaddress": rootCmd.Flags().StringP("listenaddress", "l", "", "application listenaddress or host"),
- "application.port": rootCmd.Flags().IntP("port", "p", 0, "application listenport"),
- "database.host": rootCmd.Flags().StringP("database-host", "H", "", "database host"),
- "database.port": rootCmd.Flags().IntP("database-port", "P", 0, "database port"),
- "database.dialect": rootCmd.Flags().StringP("database-dialect", "D", "", "database dialect"),
- "database.database": rootCmd.Flags().StringP("database-name", "d", "", "database name"),
- "database.user": rootCmd.Flags().StringP("database-user", "u", "", "database user"),
- "database.password": rootCmd.Flags().StringP("database-password", "s", "", "database password"),
- "database.maxopenconn": rootCmd.Flags().IntP("database-maxopen", "o", 0, "database max open connections"),
- "database.maxidleconn": rootCmd.Flags().IntP("database-maxidle", "i", 0, "database max idle connections"),
- "database.maxlifetime": rootCmd.Flags().IntP("database-maxlifetime", "t", 0, "database max connection lifetime"),
- }
- if err := rootCmd.Execute(); err != nil {
- return cfgFile, flagCfg, err
- }
- return cfgFile, flagCfg, nil
-}
-
-// func Initialize(version string, appName string, appDescription string) error {
-// var cfgFile string
-// rootCmd := newRootCmd(appName, appDescription)
-// rootCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
-// if err := cmd.Usage(); err != nil {
-// os.Exit(1)
-// }
-// os.Exit(0)
-// })
-// rootCmd.AddCommand(newVersionCmd(version, appName))
-// rootCmd.AddCommand(newSampleCfgCmd())
-// // rootCmd.AddCommand(newSampleCLICmd())
-//
-// rootCmd.Flags().StringVarP(&cfgFile, "config", "c", "", "config file")
-// flagCfg := map[string]interface{}{
-// "application.environment": rootCmd.Flags().StringP("environment", "e", "", "application environment"),
-// "application.listenaddress": rootCmd.Flags().StringP("listenaddress", "l", "", "application listenaddress or host"),
-// "application.port": rootCmd.Flags().IntP("port", "p", 0, "application listenport"),
-// "database.host": rootCmd.Flags().StringP("database-host", "H", "", "database host"),
-// "database.port": rootCmd.Flags().IntP("database-port", "P", 0, "database port"),
-// "database.dialect": rootCmd.Flags().StringP("database-dialect", "D", "", "database dialect"),
-// "database.database": rootCmd.Flags().StringP("database-name", "d", "", "database name"),
-// "database.user": rootCmd.Flags().StringP("database-user", "u", "", "database user"),
-// "database.password": rootCmd.Flags().StringP("database-password", "s", "", "database password"),
-// "database.maxopenconn": rootCmd.Flags().IntP("database-maxopen", "o", 0, "database max open connections"),
-// "database.maxidleconn": rootCmd.Flags().IntP("database-maxidle", "i", 0, "database max idle connections"),
-// "database.maxlifetime": rootCmd.Flags().IntP("database-maxlifetime", "t", 0, "database max connection lifetime"),
-// }
-// if err := rootCmd.Execute(); err != nil {
-// return err
-// }
-// if err := config.Initialize(appName, cfgFile, flagCfg); err != nil {
-// return err
-// }
-// return nil
-// }
diff --git a/cmd/root.go b/cmd/root.go
new file mode 100644
index 0000000..e04eb5d
--- /dev/null
+++ b/cmd/root.go
@@ -0,0 +1,199 @@
+package cmd
+
+import (
+ "os"
+
+ "git.devices.local/mawas/golang-api-skeleton/cmd/add"
+ "git.devices.local/mawas/golang-api-skeleton/cmd/server"
+ "git.devices.local/mawas/golang-api-skeleton/lib/config"
+ "github.com/spf13/cobra"
+)
+
+// TODO add shell completion
+
+func newRootCmd(appName string, appDescription string) *cobra.Command {
+ return &cobra.Command{
+ Use: appName,
+ Short: appDescription,
+ Long: appDescription,
+ // SilenceUsage: true,
+ // SilenceErrors: true,
+ DisableAutoGenTag: true,
+ Run: func(cmd *cobra.Command, args []string) {
+ },
+ }
+}
+
+func NewRootCmd(version string, appName string, appDescription string) *cobra.Command {
+ cmd := &cobra.Command{
+ Use: appName,
+ Short: appDescription,
+ Long: appDescription,
+ // SilenceUsage: true,
+ // SilenceErrors: true,
+ DisableAutoGenTag: true,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ cmd.Usage()
+ return nil
+ },
+ }
+ // cmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
+ // if err := cmd.Usage(); err != nil {
+ // os.Exit(1)
+ // }
+ // os.Exit(0)
+ // })
+ cmd.AddCommand(newVersionCmd(version, appName))
+ cmd.AddCommand(newSampleCfgCmd())
+ cfgFile, flagCfg := getFlags(cmd)
+ cmd.SetHelpCommand(add.Command(appName, cfgFile, flagCfg))
+ cmd.AddCommand(server.Command(appName, cfgFile, flagCfg))
+ return cmd
+}
+
+func newVersionCmd(version string, appName string) *cobra.Command {
+ return &cobra.Command{
+ Use: "version",
+ Aliases: []string{"Version"},
+ Short: "Print the version number",
+ Long: "Print the version number",
+ DisableAutoGenTag: true,
+ Run: func(cmd *cobra.Command, args []string) {
+ cmd.Println(appName, "version", version)
+ // os.Exit(0)
+ },
+ }
+}
+
+func newSampleCfgCmd() *cobra.Command {
+ return &cobra.Command{
+ Use: "sample-config",
+ Short: "Print sample config",
+ Long: "Print an example configuration file content",
+ Run: func(cmd *cobra.Command, args []string) {
+ cmd.Println(config.Sample)
+ os.Exit(0)
+ },
+ }
+}
+
+//func createAdminCmd(userService *services.UserService) *cobra.Command {
+// return &cobra.Command{
+// Use: "admin",
+// Short: "Create initial admin user",
+// Long: "Create initial admin user",
+// RunE: func(cmd *cobra.Command, args []string) error{
+// adminUser := &models.Token{
+// ExpiresAt: time.Now().Add(24 * time.Hour),
+// UserID: userID,
+// Active: true,
+// }
+// if result1, err := tokenService.Create(token1); err != nil {
+// panic(err)
+// }
+// },
+// }
+//}
+
+func newSampleCLICmd() *cobra.Command {
+ return &cobra.Command{
+ Use: "cli",
+ Short: "Interactive CLI",
+ Long: "Interactive CLI maybe",
+ Run: func(cmd *cobra.Command, args []string) {
+ cmd.Println("here might be an cli")
+ os.Exit(0)
+ },
+ }
+}
+
+func Initialize(version string, appName string, appDescription string) (string, map[string]interface{}, error) {
+ var cfgFile string
+ rootCmd := newRootCmd(appName, appDescription)
+ rootCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
+ if err := cmd.Usage(); err != nil {
+ os.Exit(1)
+ }
+ os.Exit(0)
+ })
+ rootCmd.AddCommand(newVersionCmd(version, appName))
+ rootCmd.AddCommand(newSampleCfgCmd())
+ rootCmd.AddCommand(newSampleCfgCmd())
+ // rootCmd.AddCommand(newSampleCLICmd())
+
+ rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file")
+ flagCfg := map[string]interface{}{
+ "application.environment": rootCmd.PersistentFlags().StringP("environment", "e", "", "application environment"),
+ "application.listenaddress": rootCmd.PersistentFlags().StringP("listenaddress", "l", "", "application listenaddress or host"),
+ "application.port": rootCmd.PersistentFlags().IntP("port", "p", 0, "application listenport"),
+ "database.host": rootCmd.PersistentFlags().StringP("database-host", "H", "", "database host"),
+ "database.port": rootCmd.PersistentFlags().IntP("database-port", "P", 0, "database port"),
+ "database.dialect": rootCmd.PersistentFlags().StringP("database-dialect", "D", "", "database dialect"),
+ "database.database": rootCmd.PersistentFlags().StringP("database-name", "d", "", "database name"),
+ "database.user": rootCmd.PersistentFlags().StringP("database-user", "u", "", "database user"),
+ "database.password": rootCmd.PersistentFlags().StringP("database-password", "s", "", "database password"),
+ "database.maxopenconn": rootCmd.PersistentFlags().IntP("database-maxopen", "o", 0, "database max open connections"),
+ "database.maxidleconn": rootCmd.PersistentFlags().IntP("database-maxidle", "i", 0, "database max idle connections"),
+ "database.maxlifetime": rootCmd.PersistentFlags().IntP("database-maxlifetime", "t", 0, "database max connection lifetime"),
+ }
+ if err := rootCmd.Execute(); err != nil {
+ return cfgFile, flagCfg, err
+ }
+ return cfgFile, flagCfg, nil
+}
+
+func getFlags(cmd *cobra.Command) (cfgFile string, flagCfg map[string]interface{}) {
+ cmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file")
+ flagCfg = map[string]interface{}{
+ "application.environment": cmd.PersistentFlags().StringP("environment", "e", "", "application environment"),
+ "application.listenaddress": cmd.PersistentFlags().StringP("listenaddress", "l", "", "application listenaddress or host"),
+ "application.port": cmd.PersistentFlags().IntP("port", "p", 0, "application listenport"),
+ "database.host": cmd.PersistentFlags().StringP("database-host", "H", "", "database host"),
+ "database.port": cmd.PersistentFlags().IntP("database-port", "P", 0, "database port"),
+ "database.dialect": cmd.PersistentFlags().StringP("database-dialect", "D", "", "database dialect"),
+ "database.database": cmd.PersistentFlags().StringP("database-name", "d", "", "database name"),
+ "database.user": cmd.PersistentFlags().StringP("database-user", "u", "", "database user"),
+ "database.password": cmd.PersistentFlags().StringP("database-password", "s", "", "database password"),
+ "database.maxopenconn": cmd.PersistentFlags().IntP("database-maxopen", "o", 0, "database max open connections"),
+ "database.maxidleconn": cmd.PersistentFlags().IntP("database-maxidle", "i", 0, "database max idle connections"),
+ "database.maxlifetime": cmd.PersistentFlags().IntP("database-maxlifetime", "t", 0, "database max connection lifetime"),
+ }
+ return cfgFile, flagCfg
+}
+
+// func Initialize(version string, appName string, appDescription string) error {
+// var cfgFile string
+// rootCmd := newRootCmd(appName, appDescription)
+// rootCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) {
+// if err := cmd.Usage(); err != nil {
+// os.Exit(1)
+// }
+// os.Exit(0)
+// })
+// rootCmd.AddCommand(newVersionCmd(version, appName))
+// rootCmd.AddCommand(newSampleCfgCmd())
+// // rootCmd.AddCommand(newSampleCLICmd())
+//
+// rootCmd.Flags().StringVarP(&cfgFile, "config", "c", "", "config file")
+// flagCfg := map[string]interface{}{
+// "application.environment": rootCmd.Flags().StringP("environment", "e", "", "application environment"),
+// "application.listenaddress": rootCmd.Flags().StringP("listenaddress", "l", "", "application listenaddress or host"),
+// "application.port": rootCmd.Flags().IntP("port", "p", 0, "application listenport"),
+// "database.host": rootCmd.Flags().StringP("database-host", "H", "", "database host"),
+// "database.port": rootCmd.Flags().IntP("database-port", "P", 0, "database port"),
+// "database.dialect": rootCmd.Flags().StringP("database-dialect", "D", "", "database dialect"),
+// "database.database": rootCmd.Flags().StringP("database-name", "d", "", "database name"),
+// "database.user": rootCmd.Flags().StringP("database-user", "u", "", "database user"),
+// "database.password": rootCmd.Flags().StringP("database-password", "s", "", "database password"),
+// "database.maxopenconn": rootCmd.Flags().IntP("database-maxopen", "o", 0, "database max open connections"),
+// "database.maxidleconn": rootCmd.Flags().IntP("database-maxidle", "i", 0, "database max idle connections"),
+// "database.maxlifetime": rootCmd.Flags().IntP("database-maxlifetime", "t", 0, "database max connection lifetime"),
+// }
+// if err := rootCmd.Execute(); err != nil {
+// return err
+// }
+// if err := config.Initialize(appName, cfgFile, flagCfg); err != nil {
+// return err
+// }
+// return nil
+// }
diff --git a/cmd/cmd_test.go b/cmd/root_test.go
similarity index 100%
rename from cmd/cmd_test.go
rename to cmd/root_test.go
diff --git a/cmd/server/server.go b/cmd/server/server.go
new file mode 100644
index 0000000..5b39ffc
--- /dev/null
+++ b/cmd/server/server.go
@@ -0,0 +1,68 @@
+package server
+
+import (
+ "fmt"
+
+ "git.devices.local/mawas/golang-api-skeleton/api"
+ "git.devices.local/mawas/golang-api-skeleton/lib/config"
+ "github.com/gin-gonic/gin"
+ "github.com/spf13/cobra"
+ "github.com/spf13/viper"
+)
+
+func Command(appName string, cfgFile string, flagCfg map[string]interface{}) *cobra.Command {
+ return &cobra.Command{
+ Use: "server",
+ Short: "Starts application",
+ Long: "Starts application",
+ // SilenceUsage: true,
+ // SilenceErrors: true,
+ DisableAutoGenTag: true,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ return startServer(appName, cfgFile, flagCfg)
+ },
+ }
+}
+
+func startServer(appName string, cfgFile string, flagCfg map[string]interface{}) error {
+ if err := config.Initialize(appName, cfgFile, flagCfg); err != nil {
+ return fmt.Errorf("config:" + err.Error())
+ }
+ // 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 {
+ // return fmt.Errorf("database:%v", err)
+ // }
+ app := gin.New()
+ app.Use(gin.Recovery())
+ // app.Use(inject(db))
+ api.ApplyRoutes(app)
+ if err := app.Run(fmt.Sprintf("%s:%s", viper.GetString("application.listenaddress"), viper.GetString("application.port"))); err != nil {
+ return fmt.Errorf("api:%v", err)
+ }
+ return nil
+}
+
+// func ping(c *gin.Context) {
+// c.JSON(200, gin.H{
+// "message": "pong",
+// })
+// }
+
+// Inject injects database to gin context
+// func inject(db *gorm.DB) gin.HandlerFunc {
+// return func(c *gin.Context) {
+// c.Set("db", db)
+// c.Next()
+// }
+// }
diff --git a/go.mod b/go.mod
index 2e9ae8e..3a3de7b 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@ require (
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
github.com/dgraph-io/badger/v2 v2.2007.2
github.com/fsnotify/fsnotify v1.4.9 // indirect
+ github.com/gin-gonic/gin v1.7.1 // indirect
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
github.com/magiconair/properties v1.8.4 // indirect
@@ -21,6 +22,8 @@ require (
github.com/spf13/viper v1.7.1
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
golang.org/x/text v0.3.5 // indirect
+ gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
+ gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gorm.io/driver/mysql v1.0.3
diff --git a/go.sum b/go.sum
index c620e86..669c69e 100644
--- a/go.sum
+++ b/go.sum
@@ -67,6 +67,10 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.7.1 h1:qC89GU3p8TvKWMAVhEpmpB2CIb1hnqt2UdKZaP93mS8=
+github.com/gin-gonic/gin v1.7.1/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@@ -75,6 +79,13 @@ github.com/go-ozzo/ozzo-validation v3.6.0+incompatible h1:msy24VGS42fKO9K1vLz82/
github.com/go-ozzo/ozzo-validation v3.6.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
+github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@@ -93,12 +104,15 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -192,6 +206,8 @@ github.com/jinzhu/now v1.1.1 h1:g39TucaRWyV3dwDO++eEc6qf8TVIQ/Da48WmqjZ3i7E=
github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
@@ -208,6 +224,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
@@ -226,6 +244,7 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v1.14.3 h1:j7a/xn1U6TKA/PHHxqZuzh64CdtRc7rU9M+AvkOl5bA=
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
@@ -241,7 +260,11 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
@@ -326,7 +349,11 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
+github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
+github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
@@ -483,6 +510,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
+gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
diff --git a/lib/common/common.go b/lib/common/common.go
index ea02a61..78a7410 100644
--- a/lib/common/common.go
+++ b/lib/common/common.go
@@ -1,13 +1,16 @@
package common
import (
- "crypto/rand"
"time"
- "github.com/oklog/ulid"
"gorm.io/gorm"
)
+const (
+ CLIUserID = "01F5D7K0754ZDRPSKYDHE5CE0H"
+ CLIUsername = "cli"
+)
+
type BasicFields struct {
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
CreatedByDB GUID `gorm:"column:created_by" json:"-"`
@@ -22,7 +25,11 @@ type BasicFields struct {
func (basicFields *BasicFields) BeforeCreate(tx *gorm.DB) error {
if userID, ok := tx.Get("userID"); ok {
- if guid, ok := userID.(GUID); ok {
+ if guidString, ok := userID.(string); ok {
+ guid, err := StringToGUID(guidString)
+ if err != nil {
+ return err
+ }
basicFields.CreatedByDB = guid
basicFields.UpdatedByDB = guid
}
@@ -51,10 +58,17 @@ type ModelGUIDPK struct {
}
func (guidPK *ModelGUIDPK) BeforeCreate(tx *gorm.DB) error {
- id, err := ulid.New(ulid.Now(), rand.Reader)
+ id, err := NewGUID()
+ if err != nil {
+ return err
+ }
guidPK.ID = GUID(id)
if userID, ok := tx.Get("userID"); ok {
- if guid, ok := userID.(GUID); ok {
+ if guidString, ok := userID.(string); ok {
+ guid, err := StringToGUID(guidString)
+ if err != nil {
+ return err
+ }
guidPK.CreatedByDB = guid
guidPK.UpdatedByDB = guid
}
@@ -68,10 +82,17 @@ type ModelHiddenGUIDPK struct {
}
func (guidPK *ModelHiddenGUIDPK) BeforeCreate(tx *gorm.DB) error {
- id, err := ulid.New(ulid.Now(), rand.Reader)
+ id, err := NewGUID()
+ if err != nil {
+ return err
+ }
guidPK.ID = GUID(id)
if userID, ok := tx.Get("userID"); ok {
- if guid, ok := userID.(GUID); ok {
+ if guidString, ok := userID.(string); ok {
+ guid, err := StringToGUID(guidString)
+ if err != nil {
+ return err
+ }
guidPK.CreatedByDB = guid
guidPK.UpdatedByDB = guid
}
diff --git a/lib/common/guid.go b/lib/common/guid.go
index fb8e70b..e1e8286 100644
--- a/lib/common/guid.go
+++ b/lib/common/guid.go
@@ -1,6 +1,7 @@
package common
import (
+ "crypto/rand"
"database/sql/driver"
"github.com/oklog/ulid/v2"
@@ -9,6 +10,11 @@ import (
// GUID our new ULID based datatype
type GUID ulid.ULID
+func NewGUID() (GUID, error) {
+ id, err := ulid.New(ulid.Now(), rand.Reader)
+ return GUID(id), err
+}
+
// StringToGUID -> parse string to ULID GUID
func StringToGUID(s string) (GUID, error) {
id, err := ulid.Parse(s) // maybe ParseStrict
diff --git a/main.go b/main.go
index 1c9f6a7..c06c981 100644
--- a/main.go
+++ b/main.go
@@ -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")
+ //
}
diff --git a/models/token.go b/models/token.go
index 32ee467..085322a 100644
--- a/models/token.go
+++ b/models/token.go
@@ -1,13 +1,11 @@
package models
import (
- "crypto/rand"
"database/sql"
"time"
"git.devices.local/mawas/golang-api-skeleton/lib/cache"
"git.devices.local/mawas/golang-api-skeleton/lib/common"
- "github.com/oklog/ulid"
"gorm.io/gorm"
)
@@ -19,15 +17,22 @@ type Token struct {
LastUsedJSON *time.Time `gorm:"-" json:"last_used,omitempty"`
ExpiresAt time.Time `json:"expires_at"`
UserID common.GUID `json:"asd"`
- Username string `json:"username"`
+ Username string `gorm:"-" json:"username"`
Active bool `json:"active"`
}
func (token *Token) BeforeCreate(tx *gorm.DB) error {
- id, err := ulid.New(ulid.Now(), rand.Reader)
- token.Token = common.GUID(id)
+ id, err := common.NewGUID()
+ if err != nil {
+ return err
+ }
+ token.Token = id
if userID, ok := tx.Get("userID"); ok {
- if guid, ok := userID.(common.GUID); ok {
+ if guidString, ok := userID.(string); ok {
+ guid, err := common.StringToGUID(guidString)
+ if err != nil {
+ return err
+ }
token.CreatedByDB = guid
token.UpdatedByDB = guid
}
diff --git a/repositories/repsitory.go b/repositories/repsitory.go
index a004e53..a80e7f4 100644
--- a/repositories/repsitory.go
+++ b/repositories/repsitory.go
@@ -1,12 +1,11 @@
package repositories
import (
- "git.devices.local/mawas/golang-api-skeleton/lib/common"
"gorm.io/gorm"
)
type repo struct {
db *gorm.DB
- userID common.GUID
+ userID string
username string
}
diff --git a/repositories/token.go b/repositories/token.go
index 9c71880..fd27541 100644
--- a/repositories/token.go
+++ b/repositories/token.go
@@ -3,7 +3,6 @@ package repositories
import (
"git.devices.local/mawas/golang-api-skeleton/lib/cache"
- "git.devices.local/mawas/golang-api-skeleton/lib/common"
model "git.devices.local/mawas/golang-api-skeleton/models"
"gorm.io/gorm"
)
@@ -13,7 +12,7 @@ type tokenDAO struct {
}
// NewTokenRepository create new repository for model token
-func NewTokenRepository(db *gorm.DB, userID common.GUID, username string, cache cache.Cache) *tokenDAO {
+func NewTokenRepository(db *gorm.DB, userID string, username string, cache cache.Cache) *tokenDAO {
db = db.Set("username", username).Set("userID", userID).Set("cache", cache)
return &tokenDAO{
repo: repo{
diff --git a/repositories/user.go b/repositories/user.go
index 4045872..8e14218 100644
--- a/repositories/user.go
+++ b/repositories/user.go
@@ -3,7 +3,6 @@ package repositories
import (
"git.devices.local/mawas/golang-api-skeleton/lib/cache"
- "git.devices.local/mawas/golang-api-skeleton/lib/common"
model "git.devices.local/mawas/golang-api-skeleton/models"
"gorm.io/gorm"
)
@@ -13,7 +12,7 @@ type UserDAO struct {
}
// NewUserDAO create new repository for model user
-func NewUserRepository(db *gorm.DB, userID common.GUID, username string, cache cache.Cache) *UserDAO {
+func NewUserRepository(db *gorm.DB, userID string, username string, cache cache.Cache) *UserDAO {
db = db.Set("username", username).Set("userID", userID).Set("cache", cache)
return &UserDAO{
repo: repo{
diff --git a/services/user.go b/services/user.go
index 0f7007d..fcb15bf 100644
--- a/services/user.go
+++ b/services/user.go
@@ -33,3 +33,14 @@ func (s *UserService) ReadAll() ([]*model.User, error) {
func (s *UserService) Create(user *model.User) (*model.User, error) {
return s.repo.Create(user)
}
+
+// NewUser returns a new created user object
+func NewUser(username, firstname, lastname, email string) model.User {
+ return model.User{
+ Username: username,
+ Firstname: firstname,
+ Lastname: lastname,
+ Email: email,
+ PasswordHash: "generate_password_here",
+ }
+}