Skip to content

Commit

Permalink
WIP: removed some complexity from data service
Browse files Browse the repository at this point in the history
  • Loading branch information
tomvodi committed Aug 30, 2024
1 parent 1f34256 commit 0902d7d
Show file tree
Hide file tree
Showing 3 changed files with 226 additions and 79 deletions.
12 changes: 12 additions & 0 deletions internal/common/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,15 @@ func HashFromData(data []byte) (string, error) {

return fmt.Sprintf("%x", hash.Sum(nil)), nil
}

func RemoveDuplicates[T comparable](sliceList []T) []T {
allKeys := make(map[T]bool)
list := []T{}
for _, item := range sliceList {
if _, value := allKeys[item]; !value {
allKeys[item] = true
list = append(list, item)
}
}
return list
}
227 changes: 149 additions & 78 deletions internal/database/db_data_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func (d *dbService) initDbTuneForCreation(

err = copier.Copy(&dbTune, &ct)
if err != nil {
return nil, fmt.Errorf("could not create db tune object")
return nil, fmt.Errorf("could not create db tune object: %v", err)
}
if tuneType != nil {
dbTune.TuneTypeID = &tuneType.ID
Expand Down Expand Up @@ -274,16 +274,48 @@ func (d *dbService) CreateMusicSet(
return nil, fmt.Errorf("can't create music set without a title")
}

dbSet := model.MusicSet{}
dbSet, err := d.initDbSetForCreation(musicSet, importFile)
if err != nil {
return nil, err
}

apiSet, err := d.createMusicSetWithTuneIDs(
dbSet,
musicSet.Tunes,
)
if err != nil {
return nil, err
}

return apiSet, nil
}

func (d *dbService) initDbSetForCreation(
musicSet apimodel.CreateSet,
importFile *model.ImportFile,
) (*model.MusicSet, error) {
dbSet := &model.MusicSet{}
if importFile != nil {
dbSet.ImportFileID = importFile.ID
}
if err := copier.Copy(&dbSet, &musicSet); err != nil {
return &apimodel.MusicSet{}, fmt.Errorf("could not create db object")

err := copier.Copy(dbSet, &musicSet)
if err != nil {
return nil, fmt.Errorf("could not create db music set object: %v", err)
}

// reset tunes to nil as copier creates an empty tune object for every tune id
// this leads to a foreign key constraint violation
dbSet.Tunes = nil

newTunes, err := d.dbTunesFromIDs(musicSet.Tunes)
return dbSet, nil
}

func (d *dbService) createMusicSetWithTuneIDs(
dbSet *model.MusicSet,
tuneIDs []uuid.UUID,
) (*apimodel.MusicSet, error) {
mSetTunes, err := d.dbTunesFromIDs(tuneIDs)
if err != nil {
return nil, err
}
Expand All @@ -293,7 +325,7 @@ func (d *dbService) CreateMusicSet(
return err
}

if err := d.assignMusicSetTunes(dbSet.ID, musicSet.Tunes); err != nil {
if err := d.createMusicSetTuneRelations(dbSet.ID, tuneIDs); err != nil {
return err
}

Expand All @@ -303,10 +335,10 @@ func (d *dbService) CreateMusicSet(
return nil, err
}

dbSet.Tunes = newTunes
apiSet, err := apiSetFromDbSet(&dbSet)
dbSet.Tunes = mSetTunes
apiSet, err := apiSetFromDbSet(dbSet)
if err != nil {
return &apimodel.MusicSet{}, err
return nil, err
}

return apiSet, nil
Expand Down Expand Up @@ -344,6 +376,7 @@ func apiSetFromDbSet(dbSet *model.MusicSet) (*apimodel.MusicSet, error) {
return apiSet, nil
}

// returns that music set that contains all tunes in the given order
func (d *dbService) getMusicSetByTuneIDs(tuneIDs []uuid.UUID) (*apimodel.MusicSet, error) {
allMusicSets, err := d.MusicSets()
if err != nil {
Expand All @@ -352,18 +385,7 @@ func (d *dbService) getMusicSetByTuneIDs(tuneIDs []uuid.UUID) (*apimodel.MusicSe

var matchingSet *apimodel.MusicSet
for _, set := range allMusicSets {
if len(set.Tunes) != len(tuneIDs) {
continue
}

allTunesMatch := true
for i, t := range set.Tunes {
if tuneIDs[i] != t.Id {
allTunesMatch = false
break
}
}
if allTunesMatch {
if musicSetHasTunesInOrder(set, tuneIDs) {
matchingSet = set
break
}
Expand All @@ -376,6 +398,23 @@ func (d *dbService) getMusicSetByTuneIDs(tuneIDs []uuid.UUID) (*apimodel.MusicSe
return matchingSet, nil
}

func musicSetHasTunesInOrder(
set *apimodel.MusicSet,
tuneIDs []uuid.UUID,
) bool {
if len(set.Tunes) != len(tuneIDs) {
return false
}

for i, t := range set.Tunes {
if t.Id != tuneIDs[i] {
return false
}
}

return true
}

func (d *dbService) setTunesInAPISet(apiSet *apimodel.MusicSet) error {
var setTunes []model.Tune
err := d.db.Joins("JOIN music_set_tunes mst on tunes.id = mst.tune_id").
Expand All @@ -395,36 +434,70 @@ func (d *dbService) setTunesInAPISet(apiSet *apimodel.MusicSet) error {
return nil
}

func (d *dbService) UpdateMusicSet(id uuid.UUID, updateSet apimodel.UpdateSet) (*apimodel.MusicSet, error) {
if err := d.validator.ValidateUpdateSet(updateSet); err != nil {
func (d *dbService) UpdateMusicSet(
id uuid.UUID,
updateSet apimodel.UpdateSet,
) (*apimodel.MusicSet, error) {
var err error
if err = d.validator.ValidateUpdateSet(updateSet); err != nil {
return nil, err
}

var dbSet = &model.MusicSet{}
if err := d.db.Preload("Tunes").First(dbSet, id).Error; err != nil {
return nil, common.ErrNotFound
// Check whether there is a music set with that id
_, err = d.GetMusicSet(id)
if err != nil {
return nil, err
}

var updateVals = map[string]any{}
if err := mapstructure.Decode(&updateSet, &updateVals); err != nil {
return nil, err
}

// Check if all given tune IDs exist and are valid
newTunes, err := d.dbTunesFromIDs(updateSet.Tunes)
if err != nil {
return nil, err
}

err = d.db.Transaction(func(_ *gorm.DB) error {
if err := d.deleteMusicSetTunes(dbSet); err != nil {
var dbSet *model.MusicSet
dbSet, err = d.updateMusicSetWithValuesAndTunes(id, updateVals, newTunes)

var apiSet *apimodel.MusicSet
apiSet, err = apiSetFromDbSet(dbSet)
if err != nil {
return nil, err
}

return apiSet, nil
}

func (d *dbService) updateMusicSetWithValuesAndTunes(
ID uuid.UUID,
updateVals map[string]any,
tunes []model.Tune,
) (*model.MusicSet, error) {
var tuneIDs []uuid.UUID
for _, t := range tunes {
tuneIDs = append(tuneIDs, t.ID)
}

dbSet := &model.MusicSet{
BaseModel: model.BaseModel{
ID: ID,
},
}

err := d.db.Transaction(func(_ *gorm.DB) error {
if err := d.deleteMusicSetTuneRelations(dbSet); err != nil {
return err
}

if err := d.db.Model(dbSet).Updates(updateVals).Error; err != nil {
return err
}

if err := d.assignMusicSetTunes(dbSet.ID, updateSet.Tunes); err != nil {
if err := d.createMusicSetTuneRelations(dbSet.ID, tuneIDs); err != nil {
return err
}

Expand All @@ -434,13 +507,8 @@ func (d *dbService) UpdateMusicSet(id uuid.UUID, updateSet apimodel.UpdateSet) (
return nil, err
}

dbSet.Tunes = newTunes
apiSet, err := apiSetFromDbSet(dbSet)
if err != nil {
return nil, err
}

return apiSet, nil
dbSet.Tunes = tunes
return dbSet, nil
}

func (d *dbService) DeleteMusicSet(id uuid.UUID) error {
Expand All @@ -450,7 +518,7 @@ func (d *dbService) DeleteMusicSet(id uuid.UUID) error {
}

err := d.db.Transaction(func(_ *gorm.DB) error {
if err := d.deleteMusicSetTunes(set); err != nil {
if err := d.deleteMusicSetTuneRelations(set); err != nil {
return err
}

Expand Down Expand Up @@ -478,18 +546,7 @@ func (d *dbService) AssignTunesToMusicSet(
return nil, err
}

// delete old music set -> tune relations and create new ones
err = d.db.Transaction(func(_ *gorm.DB) error {
if err := d.deleteMusicSetTunes(set); err != nil {
return err
}

if err := d.assignMusicSetTunes(set.ID, tuneIDs); err != nil {
return err
}

return nil
})
err = d.replaceMusicSetTuneRelations(set, tuneIDs)
if err != nil {
return nil, err
}
Expand All @@ -505,6 +562,29 @@ func (d *dbService) AssignTunesToMusicSet(
return apiSet, nil
}

func (d *dbService) replaceMusicSetTuneRelations(
set *model.MusicSet,
tuneIDs []uuid.UUID,
) error {
// delete old music set-tune relations and create new ones
err := d.db.Transaction(func(_ *gorm.DB) error {
if err := d.deleteMusicSetTuneRelations(set); err != nil {
return err
}

if err := d.createMusicSetTuneRelations(set.ID, tuneIDs); err != nil {
return err
}

return nil
})
if err != nil {
return err
}

return nil
}

// dbTunesFromIDs returns the database tune objects in the same order as the
// given tuneIDs. If there is an id that belongs to a non existing tune,
// an error will be returned.
Expand All @@ -513,18 +593,7 @@ func (d *dbService) dbTunesFromIDs(tuneIDs []uuid.UUID) ([]model.Tune, error) {
return nil, nil
}

var distinctTuneIDs []uuid.UUID
for _, id := range tuneIDs {
inDistinct := false
for _, distTuneID := range distinctTuneIDs {
if id == distTuneID {
inDistinct = true
}
}
if !inDistinct {
distinctTuneIDs = append(distinctTuneIDs, id)
}
}
distinctTuneIDs := common.RemoveDuplicates(tuneIDs)

var dbTunes []model.Tune
if err := d.db.Where("id IN (?)", distinctTuneIDs).Find(&dbTunes).Error; err != nil {
Expand All @@ -538,7 +607,7 @@ func (d *dbService) dbTunesFromIDs(tuneIDs []uuid.UUID) ([]model.Tune, error) {
return tunesOrderedByIDs(dbTunes, tuneIDs), nil
}

func (d *dbService) deleteMusicSetTunes(set *model.MusicSet) error {
func (d *dbService) deleteMusicSetTuneRelations(set *model.MusicSet) error {
err := d.db.Where(&model.MusicSetTunes{
MusicSetID: set.ID,
}).Delete(&model.MusicSetTunes{}).Error
Expand All @@ -553,7 +622,7 @@ func (d *dbService) deleteMusicSetTunes(set *model.MusicSet) error {
return nil
}

func (d *dbService) assignMusicSetTunes(setID uuid.UUID, tuneIDs []uuid.UUID) error {
func (d *dbService) createMusicSetTuneRelations(setID uuid.UUID, tuneIDs []uuid.UUID) error {
for i, tuneID := range tuneIDs {
setTune := &model.MusicSetTunes{
MusicSetID: setID,
Expand Down Expand Up @@ -687,26 +756,28 @@ func (d *dbService) ImportTunes(
func (d *dbService) getExistingImportedTune(
impTune *messages.ImportedTune,
) (*apimodel.ImportTune, error) {
hasData, err := d.hasSingleFileData(impTune.TuneFileData)
hasData, err := d.hasSingleTuneFileData(impTune.TuneFileData)
if err != nil {
return nil, err
}

if hasData {
alreadyImportedTune, err := d.getTuneWithSingleFileData(impTune.TuneFileData)
if err != nil {
return nil, err
}
if !hasData {
return nil, common.ErrNotFound
}

if alreadyImportedTune != nil {
impTune := &apimodel.ImportTune{}
err = copier.Copy(impTune, alreadyImportedTune)
if err != nil {
return nil, fmt.Errorf("failed creating import tune from already imported tune: %s", err.Error())
}
alreadyImportedTune, err := d.getTuneWithSingleFileData(impTune.TuneFileData)
if err != nil {
return nil, err
}

return impTune, nil
if alreadyImportedTune != nil {
impTune := &apimodel.ImportTune{}
err = copier.Copy(impTune, alreadyImportedTune)
if err != nil {
return nil, fmt.Errorf("failed creating import tune from already imported tune: %s", err.Error())
}

return impTune, nil
}

return nil, common.ErrNotFound
Expand Down Expand Up @@ -826,8 +897,8 @@ func isMsr(tunes []*apimodel.ImportTune) bool {
return false
}

// hasSingleFileData true if a tune with the same single tune file data exists in the database.
func (d *dbService) hasSingleFileData(
// hasSingleTuneFileData true if a tune with the same single tune file data exists in the database.
func (d *dbService) hasSingleTuneFileData(
data []byte,
) (bool, error) {
if len(data) == 0 {
Expand Down
Loading

0 comments on commit 0902d7d

Please sign in to comment.