mirror of https://github.com/stashapp/stash.git
394 lines
8.4 KiB
Go
394 lines
8.4 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/99designs/gqlgen/graphql"
|
|
|
|
"github.com/stashapp/stash/pkg/models"
|
|
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
|
|
)
|
|
|
|
const updateInputField = "input"
|
|
|
|
func getArgumentMap(ctx context.Context) map[string]interface{} {
|
|
rctx := graphql.GetFieldContext(ctx)
|
|
reqCtx := graphql.GetOperationContext(ctx)
|
|
return rctx.Field.ArgumentMap(reqCtx.Variables)
|
|
}
|
|
|
|
func getUpdateInputMap(ctx context.Context) map[string]interface{} {
|
|
return getNamedUpdateInputMap(ctx, updateInputField)
|
|
}
|
|
|
|
func getNamedUpdateInputMap(ctx context.Context, field string) map[string]interface{} {
|
|
args := getArgumentMap(ctx)
|
|
|
|
// field can be qualified
|
|
fields := strings.Split(field, ".")
|
|
|
|
currArgs := args
|
|
|
|
for _, f := range fields {
|
|
v, found := currArgs[f]
|
|
if !found {
|
|
currArgs = nil
|
|
break
|
|
}
|
|
|
|
currArgs, _ = v.(map[string]interface{})
|
|
if currArgs == nil {
|
|
break
|
|
}
|
|
}
|
|
|
|
if currArgs != nil {
|
|
return currArgs
|
|
}
|
|
|
|
return make(map[string]interface{})
|
|
}
|
|
|
|
func getUpdateInputMaps(ctx context.Context) []map[string]interface{} {
|
|
args := getArgumentMap(ctx)
|
|
|
|
input := args[updateInputField]
|
|
var ret []map[string]interface{}
|
|
if input != nil {
|
|
// convert []interface{} into []map[string]interface{}
|
|
iSlice, _ := input.([]interface{})
|
|
for _, i := range iSlice {
|
|
m, _ := i.(map[string]interface{})
|
|
if m != nil {
|
|
ret = append(ret, m)
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
type changesetTranslator struct {
|
|
inputMap map[string]interface{}
|
|
}
|
|
|
|
func (t changesetTranslator) hasField(field string) bool {
|
|
if t.inputMap == nil {
|
|
return false
|
|
}
|
|
|
|
_, found := t.inputMap[field]
|
|
return found
|
|
}
|
|
|
|
func (t changesetTranslator) getFields() []string {
|
|
var ret []string
|
|
for k := range t.inputMap {
|
|
ret = append(ret, k)
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func (t changesetTranslator) string(value *string) string {
|
|
if value == nil {
|
|
return ""
|
|
}
|
|
|
|
return *value
|
|
}
|
|
|
|
func (t changesetTranslator) optionalString(value *string, field string) models.OptionalString {
|
|
if !t.hasField(field) {
|
|
return models.OptionalString{}
|
|
}
|
|
|
|
return models.NewOptionalStringPtr(value)
|
|
}
|
|
|
|
func (t changesetTranslator) optionalDate(value *string, field string) (models.OptionalDate, error) {
|
|
if !t.hasField(field) {
|
|
return models.OptionalDate{}, nil
|
|
}
|
|
|
|
if value == nil || *value == "" {
|
|
return models.OptionalDate{
|
|
Set: true,
|
|
Null: true,
|
|
}, nil
|
|
}
|
|
|
|
date, err := models.ParseDate(*value)
|
|
if err != nil {
|
|
return models.OptionalDate{}, err
|
|
}
|
|
|
|
return models.NewOptionalDate(date), nil
|
|
}
|
|
|
|
func (t changesetTranslator) datePtr(value *string) (*models.Date, error) {
|
|
if value == nil || *value == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
date, err := models.ParseDate(*value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &date, nil
|
|
}
|
|
|
|
func (t changesetTranslator) intPtrFromString(value *string) (*int, error) {
|
|
if value == nil || *value == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
vv, err := strconv.Atoi(*value)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("converting %v to int: %w", *value, err)
|
|
}
|
|
return &vv, nil
|
|
}
|
|
|
|
func (t changesetTranslator) optionalInt(value *int, field string) models.OptionalInt {
|
|
if !t.hasField(field) {
|
|
return models.OptionalInt{}
|
|
}
|
|
|
|
return models.NewOptionalIntPtr(value)
|
|
}
|
|
|
|
func (t changesetTranslator) optionalIntFromString(value *string, field string) (models.OptionalInt, error) {
|
|
if !t.hasField(field) {
|
|
return models.OptionalInt{}, nil
|
|
}
|
|
|
|
if value == nil {
|
|
return models.OptionalInt{
|
|
Set: true,
|
|
Null: true,
|
|
}, nil
|
|
}
|
|
|
|
vv, err := strconv.Atoi(*value)
|
|
if err != nil {
|
|
return models.OptionalInt{}, fmt.Errorf("converting %v to int: %w", *value, err)
|
|
}
|
|
return models.NewOptionalInt(vv), nil
|
|
}
|
|
|
|
func (t changesetTranslator) bool(value *bool) bool {
|
|
if value == nil {
|
|
return false
|
|
}
|
|
|
|
return *value
|
|
}
|
|
|
|
func (t changesetTranslator) optionalBool(value *bool, field string) models.OptionalBool {
|
|
if !t.hasField(field) {
|
|
return models.OptionalBool{}
|
|
}
|
|
|
|
return models.NewOptionalBoolPtr(value)
|
|
}
|
|
|
|
func (t changesetTranslator) optionalFloat64(value *float64, field string) models.OptionalFloat64 {
|
|
if !t.hasField(field) {
|
|
return models.OptionalFloat64{}
|
|
}
|
|
|
|
return models.NewOptionalFloat64Ptr(value)
|
|
}
|
|
|
|
func (t changesetTranslator) fileIDPtrFromString(value *string) (*models.FileID, error) {
|
|
if value == nil || *value == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
vv, err := strconv.Atoi(*value)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("converting %v to int: %w", *value, err)
|
|
}
|
|
|
|
id := models.FileID(vv)
|
|
return &id, nil
|
|
}
|
|
|
|
func (t changesetTranslator) fileIDSliceFromStringSlice(value []string) ([]models.FileID, error) {
|
|
ints, err := stringslice.StringSliceToIntSlice(value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fileIDs := make([]models.FileID, len(ints))
|
|
for i, v := range ints {
|
|
fileIDs[i] = models.FileID(v)
|
|
}
|
|
|
|
return fileIDs, nil
|
|
}
|
|
|
|
func (t changesetTranslator) relatedIds(value []string) (models.RelatedIDs, error) {
|
|
ids, err := stringslice.StringSliceToIntSlice(value)
|
|
if err != nil {
|
|
return models.RelatedIDs{}, err
|
|
}
|
|
|
|
return models.NewRelatedIDs(ids), nil
|
|
}
|
|
|
|
func (t changesetTranslator) updateIds(value []string, field string) (*models.UpdateIDs, error) {
|
|
if !t.hasField(field) {
|
|
return nil, nil
|
|
}
|
|
|
|
ids, err := stringslice.StringSliceToIntSlice(value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &models.UpdateIDs{
|
|
IDs: ids,
|
|
Mode: models.RelationshipUpdateModeSet,
|
|
}, nil
|
|
}
|
|
|
|
func (t changesetTranslator) updateIdsBulk(value *BulkUpdateIds, field string) (*models.UpdateIDs, error) {
|
|
if !t.hasField(field) || value == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
ids, err := stringslice.StringSliceToIntSlice(value.Ids)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("converting ids [%v]: %w", value.Ids, err)
|
|
}
|
|
|
|
return &models.UpdateIDs{
|
|
IDs: ids,
|
|
Mode: value.Mode,
|
|
}, nil
|
|
}
|
|
|
|
func (t changesetTranslator) optionalURLs(value []string, legacyValue *string) *models.UpdateStrings {
|
|
const (
|
|
legacyField = "url"
|
|
field = "urls"
|
|
)
|
|
|
|
// prefer urls over url
|
|
if t.hasField(field) {
|
|
return t.updateStrings(value, field)
|
|
} else if t.hasField(legacyField) {
|
|
var valueSlice []string
|
|
if legacyValue != nil {
|
|
valueSlice = []string{*legacyValue}
|
|
}
|
|
return t.updateStrings(valueSlice, legacyField)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t changesetTranslator) optionalURLsBulk(value *BulkUpdateStrings, legacyValue *string) *models.UpdateStrings {
|
|
const (
|
|
legacyField = "url"
|
|
field = "urls"
|
|
)
|
|
|
|
// prefer urls over url
|
|
if t.hasField("urls") {
|
|
return t.updateStringsBulk(value, field)
|
|
} else if t.hasField(legacyField) {
|
|
var valueSlice []string
|
|
if legacyValue != nil {
|
|
valueSlice = []string{*legacyValue}
|
|
}
|
|
return t.updateStrings(valueSlice, legacyField)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (t changesetTranslator) updateStrings(value []string, field string) *models.UpdateStrings {
|
|
if !t.hasField(field) {
|
|
return nil
|
|
}
|
|
|
|
return &models.UpdateStrings{
|
|
Values: value,
|
|
Mode: models.RelationshipUpdateModeSet,
|
|
}
|
|
}
|
|
|
|
func (t changesetTranslator) updateStringsBulk(value *BulkUpdateStrings, field string) *models.UpdateStrings {
|
|
if !t.hasField(field) || value == nil {
|
|
return nil
|
|
}
|
|
|
|
return &models.UpdateStrings{
|
|
Values: value.Values,
|
|
Mode: value.Mode,
|
|
}
|
|
}
|
|
|
|
func (t changesetTranslator) updateStashIDs(value []models.StashID, field string) *models.UpdateStashIDs {
|
|
if !t.hasField(field) {
|
|
return nil
|
|
}
|
|
|
|
return &models.UpdateStashIDs{
|
|
StashIDs: value,
|
|
Mode: models.RelationshipUpdateModeSet,
|
|
}
|
|
}
|
|
|
|
func (t changesetTranslator) relatedMovies(value []models.SceneMovieInput) (models.RelatedMovies, error) {
|
|
moviesScenes, err := models.MoviesScenesFromInput(value)
|
|
if err != nil {
|
|
return models.RelatedMovies{}, err
|
|
}
|
|
|
|
return models.NewRelatedMovies(moviesScenes), nil
|
|
}
|
|
|
|
func (t changesetTranslator) updateMovieIDs(value []models.SceneMovieInput, field string) (*models.UpdateMovieIDs, error) {
|
|
if !t.hasField(field) {
|
|
return nil, nil
|
|
}
|
|
|
|
moviesScenes, err := models.MoviesScenesFromInput(value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &models.UpdateMovieIDs{
|
|
Movies: moviesScenes,
|
|
Mode: models.RelationshipUpdateModeSet,
|
|
}, nil
|
|
}
|
|
|
|
func (t changesetTranslator) updateMovieIDsBulk(value *BulkUpdateIds, field string) (*models.UpdateMovieIDs, error) {
|
|
if !t.hasField(field) || value == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
ids, err := stringslice.StringSliceToIntSlice(value.Ids)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("converting ids [%v]: %w", value.Ids, err)
|
|
}
|
|
|
|
movies := make([]models.MoviesScenes, len(ids))
|
|
for i, id := range ids {
|
|
movies[i] = models.MoviesScenes{MovieID: id}
|
|
}
|
|
|
|
return &models.UpdateMovieIDs{
|
|
Movies: movies,
|
|
Mode: value.Mode,
|
|
}, nil
|
|
}
|