mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 04:44:37 +03:00
Fix issues linking a tag that already exists in the tag list (#6395)
* Add stash-id to existing when linking tag * Validate id list for duplicates in find queries * Filter out duplicate ids after linking tag
This commit is contained in:
35
internal/api/input.go
Normal file
35
internal/api/input.go
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO - apply handleIDs to other resolvers that accept ID lists
|
||||||
|
|
||||||
|
// handleIDList validates and converts a list of string IDs to integers
|
||||||
|
func handleIDList(idList []string, field string) ([]int, error) {
|
||||||
|
if err := validateIDList(idList); err != nil {
|
||||||
|
return nil, fmt.Errorf("validating %s: %w", field, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ids, err := stringslice.StringSliceToIntSlice(idList)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("converting %s: %w", field, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ids, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateIDList returns an error if there are any duplicate ids in the list
|
||||||
|
func validateIDList(ids []string) error {
|
||||||
|
seen := make(map[string]struct{})
|
||||||
|
for _, id := range ids {
|
||||||
|
if _, exists := seen[id]; exists {
|
||||||
|
return fmt.Errorf("duplicate id found: %s", id)
|
||||||
|
}
|
||||||
|
seen[id] = struct{}{}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *queryResolver) FindFolder(ctx context.Context, id *string, path *string) (*models.Folder, error) {
|
func (r *queryResolver) FindFolder(ctx context.Context, id *string, path *string) (*models.Folder, error) {
|
||||||
@@ -49,7 +48,7 @@ func (r *queryResolver) FindFolders(
|
|||||||
) (ret *FindFoldersResultType, err error) {
|
) (ret *FindFoldersResultType, err error) {
|
||||||
var folderIDs []models.FolderID
|
var folderIDs []models.FolderID
|
||||||
if len(ids) > 0 {
|
if len(ids) > 0 {
|
||||||
folderIDsInt, err := stringslice.StringSliceToIntSlice(ids)
|
folderIDsInt, err := handleIDList(ids, "ids")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *queryResolver) FindGallery(ctx context.Context, id string) (ret *models.Gallery, err error) {
|
func (r *queryResolver) FindGallery(ctx context.Context, id string) (ret *models.Gallery, err error) {
|
||||||
@@ -25,7 +24,7 @@ func (r *queryResolver) FindGallery(ctx context.Context, id string) (ret *models
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *queryResolver) FindGalleries(ctx context.Context, galleryFilter *models.GalleryFilterType, filter *models.FindFilterType, ids []string) (ret *FindGalleriesResultType, err error) {
|
func (r *queryResolver) FindGalleries(ctx context.Context, galleryFilter *models.GalleryFilterType, filter *models.FindFilterType, ids []string) (ret *FindGalleriesResultType, err error) {
|
||||||
idInts, err := stringslice.StringSliceToIntSlice(ids)
|
idInts, err := handleIDList(ids, "ids")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *queryResolver) FindGroup(ctx context.Context, id string) (ret *models.Group, err error) {
|
func (r *queryResolver) FindGroup(ctx context.Context, id string) (ret *models.Group, err error) {
|
||||||
@@ -25,7 +24,7 @@ func (r *queryResolver) FindGroup(ctx context.Context, id string) (ret *models.G
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *queryResolver) FindGroups(ctx context.Context, groupFilter *models.GroupFilterType, filter *models.FindFilterType, ids []string) (ret *FindGroupsResultType, err error) {
|
func (r *queryResolver) FindGroups(ctx context.Context, groupFilter *models.GroupFilterType, filter *models.FindFilterType, ids []string) (ret *FindGroupsResultType, err error) {
|
||||||
idInts, err := stringslice.StringSliceToIntSlice(ids)
|
idInts, err := handleIDList(ids, "ids")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import (
|
|||||||
|
|
||||||
"github.com/99designs/gqlgen/graphql"
|
"github.com/99designs/gqlgen/graphql"
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *queryResolver) FindImage(ctx context.Context, id *string, checksum *string) (*models.Image, error) {
|
func (r *queryResolver) FindImage(ctx context.Context, id *string, checksum *string) (*models.Image, error) {
|
||||||
@@ -55,7 +54,7 @@ func (r *queryResolver) FindImages(
|
|||||||
filter *models.FindFilterType,
|
filter *models.FindFilterType,
|
||||||
) (ret *FindImagesResultType, err error) {
|
) (ret *FindImagesResultType, err error) {
|
||||||
if len(ids) > 0 {
|
if len(ids) > 0 {
|
||||||
imageIds, err = stringslice.StringSliceToIntSlice(ids)
|
imageIds, err = handleIDList(ids, "ids")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *queryResolver) FindMovie(ctx context.Context, id string) (ret *models.Group, err error) {
|
func (r *queryResolver) FindMovie(ctx context.Context, id string) (ret *models.Group, err error) {
|
||||||
@@ -25,7 +24,7 @@ func (r *queryResolver) FindMovie(ctx context.Context, id string) (ret *models.G
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *queryResolver) FindMovies(ctx context.Context, movieFilter *models.GroupFilterType, filter *models.FindFilterType, ids []string) (ret *FindMoviesResultType, err error) {
|
func (r *queryResolver) FindMovies(ctx context.Context, movieFilter *models.GroupFilterType, filter *models.FindFilterType, ids []string) (ret *FindMoviesResultType, err error) {
|
||||||
idInts, err := stringslice.StringSliceToIntSlice(ids)
|
idInts, err := handleIDList(ids, "ids")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *queryResolver) FindPerformer(ctx context.Context, id string) (ret *models.Performer, err error) {
|
func (r *queryResolver) FindPerformer(ctx context.Context, id string) (ret *models.Performer, err error) {
|
||||||
@@ -26,7 +25,7 @@ func (r *queryResolver) FindPerformer(ctx context.Context, id string) (ret *mode
|
|||||||
|
|
||||||
func (r *queryResolver) FindPerformers(ctx context.Context, performerFilter *models.PerformerFilterType, filter *models.FindFilterType, performerIDs []int, ids []string) (ret *FindPerformersResultType, err error) {
|
func (r *queryResolver) FindPerformers(ctx context.Context, performerFilter *models.PerformerFilterType, filter *models.FindFilterType, performerIDs []int, ids []string) (ret *FindPerformersResultType, err error) {
|
||||||
if len(ids) > 0 {
|
if len(ids) > 0 {
|
||||||
performerIDs, err = stringslice.StringSliceToIntSlice(ids)
|
performerIDs, err = handleIDList(ids, "ids")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
|
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/scene"
|
"github.com/stashapp/stash/pkg/scene"
|
||||||
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *queryResolver) FindScene(ctx context.Context, id *string, checksum *string) (*models.Scene, error) {
|
func (r *queryResolver) FindScene(ctx context.Context, id *string, checksum *string) (*models.Scene, error) {
|
||||||
@@ -83,7 +82,7 @@ func (r *queryResolver) FindScenes(
|
|||||||
filter *models.FindFilterType,
|
filter *models.FindFilterType,
|
||||||
) (ret *FindScenesResultType, err error) {
|
) (ret *FindScenesResultType, err error) {
|
||||||
if len(ids) > 0 {
|
if len(ids) > 0 {
|
||||||
sceneIDs, err = stringslice.StringSliceToIntSlice(ids)
|
sceneIDs, err = handleIDList(ids, "ids")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *queryResolver) FindSceneMarkers(ctx context.Context, sceneMarkerFilter *models.SceneMarkerFilterType, filter *models.FindFilterType, ids []string) (ret *FindSceneMarkersResultType, err error) {
|
func (r *queryResolver) FindSceneMarkers(ctx context.Context, sceneMarkerFilter *models.SceneMarkerFilterType, filter *models.FindFilterType, ids []string) (ret *FindSceneMarkersResultType, err error) {
|
||||||
idInts, err := stringslice.StringSliceToIntSlice(ids)
|
idInts, err := handleIDList(ids, "ids")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *queryResolver) FindStudio(ctx context.Context, id string) (ret *models.Studio, err error) {
|
func (r *queryResolver) FindStudio(ctx context.Context, id string) (ret *models.Studio, err error) {
|
||||||
@@ -26,7 +25,7 @@ func (r *queryResolver) FindStudio(ctx context.Context, id string) (ret *models.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *queryResolver) FindStudios(ctx context.Context, studioFilter *models.StudioFilterType, filter *models.FindFilterType, ids []string) (ret *FindStudiosResultType, err error) {
|
func (r *queryResolver) FindStudios(ctx context.Context, studioFilter *models.StudioFilterType, filter *models.FindFilterType, ids []string) (ret *FindStudiosResultType, err error) {
|
||||||
idInts, err := stringslice.StringSliceToIntSlice(ids)
|
idInts, err := handleIDList(ids, "ids")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/stashapp/stash/pkg/models"
|
"github.com/stashapp/stash/pkg/models"
|
||||||
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r *queryResolver) FindTag(ctx context.Context, id string) (ret *models.Tag, err error) {
|
func (r *queryResolver) FindTag(ctx context.Context, id string) (ret *models.Tag, err error) {
|
||||||
@@ -25,7 +24,7 @@ func (r *queryResolver) FindTag(ctx context.Context, id string) (ret *models.Tag
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *queryResolver) FindTags(ctx context.Context, tagFilter *models.TagFilterType, filter *models.FindFilterType, ids []string) (ret *FindTagsResultType, err error) {
|
func (r *queryResolver) FindTags(ctx context.Context, tagFilter *models.TagFilterType, filter *models.FindFilterType, ids []string) (ret *FindTagsResultType, err error) {
|
||||||
idInts, err := stringslice.StringSliceToIntSlice(ids)
|
idInts, err := handleIDList(ids, "ids")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,4 +6,10 @@ fragment SlimTagData on Tag {
|
|||||||
image_path
|
image_path
|
||||||
parent_count
|
parent_count
|
||||||
child_count
|
child_count
|
||||||
|
|
||||||
|
stash_ids {
|
||||||
|
endpoint
|
||||||
|
stash_id
|
||||||
|
updated_at
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,4 +50,10 @@ fragment SelectTagData on Tag {
|
|||||||
name
|
name
|
||||||
sort_name
|
sort_name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stash_ids {
|
||||||
|
endpoint
|
||||||
|
stash_id
|
||||||
|
updated_at
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,9 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setPrimaryTag(
|
setPrimaryTag(
|
||||||
marker?.primary_tag ? { ...marker.primary_tag, aliases: [] } : undefined
|
marker?.primary_tag
|
||||||
|
? { ...marker.primary_tag, aliases: [], stash_ids: [] }
|
||||||
|
: undefined
|
||||||
);
|
);
|
||||||
}, [marker?.primary_tag]);
|
}, [marker?.primary_tag]);
|
||||||
|
|
||||||
@@ -105,6 +107,7 @@ export const SceneMarkerForm: React.FC<ISceneMarkerForm> = ({
|
|||||||
marker?.tags.map((t) => ({
|
marker?.tags.map((t) => ({
|
||||||
...t,
|
...t,
|
||||||
aliases: [],
|
aliases: [],
|
||||||
|
stash_ids: [],
|
||||||
})) ?? []
|
})) ?? []
|
||||||
);
|
);
|
||||||
}, [marker?.tags]);
|
}, [marker?.tags]);
|
||||||
|
|||||||
@@ -45,9 +45,13 @@ export const CreateLinkTagDialog: React.FC<{
|
|||||||
aliases: addAsAlias
|
aliases: addAsAlias
|
||||||
? [...(existingTag.aliases || []), tag.name]
|
? [...(existingTag.aliases || []), tag.name]
|
||||||
: undefined,
|
: undefined,
|
||||||
|
// add stash id if applicable
|
||||||
stash_ids:
|
stash_ids:
|
||||||
endpoint && tag.remote_site_id
|
endpoint && tag.remote_site_id
|
||||||
? [{ endpoint: endpoint!, stash_id: tag.remote_site_id }]
|
? [
|
||||||
|
...(existingTag.stash_ids || []),
|
||||||
|
{ endpoint: endpoint!, stash_id: tag.remote_site_id },
|
||||||
|
]
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
onClose({ update: updateInput });
|
onClose({ update: updateInput });
|
||||||
|
|||||||
@@ -467,7 +467,7 @@ const StashSearchResult: React.FC<IStashSearchResultProps> = ({
|
|||||||
updateInput: GQL.TagUpdateInput
|
updateInput: GQL.TagUpdateInput
|
||||||
) {
|
) {
|
||||||
await updateTag(t, updateInput);
|
await updateTag(t, updateInput);
|
||||||
setTagIDs([...tagIDs, updateInput.id]);
|
setTagIDs(uniq([...tagIDs, updateInput.id]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function showTagModal(t: GQL.ScrapedTag) {
|
function showTagModal(t: GQL.ScrapedTag) {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export type SelectObject = {
|
|||||||
|
|
||||||
export type Tag = Pick<
|
export type Tag = Pick<
|
||||||
GQL.Tag,
|
GQL.Tag,
|
||||||
"id" | "name" | "sort_name" | "aliases" | "image_path"
|
"id" | "name" | "sort_name" | "aliases" | "image_path" | "stash_ids"
|
||||||
>;
|
>;
|
||||||
type Option = SelectOption<Tag>;
|
type Option = SelectOption<Tag>;
|
||||||
|
|
||||||
@@ -198,6 +198,7 @@ const _TagSelect: React.FC<TagSelectProps> = (props) => {
|
|||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
aliases: [],
|
aliases: [],
|
||||||
|
stash_ids: [],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ export function useTagsEdit(
|
|||||||
id: result.data.tagCreate.id,
|
id: result.data.tagCreate.id,
|
||||||
name: toCreate.name ?? "",
|
name: toCreate.name ?? "",
|
||||||
aliases: [],
|
aliases: [],
|
||||||
|
stash_ids: result.data.tagCreate.stash_ids,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
@@ -93,6 +94,7 @@ export function useTagsEdit(
|
|||||||
id: p.stored_id!,
|
id: p.stored_id!,
|
||||||
name: p.name ?? "",
|
name: p.name ?? "",
|
||||||
aliases: [],
|
aliases: [],
|
||||||
|
stash_ids: [],
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user