package storage import ( "fmt" "strings" "git.mstar.dev/mstar/goutils/other" "git.mstar.dev/mstar/goutils/sliceutils" "github.com/rs/zerolog" "gorm.io/gorm" "git.mstar.dev/mstar/linstrom/storage-new/models" ) // Auto-migrate all tables and types used func Migrate(db *gorm.DB) error { // Shut up gorm's queries during automigrate by setting log level to info during migration // and then back to the previous value on exit currentLogLevel := zerolog.GlobalLevel() zerolog.SetGlobalLevel(zerolog.InfoLevel) defer zerolog.SetGlobalLevel(currentLogLevel) if err := createAccountAuthMethodType(db); err != nil { return other.Error("storage", "Failed to create Auth Method type", err) } if err := createBeingType(db); err != nil { return other.Error("storage", "Failed to create Being type", err) } if err := createAccountRelationType(db); err != nil { return other.Error("storage", "Failed to create Account Relation type", err) } if err := createRemoteServerSoftwareType(db); err != nil { return other.Error("storage", "Failed to create Server Software type", err) } if err := migrateTypes(db); err != nil { return other.Error("storage", "Failed to automigrate data structs", err) } return nil } // Returns the raw error created by gorm, with no wrapping func migrateTypes(db *gorm.DB) error { if err := db.AutoMigrate(models.AllTypes...); err != nil { return err } return nil } // Ensure the being enum exists for the user func createBeingType(db *gorm.DB) error { return migrateEnum( db, "being_type", sliceutils.Map(models.AllBeings, func(t models.BeingType) string { return string(t) }), ) } func createAccountRelationType(db *gorm.DB) error { return migrateEnum( db, "relation_type", sliceutils.Map( models.AllRelations, func(t models.RelationType) string { return string(t) }, ), ) } func createAccountAuthMethodType(db *gorm.DB) error { return migrateEnum( db, "auth_method_type", sliceutils.Map( models.AllAuthMethods, func(t models.AuthenticationMethodType) string { return string(t) }, ), ) } func createRemoteServerSoftwareType(db *gorm.DB) error { return migrateEnum( db, "server_software_type", sliceutils.Map( models.AllServerSoftwareTypes, func(t models.ServerSoftwareType) string { return string(t) }, ), ) } // Helper function for ensuring the existence of an enum with the given values func migrateEnum(db *gorm.DB, name string, values []string) error { if err := db.Exec("DROP TYPE IF EXISTS " + name + " CASCADE;").Error; err != nil { return other.Error( "storage", fmt.Sprintf("Failed to remove old type %s (if it exists)", name), err, ) } queryBuilder := strings.Builder{} queryBuilder.WriteString("CREATE TYPE ") queryBuilder.WriteString(name) queryBuilder.WriteString(" AS ENUM (") blen := len(values) for i, btype := range values { queryBuilder.WriteString("'" + string(btype) + "'") // Append comma everywhere except last entry if i+1 < blen { queryBuilder.WriteString(",") } } queryBuilder.WriteString(");") if err := db.Exec(queryBuilder.String()).Error; err != nil { return err } return nil }