Skip to content

Commit

Permalink
Add edit foreign key validation (#875)
Browse files Browse the repository at this point in the history
  • Loading branch information
InfiniteStash authored Dec 30, 2024
1 parent 9a90e38 commit 45ce233
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 42 deletions.
42 changes: 0 additions & 42 deletions pkg/manager/edit/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,8 @@ import (
"github.com/stashapp/stash-box/pkg/utils"
)

var ErrInvalidVoteStatus = errors.New("invalid vote status")
var ErrEditNotFound = errors.New("edit not found")
var ErrEditAlreadyApplied = errors.New("edit already applied")
var ErrNoChanges = errors.New("edit contains no changes")
var ErrMergeIDMissing = errors.New("merge target ID is required")
var ErrEntityNotFound = errors.New("entity not found")
var ErrEntityDeleted = errors.New("entity is deleted")
var ErrMergeTargetIsSource = errors.New("merge target cannot be used as source")
var ErrNoMergeSources = errors.New("no merge sources found")

Expand Down Expand Up @@ -81,28 +76,6 @@ type editApplyer interface {
apply() error
}

func validateEditPresence(edit *models.Edit) error {
if edit == nil {
return ErrEditNotFound
}

if edit.Applied {
return ErrEditAlreadyApplied
}

return nil
}

func validateEditPrerequisites(fac models.Repo, edit *models.Edit) error {
var status models.VoteStatusEnum
utils.ResolveEnumString(edit.Status, &status)
if status != models.VoteStatusEnumPending {
return fmt.Errorf("%w: %s", ErrInvalidVoteStatus, edit.Status)
}

return nil
}

func ApplyEdit(fac models.Repo, editID uuid.UUID, immediate bool) (*models.Edit, error) {
var updatedEdit *models.Edit
err := fac.WithTxn(func() error {
Expand Down Expand Up @@ -310,18 +283,3 @@ func ResolveVotingThreshold(fac models.Repo, edit *models.Edit) (models.VoteStat

return models.VoteStatusEnumPending, nil
}

type editEntity interface {
IsDeleted() bool
}

func validateEditEntity(entity *editEntity, id uuid.UUID, typeName string) error {
if entity == nil {
return fmt.Errorf("%w: %s %s", ErrEntityNotFound, typeName, id.String())
}
if (*entity).IsDeleted() {
return fmt.Errorf("%w: %s %s", ErrEntityDeleted, typeName, id.String())
}

return nil
}
4 changes: 4 additions & 0 deletions pkg/manager/edit/performer.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ func Performer(fac models.Repo, edit *models.Edit) *PerformerEditProcessor {
}

func (m *PerformerEditProcessor) Edit(input models.PerformerEditInput, inputArgs utils.ArgumentsQuery) error {
if err := validatePerformerEditInput(m.fac, input); err != nil {
return err
}

var err error
switch input.Edit.Operation {
case models.OperationEnumModify:
Expand Down
4 changes: 4 additions & 0 deletions pkg/manager/edit/scene.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ func Scene(fac models.Repo, edit *models.Edit) *SceneEditProcessor {
}

func (m *SceneEditProcessor) Edit(input models.SceneEditInput, inputArgs utils.ArgumentsQuery) error {
if err := validateSceneEditInput(m.fac, input); err != nil {
return err
}

var err error
switch input.Edit.Operation {
case models.OperationEnumModify:
Expand Down
4 changes: 4 additions & 0 deletions pkg/manager/edit/studio.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ func Studio(fac models.Repo, edit *models.Edit) *StudioEditProcessor {
}

func (m *StudioEditProcessor) Edit(input models.StudioEditInput, inputArgs utils.ArgumentsQuery) error {
if err := validateStudioEditInput(m.fac, input); err != nil {
return err
}

var err error
switch input.Edit.Operation {
case models.OperationEnumModify:
Expand Down
223 changes: 223 additions & 0 deletions pkg/manager/edit/validate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
package edit

import (
"errors"
"fmt"

"github.com/gofrs/uuid"
"github.com/stashapp/stash-box/pkg/models"
"github.com/stashapp/stash-box/pkg/utils"
)

var ErrEditAlreadyApplied = errors.New("edit already applied")
var ErrInvalidVoteStatus = errors.New("invalid vote status")
var ErrEditNotFound = errors.New("edit not found")
var ErrEntityNotFound = errors.New("entity not found")
var ErrEntityDeleted = errors.New("entity is deleted")
var ErrInvalidDraft = errors.New("invalid draft id")
var ErrInvalidImage = errors.New("invalid image id")
var ErrInvalidStudio = errors.New("invalid studio id")
var ErrInvalidPerformer = errors.New("invalid performer id")
var ErrInvalidTag = errors.New("invalid tag id")
var ErrInvalidSite = errors.New("invalid url site id")

type editEntity interface {
IsDeleted() bool
}

func validateEditEntity(entity *editEntity, id uuid.UUID, typeName string) error {
if entity == nil {
return fmt.Errorf("%w: %s %s", ErrEntityNotFound, typeName, id.String())
}
if (*entity).IsDeleted() {
return fmt.Errorf("%w: %s %s", ErrEntityDeleted, typeName, id.String())
}

return nil
}

func validateEditPresence(edit *models.Edit) error {
if edit == nil {
return ErrEditNotFound
}

if edit.Applied {
return ErrEditAlreadyApplied
}

return nil
}

func validateEditPrerequisites(fac models.Repo, edit *models.Edit) error {
var status models.VoteStatusEnum
utils.ResolveEnumString(edit.Status, &status)
if status != models.VoteStatusEnumPending {
return fmt.Errorf("%w: %s", ErrInvalidVoteStatus, edit.Status)
}

return nil
}

func validateSceneEditInput(fac models.Repo, input models.SceneEditInput) error {
if input.Details == nil {
return nil
}

if input.Details.DraftID != nil {
draft, err := fac.Draft().Find(*input.Details.DraftID)
if err != nil {
return err
}
if draft == nil {
return fmt.Errorf("%w: %s", ErrInvalidDraft, *input.Details.DraftID)
}
}
if input.Details.StudioID != nil {
draft, err := fac.Studio().Find(*input.Details.StudioID)
if err != nil {
return err
}
if draft == nil {
return fmt.Errorf("%w: %s", ErrInvalidStudio, *input.Details.StudioID)
}
}
if len(input.Details.ImageIds) > 0 {
images, errs := fac.Image().FindByIds(input.Details.ImageIds)
for i := range images {
if errs != nil && errs[i] != nil {
return errs[i]
}
if images[i] == nil {
return fmt.Errorf("%w: %s", ErrInvalidImage, input.Details.ImageIds[i])
}
}
}
if len(input.Details.TagIds) > 0 {
tags, errs := fac.Tag().FindByIds(input.Details.TagIds)
for i := range tags {
if errs != nil && errs[i] != nil {
return errs[i]
}
if tags[i] == nil {
return fmt.Errorf("%w: %s", ErrInvalidTag, input.Details.TagIds[i])
}
}
}
if len(input.Details.Performers) > 0 {
var ids []uuid.UUID
for _, appearance := range input.Details.Performers {
ids = append(ids, appearance.PerformerID)
}
performers, errs := fac.Performer().FindByIds(ids)
for i := range performers {
if errs != nil && errs[i] != nil {
return errs[i]
}
if performers[i] == nil {
return fmt.Errorf("%w: %s", ErrInvalidPerformer, ids[i])
}
}
}
if len(input.Details.Urls) > 0 {
var ids []uuid.UUID
for _, url := range input.Details.Urls {
ids = append(ids, url.SiteID)
}
sites, errs := fac.Site().FindByIds(ids)
for i := range sites {
if errs != nil && errs[i] != nil {
return errs[i]
}
if sites[i] == nil {
return fmt.Errorf("%w: %s", ErrInvalidSite, ids[i])
}
}
}
return nil
}

func validatePerformerEditInput(fac models.Repo, input models.PerformerEditInput) error {
if input.Details == nil {
return nil
}

if input.Details.DraftID != nil {
draft, err := fac.Draft().Find(*input.Details.DraftID)
if err != nil {
return err
}
if draft == nil {
return fmt.Errorf("%w: %s", ErrInvalidDraft, *input.Details.DraftID)
}
}
if len(input.Details.ImageIds) > 0 {
images, errs := fac.Image().FindByIds(input.Details.ImageIds)
for i := range images {
if errs != nil && errs[i] != nil {
return errs[i]
}
if images[i] == nil {
return fmt.Errorf("%w: %s", ErrInvalidImage, input.Details.ImageIds[i])
}
}
}
if len(input.Details.Urls) > 0 {
var ids []uuid.UUID
for _, url := range input.Details.Urls {
ids = append(ids, url.SiteID)
}
sites, errs := fac.Site().FindByIds(ids)
for i := range sites {
if errs != nil && errs[i] != nil {
return errs[i]
}
if sites[i] == nil {
return fmt.Errorf("%w: %s", ErrInvalidSite, ids[i])
}
}
}
return nil
}

func validateStudioEditInput(fac models.Repo, input models.StudioEditInput) error {
if input.Details == nil {
return nil
}

if input.Details.ParentID != nil {
draft, err := fac.Studio().Find(*input.Details.ParentID)
if err != nil {
return err
}
if draft == nil {
return fmt.Errorf("%w: %s", ErrInvalidStudio, *input.Details.ParentID)
}
}
if len(input.Details.ImageIds) > 0 {
images, errs := fac.Image().FindByIds(input.Details.ImageIds)
for i := range images {
if errs != nil && errs[i] != nil {
return errs[i]
}
if images[i] == nil {
return fmt.Errorf("%w: %s", ErrInvalidImage, input.Details.ImageIds[i])
}
}
}
if len(input.Details.Urls) > 0 {
var ids []uuid.UUID
for _, url := range input.Details.Urls {
ids = append(ids, url.SiteID)
}
sites, errs := fac.Site().FindByIds(ids)
for i := range sites {
if errs != nil && errs[i] != nil {
return errs[i]
}
if sites[i] == nil {
return fmt.Errorf("%w: %s", ErrInvalidSite, ids[i])
}
}
}
return nil
}

0 comments on commit 45ce233

Please sign in to comment.