From 9bf9c355f18797ec39395543cc81266f5c726f4c Mon Sep 17 00:00:00 2001 From: WithoutPants <53250216+WithoutPants@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:01:54 +1100 Subject: [PATCH] Include custom fields in import/export --- pkg/models/custom_fields.go | 1 + pkg/models/jsonschema/performer.go | 2 + pkg/models/mocks/PerformerReaderWriter.go | 23 +++++++++ pkg/performer/export.go | 7 +++ pkg/performer/export_test.go | 59 +++++++++++++++++++---- pkg/performer/import.go | 1 + pkg/performer/import_test.go | 3 +- 7 files changed, 85 insertions(+), 11 deletions(-) diff --git a/pkg/models/custom_fields.go b/pkg/models/custom_fields.go index 7c7fe1bc302..977c2fe89f3 100644 --- a/pkg/models/custom_fields.go +++ b/pkg/models/custom_fields.go @@ -12,5 +12,6 @@ type CustomFieldsInput struct { } type CustomFieldsReader interface { + GetCustomFields(ctx context.Context, id int) (map[string]interface{}, error) GetCustomFieldsBulk(ctx context.Context, ids []int) ([]CustomFieldMap, error) } diff --git a/pkg/models/jsonschema/performer.go b/pkg/models/jsonschema/performer.go index 7ffa69983b4..5edd5724c63 100644 --- a/pkg/models/jsonschema/performer.go +++ b/pkg/models/jsonschema/performer.go @@ -65,6 +65,8 @@ type Performer struct { StashIDs []models.StashID `json:"stash_ids,omitempty"` IgnoreAutoTag bool `json:"ignore_auto_tag,omitempty"` + CustomFields map[string]interface{} `json:"custom_fields,omitempty"` + // deprecated - for import only URL string `json:"url,omitempty"` Twitter string `json:"twitter,omitempty"` diff --git a/pkg/models/mocks/PerformerReaderWriter.go b/pkg/models/mocks/PerformerReaderWriter.go index a6ce6cda269..dbf19a3cdce 100644 --- a/pkg/models/mocks/PerformerReaderWriter.go +++ b/pkg/models/mocks/PerformerReaderWriter.go @@ -314,6 +314,29 @@ func (_m *PerformerReaderWriter) GetAliases(ctx context.Context, relatedID int) return r0, r1 } +// GetCustomFields provides a mock function with given fields: ctx, id +func (_m *PerformerReaderWriter) GetCustomFields(ctx context.Context, id int) (map[string]interface{}, error) { + ret := _m.Called(ctx, id) + + var r0 map[string]interface{} + if rf, ok := ret.Get(0).(func(context.Context, int) map[string]interface{}); ok { + r0 = rf(ctx, id) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[string]interface{}) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, int) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetCustomFieldsBulk provides a mock function with given fields: ctx, ids func (_m *PerformerReaderWriter) GetCustomFieldsBulk(ctx context.Context, ids []int) ([]models.CustomFieldMap, error) { ret := _m.Called(ctx, ids) diff --git a/pkg/performer/export.go b/pkg/performer/export.go index 8f720338f3d..1455fb7bfa0 100644 --- a/pkg/performer/export.go +++ b/pkg/performer/export.go @@ -17,6 +17,7 @@ type ImageAliasStashIDGetter interface { models.AliasLoader models.StashIDLoader models.URLLoader + models.CustomFieldsReader } // ToJSON converts a Performer object into its JSON equivalent. @@ -87,6 +88,12 @@ func ToJSON(ctx context.Context, reader ImageAliasStashIDGetter, performer *mode newPerformerJSON.StashIDs = performer.StashIDs.List() + var err error + newPerformerJSON.CustomFields, err = reader.GetCustomFields(ctx, performer.ID) + if err != nil { + return nil, fmt.Errorf("getting performer custom fields: %v", err) + } + image, err := reader.GetImage(ctx, performer.ID) if err != nil { logger.Errorf("Error getting performer image: %v", err) diff --git a/pkg/performer/export_test.go b/pkg/performer/export_test.go index 36353b17de7..e51049e1491 100644 --- a/pkg/performer/export_test.go +++ b/pkg/performer/export_test.go @@ -15,9 +15,11 @@ import ( ) const ( - performerID = 1 - noImageID = 2 - errImageID = 3 + performerID = 1 + noImageID = 2 + errImageID = 3 + customFieldsID = 4 + errCustomFieldsID = 5 ) const ( @@ -50,6 +52,11 @@ var ( penisLength = 1.23 circumcisedEnum = models.CircumisedEnumCut circumcised = circumcisedEnum.String() + + emptyCustomFields = make(map[string]interface{}) + customFields = map[string]interface{}{ + "customField1": "customValue1", + } ) var imageBytes = []byte("imageBytes") @@ -118,8 +125,8 @@ func createEmptyPerformer(id int) models.Performer { } } -func createFullJSONPerformer(name string, image string) *jsonschema.Performer { - return &jsonschema.Performer{ +func createFullJSONPerformer(name string, image string, withCustomFields bool) *jsonschema.Performer { + ret := &jsonschema.Performer{ Name: name, Disambiguation: disambiguation, URLs: []string{url, twitter, instagram}, @@ -152,7 +159,13 @@ func createFullJSONPerformer(name string, image string) *jsonschema.Performer { Weight: weight, StashIDs: stashIDs, IgnoreAutoTag: autoTagIgnored, + CustomFields: emptyCustomFields, } + + if withCustomFields { + ret.CustomFields = customFields + } + return ret } func createEmptyJSONPerformer() *jsonschema.Performer { @@ -166,13 +179,15 @@ func createEmptyJSONPerformer() *jsonschema.Performer { UpdatedAt: json.JSONTime{ Time: updateTime, }, + CustomFields: emptyCustomFields, } } type testScenario struct { - input models.Performer - expected *jsonschema.Performer - err bool + input models.Performer + customFields map[string]interface{} + expected *jsonschema.Performer + err bool } var scenarios []testScenario @@ -181,20 +196,36 @@ func initTestTable() { scenarios = []testScenario{ { *createFullPerformer(performerID, performerName), - createFullJSONPerformer(performerName, image), + emptyCustomFields, + createFullJSONPerformer(performerName, image, false), + false, + }, + { + *createFullPerformer(customFieldsID, performerName), + customFields, + createFullJSONPerformer(performerName, image, true), false, }, { createEmptyPerformer(noImageID), + emptyCustomFields, createEmptyJSONPerformer(), false, }, { *createFullPerformer(errImageID, performerName), - createFullJSONPerformer(performerName, ""), + emptyCustomFields, + createFullJSONPerformer(performerName, "", false), // failure to get image should not cause an error false, }, + { + *createFullPerformer(errCustomFieldsID, performerName), + customFields, + nil, + // failure to get custom fields should cause an error + true, + }, } } @@ -204,11 +235,19 @@ func TestToJSON(t *testing.T) { db := mocks.NewDatabase() imageErr := errors.New("error getting image") + customFieldsErr := errors.New("error getting custom fields") db.Performer.On("GetImage", testCtx, performerID).Return(imageBytes, nil).Once() + db.Performer.On("GetImage", testCtx, customFieldsID).Return(imageBytes, nil).Once() db.Performer.On("GetImage", testCtx, noImageID).Return(nil, nil).Once() db.Performer.On("GetImage", testCtx, errImageID).Return(nil, imageErr).Once() + db.Performer.On("GetCustomFields", testCtx, performerID).Return(emptyCustomFields, nil).Once() + db.Performer.On("GetCustomFields", testCtx, customFieldsID).Return(customFields, nil).Once() + db.Performer.On("GetCustomFields", testCtx, noImageID).Return(emptyCustomFields, nil).Once() + db.Performer.On("GetCustomFields", testCtx, errImageID).Return(emptyCustomFields, nil).Once() + db.Performer.On("GetCustomFields", testCtx, errCustomFieldsID).Return(nil, customFieldsErr).Once() + for i, s := range scenarios { tag := s.input json, err := ToJSON(testCtx, db.Performer, &tag) diff --git a/pkg/performer/import.go b/pkg/performer/import.go index b6f607dfd79..3aaacdb8b69 100644 --- a/pkg/performer/import.go +++ b/pkg/performer/import.go @@ -33,6 +33,7 @@ type Importer struct { func (i *Importer) PreImport(ctx context.Context) error { i.performer = performerJSONToPerformer(i.Input) + i.customFields = i.Input.CustomFields if err := i.populateTags(ctx); err != nil { return err diff --git a/pkg/performer/import_test.go b/pkg/performer/import_test.go index 1aa79439024..0a3f862914a 100644 --- a/pkg/performer/import_test.go +++ b/pkg/performer/import_test.go @@ -53,13 +53,14 @@ func TestImporterPreImport(t *testing.T) { assert.NotNil(t, err) - i.Input = *createFullJSONPerformer(performerName, image) + i.Input = *createFullJSONPerformer(performerName, image, true) err = i.PreImport(testCtx) assert.Nil(t, err) expectedPerformer := *createFullPerformer(0, performerName) assert.Equal(t, expectedPerformer, i.performer) + assert.Equal(t, models.CustomFieldMap(customFields), i.customFields) } func TestImporterPreImportWithTag(t *testing.T) {