From d1f16907cf2ca7f945da9d52447c3364ab82e6bb Mon Sep 17 00:00:00 2001 From: WithoutPants <53250216+WithoutPants@users.noreply.github.com> Date: Fri, 6 Feb 2026 14:17:27 +1100 Subject: [PATCH] Change tag merge to accept values. MergeHierarchy is removed as it is no longer needed --- graphql/schema/types/tag.graphql | 2 + internal/api/resolver_mutation_tag.go | 86 ++++++++++++++++++--------- pkg/tag/update.go | 46 -------------- 3 files changed, 59 insertions(+), 75 deletions(-) diff --git a/graphql/schema/types/tag.graphql b/graphql/schema/types/tag.graphql index 2210c900e..0acbc927f 100644 --- a/graphql/schema/types/tag.graphql +++ b/graphql/schema/types/tag.graphql @@ -78,6 +78,8 @@ type FindTagsResultType { input TagsMergeInput { source: [ID!]! destination: ID! + # values defined here will override values in the destination + values: TagUpdateInput } input BulkTagUpdateInput { diff --git a/internal/api/resolver_mutation_tag.go b/internal/api/resolver_mutation_tag.go index 31c7980f6..ac0183b74 100644 --- a/internal/api/resolver_mutation_tag.go +++ b/internal/api/resolver_mutation_tag.go @@ -6,7 +6,6 @@ import ( "strconv" "strings" - "github.com/stashapp/stash/pkg/logger" "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/plugin/hook" "github.com/stashapp/stash/pkg/sliceutil/stringslice" @@ -103,17 +102,7 @@ func (r *mutationResolver) TagCreate(ctx context.Context, input TagCreateInput) return r.getTag(ctx, newTag.ID) } -func (r *mutationResolver) TagUpdate(ctx context.Context, input TagUpdateInput) (*models.Tag, error) { - tagID, err := strconv.Atoi(input.ID) - if err != nil { - return nil, fmt.Errorf("converting id: %w", err) - } - - translator := changesetTranslator{ - inputMap: getUpdateInputMap(ctx), - } - - // Populate tag from the input +func tagPartialFromInput(input TagUpdateInput, translator changesetTranslator) (*models.TagPartial, error) { updatedTag := models.NewTagPartial() updatedTag.Name = translator.optionalString(input.Name, "name") @@ -132,6 +121,7 @@ func (r *mutationResolver) TagUpdate(ctx context.Context, input TagUpdateInput) } updatedTag.StashIDs = translator.updateStashIDs(updateStashIDInputs, "stash_ids") + var err error updatedTag.ParentIDs, err = translator.updateIds(input.ParentIds, "parent_ids") if err != nil { return nil, fmt.Errorf("converting parent tag ids: %w", err) @@ -149,6 +139,25 @@ func (r *mutationResolver) TagUpdate(ctx context.Context, input TagUpdateInput) updatedTag.CustomFields.Partial = convertMapJSONNumbers(updatedTag.CustomFields.Partial) } + return &updatedTag, nil +} + +func (r *mutationResolver) TagUpdate(ctx context.Context, input TagUpdateInput) (*models.Tag, error) { + tagID, err := strconv.Atoi(input.ID) + if err != nil { + return nil, fmt.Errorf("converting id: %w", err) + } + + translator := changesetTranslator{ + inputMap: getUpdateInputMap(ctx), + } + + // Populate tag from the input + updatedTag, err := tagPartialFromInput(input, translator) + if err != nil { + return nil, err + } + var imageData []byte imageIncluded := translator.hasField("image") if input.Image != nil { @@ -185,11 +194,11 @@ func (r *mutationResolver) TagUpdate(ctx context.Context, input TagUpdateInput) } } - if err := tag.ValidateUpdate(ctx, tagID, updatedTag, qb); err != nil { + if err := tag.ValidateUpdate(ctx, tagID, *updatedTag, qb); err != nil { return err } - t, err = qb.UpdatePartial(ctx, tagID, updatedTag) + t, err = qb.UpdatePartial(ctx, tagID, *updatedTag) if err != nil { return err } @@ -337,6 +346,31 @@ func (r *mutationResolver) TagsMerge(ctx context.Context, input TagsMergeInput) return nil, nil } + var values *models.TagPartial + var imageData []byte + + if input.Values != nil { + translator := changesetTranslator{ + inputMap: getNamedUpdateInputMap(ctx, "input.values"), + } + + values, err = tagPartialFromInput(*input.Values, translator) + if err != nil { + return nil, err + } + + if input.Values.Image != nil { + var err error + imageData, err = utils.ProcessImageInput(ctx, *input.Values.Image) + if err != nil { + return nil, fmt.Errorf("processing cover image: %w", err) + } + } + } else { + v := models.NewTagPartial() + values = &v + } + var t *models.Tag if err := r.withTxn(ctx, func(ctx context.Context) error { qb := r.repository.Tag @@ -351,28 +385,22 @@ func (r *mutationResolver) TagsMerge(ctx context.Context, input TagsMergeInput) return fmt.Errorf("tag with id %d not found", destination) } - parents, children, err := tag.MergeHierarchy(ctx, destination, source, qb) - if err != nil { - return err - } - if err = qb.Merge(ctx, source, destination); err != nil { return err } - err = qb.UpdateParentTags(ctx, destination, parents) - if err != nil { - return err - } - err = qb.UpdateChildTags(ctx, destination, children) - if err != nil { + if err := tag.ValidateUpdate(ctx, destination, *values, qb); err != nil { return err } - err = tag.ValidateHierarchyExisting(ctx, t, parents, children, qb) - if err != nil { - logger.Errorf("Error merging tag: %s", err) - return err + if _, err := qb.UpdatePartial(ctx, destination, *values); err != nil { + return fmt.Errorf("updating tag: %w", err) + } + + if len(imageData) > 0 { + if err := qb.UpdateImage(ctx, destination, imageData); err != nil { + return err + } } return nil diff --git a/pkg/tag/update.go b/pkg/tag/update.go index 99e9b9165..4a3a2901a 100644 --- a/pkg/tag/update.go +++ b/pkg/tag/update.go @@ -220,49 +220,3 @@ func ValidateHierarchyExisting(ctx context.Context, tag *models.Tag, parentIDs, return nil } - -func MergeHierarchy(ctx context.Context, destination int, sources []int, qb RelationshipFinder) ([]int, []int, error) { - var mergedParents, mergedChildren []int - allIds := append([]int{destination}, sources...) - - addTo := func(mergedItems []int, tagIDs []int) []int { - Tags: - for _, tagID := range tagIDs { - // Ignore tags which are already set - for _, existingItem := range mergedItems { - if tagID == existingItem { - continue Tags - } - } - - // Ignore tags which are being merged, as these are rolled up anyway (if A is merged into B any direct link between them can be ignored) - for _, id := range allIds { - if tagID == id { - continue Tags - } - } - - mergedItems = append(mergedItems, tagID) - } - - return mergedItems - } - - for _, id := range allIds { - parents, err := qb.GetParentIDs(ctx, id) - if err != nil { - return nil, nil, err - } - - mergedParents = addTo(mergedParents, parents) - - children, err := qb.GetChildIDs(ctx, id) - if err != nil { - return nil, nil, err - } - - mergedChildren = addTo(mergedChildren, children) - } - - return mergedParents, mergedChildren, nil -}