From af1be28c2bdc572c470b37d7eb0843a3f6d6795c Mon Sep 17 00:00:00 2001 From: kim Date: Wed, 11 Sep 2024 23:30:36 +0100 Subject: [PATCH] fix nil panic in AddToList() --- internal/processing/list/updateentries.go | 28 ++++++++++++++++++----- internal/util/unique.go | 12 ++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/internal/processing/list/updateentries.go b/internal/processing/list/updateentries.go index bd5125ef1..b876695be 100644 --- a/internal/processing/list/updateentries.go +++ b/internal/processing/list/updateentries.go @@ -52,8 +52,8 @@ func (p *Processor) AddToList(ctx context.Context, account *gtsmodel.Account, li return gtserror.NewErrorInternalError(err) } - // Convert the follows to a map keyed by the target account ID. - followsMap := util.KeyBy(follows, func(follow *gtsmodel.Follow) string { + // Convert the follows to a hash set containing the target account IDs. + inFollows := util.ToSetFunc(follows, func(follow *gtsmodel.Follow) string { return follow.TargetAccountID }) @@ -65,14 +65,30 @@ func (p *Processor) AddToList(ctx context.Context, account *gtsmodel.Account, li // Iterate all the account IDs in given target list. for _, targetAccountID := range targetAccountIDs { - // Look for existing follow targetting ID. - follow, ok := followsMap[targetAccountID] - - if ok { + // Look for follow to target account. + if inFollows.Has(targetAccountID) { text := fmt.Sprintf("account %s is already in list %s", targetAccountID, listID) return gtserror.NewErrorUnprocessableEntity(errors.New(text), text) } + // Get the actual follow to target. + follow, err := p.state.DB.GetFollow( + + // We don't need any sub-models. + gtscontext.SetBarebones(ctx), + account.ID, + targetAccountID, + ) + if err != nil && !errors.Is(err, db.ErrNoEntries) { + err := gtserror.Newf("db error getting follow: %w", err) + return gtserror.NewErrorInternalError(err) + } + + if follow == nil { + text := fmt.Sprintf("account %s not currently followed", targetAccountID) + return gtserror.NewErrorUnprocessableEntity(errors.New(text), text) + } + // Generate new entry for this follow in list. entries = append(entries, >smodel.ListEntry{ ID: id.NewULID(), diff --git a/internal/util/unique.go b/internal/util/unique.go index 58abc21d3..68c1d1235 100644 --- a/internal/util/unique.go +++ b/internal/util/unique.go @@ -42,6 +42,18 @@ func ToSet[T comparable](in []T) Set[T] { return set } +// ToSetFunc creates a Set[T] from input slice, keys provided by func. +func ToSetFunc[S any, T comparable](in []S, key func(S) T) Set[T] { + if key == nil { + panic("nil func") + } + set := make(Set[T], len(in)) + for _, v := range in { + set[key(v)] = struct{}{} + } + return set +} + // FromSet extracts the values from set to slice. func FromSet[T comparable](in Set[T]) []T { out := make([]T, len(in))