From 783e1c84c398bb00406d31a7f81dea1e273ce48d Mon Sep 17 00:00:00 2001 From: Jordan Knott Date: Tue, 5 Jan 2021 16:46:15 -0600 Subject: [PATCH] feat: add seed command to generate fake project data --- go.mod | 2 + go.sum | 16 ++++ internal/commands/commands.go | 2 +- internal/commands/seed.go | 152 ++++++++++++++++++++++++++++++++++ 4 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 internal/commands/seed.go diff --git a/go.mod b/go.mod index b6aa1b4..09d0a73 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.13 require ( github.com/99designs/gqlgen v0.11.3 github.com/RichardKnop/machinery v1.9.1 + github.com/brianvoe/gofakeit/v5 v5.11.2 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/go-chi/chi v3.3.2+incompatible github.com/golang-migrate/migrate/v4 v4.11.0 @@ -14,6 +15,7 @@ require ( github.com/lib/pq v1.3.0 github.com/lithammer/fuzzysearch v1.1.0 github.com/magefile/mage v1.11.0 + github.com/manifoldco/promptui v0.8.0 github.com/matcornic/hermes/v2 v2.1.0 github.com/pelletier/go-toml v1.8.0 // indirect github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum index 28ebd6d..4474b77 100644 --- a/go.sum +++ b/go.sum @@ -96,10 +96,16 @@ github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwj github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0= github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= +github.com/brianvoe/gofakeit v1.2.0 h1:GGbzCqQx9ync4ObAUhRa3F/M73eL9VZL3X09WoTwphM= +github.com/brianvoe/gofakeit v3.18.0+incompatible h1:wDOmHc9DLG4nRjUVVaxA+CEglKOW72Y5+4WNxUIkjM8= +github.com/brianvoe/gofakeit v3.18.0+incompatible/go.mod h1:kfwdRA90vvNhPutZWfH7WPaDzUjz+CZFqG+rPkOjGOc= +github.com/brianvoe/gofakeit/v5 v5.11.2 h1:Ny5Nsf4z2023ZvYP8ujW8p5B1t5sxhdFaQ/0IYXbeSA= +github.com/brianvoe/gofakeit/v5 v5.11.2/go.mod h1:/ZENnKqX+XrN8SORLe/fu5lZDIo1tuPncWuRD+eyhSI= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -351,6 +357,8 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= +github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= @@ -383,10 +391,14 @@ github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lithammer/fuzzysearch v1.1.0 h1:go9v8tLCrNTTlH42OAaq4eHFe81TDHEnlrMEb6R4f+A= github.com/lithammer/fuzzysearch v1.1.0/go.mod h1:Bqx4wo8lTOFcJr3ckpY6HA9lEIOO0H5HrkJ5CsN56HQ= github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw= +github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/magefile/mage v1.11.0 h1:C/55Ywp9BpgVVclD3lRnSYCwXTYxmSppIgLeDYlNuls= github.com/magefile/mage v1.11.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/manifoldco/promptui v0.8.0 h1:R95mMF+McvXZQ7j1g8ucVZE1gLP3Sv6j9vlF9kyRqQo= +github.com/manifoldco/promptui v0.8.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= github.com/markbates/pkger v0.15.1/go.mod h1:0JoVlrol20BSywW79rN3kdFFsE5xYM+rSCQDXbLhiuI= github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= @@ -394,11 +406,15 @@ github.com/matcornic/hermes/v2 v2.1.0 h1:9TDYFBPFv6mcXanaDmRDEp/RTWj0dTTi+LpFnnn github.com/matcornic/hermes/v2 v2.1.0/go.mod h1:2+ziJeoyRfaLiATIL8VZ7f9hpzH4oDHqTmn0bhrsgVI= github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007 h1:reVOUXwnhsYv/8UqjvhrMOu5CNT9UapHFLbQ2JcXsmg= github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 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.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= diff --git a/internal/commands/commands.go b/internal/commands/commands.go index 082cf02..be7de7d 100644 --- a/internal/commands/commands.go +++ b/internal/commands/commands.go @@ -86,6 +86,6 @@ func Execute() { viper.SetDefault("queue.store", "memcache://localhost:11211") rootCmd.SetVersionTemplate(VersionTemplate()) - rootCmd.AddCommand(newWebCmd(), newMigrateCmd(), newTokenCmd(), newWorkerCmd(), newResetPasswordCmd()) + rootCmd.AddCommand(newWebCmd(), newMigrateCmd(), newTokenCmd(), newWorkerCmd(), newResetPasswordCmd(), newSeedCmd()) rootCmd.Execute() } diff --git a/internal/commands/seed.go b/internal/commands/seed.go new file mode 100644 index 0000000..380896c --- /dev/null +++ b/internal/commands/seed.go @@ -0,0 +1,152 @@ +package commands + +import ( + "context" + "fmt" + "time" + + "github.com/brianvoe/gofakeit/v5" + "github.com/manifoldco/promptui" + + "github.com/jordanknott/taskcafe/internal/db" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "github.com/jmoiron/sqlx" + log "github.com/sirupsen/logrus" +) + +var ( + teams int + projects int + taskGroups int + tasks int +) + +func newSeedCmd() *cobra.Command { + cc := &cobra.Command{ + Use: "seed", + Short: "Seeds the database with random data for testing", + Long: "Seeds the database with random data for testing. CAN NOT BE UNDONE.", + RunE: func(cmd *cobra.Command, args []string) error { + Formatter := new(log.TextFormatter) + Formatter.TimestampFormat = "02-01-2006 15:04:05" + Formatter.FullTimestamp = true + log.SetFormatter(Formatter) + log.SetLevel(log.InfoLevel) + + connection := fmt.Sprintf("user=%s password=%s host=%s dbname=%s port=%s sslmode=disable", + viper.GetString("database.user"), + viper.GetString("database.password"), + viper.GetString("database.host"), + viper.GetString("database.name"), + viper.GetString("database.port"), + ) + var dbConnection *sqlx.DB + var err error + var retryDuration time.Duration + maxRetryNumber := 4 + for i := 0; i < maxRetryNumber; i++ { + dbConnection, err = sqlx.Connect("postgres", connection) + if err == nil { + break + } + retryDuration = time.Duration(i*2) * time.Second + log.WithFields(log.Fields{"retryNumber": i, "retryDuration": retryDuration}).WithError(err).Error("issue connecting to database, retrying") + if i != maxRetryNumber-1 { + time.Sleep(retryDuration) + } + } + if err != nil { + return err + } + dbConnection.SetMaxOpenConns(25) + dbConnection.SetMaxIdleConns(25) + dbConnection.SetConnMaxLifetime(5 * time.Minute) + defer dbConnection.Close() + + if viper.GetBool("migrate") { + log.Info("running auto schema migrations") + if err = runMigration(dbConnection); err != nil { + return err + } + } + + prompt := promptui.Prompt{ + Label: "Seed database", + IsConfirm: true, + } + + _, err = prompt.Run() + + if err != nil { + return err + } + + ctx := context.Background() + repository := db.NewRepository(dbConnection) + now := time.Now().UTC() + organizations, err := repository.GetAllOrganizations(ctx) + organizationId := organizations[0].OrganizationID + for teamIdx := 0; teamIdx <= teams; teamIdx++ { + teamName := gofakeit.Company() + team, err := repository.CreateTeam(ctx, db.CreateTeamParams{ + Name: teamName, + CreatedAt: now, + OrganizationID: organizationId, + }) + if err != nil { + return err + } + + for projectIdx := 0; projectIdx <= projects; projectIdx++ { + projectName := gofakeit.Dessert() + project, err := repository.CreateTeamProject(ctx, db.CreateTeamProjectParams{ + TeamID: team.TeamID, + Name: projectName, + CreatedAt: now, + }) + if err != nil { + return err + } + for taskGroupIdx := 0; taskGroupIdx <= taskGroups; taskGroupIdx++ { + taskGroupName := gofakeit.LoremIpsumSentence(8) + taskGroup, err := repository.CreateTaskGroup(ctx, db.CreateTaskGroupParams{ + Name: taskGroupName, + ProjectID: project.ProjectID, + CreatedAt: now, + Position: float64(65535 * (taskGroupIdx + 1)), + }) + if err != nil { + return err + } + for taskIdx := 0; taskIdx <= tasks; taskIdx++ { + taskName := gofakeit.Sentence(8) + task, err := repository.CreateTask(ctx, db.CreateTaskParams{ + Name: taskName, + TaskGroupID: taskGroup.TaskGroupID, + CreatedAt: now, + Position: float64(65535 * (taskGroupIdx + 1)), + }) + if err != nil { + return err + } + fmt.Printf("Creating %d / %d / %d / %d - %d\n", teamIdx, projectIdx, taskGroupIdx, taskIdx, task.TaskID) + } + } + } + } + + return nil + }, + } + + cc.Flags().Bool("migrate", false, "if true, auto run's schema migrations before starting the web server") + cc.Flags().IntVar(&teams, "teams", 5, "number of teams to generate") + cc.Flags().IntVar(&projects, "projects", 10, "number of projects to create per team (personal projects are included)") + cc.Flags().IntVar(&taskGroups, "task_groups", 5, "number of task groups to generate per project") + cc.Flags().IntVar(&tasks, "tasks", 25, "number of tasks to generate per task group") + + viper.SetDefault("migrate", false) + return cc +}