SQLite model refactoring (#3791)

* Remove ID from PerformerPartial
* Separate studio model from sqlite model
* Separate movie model from sqlite model
* Separate tag model from sqlite model
* Separate saved filter model from sqlite model
* Separate scene marker model from sqlite model
* Separate gallery chapter model from sqlite model
* Move ErrNoRows checks into sqlite, improve empty result error messages
* Move SQLiteDate and SQLiteTimestamp to sqlite
* Use changesetTranslator everywhere, refactor for consistency
* Make PerformerStore.DestroyImage private
* Fix rating on movie create
This commit is contained in:
DingDongSoLong4
2023-06-15 04:46:09 +02:00
committed by GitHub
parent 9180a68c45
commit 1c13c9e1b1
150 changed files with 3279 additions and 3129 deletions

View File

@@ -20,46 +20,33 @@ type ImageGetter interface {
// ToJSON converts a Movie into its JSON equivalent.
func ToJSON(ctx context.Context, reader ImageGetter, studioReader studio.Finder, movie *models.Movie) (*jsonschema.Movie, error) {
newMovieJSON := jsonschema.Movie{
CreatedAt: json.JSONTime{Time: movie.CreatedAt.Timestamp},
UpdatedAt: json.JSONTime{Time: movie.UpdatedAt.Timestamp},
Name: movie.Name,
Aliases: movie.Aliases,
Director: movie.Director,
Synopsis: movie.Synopsis,
URL: movie.URL,
CreatedAt: json.JSONTime{Time: movie.CreatedAt},
UpdatedAt: json.JSONTime{Time: movie.UpdatedAt},
}
if movie.Name.Valid {
newMovieJSON.Name = movie.Name.String
if movie.Date != nil {
newMovieJSON.Date = movie.Date.String()
}
if movie.Aliases.Valid {
newMovieJSON.Aliases = movie.Aliases.String
if movie.Rating != nil {
newMovieJSON.Rating = *movie.Rating
}
if movie.Date.Valid {
newMovieJSON.Date = utils.GetYMDFromDatabaseDate(movie.Date.String)
}
if movie.Rating.Valid {
newMovieJSON.Rating = int(movie.Rating.Int64)
}
if movie.Duration.Valid {
newMovieJSON.Duration = int(movie.Duration.Int64)
if movie.Duration != nil {
newMovieJSON.Duration = *movie.Duration
}
if movie.Director.Valid {
newMovieJSON.Director = movie.Director.String
}
if movie.Synopsis.Valid {
newMovieJSON.Synopsis = movie.Synopsis.String
}
if movie.URL.Valid {
newMovieJSON.URL = movie.URL.String
}
if movie.StudioID.Valid {
studio, err := studioReader.Find(ctx, int(movie.StudioID.Int64))
if movie.StudioID != nil {
studio, err := studioReader.Find(ctx, *movie.StudioID)
if err != nil {
return nil, fmt.Errorf("error getting movie studio: %v", err)
}
if studio != nil {
newMovieJSON.Studio = studio.Name.String
newMovieJSON.Studio = studio.Name
}
}

View File

@@ -1,7 +1,6 @@
package movie
import (
"database/sql"
"errors"
"github.com/stashapp/stash/pkg/models"
@@ -32,16 +31,15 @@ const (
const movieName = "testMovie"
const movieAliases = "aliases"
var date = models.SQLiteDate{
String: "2001-01-01",
Valid: true,
}
const rating = 5
const duration = 100
const director = "director"
const synopsis = "synopsis"
const url = "url"
var (
date = "2001-01-01"
dateObj = models.NewDate(date)
rating = 5
duration = 100
director = "director"
synopsis = "synopsis"
url = "url"
)
const studioName = "studio"
@@ -56,7 +54,7 @@ var (
)
var movieStudio models.Studio = models.Studio{
Name: models.NullString(studioName),
Name: studioName,
}
var (
@@ -66,43 +64,26 @@ var (
func createFullMovie(id int, studioID int) models.Movie {
return models.Movie{
ID: id,
Name: models.NullString(movieName),
Aliases: models.NullString(movieAliases),
Date: date,
Rating: sql.NullInt64{
Int64: rating,
Valid: true,
},
Duration: sql.NullInt64{
Int64: duration,
Valid: true,
},
Director: models.NullString(director),
Synopsis: models.NullString(synopsis),
URL: models.NullString(url),
StudioID: sql.NullInt64{
Int64: int64(studioID),
Valid: true,
},
CreatedAt: models.SQLiteTimestamp{
Timestamp: createTime,
},
UpdatedAt: models.SQLiteTimestamp{
Timestamp: updateTime,
},
ID: id,
Name: movieName,
Aliases: movieAliases,
Date: &dateObj,
Rating: &rating,
Duration: &duration,
Director: director,
Synopsis: synopsis,
URL: url,
StudioID: &studioID,
CreatedAt: createTime,
UpdatedAt: updateTime,
}
}
func createEmptyMovie(id int) models.Movie {
return models.Movie{
ID: id,
CreatedAt: models.SQLiteTimestamp{
Timestamp: createTime,
},
UpdatedAt: models.SQLiteTimestamp{
Timestamp: updateTime,
},
ID: id,
CreatedAt: createTime,
UpdatedAt: updateTime,
}
}
@@ -110,7 +91,7 @@ func createFullJSONMovie(studio, frontImage, backImage string) *jsonschema.Movie
return &jsonschema.Movie{
Name: movieName,
Aliases: movieAliases,
Date: date.String,
Date: date,
Rating: rating,
Duration: duration,
Director: director,

View File

@@ -2,7 +2,6 @@ package movie
import (
"context"
"database/sql"
"fmt"
"github.com/stashapp/stash/pkg/hash/md5"
@@ -19,7 +18,7 @@ type ImageUpdater interface {
type NameFinderCreatorUpdater interface {
NameFinderCreator
UpdateFull(ctx context.Context, updatedMovie models.Movie) (*models.Movie, error)
Update(ctx context.Context, updatedMovie *models.Movie) error
ImageUpdater
}
@@ -63,22 +62,25 @@ func (i *Importer) movieJSONToMovie(movieJSON jsonschema.Movie) models.Movie {
newMovie := models.Movie{
Checksum: checksum,
Name: sql.NullString{String: movieJSON.Name, Valid: true},
Aliases: sql.NullString{String: movieJSON.Aliases, Valid: true},
Date: models.SQLiteDate{String: movieJSON.Date, Valid: true},
Director: sql.NullString{String: movieJSON.Director, Valid: true},
Synopsis: sql.NullString{String: movieJSON.Synopsis, Valid: true},
URL: sql.NullString{String: movieJSON.URL, Valid: true},
CreatedAt: models.SQLiteTimestamp{Timestamp: movieJSON.CreatedAt.GetTime()},
UpdatedAt: models.SQLiteTimestamp{Timestamp: movieJSON.UpdatedAt.GetTime()},
Name: movieJSON.Name,
Aliases: movieJSON.Aliases,
Director: movieJSON.Director,
Synopsis: movieJSON.Synopsis,
URL: movieJSON.URL,
CreatedAt: movieJSON.CreatedAt.GetTime(),
UpdatedAt: movieJSON.UpdatedAt.GetTime(),
}
if movieJSON.Date != "" {
d := models.NewDate(movieJSON.Date)
newMovie.Date = &d
}
if movieJSON.Rating != 0 {
newMovie.Rating = sql.NullInt64{Int64: int64(movieJSON.Rating), Valid: true}
newMovie.Rating = &movieJSON.Rating
}
if movieJSON.Duration != 0 {
newMovie.Duration = sql.NullInt64{Int64: int64(movieJSON.Duration), Valid: true}
newMovie.Duration = &movieJSON.Duration
}
return newMovie
@@ -105,13 +107,10 @@ func (i *Importer) populateStudio(ctx context.Context) error {
if err != nil {
return err
}
i.movie.StudioID = sql.NullInt64{
Int64: int64(studioID),
Valid: true,
}
i.movie.StudioID = &studioID
}
} else {
i.movie.StudioID = sql.NullInt64{Int64: int64(studio.ID), Valid: true}
i.movie.StudioID = &studio.ID
}
}
@@ -119,14 +118,14 @@ func (i *Importer) populateStudio(ctx context.Context) error {
}
func (i *Importer) createStudio(ctx context.Context, name string) (int, error) {
newStudio := *models.NewStudio(name)
newStudio := models.NewStudio(name)
created, err := i.StudioWriter.Create(ctx, newStudio)
err := i.StudioWriter.Create(ctx, newStudio)
if err != nil {
return 0, err
}
return created.ID, nil
return newStudio.ID, nil
}
func (i *Importer) PostImport(ctx context.Context, id int) error {
@@ -165,19 +164,19 @@ func (i *Importer) FindExistingID(ctx context.Context) (*int, error) {
}
func (i *Importer) Create(ctx context.Context) (*int, error) {
created, err := i.ReaderWriter.Create(ctx, i.movie)
err := i.ReaderWriter.Create(ctx, &i.movie)
if err != nil {
return nil, fmt.Errorf("error creating movie: %v", err)
}
id := created.ID
id := i.movie.ID
return &id, nil
}
func (i *Importer) Update(ctx context.Context, id int) error {
movie := i.movie
movie.ID = id
_, err := i.ReaderWriter.UpdateFull(ctx, movie)
err := i.ReaderWriter.Update(ctx, &movie)
if err != nil {
return fmt.Errorf("error updating existing movie: %v", err)
}

View File

@@ -89,7 +89,7 @@ func TestImporterPreImportWithStudio(t *testing.T) {
err := i.PreImport(testCtx)
assert.Nil(t, err)
assert.Equal(t, int64(existingStudioID), i.movie.StudioID.Int64)
assert.Equal(t, existingStudioID, *i.movie.StudioID)
i.Input.Studio = existingStudioErr
err = i.PreImport(testCtx)
@@ -112,9 +112,10 @@ func TestImporterPreImportWithMissingStudio(t *testing.T) {
}
studioReaderWriter.On("FindByName", testCtx, missingStudioName, false).Return(nil, nil).Times(3)
studioReaderWriter.On("Create", testCtx, mock.AnythingOfType("models.Studio")).Return(&models.Studio{
ID: existingStudioID,
}, nil)
studioReaderWriter.On("Create", testCtx, mock.AnythingOfType("*models.Studio")).Run(func(args mock.Arguments) {
s := args.Get(1).(*models.Studio)
s.ID = existingStudioID
}).Return(nil)
err := i.PreImport(testCtx)
assert.NotNil(t, err)
@@ -126,7 +127,7 @@ func TestImporterPreImportWithMissingStudio(t *testing.T) {
i.MissingRefBehaviour = models.ImportMissingRefEnumCreate
err = i.PreImport(testCtx)
assert.Nil(t, err)
assert.Equal(t, int64(existingStudioID), i.movie.StudioID.Int64)
assert.Equal(t, existingStudioID, *i.movie.StudioID)
studioReaderWriter.AssertExpectations(t)
}
@@ -145,7 +146,7 @@ func TestImporterPreImportWithMissingStudioCreateErr(t *testing.T) {
}
studioReaderWriter.On("FindByName", testCtx, missingStudioName, false).Return(nil, nil).Once()
studioReaderWriter.On("Create", testCtx, mock.AnythingOfType("models.Studio")).Return(nil, errors.New("Create error"))
studioReaderWriter.On("Create", testCtx, mock.AnythingOfType("*models.Studio")).Return(errors.New("Create error"))
err := i.PreImport(testCtx)
assert.NotNil(t, err)
@@ -213,11 +214,11 @@ func TestCreate(t *testing.T) {
readerWriter := &mocks.MovieReaderWriter{}
movie := models.Movie{
Name: models.NullString(movieName),
Name: movieName,
}
movieErr := models.Movie{
Name: models.NullString(movieNameErr),
Name: movieNameErr,
}
i := Importer{
@@ -226,10 +227,11 @@ func TestCreate(t *testing.T) {
}
errCreate := errors.New("Create error")
readerWriter.On("Create", testCtx, movie).Return(&models.Movie{
ID: movieID,
}, nil).Once()
readerWriter.On("Create", testCtx, movieErr).Return(nil, errCreate).Once()
readerWriter.On("Create", testCtx, &movie).Run(func(args mock.Arguments) {
m := args.Get(1).(*models.Movie)
m.ID = movieID
}).Return(nil).Once()
readerWriter.On("Create", testCtx, &movieErr).Return(errCreate).Once()
id, err := i.Create(testCtx)
assert.Equal(t, movieID, *id)
@@ -247,11 +249,11 @@ func TestUpdate(t *testing.T) {
readerWriter := &mocks.MovieReaderWriter{}
movie := models.Movie{
Name: models.NullString(movieName),
Name: movieName,
}
movieErr := models.Movie{
Name: models.NullString(movieNameErr),
Name: movieNameErr,
}
i := Importer{
@@ -263,7 +265,7 @@ func TestUpdate(t *testing.T) {
// id needs to be set for the mock input
movie.ID = movieID
readerWriter.On("UpdateFull", testCtx, movie).Return(nil, nil).Once()
readerWriter.On("Update", testCtx, &movie).Return(nil).Once()
err := i.Update(testCtx, movieID)
assert.Nil(t, err)
@@ -272,7 +274,7 @@ func TestUpdate(t *testing.T) {
// need to set id separately
movieErr.ID = errImageID
readerWriter.On("UpdateFull", testCtx, movieErr).Return(nil, errUpdate).Once()
readerWriter.On("Update", testCtx, &movieErr).Return(errUpdate).Once()
err = i.Update(testCtx, errImageID)
assert.NotNil(t, err)

View File

@@ -8,5 +8,5 @@ import (
type NameFinderCreator interface {
FindByName(ctx context.Context, name string, nocase bool) (*models.Movie, error)
Create(ctx context.Context, newMovie models.Movie) (*models.Movie, error)
Create(ctx context.Context, newMovie *models.Movie) error
}