mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
Restructure data layer (#2532)
* Add new txn manager interface * Add txn management to sqlite * Rename get to getByID * Add contexts to repository methods * Update query builders * Add context to reader writer interfaces * Use repository in resolver * Tighten interfaces * Tighten interfaces in dlna * Tighten interfaces in match package * Tighten interfaces in scraper package * Tighten interfaces in scan code * Tighten interfaces on autotag package * Remove ReaderWriter usage * Merge database package into sqlite
This commit is contained in:
@@ -1,16 +1,23 @@
|
||||
package movie
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/models/json"
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
"github.com/stashapp/stash/pkg/studio"
|
||||
"github.com/stashapp/stash/pkg/utils"
|
||||
)
|
||||
|
||||
type ImageGetter interface {
|
||||
GetFrontImage(ctx context.Context, movieID int) ([]byte, error)
|
||||
GetBackImage(ctx context.Context, movieID int) ([]byte, error)
|
||||
}
|
||||
|
||||
// ToJSON converts a Movie into its JSON equivalent.
|
||||
func ToJSON(reader models.MovieReader, studioReader models.StudioReader, movie *models.Movie) (*jsonschema.Movie, error) {
|
||||
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},
|
||||
@@ -45,7 +52,7 @@ func ToJSON(reader models.MovieReader, studioReader models.StudioReader, movie *
|
||||
}
|
||||
|
||||
if movie.StudioID.Valid {
|
||||
studio, err := studioReader.Find(int(movie.StudioID.Int64))
|
||||
studio, err := studioReader.Find(ctx, int(movie.StudioID.Int64))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting movie studio: %v", err)
|
||||
}
|
||||
@@ -55,7 +62,7 @@ func ToJSON(reader models.MovieReader, studioReader models.StudioReader, movie *
|
||||
}
|
||||
}
|
||||
|
||||
frontImage, err := reader.GetFrontImage(movie.ID)
|
||||
frontImage, err := reader.GetFrontImage(ctx, movie.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting movie front image: %v", err)
|
||||
}
|
||||
@@ -64,7 +71,7 @@ func ToJSON(reader models.MovieReader, studioReader models.StudioReader, movie *
|
||||
newMovieJSON.FrontImage = utils.GetBase64StringFromData(frontImage)
|
||||
}
|
||||
|
||||
backImage, err := reader.GetBackImage(movie.ID)
|
||||
backImage, err := reader.GetBackImage(ctx, movie.ID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error getting movie back image: %v", err)
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ var (
|
||||
backImageBytes = []byte("backImageBytes")
|
||||
)
|
||||
|
||||
var studio models.Studio = models.Studio{
|
||||
var movieStudio models.Studio = models.Studio{
|
||||
Name: models.NullString(studioName),
|
||||
}
|
||||
|
||||
@@ -189,30 +189,30 @@ func TestToJSON(t *testing.T) {
|
||||
|
||||
imageErr := errors.New("error getting image")
|
||||
|
||||
mockMovieReader.On("GetFrontImage", movieID).Return(frontImageBytes, nil).Once()
|
||||
mockMovieReader.On("GetFrontImage", missingStudioMovieID).Return(frontImageBytes, nil).Once()
|
||||
mockMovieReader.On("GetFrontImage", emptyID).Return(nil, nil).Once().Maybe()
|
||||
mockMovieReader.On("GetFrontImage", errFrontImageID).Return(nil, imageErr).Once()
|
||||
mockMovieReader.On("GetFrontImage", errBackImageID).Return(frontImageBytes, nil).Once()
|
||||
mockMovieReader.On("GetFrontImage", testCtx, movieID).Return(frontImageBytes, nil).Once()
|
||||
mockMovieReader.On("GetFrontImage", testCtx, missingStudioMovieID).Return(frontImageBytes, nil).Once()
|
||||
mockMovieReader.On("GetFrontImage", testCtx, emptyID).Return(nil, nil).Once().Maybe()
|
||||
mockMovieReader.On("GetFrontImage", testCtx, errFrontImageID).Return(nil, imageErr).Once()
|
||||
mockMovieReader.On("GetFrontImage", testCtx, errBackImageID).Return(frontImageBytes, nil).Once()
|
||||
|
||||
mockMovieReader.On("GetBackImage", movieID).Return(backImageBytes, nil).Once()
|
||||
mockMovieReader.On("GetBackImage", missingStudioMovieID).Return(backImageBytes, nil).Once()
|
||||
mockMovieReader.On("GetBackImage", emptyID).Return(nil, nil).Once()
|
||||
mockMovieReader.On("GetBackImage", errBackImageID).Return(nil, imageErr).Once()
|
||||
mockMovieReader.On("GetBackImage", errFrontImageID).Return(backImageBytes, nil).Maybe()
|
||||
mockMovieReader.On("GetBackImage", errStudioMovieID).Return(backImageBytes, nil).Maybe()
|
||||
mockMovieReader.On("GetBackImage", testCtx, movieID).Return(backImageBytes, nil).Once()
|
||||
mockMovieReader.On("GetBackImage", testCtx, missingStudioMovieID).Return(backImageBytes, nil).Once()
|
||||
mockMovieReader.On("GetBackImage", testCtx, emptyID).Return(nil, nil).Once()
|
||||
mockMovieReader.On("GetBackImage", testCtx, errBackImageID).Return(nil, imageErr).Once()
|
||||
mockMovieReader.On("GetBackImage", testCtx, errFrontImageID).Return(backImageBytes, nil).Maybe()
|
||||
mockMovieReader.On("GetBackImage", testCtx, errStudioMovieID).Return(backImageBytes, nil).Maybe()
|
||||
|
||||
mockStudioReader := &mocks.StudioReaderWriter{}
|
||||
|
||||
studioErr := errors.New("error getting studio")
|
||||
|
||||
mockStudioReader.On("Find", studioID).Return(&studio, nil)
|
||||
mockStudioReader.On("Find", missingStudioID).Return(nil, nil)
|
||||
mockStudioReader.On("Find", errStudioID).Return(nil, studioErr)
|
||||
mockStudioReader.On("Find", testCtx, studioID).Return(&movieStudio, nil)
|
||||
mockStudioReader.On("Find", testCtx, missingStudioID).Return(nil, nil)
|
||||
mockStudioReader.On("Find", testCtx, errStudioID).Return(nil, studioErr)
|
||||
|
||||
for i, s := range scenarios {
|
||||
movie := s.movie
|
||||
json, err := ToJSON(mockMovieReader, mockStudioReader, &movie)
|
||||
json, err := ToJSON(testCtx, mockMovieReader, mockStudioReader, &movie)
|
||||
|
||||
switch {
|
||||
case !s.err && err != nil:
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
package movie
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/stashapp/stash/pkg/hash/md5"
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
"github.com/stashapp/stash/pkg/models/jsonschema"
|
||||
"github.com/stashapp/stash/pkg/studio"
|
||||
"github.com/stashapp/stash/pkg/utils"
|
||||
)
|
||||
|
||||
type NameFinderCreatorUpdater interface {
|
||||
NameFinderCreator
|
||||
UpdateFull(ctx context.Context, updatedMovie models.Movie) (*models.Movie, error)
|
||||
UpdateImages(ctx context.Context, movieID int, frontImage []byte, backImage []byte) error
|
||||
}
|
||||
|
||||
type Importer struct {
|
||||
ReaderWriter models.MovieReaderWriter
|
||||
StudioWriter models.StudioReaderWriter
|
||||
ReaderWriter NameFinderCreatorUpdater
|
||||
StudioWriter studio.NameFinderCreator
|
||||
Input jsonschema.Movie
|
||||
MissingRefBehaviour models.ImportMissingRefEnum
|
||||
|
||||
@@ -21,10 +29,10 @@ type Importer struct {
|
||||
backImageData []byte
|
||||
}
|
||||
|
||||
func (i *Importer) PreImport() error {
|
||||
func (i *Importer) PreImport(ctx context.Context) error {
|
||||
i.movie = i.movieJSONToMovie(i.Input)
|
||||
|
||||
if err := i.populateStudio(); err != nil {
|
||||
if err := i.populateStudio(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -71,9 +79,9 @@ func (i *Importer) movieJSONToMovie(movieJSON jsonschema.Movie) models.Movie {
|
||||
return newMovie
|
||||
}
|
||||
|
||||
func (i *Importer) populateStudio() error {
|
||||
func (i *Importer) populateStudio(ctx context.Context) error {
|
||||
if i.Input.Studio != "" {
|
||||
studio, err := i.StudioWriter.FindByName(i.Input.Studio, false)
|
||||
studio, err := i.StudioWriter.FindByName(ctx, i.Input.Studio, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error finding studio by name: %v", err)
|
||||
}
|
||||
@@ -88,7 +96,7 @@ func (i *Importer) populateStudio() error {
|
||||
}
|
||||
|
||||
if i.MissingRefBehaviour == models.ImportMissingRefEnumCreate {
|
||||
studioID, err := i.createStudio(i.Input.Studio)
|
||||
studioID, err := i.createStudio(ctx, i.Input.Studio)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -105,10 +113,10 @@ func (i *Importer) populateStudio() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Importer) createStudio(name string) (int, error) {
|
||||
func (i *Importer) createStudio(ctx context.Context, name string) (int, error) {
|
||||
newStudio := *models.NewStudio(name)
|
||||
|
||||
created, err := i.StudioWriter.Create(newStudio)
|
||||
created, err := i.StudioWriter.Create(ctx, newStudio)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -116,9 +124,9 @@ func (i *Importer) createStudio(name string) (int, error) {
|
||||
return created.ID, nil
|
||||
}
|
||||
|
||||
func (i *Importer) PostImport(id int) error {
|
||||
func (i *Importer) PostImport(ctx context.Context, id int) error {
|
||||
if len(i.frontImageData) > 0 {
|
||||
if err := i.ReaderWriter.UpdateImages(id, i.frontImageData, i.backImageData); err != nil {
|
||||
if err := i.ReaderWriter.UpdateImages(ctx, id, i.frontImageData, i.backImageData); err != nil {
|
||||
return fmt.Errorf("error setting movie images: %v", err)
|
||||
}
|
||||
}
|
||||
@@ -130,9 +138,9 @@ func (i *Importer) Name() string {
|
||||
return i.Input.Name
|
||||
}
|
||||
|
||||
func (i *Importer) FindExistingID() (*int, error) {
|
||||
func (i *Importer) FindExistingID(ctx context.Context) (*int, error) {
|
||||
const nocase = false
|
||||
existing, err := i.ReaderWriter.FindByName(i.Name(), nocase)
|
||||
existing, err := i.ReaderWriter.FindByName(ctx, i.Name(), nocase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -145,8 +153,8 @@ func (i *Importer) FindExistingID() (*int, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (i *Importer) Create() (*int, error) {
|
||||
created, err := i.ReaderWriter.Create(i.movie)
|
||||
func (i *Importer) Create(ctx context.Context) (*int, error) {
|
||||
created, err := i.ReaderWriter.Create(ctx, i.movie)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating movie: %v", err)
|
||||
}
|
||||
@@ -155,10 +163,10 @@ func (i *Importer) Create() (*int, error) {
|
||||
return &id, nil
|
||||
}
|
||||
|
||||
func (i *Importer) Update(id int) error {
|
||||
func (i *Importer) Update(ctx context.Context, id int) error {
|
||||
movie := i.movie
|
||||
movie.ID = id
|
||||
_, err := i.ReaderWriter.UpdateFull(movie)
|
||||
_, err := i.ReaderWriter.UpdateFull(ctx, movie)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error updating existing movie: %v", err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package movie
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
@@ -27,6 +28,8 @@ const (
|
||||
errImageID = 3
|
||||
)
|
||||
|
||||
var testCtx = context.Background()
|
||||
|
||||
func TestImporterName(t *testing.T) {
|
||||
i := Importer{
|
||||
Input: jsonschema.Movie{
|
||||
@@ -45,23 +48,23 @@ func TestImporterPreImport(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
err := i.PreImport()
|
||||
err := i.PreImport(testCtx)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
i.Input.FrontImage = frontImage
|
||||
i.Input.BackImage = invalidImage
|
||||
|
||||
err = i.PreImport()
|
||||
err = i.PreImport(testCtx)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
i.Input.BackImage = ""
|
||||
|
||||
err = i.PreImport()
|
||||
err = i.PreImport(testCtx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
i.Input.BackImage = backImage
|
||||
|
||||
err = i.PreImport()
|
||||
err = i.PreImport(testCtx)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
@@ -79,17 +82,17 @@ func TestImporterPreImportWithStudio(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
studioReaderWriter.On("FindByName", existingStudioName, false).Return(&models.Studio{
|
||||
studioReaderWriter.On("FindByName", testCtx, existingStudioName, false).Return(&models.Studio{
|
||||
ID: existingStudioID,
|
||||
}, nil).Once()
|
||||
studioReaderWriter.On("FindByName", existingStudioErr, false).Return(nil, errors.New("FindByName error")).Once()
|
||||
studioReaderWriter.On("FindByName", testCtx, existingStudioErr, false).Return(nil, errors.New("FindByName error")).Once()
|
||||
|
||||
err := i.PreImport()
|
||||
err := i.PreImport(testCtx)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(existingStudioID), i.movie.StudioID.Int64)
|
||||
|
||||
i.Input.Studio = existingStudioErr
|
||||
err = i.PreImport()
|
||||
err = i.PreImport(testCtx)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
studioReaderWriter.AssertExpectations(t)
|
||||
@@ -108,20 +111,20 @@ func TestImporterPreImportWithMissingStudio(t *testing.T) {
|
||||
MissingRefBehaviour: models.ImportMissingRefEnumFail,
|
||||
}
|
||||
|
||||
studioReaderWriter.On("FindByName", missingStudioName, false).Return(nil, nil).Times(3)
|
||||
studioReaderWriter.On("Create", mock.AnythingOfType("models.Studio")).Return(&models.Studio{
|
||||
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)
|
||||
|
||||
err := i.PreImport()
|
||||
err := i.PreImport(testCtx)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
i.MissingRefBehaviour = models.ImportMissingRefEnumIgnore
|
||||
err = i.PreImport()
|
||||
err = i.PreImport(testCtx)
|
||||
assert.Nil(t, err)
|
||||
|
||||
i.MissingRefBehaviour = models.ImportMissingRefEnumCreate
|
||||
err = i.PreImport()
|
||||
err = i.PreImport(testCtx)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(existingStudioID), i.movie.StudioID.Int64)
|
||||
|
||||
@@ -141,10 +144,10 @@ func TestImporterPreImportWithMissingStudioCreateErr(t *testing.T) {
|
||||
MissingRefBehaviour: models.ImportMissingRefEnumCreate,
|
||||
}
|
||||
|
||||
studioReaderWriter.On("FindByName", missingStudioName, false).Return(nil, nil).Once()
|
||||
studioReaderWriter.On("Create", mock.AnythingOfType("models.Studio")).Return(nil, errors.New("Create error"))
|
||||
studioReaderWriter.On("FindByName", testCtx, missingStudioName, false).Return(nil, nil).Once()
|
||||
studioReaderWriter.On("Create", testCtx, mock.AnythingOfType("models.Studio")).Return(nil, errors.New("Create error"))
|
||||
|
||||
err := i.PreImport()
|
||||
err := i.PreImport(testCtx)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
@@ -159,13 +162,13 @@ func TestImporterPostImport(t *testing.T) {
|
||||
|
||||
updateMovieImageErr := errors.New("UpdateImages error")
|
||||
|
||||
readerWriter.On("UpdateImages", movieID, frontImageBytes, backImageBytes).Return(nil).Once()
|
||||
readerWriter.On("UpdateImages", errImageID, frontImageBytes, backImageBytes).Return(updateMovieImageErr).Once()
|
||||
readerWriter.On("UpdateImages", testCtx, movieID, frontImageBytes, backImageBytes).Return(nil).Once()
|
||||
readerWriter.On("UpdateImages", testCtx, errImageID, frontImageBytes, backImageBytes).Return(updateMovieImageErr).Once()
|
||||
|
||||
err := i.PostImport(movieID)
|
||||
err := i.PostImport(testCtx, movieID)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = i.PostImport(errImageID)
|
||||
err = i.PostImport(testCtx, errImageID)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
readerWriter.AssertExpectations(t)
|
||||
@@ -182,23 +185,23 @@ func TestImporterFindExistingID(t *testing.T) {
|
||||
}
|
||||
|
||||
errFindByName := errors.New("FindByName error")
|
||||
readerWriter.On("FindByName", movieName, false).Return(nil, nil).Once()
|
||||
readerWriter.On("FindByName", existingMovieName, false).Return(&models.Movie{
|
||||
readerWriter.On("FindByName", testCtx, movieName, false).Return(nil, nil).Once()
|
||||
readerWriter.On("FindByName", testCtx, existingMovieName, false).Return(&models.Movie{
|
||||
ID: existingMovieID,
|
||||
}, nil).Once()
|
||||
readerWriter.On("FindByName", movieNameErr, false).Return(nil, errFindByName).Once()
|
||||
readerWriter.On("FindByName", testCtx, movieNameErr, false).Return(nil, errFindByName).Once()
|
||||
|
||||
id, err := i.FindExistingID()
|
||||
id, err := i.FindExistingID(testCtx)
|
||||
assert.Nil(t, id)
|
||||
assert.Nil(t, err)
|
||||
|
||||
i.Input.Name = existingMovieName
|
||||
id, err = i.FindExistingID()
|
||||
id, err = i.FindExistingID(testCtx)
|
||||
assert.Equal(t, existingMovieID, *id)
|
||||
assert.Nil(t, err)
|
||||
|
||||
i.Input.Name = movieNameErr
|
||||
id, err = i.FindExistingID()
|
||||
id, err = i.FindExistingID(testCtx)
|
||||
assert.Nil(t, id)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
@@ -222,17 +225,17 @@ func TestCreate(t *testing.T) {
|
||||
}
|
||||
|
||||
errCreate := errors.New("Create error")
|
||||
readerWriter.On("Create", movie).Return(&models.Movie{
|
||||
readerWriter.On("Create", testCtx, movie).Return(&models.Movie{
|
||||
ID: movieID,
|
||||
}, nil).Once()
|
||||
readerWriter.On("Create", movieErr).Return(nil, errCreate).Once()
|
||||
readerWriter.On("Create", testCtx, movieErr).Return(nil, errCreate).Once()
|
||||
|
||||
id, err := i.Create()
|
||||
id, err := i.Create(testCtx)
|
||||
assert.Equal(t, movieID, *id)
|
||||
assert.Nil(t, err)
|
||||
|
||||
i.movie = movieErr
|
||||
id, err = i.Create()
|
||||
id, err = i.Create(testCtx)
|
||||
assert.Nil(t, id)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
@@ -259,18 +262,18 @@ func TestUpdate(t *testing.T) {
|
||||
|
||||
// id needs to be set for the mock input
|
||||
movie.ID = movieID
|
||||
readerWriter.On("UpdateFull", movie).Return(nil, nil).Once()
|
||||
readerWriter.On("UpdateFull", testCtx, movie).Return(nil, nil).Once()
|
||||
|
||||
err := i.Update(movieID)
|
||||
err := i.Update(testCtx, movieID)
|
||||
assert.Nil(t, err)
|
||||
|
||||
i.movie = movieErr
|
||||
|
||||
// need to set id separately
|
||||
movieErr.ID = errImageID
|
||||
readerWriter.On("UpdateFull", movieErr).Return(nil, errUpdate).Once()
|
||||
readerWriter.On("UpdateFull", testCtx, movieErr).Return(nil, errUpdate).Once()
|
||||
|
||||
err = i.Update(errImageID)
|
||||
err = i.Update(testCtx, errImageID)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
readerWriter.AssertExpectations(t)
|
||||
|
||||
12
pkg/movie/update.go
Normal file
12
pkg/movie/update.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package movie
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
type NameFinderCreator interface {
|
||||
FindByName(ctx context.Context, name string, nocase bool) (*models.Movie, error)
|
||||
Create(ctx context.Context, newMovie models.Movie) (*models.Movie, error)
|
||||
}
|
||||
Reference in New Issue
Block a user