Browse Source

code restructure, server command for app start added

master
Bernd-René Predota 5 years ago
parent
commit
e1744934e2
  1. 2
      README.md
  2. 15
      api/api.go
  3. 11
      api/v1/v1.go
  4. 23
      cmd/add/add.go
  5. 68
      cmd/add/admin/admin.go
  6. 132
      cmd/cmd.go
  7. 199
      cmd/root.go
  8. 0
      cmd/root_test.go
  9. 68
      cmd/server/server.go
  10. 3
      go.mod
  11. 29
      go.sum
  12. 35
      lib/common/common.go
  13. 6
      lib/common/guid.go
  14. 373
      main.go
  15. 17
      models/token.go
  16. 3
      repositories/repsitory.go
  17. 3
      repositories/token.go
  18. 3
      repositories/user.go
  19. 11
      services/user.go

2
README.md

@ -1,6 +1,6 @@
# golang API Skeleton
[![Build Status](https://drone.devices.local/api/badges/mawas/golang-api-skeleton/status.svg)](https://drone.devices.local/mawas/golang-api-skeleton)<a href='https://github.com/jpoles1/gopherbadger' target='_blank'>![gopherbadger-tag-do-not-edit](https://img.shields.io/badge/Go%20Coverage-77%25-brightgreen.svg?longCache=true&style=flat)</a>
[![Build Status](https://drone.devices.local/api/badges/mawas/golang-api-skeleton/status.svg)](https://drone.devices.local/mawas/golang-api-skeleton)<a href='https://github.com/jpoles1/gopherbadger' target='_blank'>![gopherbadger-tag-do-not-edit](https://img.shields.io/badge/Go%20Coverage-67%25-brightgreen.svg?longCache=true&style=flat)</a>
refined skeleton future apis should be based on

15
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
}

11
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
}

23
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
}

68
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
}

132
cmd/cmd.go

@ -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
// }

199
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
// }

0
cmd/cmd_test.go → cmd/root_test.go

68
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()
// }
// }

3
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

29
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=

35
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
}

6
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

373
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")
//
}

17
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
}

3
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
}

3
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{

3
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{

11
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",
}
}
Loading…
Cancel
Save