Use `structs` lib to map models to DB. Fix #1266

This commit is contained in:
Deluan 2021-08-01 01:21:20 -04:00
parent 344d7a4392
commit c831dc4cdf
22 changed files with 207 additions and 235 deletions

1
go.mod
View File

@ -16,6 +16,7 @@ require (
github.com/disintegration/imaging v1.6.2
github.com/djherbis/fscache v0.10.2-0.20201024185917-a0daa9e52747
github.com/dustin/go-humanize v1.0.0
github.com/fatih/structs v1.1.0
github.com/go-chi/chi/v5 v5.0.3
github.com/go-chi/cors v1.2.0
github.com/go-chi/httprate v0.5.1

2
go.sum
View File

@ -204,6 +204,8 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=

View File

@ -3,39 +3,39 @@ package model
import "time"
type Album struct {
Annotations
Annotations `structs:"-"`
ID string `json:"id" orm:"column(id)"`
Name string `json:"name"`
CoverArtPath string `json:"coverArtPath"`
CoverArtId string `json:"coverArtId"`
ArtistID string `json:"artistId" orm:"column(artist_id)"`
Artist string `json:"artist"`
AlbumArtistID string `json:"albumArtistId" orm:"column(album_artist_id)"`
AlbumArtist string `json:"albumArtist"`
AllArtistIDs string `json:"allArtistIds" orm:"column(all_artist_ids)"`
MaxYear int `json:"maxYear"`
MinYear int `json:"minYear"`
Compilation bool `json:"compilation"`
Comment string `json:"comment,omitempty"`
SongCount int `json:"songCount"`
Duration float32 `json:"duration"`
Size int64 `json:"size"`
Genre string `json:"genre"`
Genres Genres `json:"genres"`
FullText string `json:"fullText"`
SortAlbumName string `json:"sortAlbumName,omitempty"`
SortArtistName string `json:"sortArtistName,omitempty"`
SortAlbumArtistName string `json:"sortAlbumArtistName,omitempty"`
OrderAlbumName string `json:"orderAlbumName"`
OrderAlbumArtistName string `json:"orderAlbumArtistName"`
CatalogNum string `json:"catalogNum,omitempty"`
MbzAlbumID string `json:"mbzAlbumId,omitempty" orm:"column(mbz_album_id)"`
MbzAlbumArtistID string `json:"mbzAlbumArtistId,omitempty" orm:"column(mbz_album_artist_id)"`
MbzAlbumType string `json:"mbzAlbumType,omitempty"`
MbzAlbumComment string `json:"mbzAlbumComment,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
ID string `structs:"id" json:"id" orm:"column(id)"`
Name string `structs:"name" json:"name"`
CoverArtPath string `structs:"cover_art_path" json:"coverArtPath"`
CoverArtId string `structs:"cover_art_id" json:"coverArtId"`
ArtistID string `structs:"artist_id" json:"artistId" orm:"column(artist_id)"`
Artist string `structs:"artist" json:"artist"`
AlbumArtistID string `structs:"album_artist_id" json:"albumArtistId" orm:"column(album_artist_id)"`
AlbumArtist string `structs:"album_artist" json:"albumArtist"`
AllArtistIDs string `structs:"all_artist_ids" json:"allArtistIds" orm:"column(all_artist_ids)"`
MaxYear int `structs:"max_year" json:"maxYear"`
MinYear int `structs:"min_year" json:"minYear"`
Compilation bool `structs:"compilation" json:"compilation"`
Comment string `structs:"comment" json:"comment,omitempty"`
SongCount int `structs:"song_count" json:"songCount"`
Duration float32 `structs:"duration" json:"duration"`
Size int64 `structs:"size" json:"size"`
Genre string `structs:"genre" json:"genre"`
Genres Genres `structs:"-" json:"genres"`
FullText string `structs:"full_text" json:"fullText"`
SortAlbumName string `structs:"sort_album_name" json:"sortAlbumName,omitempty"`
SortArtistName string `structs:"sort_artist_name" json:"sortArtistName,omitempty"`
SortAlbumArtistName string `structs:"sort_album_artist_name" json:"sortAlbumArtistName,omitempty"`
OrderAlbumName string `structs:"order_album_name" json:"orderAlbumName"`
OrderAlbumArtistName string `structs:"order_album_artist_name" json:"orderAlbumArtistName"`
CatalogNum string `structs:"catalog_num" json:"catalogNum,omitempty"`
MbzAlbumID string `structs:"mbz_album_id" json:"mbzAlbumId,omitempty" orm:"column(mbz_album_id)"`
MbzAlbumArtistID string `structs:"mbz_album_artist_id" json:"mbzAlbumArtistId,omitempty" orm:"column(mbz_album_artist_id)"`
MbzAlbumType string `structs:"mbz_album_type" json:"mbzAlbumType,omitempty"`
MbzAlbumComment string `structs:"mbz_album_comment" json:"mbzAlbumComment,omitempty"`
CreatedAt time.Time `structs:"created_at" json:"createdAt"`
UpdatedAt time.Time `structs:"updated_at" json:"updatedAt"`
}
type Albums []Album

View File

@ -3,11 +3,11 @@ package model
import "time"
type Annotations struct {
PlayCount int64 `json:"playCount"`
PlayDate time.Time `json:"playDate"`
Rating int `json:"rating"`
Starred bool `json:"starred"`
StarredAt time.Time `json:"starredAt"`
PlayCount int64 `structs:"-" json:"playCount"`
PlayDate time.Time `structs:"-" json:"playDate" `
Rating int `structs:"-" json:"rating" `
Starred bool `structs:"-" json:"starred" `
StarredAt time.Time `structs:"-" json:"starredAt"`
}
type AnnotatedModel interface {
@ -19,7 +19,3 @@ type AnnotatedRepository interface {
SetStar(starred bool, itemIDs ...string) error
SetRating(rating int, itemID string) error
}
// While I can't find a better way to make these fields optional in the models, I keep this list here
// to be used in other packages
var AnnotationFields = []string{"playCount", "playDate", "rating", "starred", "starredAt"}

View File

@ -3,25 +3,25 @@ package model
import "time"
type Artist struct {
Annotations
Annotations `structs:"-"`
ID string `json:"id" orm:"column(id)"`
Name string `json:"name"`
AlbumCount int `json:"albumCount"`
SongCount int `json:"songCount"`
Genres Genres `json:"genres"`
FullText string `json:"fullText"`
SortArtistName string `json:"sortArtistName,omitempty"`
OrderArtistName string `json:"orderArtistName"`
Size int64 `json:"size"`
MbzArtistID string `json:"mbzArtistId,omitempty" orm:"column(mbz_artist_id)"`
Biography string `json:"biography,omitempty"`
SmallImageUrl string `json:"smallImageUrl,omitempty"`
MediumImageUrl string `json:"mediumImageUrl,omitempty"`
LargeImageUrl string `json:"largeImageUrl,omitempty"`
ExternalUrl string `json:"externalUrl,omitempty" orm:"column(external_url)"`
SimilarArtists Artists `json:"-" orm:"-"`
ExternalInfoUpdatedAt time.Time `json:"externalInfoUpdatedAt"`
ID string `structs:"id" json:"id" orm:"column(id)"`
Name string `structs:"name" json:"name"`
AlbumCount int `structs:"album_count" json:"albumCount"`
SongCount int `structs:"song_count" json:"songCount"`
Genres Genres `structs:"-" json:"genres"`
FullText string `structs:"full_text" json:"fullText"`
SortArtistName string `structs:"sort_artist_name" json:"sortArtistName,omitempty"`
OrderArtistName string `structs:"order_artist_name" json:"orderArtistName"`
Size int64 `structs:"size" json:"size"`
MbzArtistID string `structs:"mbz_artist_id" json:"mbzArtistId,omitempty" orm:"column(mbz_artist_id)"`
Biography string `structs:"biography" json:"biography,omitempty"`
SmallImageUrl string `structs:"small_image_url" json:"smallImageUrl,omitempty"`
MediumImageUrl string `structs:"medium_image_url" json:"mediumImageUrl,omitempty"`
LargeImageUrl string `structs:"large_image_url" json:"largeImageUrl,omitempty"`
ExternalUrl string `structs:"external_url" json:"externalUrl,omitempty" orm:"column(external_url)"`
SimilarArtists Artists `structs:"-" json:"-" orm:"-"`
ExternalInfoUpdatedAt time.Time `structs:"external_info_updated_at" json:"externalInfoUpdatedAt"`
}
func (a Artist) ArtistImageUrl() string {

View File

@ -3,7 +3,7 @@ package model
import "time"
type Bookmarkable struct {
BookmarkPosition int64 `json:"bookmarkPosition"`
BookmarkPosition int64 `structs:"-" json:"bookmarkPosition"`
}
type BookmarkableRepository interface {
@ -13,16 +13,12 @@ type BookmarkableRepository interface {
}
type Bookmark struct {
Item MediaFile `json:"item"`
Comment string `json:"comment"`
Position int64 `json:"position"`
ChangedBy string `json:"changed_by"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Item MediaFile `structs:"item" json:"item"`
Comment string `structs:"comment" json:"comment"`
Position int64 `structs:"position" json:"position"`
ChangedBy string `structs:"changed_by" json:"changed_by"`
CreatedAt time.Time `structs:"created_at" json:"createdAt"`
UpdatedAt time.Time `structs:"updated_at" json:"updatedAt"`
}
type Bookmarks []Bookmark
// While I can't find a better way to make these fields optional in the models, I keep this list here
// to be used in other packages
var BookmarkFields = []string{"bookmarkPosition"}

View File

@ -1,10 +1,10 @@
package model
type Genre struct {
ID string `json:"id" orm:"column(id)"`
Name string `json:"name"`
SongCount int `json:"-"`
AlbumCount int `json:"-"`
ID string `structs:"id" json:"id" orm:"column(id)"`
Name string `structs:"name" json:"name"`
SongCount int `structs:"-" json:"-"`
AlbumCount int `structs:"-" json:"-"`
}
type Genres []Genre

View File

@ -6,50 +6,50 @@ import (
)
type MediaFile struct {
Annotations
Bookmarkable
Annotations `structs:"-"`
Bookmarkable `structs:"-"`
ID string `json:"id" orm:"pk;column(id)"`
Path string `json:"path"`
Title string `json:"title"`
Album string `json:"album"`
ArtistID string `json:"artistId" orm:"pk;column(artist_id)"`
Artist string `json:"artist"`
AlbumArtistID string `json:"albumArtistId" orm:"pk;column(album_artist_id)"`
AlbumArtist string `json:"albumArtist"`
AlbumID string `json:"albumId" orm:"pk;column(album_id)"`
HasCoverArt bool `json:"hasCoverArt"`
TrackNumber int `json:"trackNumber"`
DiscNumber int `json:"discNumber"`
DiscSubtitle string `json:"discSubtitle,omitempty"`
Year int `json:"year"`
Size int64 `json:"size"`
Suffix string `json:"suffix"`
Duration float32 `json:"duration"`
BitRate int `json:"bitRate"`
Genre string `json:"genre"`
Genres Genres `json:"genres"`
FullText string `json:"fullText"`
SortTitle string `json:"sortTitle,omitempty"`
SortAlbumName string `json:"sortAlbumName,omitempty"`
SortArtistName string `json:"sortArtistName,omitempty"`
SortAlbumArtistName string `json:"sortAlbumArtistName,omitempty"`
OrderAlbumName string `json:"orderAlbumName"`
OrderArtistName string `json:"orderArtistName"`
OrderAlbumArtistName string `json:"orderAlbumArtistName"`
Compilation bool `json:"compilation"`
Comment string `json:"comment,omitempty"`
Lyrics string `json:"lyrics,omitempty"`
Bpm int `json:"bpm,omitempty"`
CatalogNum string `json:"catalogNum,omitempty"`
MbzTrackID string `json:"mbzTrackId,omitempty" orm:"column(mbz_track_id)"`
MbzAlbumID string `json:"mbzAlbumId,omitempty" orm:"column(mbz_album_id)"`
MbzArtistID string `json:"mbzArtistId,omitempty" orm:"column(mbz_artist_id)"`
MbzAlbumArtistID string `json:"mbzAlbumArtistId,omitempty" orm:"column(mbz_album_artist_id)"`
MbzAlbumType string `json:"mbzAlbumType,omitempty"`
MbzAlbumComment string `json:"mbzAlbumComment,omitempty"`
CreatedAt time.Time `json:"createdAt"` // Time this entry was created in the DB
UpdatedAt time.Time `json:"updatedAt"` // Time of file last update (mtime)
ID string `structs:"id" json:"id" orm:"pk;column(id)"`
Path string `structs:"path" json:"path"`
Title string `structs:"title" json:"title"`
Album string `structs:"album" json:"album"`
ArtistID string `structs:"artist_id" json:"artistId" orm:"pk;column(artist_id)"`
Artist string `structs:"artist" json:"artist"`
AlbumArtistID string `structs:"album_artist_id" json:"albumArtistId" orm:"pk;column(album_artist_id)"`
AlbumArtist string `structs:"album_artist" json:"albumArtist"`
AlbumID string `structs:"album_id" json:"albumId" orm:"pk;column(album_id)"`
HasCoverArt bool `structs:"has_cover_art" json:"hasCoverArt"`
TrackNumber int `structs:"track_number" json:"trackNumber"`
DiscNumber int `structs:"disc_number" json:"discNumber"`
DiscSubtitle string `structs:"disc_subtitle" json:"discSubtitle,omitempty"`
Year int `structs:"year" json:"year"`
Size int64 `structs:"size" json:"size"`
Suffix string `structs:"suffix" json:"suffix"`
Duration float32 `structs:"duration" json:"duration"`
BitRate int `structs:"bit_rate" json:"bitRate"`
Genre string `structs:"genre" json:"genre"`
Genres Genres `structs:"-" json:"genres"`
FullText string `structs:"full_text" json:"fullText"`
SortTitle string `structs:"sort_title" json:"sortTitle,omitempty"`
SortAlbumName string `structs:"sort_album_name" json:"sortAlbumName,omitempty"`
SortArtistName string `structs:"sort_artist_name" json:"sortArtistName,omitempty"`
SortAlbumArtistName string `structs:"sort_album_artist_name" json:"sortAlbumArtistName,omitempty"`
OrderAlbumName string `structs:"order_album_name" json:"orderAlbumName"`
OrderArtistName string `structs:"order_artist_name" json:"orderArtistName"`
OrderAlbumArtistName string `structs:"order_album_artist_name" json:"orderAlbumArtistName"`
Compilation bool `structs:"compilation" json:"compilation"`
Comment string `structs:"comment" json:"comment,omitempty"`
Lyrics string `structs:"lyrics" json:"lyrics,omitempty"`
Bpm int `structs:"bpm" json:"bpm,omitempty"`
CatalogNum string `structs:"catalog_num" json:"catalogNum,omitempty"`
MbzTrackID string `structs:"mbz_track_id" json:"mbzTrackId,omitempty" orm:"column(mbz_track_id)"`
MbzAlbumID string `structs:"mbz_album_id" json:"mbzAlbumId,omitempty" orm:"column(mbz_album_id)"`
MbzArtistID string `structs:"mbz_artist_id" json:"mbzArtistId,omitempty" orm:"column(mbz_artist_id)"`
MbzAlbumArtistID string `structs:"mbz_album_artist_id" json:"mbzAlbumArtistId,omitempty" orm:"column(mbz_album_artist_id)"`
MbzAlbumType string `structs:"mbz_album_type" json:"mbzAlbumType,omitempty"`
MbzAlbumComment string `structs:"mbz_album_comment" json:"mbzAlbumComment,omitempty"`
CreatedAt time.Time `structs:"created_at" json:"createdAt"` // Time this entry was created in the DB
UpdatedAt time.Time `structs:"updated_at" json:"updatedAt"` // Time of file last update (mtime)
}
func (mf *MediaFile) ContentType() string {

View File

@ -5,17 +5,17 @@ import (
)
type Player struct {
ID string `json:"id" orm:"column(id)"`
Name string `json:"name"`
UserAgent string `json:"userAgent"`
UserName string `json:"userName"`
Client string `json:"client"`
IPAddress string `json:"ipAddress"`
LastSeen time.Time `json:"lastSeen"`
TranscodingId string `json:"transcodingId"`
MaxBitRate int `json:"maxBitRate"`
ReportRealPath bool `json:"reportRealPath"`
ScrobbleEnabled bool `json:"scrobbleEnabled"`
ID string `structs:"id" json:"id" orm:"column(id)"`
Name string `structs:"name" json:"name"`
UserAgent string `structs:"user_agent" json:"userAgent"`
UserName string `structs:"user_name" json:"userName"`
Client string `structs:"client" json:"client"`
IPAddress string `structs:"ip_address" json:"ipAddress"`
LastSeen time.Time `structs:"last_seen" json:"lastSeen"`
TranscodingId string `structs:"transcoding_id" json:"transcodingId"`
MaxBitRate int `structs:"max_bit_rate" json:"maxBitRate"`
ReportRealPath bool `structs:"report_real_path" json:"reportRealPath"`
ScrobbleEnabled bool `structs:"scrobble_enabled" json:"scrobbleEnabled"`
}
type Players []Player

View File

@ -5,19 +5,19 @@ import (
)
type Playlist struct {
ID string `json:"id" orm:"column(id)"`
Name string `json:"name"`
Comment string `json:"comment"`
Duration float32 `json:"duration"`
Size int64 `json:"size"`
SongCount int `json:"songCount"`
Owner string `json:"owner"`
Public bool `json:"public"`
Tracks MediaFiles `json:"tracks,omitempty"`
Path string `json:"path"`
Sync bool `json:"sync"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
ID string `structs:"id" json:"id" orm:"column(id)"`
Name string `structs:"name" json:"name"`
Comment string `structs:"comment" json:"comment"`
Duration float32 `structs:"duration" json:"duration"`
Size int64 `structs:"size" json:"size"`
SongCount int `structs:"song_count" json:"songCount"`
Owner string `structs:"owner" json:"owner"`
Public bool `structs:"public" json:"public"`
Tracks MediaFiles `structs:"-" json:"tracks,omitempty"`
Path string `structs:"path" json:"path"`
Sync bool `structs:"sync" json:"sync"`
CreatedAt time.Time `structs:"created_at" json:"createdAt"`
UpdatedAt time.Time `structs:"updated_at" json:"updatedAt"`
}
type Playlists []Playlist

View File

@ -5,14 +5,14 @@ import (
)
type PlayQueue struct {
ID string `json:"id" orm:"column(id)"`
UserID string `json:"userId" orm:"column(user_id)"`
Current string `json:"current"`
Position int64 `json:"position"`
ChangedBy string `json:"changedBy"`
Items MediaFiles `json:"items,omitempty"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
ID string `structs:"id" json:"id" orm:"column(id)"`
UserID string `structs:"user_id" json:"userId" orm:"column(user_id)"`
Current string `structs:"current" json:"current"`
Position int64 `structs:"position" json:"position"`
ChangedBy string `structs:"changed_by" json:"changedBy"`
Items MediaFiles `structs:"-" json:"items,omitempty"`
CreatedAt time.Time `structs:"created_at" json:"createdAt"`
UpdatedAt time.Time `structs:"updated_at" json:"updatedAt"`
}
type PlayQueues []PlayQueue

View File

@ -5,7 +5,7 @@ import "time"
type ScrobbleEntry struct {
MediaFile
Service string
UserID string `json:"user_id" orm:"column(user_id)"`
UserID string `structs:"user_id" orm:"column(user_id)"`
PlayTime time.Time
EnqueueTime time.Time
}

View File

@ -5,15 +5,15 @@ import (
)
type Share struct {
ID string `json:"id" orm:"column(id)"`
Name string `json:"name"`
Description string `json:"description"`
ExpiresAt time.Time `json:"expiresAt"`
CreatedAt time.Time `json:"createdAt"`
LastVisitedAt time.Time `json:"lastVisitedAt"`
ResourceIDs string `json:"resourceIds" orm:"column(resource_ids)"`
ResourceType string `json:"resourceType"`
VisitCount int `json:"visitCount"`
ID string `structs:"id" json:"id" orm:"column(id)"`
Name string `structs:"name" json:"name"`
Description string `structs:"description" json:"description"`
ExpiresAt time.Time `structs:"expires_at" json:"expiresAt"`
CreatedAt time.Time `structs:"created_at" json:"createdAt"`
LastVisitedAt time.Time `structs:"last_visited_at" json:"lastVisitedAt"`
ResourceIDs string `structs:"resource_ids" json:"resourceIds" orm:"column(resource_ids)"`
ResourceType string `structs:"resource_type" json:"resourceType"`
VisitCount int `structs:"visit_count" json:"visitCount"`
}
type Shares []Share

View File

@ -1,11 +1,11 @@
package model
type Transcoding struct {
ID string `json:"id" orm:"column(id)"`
Name string `json:"name"`
TargetFormat string `json:"targetFormat"`
Command string `json:"command"`
DefaultBitRate int `json:"defaultBitRate"`
ID string `structs:"id" json:"id" orm:"column(id)"`
Name string `structs:"name" json:"name"`
TargetFormat string `structs:"target_format" json:"targetFormat"`
Command string `structs:"command" json:"command"`
DefaultBitRate int `structs:"default_bit_rate" json:"defaultBitRate"`
}
type Transcodings []Transcoding

View File

@ -3,23 +3,23 @@ package model
import "time"
type User struct {
ID string `json:"id" orm:"column(id)"`
UserName string `json:"userName"`
Name string `json:"name"`
Email string `json:"email"`
IsAdmin bool `json:"isAdmin"`
LastLoginAt *time.Time `json:"lastLoginAt"`
LastAccessAt *time.Time `json:"lastAccessAt"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
ID string `structs:"id" json:"id" orm:"column(id)"`
UserName string `structs:"user_name" json:"userName"`
Name string `structs:"name" json:"name"`
Email string `structs:"email" json:"email"`
IsAdmin bool `structs:"is_admin" json:"isAdmin"`
LastLoginAt time.Time `structs:"last_login_at" json:"lastLoginAt"`
LastAccessAt time.Time `structs:"last_access_at" json:"lastAccessAt"`
CreatedAt time.Time `structs:"created_at" json:"createdAt"`
UpdatedAt time.Time `structs:"updated_at" json:"updatedAt"`
// This is only available on the backend, and it is never sent over the wire
Password string `json:"-"`
Password string `structs:"-" json:"-"`
// This is used to set or change a password when calling Put. If it is empty, the password is not changed.
// It is received from the UI with the name "password"
NewPassword string `json:"password,omitempty"`
NewPassword string `structs:"password,omitempty" json:"password,omitempty"`
// If changing the password, this is also required
CurrentPassword string `json:"currentPassword,omitempty"`
CurrentPassword string `structs:"current_password,omitempty" json:"currentPassword,omitempty"`
}
type Users []User

View File

@ -110,14 +110,11 @@ func (r *albumRepository) Get(id string) (*model.Album, error) {
}
func (r *albumRepository) Put(m *model.Album) error {
genres := m.Genres
m.Genres = nil
defer func() { m.Genres = genres }()
_, err := r.put(m.ID, m)
if err != nil {
return err
}
return r.updateGenres(m.ID, r.tableName, genres)
return r.updateGenres(m.ID, r.tableName, m.Genres)
}
func (r *albumRepository) GetAll(options ...model.QueryOptions) (model.Albums, error) {

View File

@ -23,8 +23,8 @@ type artistRepository struct {
}
type dbArtist struct {
model.Artist
SimilarArtists string `json:"similarArtists"`
model.Artist `structs:",flatten"`
SimilarArtists string `structs:"similar_artists" json:"similarArtists"`
}
func NewArtistRepository(ctx context.Context, o orm.Ormer) model.ArtistRepository {
@ -60,16 +60,13 @@ func (r *artistRepository) Exists(id string) (bool, error) {
}
func (r *artistRepository) Put(a *model.Artist) error {
genres := a.Genres
a.Genres = nil
defer func() { a.Genres = genres }()
a.FullText = getFullText(a.Name, a.SortArtistName)
dba := r.fromModel(a)
_, err := r.put(dba.ID, dba)
if err != nil {
return err
}
return r.updateGenres(a.ID, r.tableName, genres)
return r.updateGenres(a.ID, r.tableName, a.Genres)
}
func (r *artistRepository) Get(id string) (*model.Artist, error) {

View File

@ -2,38 +2,29 @@ package persistence
import (
"context"
"encoding/json"
"fmt"
"regexp"
"strings"
"github.com/navidrome/navidrome/consts"
"time"
"github.com/Masterminds/squirrel"
"github.com/fatih/structs"
"github.com/navidrome/navidrome/consts"
"github.com/navidrome/navidrome/log"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/utils"
)
func toSqlArgs(rec interface{}) (map[string]interface{}, error) {
// Convert to JSON...
b, err := json.Marshal(rec)
if err != nil {
return nil, err
}
// ... then convert to map
var m map[string]interface{}
err = json.Unmarshal(b, &m)
r := make(map[string]interface{}, len(m))
for f, v := range m {
isAnnotationField := utils.StringInSlice(f, model.AnnotationFields)
isBookmarkField := utils.StringInSlice(f, model.BookmarkFields)
if !isAnnotationField && !isBookmarkField && v != nil {
r[toSnakeCase(f)] = v
m := structs.Map(rec)
for k, v := range m {
if t, ok := v.(time.Time); ok {
m[k] = t.Format(time.RFC3339Nano)
}
if t, ok := v.(*time.Time); ok && t != nil {
m[k] = t.Format(time.RFC3339Nano)
}
}
return r, err
return m, nil
}
var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")

View File

@ -26,31 +26,26 @@ var _ = Describe("Helpers", func() {
})
})
Describe("toSqlArgs", func() {
type Embed struct{}
type Model struct {
ID string `json:"id"`
AlbumId string `json:"albumId"`
PlayCount int `json:"playCount"`
CreatedAt *time.Time
Embed `structs:"-"`
ID string `structs:"id" json:"id"`
AlbumId string `structs:"album_id" json:"albumId"`
PlayCount int `structs:"play_count" json:"playCount"`
UpdatedAt *time.Time `structs:"updated_at"`
CreatedAt time.Time `structs:"created_at"`
}
It("returns a map with snake_case keys", func() {
now := time.Now()
m := &Model{ID: "123", AlbumId: "456", CreatedAt: &now, PlayCount: 2}
m := &Model{ID: "123", AlbumId: "456", CreatedAt: now, UpdatedAt: &now, PlayCount: 2}
args, err := toSqlArgs(m)
Expect(err).To(BeNil())
Expect(args).To(HaveKeyWithValue("id", "123"))
Expect(args).To(HaveKeyWithValue("album_id", "456"))
Expect(args).To(HaveKey("created_at"))
Expect(args).To(HaveLen(3))
})
It("remove null fields", func() {
m := &Model{ID: "123", AlbumId: "456"}
args, err := toSqlArgs(m)
Expect(err).To(BeNil())
Expect(args).To(HaveKey("id"))
Expect(args).To(HaveKey("album_id"))
Expect(args).To(HaveLen(2))
Expect(args).To(HaveKeyWithValue("updated_at", now.Format(time.RFC3339Nano)))
Expect(args).To(HaveKeyWithValue("created_at", now.Format(time.RFC3339Nano)))
Expect(args).ToNot(HaveKey("Embed"))
})
})

View File

@ -51,14 +51,11 @@ func (r *mediaFileRepository) Exists(id string) (bool, error) {
func (r *mediaFileRepository) Put(m *model.MediaFile) error {
m.FullText = getFullText(m.Title, m.Album, m.Artist, m.AlbumArtist,
m.SortTitle, m.SortAlbumName, m.SortArtistName, m.SortAlbumArtistName, m.DiscSubtitle)
genres := m.Genres
m.Genres = nil
defer func() { m.Genres = genres }()
_, err := r.put(m.ID, m)
if err != nil {
return err
}
return r.updateGenres(m.ID, r.tableName, genres)
return r.updateGenres(m.ID, r.tableName, m.Genres)
}
func (r *mediaFileRepository) selectMediaFile(options ...model.QueryOptions) SelectBuilder {

View File

@ -25,14 +25,14 @@ func NewPlayQueueRepository(ctx context.Context, o orm.Ormer) model.PlayQueueRep
}
type playQueue struct {
ID string `orm:"column(id)"`
UserID string `orm:"column(user_id)"`
Current string
Position int64
ChangedBy string
Items string
CreatedAt time.Time
UpdatedAt time.Time
ID string `structs:"id" orm:"column(id)"`
UserID string `structs:"user_id" orm:"column(user_id)"`
Current string `structs:"current"`
Position int64 `structs:"position"`
ChangedBy string `structs:"changed_by"`
Items string `structs:"items"`
CreatedAt time.Time `structs:"created_at"`
UpdatedAt time.Time `structs:"updated_at"`
}
func (r *playQueueRepository) Store(q *model.PlayQueue) error {

View File

@ -142,7 +142,7 @@ func createAdminUser(ctx context.Context, ds model.DataStore, username, password
Email: "",
NewPassword: password,
IsAdmin: true,
LastLoginAt: &now,
LastLoginAt: now,
}
err := ds.User(ctx).Put(&initialUser)
if err != nil {