diff --git a/infra/db/converter.go b/infra/db/converter.go index 59b142ed..3e23bae9 100644 --- a/infra/db/converter.go +++ b/infra/db/converter.go @@ -15,16 +15,12 @@ func ConvCreateRoomParamsToRoom(src CreateRoomParams) (dst Room) { dst.Place = src.WriteRoomParams.Place dst.TimeStart = src.WriteRoomParams.TimeStart dst.TimeEnd = src.WriteRoomParams.TimeEnd - dst.Admins = make([]RoomAdmin, len(src.WriteRoomParams.Admins)) + dst.Admins = make([]User, len(src.WriteRoomParams.Admins)) for i := range src.WriteRoomParams.Admins { - dst.Admins[i] = convuuidUUIDToRoomAdmin(src.WriteRoomParams.Admins[i]) + dst.Admins[i] = ConvuuidUUIDToUserMeta(src.WriteRoomParams.Admins[i]) } return } -func ConvEventAdminToRoomAdmin(src EventAdmin) (dst RoomAdmin) { - dst.UserID = src.UserID - return -} func ConvEventAdminTodomainUser(src EventAdmin) (dst domain.User) { dst.ID = src.UserID @@ -100,10 +96,6 @@ func ConvGroupTodomainGroup(src Group) (dst domain.Group) { (*dst.Model.DeletedAt) = convgormDeletedAtTotimeTime(src.Model.DeletedAt) return } -func ConvRoomAdminTodomainUser(src RoomAdmin) (dst domain.User) { - dst.ID = src.UserID - return -} func ConvRoomTodomainRoom(src Room) (dst domain.Room) { dst.ID = src.ID @@ -117,7 +109,7 @@ func ConvRoomTodomainRoom(src Room) (dst domain.Room) { } dst.Admins = make([]domain.User, len(src.Admins)) for i := range src.Admins { - dst.Admins[i] = convRoomAdminTodomainUser(src.Admins[i]) + dst.Admins[i] = convUserTodomainUser(src.Admins[i]) } dst.CreatedBy = convUserTodomainUser(src.CreatedBy) dst.Model.CreatedAt = src.Model.CreatedAt @@ -127,10 +119,10 @@ func ConvRoomTodomainRoom(src Room) (dst domain.Room) { return } -func ConvSEventAdminToSRoomAdmin(src []EventAdmin) (dst []RoomAdmin) { - dst = make([]RoomAdmin, len(src)) +func ConvSEventAdminToSUser(src []EventAdmin) (dst []User) { + dst = make([]User, len(src)) for i := range src { - dst[i] = convEventAdminToRoomAdmin(src[i]) + dst[i] = convEventAdminToUser(src[i]) } return } @@ -207,9 +199,9 @@ func ConvUpdateRoomParamsToRoom(src UpdateRoomParams) (dst Room) { dst.Place = src.WriteRoomParams.Place dst.TimeStart = src.WriteRoomParams.TimeStart dst.TimeEnd = src.WriteRoomParams.TimeEnd - dst.Admins = make([]RoomAdmin, len(src.WriteRoomParams.Admins)) + dst.Admins = make([]User, len(src.WriteRoomParams.Admins)) for i := range src.WriteRoomParams.Admins { - dst.Admins[i] = convuuidUUIDToRoomAdmin(src.WriteRoomParams.Admins[i]) + dst.Admins[i] = ConvuuidUUIDToUserMeta(src.WriteRoomParams.Admins[i]) } return } @@ -288,21 +280,18 @@ func ConvuuidUUIDToGroupMember(src uuid.UUID) (dst GroupMember) { dst.UserID = src return } -func ConvuuidUUIDToRoomAdmin(src uuid.UUID) (dst RoomAdmin) { - dst.UserID = src - return -} + func ConvuuidUUIDToUserMeta(src uuid.UUID) (dst User) { dst.ID = src return } -func convEventAdminToRoomAdmin(src EventAdmin) (dst RoomAdmin) { - dst.UserID = src.UserID +func convEventAdminTodomainUser(src EventAdmin) (dst domain.User) { + dst.ID = src.UserID return } -func convEventAdminTodomainUser(src EventAdmin) (dst domain.User) { +func convEventAdminToUser(src EventAdmin) (dst User) { dst.ID = src.UserID return } @@ -375,10 +364,7 @@ func convGroupTodomainGroup(src Group) (dst domain.Group) { (*dst.Model.DeletedAt) = convgormDeletedAtTotimeTime(src.Model.DeletedAt) return } -func convRoomAdminTodomainUser(src RoomAdmin) (dst domain.User) { - dst.ID = src.UserID - return -} + func convRoomTodomainRoom(src Room) (dst domain.Room) { dst.ID = src.ID dst.Place = src.Place @@ -391,7 +377,7 @@ func convRoomTodomainRoom(src Room) (dst domain.Room) { } dst.Admins = make([]domain.User, len(src.Admins)) for i := range src.Admins { - dst.Admins[i] = convRoomAdminTodomainUser(src.Admins[i]) + dst.Admins[i] = convUserTodomainUser(src.Admins[i]) } dst.CreatedBy = convUserTodomainUser(src.CreatedBy) dst.Model.CreatedAt = src.Model.CreatedAt @@ -438,8 +424,3 @@ func convuuidUUIDToGroupMember(src uuid.UUID) (dst GroupMember) { dst.UserID = src return } - -func convuuidUUIDToRoomAdmin(src uuid.UUID) (dst RoomAdmin) { - dst.UserID = src - return -} diff --git a/infra/db/hooks.go b/infra/db/hooks.go index 078c57fb..6e174fd2 100644 --- a/infra/db/hooks.go +++ b/infra/db/hooks.go @@ -22,7 +22,7 @@ func (e *Event) BeforeSave(tx *gorm.DB) (err error) { e.Room.TimeStart = e.TimeStart e.Room.TimeEnd = e.TimeEnd e.Room.CreatedByRefer = e.CreatedByRefer - e.Room.Admins = ConvSEventAdminToSRoomAdmin(e.Admins) + e.Room.Admins = ConvSEventAdminToSUser(e.Admins) } else { return NewValueError(ErrRoomUndefined, "roomID", "place") } @@ -180,8 +180,7 @@ func (r *Room) BeforeSave(tx *gorm.DB) (err error) { } func (r *Room) BeforeUpdate(tx *gorm.DB) (err error) { - err = tx.Where("room_id", r.ID).Delete(&RoomAdmin{}).Error - if err != nil { + if err := tx.Model(&Room{ID: r.ID}).Association("Admins").Clear(); err != nil { return err } return nil diff --git a/infra/db/model.go b/infra/db/model.go index d46c13ad..e5dfd31c 100644 --- a/infra/db/model.go +++ b/infra/db/model.go @@ -10,13 +10,11 @@ import ( var tables = []interface{}{ User{}, Token{}, - Provider{}, Group{}, GroupMember{}, GroupAdmin{}, Tag{}, Room{}, - RoomAdmin{}, Event{}, EventTag{}, // Eventより下にないと、overrideされる EventAdmin{}, @@ -58,8 +56,7 @@ type Token struct { } type Provider struct { - UserID uuid.UUID `gorm:"type:char(36); primaryKey"` - Issuer string `gorm:"not null"` + Issuer string `gorm:"not null"` Subject string } @@ -68,9 +65,9 @@ type User struct { // アプリの管理者かどうか Privilege bool `gorm:"not null"` State int - IcalSecret string `gorm:"not null"` - Provider Provider `gorm:"foreignKey:UserID; constraint:OnDelete:CASCADE;"` - Token Token `gorm:"foreignKey:UserID; constraint:OnDelete:CASCADE;"` + IcalSecret string `gorm:"not null"` + Provider + Token Token `gorm:"foreignKey:UserID; constraint:OnDelete:CASCADE;"` } type UserBody struct { @@ -81,13 +78,6 @@ type UserBody struct { User User `gorm:"->; foreignKey:ID; constraint:OnDelete:CASCADE;" cvt:"->"` } -type RoomAdmin struct { - UserID uuid.UUID `gorm:"type:char(36); primaryKey"` - RoomID uuid.UUID `gorm:"type:char(36); primaryKey"` - User User `gorm:"->; foreignKey:UserID; constraint:OnDelete:CASCADE;" cvt:"->"` - Model `cvt:"-"` -} - //go:generate go run github.com/fuji8/gotypeconverter/cmd/gotypeconverter@latest -s Room -d domain.Room -o converter.go . //go:generate go run github.com/fuji8/gotypeconverter/cmd/gotypeconverter@latest -s []*Room -d []*domain.Room -o converter.go . type Room struct { @@ -97,7 +87,7 @@ type Room struct { TimeStart time.Time `gorm:"type:DATETIME; index"` TimeEnd time.Time `gorm:"type:DATETIME; index"` Events []Event `gorm:"->; constraint:-"` // readOnly - Admins []RoomAdmin + Admins []User `gorm:"many2many:room_admin_users;"` CreatedByRefer uuid.UUID `gorm:"type:char(36);" cvt:"CreatedBy, <-"` CreatedBy User `gorm:"->; foreignKey:CreatedByRefer; constraint:OnDelete:CASCADE;" cvt:"->"` Model `cvt:"->"` diff --git a/infra/db/user.go b/infra/db/user.go index d7b16368..b4dcc6e6 100644 --- a/infra/db/user.go +++ b/infra/db/user.go @@ -8,7 +8,7 @@ import ( ) func userPreload(tx *gorm.DB) *gorm.DB { - return tx.Preload("Provider") + return tx } func (repo *GormRepository) SaveUser(user User) (*User, error) { @@ -73,7 +73,7 @@ func (repo *GormRepository) SyncUsers(users []*User) error { // user.Privilegeは常に更新されません。 func saveUser(db *gorm.DB, user *User) (*User, error) { err := db.Transaction(func(tx *gorm.DB) error { - existingUser, err := getUser(tx.Preload("Provider").Preload("Token"), user.ID) + existingUser, err := getUser(tx.Preload("Token"), user.ID) if errors.Is(err, gorm.ErrRecordNotFound) { return tx.Create(&user).Error } diff --git a/infra/db/user_test.go b/infra/db/user_test.go index 5e0280f6..f3020a6a 100644 --- a/infra/db/user_test.go +++ b/infra/db/user_test.go @@ -20,7 +20,6 @@ func Test_saveUser(t *testing.T) { }, }, Provider: Provider{ - UserID: id, Issuer: "bar", Subject: id.String(), }, @@ -29,7 +28,7 @@ func Test_saveUser(t *testing.T) { t.Run("save user", func(t *testing.T) { _, err := saveUser(r.db, user) assert.NoError(err) - u, err := getUser(r.db.Preload("Provider"), id) + u, err := getUser(r.db, id) assert.NoError(err) assert.Equal(user.Provider.Issuer, u.Provider.Issuer) }) @@ -41,7 +40,7 @@ func Test_saveUser(t *testing.T) { }) assert.NoError(err) - u, err := getUser(r.db.Preload("Token").Preload("Provider"), id) + u, err := getUser(r.db.Preload("Token"), id) assert.NoError(err) // token assert.Equal(user.Token.AccessToken, u.Token.AccessToken) diff --git a/migration/current.go b/migration/current.go index b4790920..e9488bc5 100644 --- a/migration/current.go +++ b/migration/current.go @@ -19,5 +19,7 @@ func Migrations() []*gormigrate.Migration { v10(), v11(), v12(), + v13(), + v14(), } } diff --git a/migration/v13.go b/migration/v13.go new file mode 100644 index 00000000..dd0084eb --- /dev/null +++ b/migration/v13.go @@ -0,0 +1,67 @@ +package migration + +import ( + gormigrate "github.com/go-gormigrate/gormigrate/v2" + "github.com/gofrs/uuid" + "gorm.io/gorm" +) + +type v13newUser struct { + ID uuid.UUID `gorm:"type:char(36); primaryKey"` + Privilege bool `gorm:"not null"` + State int + IcalSecret string `gorm:"not null"` + Issuer string `gorm:"not null"` + Subject string +} + +func (*v13newUser) TableName() string { + return "users" +} + +type v13currentProvider struct { + UserID uuid.UUID `gorm:"type:char(36); primaryKey"` + Issuer string `gorm:"not null"` + Subject string +} + +func (*v13currentProvider) TableName() string { + return "providers" +} + +func v13() *gormigrate.Migration { + return &gormigrate.Migration{ + ID: "13", + Migrate: func(db *gorm.DB) error { + // Step 1: Add Issuer and Subject columns to the User table + if err := db.Migrator().AddColumn(&v13newUser{}, "Issuer"); err != nil { + return err + } + if err := db.Migrator().AddColumn(&v13newUser{}, "Subject"); err != nil { + return err + } + + // Step 2: Migrate data from Provider to User + providers := make([]*v13currentProvider, 0) + if err := db.Find(&providers).Error; err != nil { + return err + } + + for _, provider := range providers { + if err := db.Model(&v13newUser{}).Where("id = ?", provider.UserID).Updates(map[string]interface{}{ + "Issuer": provider.Issuer, + "Subject": provider.Subject, + }).Error; err != nil { + return err + } + } + + // Step 3: Drop the Provider table + if err := db.Migrator().DropTable(&v13currentProvider{}); err != nil { + return err + } + + return nil + }, + } +} diff --git a/migration/v14.go b/migration/v14.go new file mode 100644 index 00000000..7d5b54c7 --- /dev/null +++ b/migration/v14.go @@ -0,0 +1,78 @@ +package migration + +import ( + "time" + + "github.com/go-gormigrate/gormigrate/v2" + "github.com/gofrs/uuid" + "gorm.io/gorm" +) + +type v14Room struct { + ID uuid.UUID `gorm:"type:char(36);primaryKey"` + Place string `gorm:"type:varchar(32);"` + Verified bool + TimeStart time.Time `gorm:"type:DATETIME; index"` + TimeEnd time.Time `gorm:"type:DATETIME; index"` + CreatedByRefer uuid.UUID `gorm:"type:char(36);"` +} + +func (*v14Room) TableName() string { + return "rooms" +} + +type v14RoomUser struct { + RoomID uuid.UUID `gorm:"type:char(36); primaryKey"` + UserID uuid.UUID `gorm:"type:char(36); primaryKey"` +} + +func (*v14RoomUser) TableName() string { + return "room_admin_users" +} + +type v14RoomAdmin struct { + UserID uuid.UUID `gorm:"type:char(36); primaryKey"` + RoomID uuid.UUID `gorm:"type:char(36); primaryKey"` +} + +func (*v14RoomAdmin) TableName() string { + return "room_admins" +} + +func v14() *gormigrate.Migration { + return &gormigrate.Migration{ + ID: "14", + Migrate: func(db *gorm.DB) error { + // Step 1: Create the new many-to-many table + err := db.Migrator().CreateTable(&v14RoomUser{}) + if err != nil { + return err + } + + // Step 2: Migrate data from RoomAdmin to RoomUser + roomAdmins := []v14RoomAdmin{} + err = db.Find(&roomAdmins).Error + if err != nil { + return err + } + + roomUsers := make([]v14RoomUser, len(roomAdmins)) + for i, admin := range roomAdmins { + roomUsers[i] = v14RoomUser{ + RoomID: admin.RoomID, + UserID: admin.UserID, + } + } + + if len(roomUsers) > 0 { + err = db.Create(&roomUsers).Error + if err != nil { + return err + } + } + + // Step 3: Drop the RoomAdmin table + return db.Migrator().DropTable(&v14RoomAdmin{}) + }, + } +} diff --git a/repository/user.go b/repository/user.go index 7d2ac830..065208ed 100644 --- a/repository/user.go +++ b/repository/user.go @@ -33,7 +33,6 @@ func (repo *Repository) SyncUsers(info *domain.ConInfo) error { ID: uid, State: int(u.State), Provider: db.Provider{ - UserID: uid, Issuer: traQIssuerName, Subject: u.GetId(), }, @@ -72,7 +71,6 @@ func (repo *Repository) LoginUser(query, state, codeVerifier string) (*domain.Us }, }, Provider: db.Provider{ - UserID: uid, Issuer: traQIssuerName, Subject: traQUser.GetId(), },