mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
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:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user