diff options
Diffstat (limited to 'internal/db')
-rw-r--r-- | internal/db/bundb/status.go | 158 | ||||
-rw-r--r-- | internal/db/bundb/statusfave.go | 216 | ||||
-rw-r--r-- | internal/db/bundb/statusfave_test.go | 4 | ||||
-rw-r--r-- | internal/db/media.go | 4 | ||||
-rw-r--r-- | internal/db/status.go | 37 | ||||
-rw-r--r-- | internal/db/statusfave.go | 18 |
6 files changed, 231 insertions, 206 deletions
diff --git a/internal/db/bundb/status.go b/internal/db/bundb/status.go index 25b773dfa..c6091e2c9 100644 --- a/internal/db/bundb/status.go +++ b/internal/db/bundb/status.go @@ -20,7 +20,6 @@ package bundb import ( "container/list" "context" - "database/sql" "errors" "time" @@ -96,6 +95,26 @@ func (s *statusDB) GetStatusByURL(ctx context.Context, url string) (*gtsmodel.St ) } +func (s *statusDB) GetStatusBoost(ctx context.Context, boostOfID string, byAccountID string) (*gtsmodel.Status, error) { + return s.getStatus( + ctx, + "BoostOfID.AccountID", + func(status *gtsmodel.Status) error { + return s.newStatusQ(status). + Where("status.boost_of_id = ?", boostOfID). + Where("status.account_id = ?", byAccountID). + + // Our old code actually allowed a status to + // be boosted multiple times by the same author, + // so limit our query + order to fetch latest. + Order("status.id DESC"). // our IDs are timestamped + Limit(1). + Scan(ctx) + }, + boostOfID, byAccountID, + ) +} + func (s *statusDB) getStatus(ctx context.Context, lookup string, dbQuery func(*gtsmodel.Status) error, keyParts ...any) (*gtsmodel.Status, error) { // Fetch status from database cache with loader callback status, err := s.state.Caches.GTS.Status().Load(lookup, func() (*gtsmodel.Status, error) { @@ -245,11 +264,7 @@ func (s *statusDB) PopulateStatus(ctx context.Context, status *gtsmodel.Status) } } - if err := errs.Combine(); err != nil { - return gtserror.Newf("%w", err) - } - - return nil + return errs.Combine() } func (s *statusDB) PutStatus(ctx context.Context, status *gtsmodel.Status) error { @@ -506,25 +521,17 @@ func (s *statusDB) GetStatusChildren(ctx context.Context, status *gtsmodel.Statu } func (s *statusDB) statusChildren(ctx context.Context, status *gtsmodel.Status, foundStatuses *list.List, onlyDirect bool, minID string) { - var childIDs []string - - q := s.db. - NewSelect(). - TableExpr("? AS ?", bun.Ident("statuses"), bun.Ident("status")). - Column("status.id"). - Where("? = ?", bun.Ident("status.in_reply_to_id"), status.ID) - if minID != "" { - q = q.Where("? > ?", bun.Ident("status.id"), minID) - } - - if err := q.Scan(ctx, &childIDs); err != nil { - if err != sql.ErrNoRows { - log.Errorf(ctx, "error getting children for %q: %v", status.ID, err) - } + childIDs, err := s.getStatusReplyIDs(ctx, status.ID) + if err != nil && !errors.Is(err, db.ErrNoEntries) { + log.Errorf(ctx, "error getting status %s children: %v", status.ID, err) return } for _, id := range childIDs { + if id <= minID { + continue + } + // Fetch child with ID from database child, err := s.GetStatusByID(ctx, id) if err != nil { @@ -553,48 +560,80 @@ func (s *statusDB) statusChildren(ctx context.Context, status *gtsmodel.Status, } } -func (s *statusDB) CountStatusReplies(ctx context.Context, status *gtsmodel.Status) (int, error) { - return s.db. - NewSelect(). - TableExpr("? AS ?", bun.Ident("statuses"), bun.Ident("status")). - Where("? = ?", bun.Ident("status.in_reply_to_id"), status.ID). - Count(ctx) +func (s *statusDB) GetStatusReplies(ctx context.Context, statusID string) ([]*gtsmodel.Status, error) { + statusIDs, err := s.getStatusReplyIDs(ctx, statusID) + if err != nil { + return nil, err + } + return s.GetStatusesByIDs(ctx, statusIDs) } -func (s *statusDB) CountStatusReblogs(ctx context.Context, status *gtsmodel.Status) (int, error) { - return s.db. - NewSelect(). - TableExpr("? AS ?", bun.Ident("statuses"), bun.Ident("status")). - Where("? = ?", bun.Ident("status.boost_of_id"), status.ID). - Count(ctx) +func (s *statusDB) CountStatusReplies(ctx context.Context, statusID string) (int, error) { + statusIDs, err := s.getStatusReplyIDs(ctx, statusID) + return len(statusIDs), err } -func (s *statusDB) CountStatusFaves(ctx context.Context, status *gtsmodel.Status) (int, error) { - return s.db. - NewSelect(). - TableExpr("? AS ?", bun.Ident("status_faves"), bun.Ident("status_fave")). - Where("? = ?", bun.Ident("status_fave.status_id"), status.ID). - Count(ctx) +func (s *statusDB) getStatusReplyIDs(ctx context.Context, statusID string) ([]string, error) { + return s.state.Caches.GTS.InReplyToIDs().Load(statusID, func() ([]string, error) { + var statusIDs []string + + // Status reply IDs not in cache, perform DB query! + if err := s.db. + NewSelect(). + Table("statuses"). + Column("id"). + Where("? = ?", bun.Ident("in_reply_to_id"), statusID). + Order("id DESC"). + Scan(ctx, &statusIDs); err != nil { + return nil, s.db.ProcessError(err) + } + + return statusIDs, nil + }) } -func (s *statusDB) IsStatusFavedBy(ctx context.Context, status *gtsmodel.Status, accountID string) (bool, error) { - q := s.db. - NewSelect(). - TableExpr("? AS ?", bun.Ident("status_faves"), bun.Ident("status_fave")). - Where("? = ?", bun.Ident("status_fave.status_id"), status.ID). - Where("? = ?", bun.Ident("status_fave.account_id"), accountID) +func (s *statusDB) GetStatusBoosts(ctx context.Context, statusID string) ([]*gtsmodel.Status, error) { + statusIDs, err := s.getStatusBoostIDs(ctx, statusID) + if err != nil { + return nil, err + } + return s.GetStatusesByIDs(ctx, statusIDs) +} - return s.db.Exists(ctx, q) +func (s *statusDB) IsStatusBoostedBy(ctx context.Context, statusID string, accountID string) (bool, error) { + boost, err := s.GetStatusBoost( + gtscontext.SetBarebones(ctx), + statusID, + accountID, + ) + if err != nil && !errors.Is(err, db.ErrNoEntries) { + return false, err + } + return (boost != nil), nil } -func (s *statusDB) IsStatusRebloggedBy(ctx context.Context, status *gtsmodel.Status, accountID string) (bool, error) { - q := s.db. - NewSelect(). - TableExpr("? AS ?", bun.Ident("statuses"), bun.Ident("status")). - Where("? = ?", bun.Ident("status.boost_of_id"), status.ID). - Where("? = ?", bun.Ident("status.account_id"), accountID) +func (s *statusDB) CountStatusBoosts(ctx context.Context, statusID string) (int, error) { + statusIDs, err := s.getStatusBoostIDs(ctx, statusID) + return len(statusIDs), err +} - return s.db.Exists(ctx, q) +func (s *statusDB) getStatusBoostIDs(ctx context.Context, statusID string) ([]string, error) { + return s.state.Caches.GTS.BoostOfIDs().Load(statusID, func() ([]string, error) { + var statusIDs []string + + // Status boost IDs not in cache, perform DB query! + if err := s.db. + NewSelect(). + Table("statuses"). + Column("id"). + Where("? = ?", bun.Ident("boost_of_id"), statusID). + Order("id DESC"). + Scan(ctx, &statusIDs); err != nil { + return nil, s.db.ProcessError(err) + } + + return statusIDs, nil + }) } func (s *statusDB) IsStatusMutedBy(ctx context.Context, status *gtsmodel.Status, accountID string) (bool, error) { @@ -616,16 +655,3 @@ func (s *statusDB) IsStatusBookmarkedBy(ctx context.Context, status *gtsmodel.St return s.db.Exists(ctx, q) } - -func (s *statusDB) GetStatusReblogs(ctx context.Context, status *gtsmodel.Status) ([]*gtsmodel.Status, error) { - reblogs := []*gtsmodel.Status{} - - q := s. - newStatusQ(&reblogs). - Where("? = ?", bun.Ident("status.boost_of_id"), status.ID) - - if err := q.Scan(ctx); err != nil { - return nil, s.db.ProcessError(err) - } - return reblogs, nil -} diff --git a/internal/db/bundb/statusfave.go b/internal/db/bundb/statusfave.go index 7aff543fd..ab09fb1ba 100644 --- a/internal/db/bundb/statusfave.go +++ b/internal/db/bundb/statusfave.go @@ -19,6 +19,7 @@ package bundb import ( "context" + "database/sql" "errors" "fmt" @@ -44,8 +45,14 @@ func (s *statusFaveDB) GetStatusFave(ctx context.Context, accountID string, stat return s.db. NewSelect(). Model(fave). - Where("? = ?", bun.Ident("account_id"), accountID). - Where("? = ?", bun.Ident("status_id"), statusID). + Where("status_fave.account_id = ?", accountID). + Where("status_fave.status_id = ?", statusID). + + // Our old code actually allowed a status to + // be faved multiple times by the same author, + // so limit our query + order to fetch latest. + Order("status_fave.id DESC"). // our IDs are timestamped + Limit(1). Scan(ctx) }, accountID, @@ -89,63 +96,68 @@ func (s *statusFaveDB) getStatusFave(ctx context.Context, lookup string, dbQuery return fave, nil } - // Fetch the status fave author account. - fave.Account, err = s.state.DB.GetAccountByID( - gtscontext.SetBarebones(ctx), - fave.AccountID, - ) - if err != nil { - return nil, fmt.Errorf("error getting status fave account %q: %w", fave.AccountID, err) - } - - // Fetch the status fave target account. - fave.TargetAccount, err = s.state.DB.GetAccountByID( - gtscontext.SetBarebones(ctx), - fave.TargetAccountID, - ) - if err != nil { - return nil, fmt.Errorf("error getting status fave target account %q: %w", fave.TargetAccountID, err) - } - - // Fetch the status fave target status. - fave.Status, err = s.state.DB.GetStatusByID( - gtscontext.SetBarebones(ctx), - fave.StatusID, - ) - if err != nil { - return nil, fmt.Errorf("error getting status fave status %q: %w", fave.StatusID, err) + // Populate the status favourite model. + if err := s.PopulateStatusFave(ctx, fave); err != nil { + return nil, fmt.Errorf("error(s) populating status fave: %w", err) } return fave, nil } -func (s *statusFaveDB) GetStatusFavesForStatus(ctx context.Context, statusID string) ([]*gtsmodel.StatusFave, error) { - ids := []string{} - - if err := s.db. - NewSelect(). - Table("status_faves"). - Column("id"). - Where("? = ?", bun.Ident("status_id"), statusID). - Scan(ctx, &ids); err != nil { - return nil, s.db.ProcessError(err) +func (s *statusFaveDB) GetStatusFaves(ctx context.Context, statusID string) ([]*gtsmodel.StatusFave, error) { + // Fetch the status fave IDs for status. + faveIDs, err := s.getStatusFaveIDs(ctx, statusID) + if err != nil { + return nil, err } - faves := make([]*gtsmodel.StatusFave, 0, len(ids)) + // Preallocate a slice of expected status fave capacity. + faves := make([]*gtsmodel.StatusFave, 0, len(faveIDs)) - for _, id := range ids { + for _, id := range faveIDs { + // Fetch status fave model for each ID. fave, err := s.GetStatusFaveByID(ctx, id) if err != nil { log.Errorf(ctx, "error getting status fave %q: %v", id, err) continue } - faves = append(faves, fave) } return faves, nil } +func (s *statusFaveDB) IsStatusFavedBy(ctx context.Context, statusID string, accountID string) (bool, error) { + fave, err := s.GetStatusFave(ctx, accountID, statusID) + if err != nil && !errors.Is(err, db.ErrNoEntries) { + return false, err + } + return (fave != nil), nil +} + +func (s *statusFaveDB) CountStatusFaves(ctx context.Context, statusID string) (int, error) { + faveIDs, err := s.getStatusFaveIDs(ctx, statusID) + return len(faveIDs), err +} + +func (s *statusFaveDB) getStatusFaveIDs(ctx context.Context, statusID string) ([]string, error) { + return s.state.Caches.GTS.StatusFaveIDs().Load(statusID, func() ([]string, error) { + var faveIDs []string + + // Status fave IDs not in cache, perform DB query! + if err := s.db. + NewSelect(). + Table("status_faves"). + Column("id"). + Where("? = ?", bun.Ident("status_id"), statusID). + Scan(ctx, &faveIDs); err != nil { + return nil, s.db.ProcessError(err) + } + + return faveIDs, nil + }) +} + func (s *statusFaveDB) PopulateStatusFave(ctx context.Context, statusFave *gtsmodel.StatusFave) error { var ( err error @@ -203,26 +215,32 @@ func (s *statusFaveDB) PutStatusFave(ctx context.Context, fave *gtsmodel.StatusF } func (s *statusFaveDB) DeleteStatusFaveByID(ctx context.Context, id string) error { - defer s.state.Caches.GTS.StatusFave().Invalidate("ID", id) + var statusID string - // Load fave into cache before attempting a delete, - // as we need it cached in order to trigger the invalidate - // callback. This in turn invalidates others. - _, err := s.GetStatusFaveByID(gtscontext.SetBarebones(ctx), id) - if err != nil { - if errors.Is(err, db.ErrNoEntries) { - // not an issue. + // Perform DELETE on status fave, + // returning the status ID it was for. + if _, err := s.db.NewDelete(). + Table("status_faves"). + Where("id = ?", id). + Returning("status_id"). + Exec(ctx, &statusID); err != nil { + if err == sql.ErrNoRows { + // Not an issue, only due + // to us doing a RETURNING. err = nil } - return err + return s.db.ProcessError(err) } - // Finally delete fave from DB. - _, err = s.db.NewDelete(). - Table("status_faves"). - Where("? = ?", bun.Ident("id"), id). - Exec(ctx) - return s.db.ProcessError(err) + if statusID != "" { + // Invalidate any cached status faves for this status. + s.state.Caches.GTS.StatusFave().Invalidate("ID", id) + + // Invalidate any cached status fave IDs for this status. + s.state.Caches.GTS.StatusFaveIDs().Invalidate(statusID) + } + + return nil } func (s *statusFaveDB) DeleteStatusFaves(ctx context.Context, targetAccountID string, originAccountID string) error { @@ -230,12 +248,13 @@ func (s *statusFaveDB) DeleteStatusFaves(ctx context.Context, targetAccountID st return errors.New("DeleteStatusFaves: one of targetAccountID or originAccountID must be set") } - var faveIDs []string + var statusIDs []string - q := s.db. - NewSelect(). - Column("id"). - Table("status_faves") + // Prepare DELETE query returning + // the deleted faves for status IDs. + q := s.db.NewDelete(). + Table("status_faves"). + Returning("status_id") if targetAccountID != "" { q = q.Where("? = ?", bun.Ident("target_account_id"), targetAccountID) @@ -245,69 +264,46 @@ func (s *statusFaveDB) DeleteStatusFaves(ctx context.Context, targetAccountID st q = q.Where("? = ?", bun.Ident("account_id"), originAccountID) } - if _, err := q.Exec(ctx, &faveIDs); err != nil { + // Execute query, store favourited status IDs. + if _, err := q.Exec(ctx, &statusIDs); err != nil { + if err == sql.ErrNoRows { + // Not an issue, only due + // to us doing a RETURNING. + err = nil + } return s.db.ProcessError(err) } - defer func() { - // Invalidate all IDs on return. - for _, id := range faveIDs { - s.state.Caches.GTS.StatusFave().Invalidate("ID", id) - } - }() + // Collate (deduplicating) status IDs. + statusIDs = collate(func(i int) string { + return statusIDs[i] + }, len(statusIDs)) - // Load all faves into cache, this *really* isn't great - // but it is the only way we can ensure we invalidate all - // related caches correctly (e.g. visibility). - for _, id := range faveIDs { - _, err := s.GetStatusFaveByID(ctx, id) - if err != nil && !errors.Is(err, db.ErrNoEntries) { - return err - } + for _, id := range statusIDs { + // Invalidate any cached status faves for this status. + s.state.Caches.GTS.StatusFave().Invalidate("ID", id) + + // Invalidate any cached status fave IDs for this status. + s.state.Caches.GTS.StatusFaveIDs().Invalidate(id) } - // Finally delete all from DB. - _, err := s.db.NewDelete(). - Table("status_faves"). - Where("? IN (?)", bun.Ident("id"), bun.In(faveIDs)). - Exec(ctx) - return s.db.ProcessError(err) + return nil } func (s *statusFaveDB) DeleteStatusFavesForStatus(ctx context.Context, statusID string) error { - // Capture fave IDs in a RETURNING statement. - var faveIDs []string - - q := s.db. - NewSelect(). - Column("id"). + // Delete all status faves for status. + if _, err := s.db.NewDelete(). Table("status_faves"). - Where("? = ?", bun.Ident("status_id"), statusID) - if _, err := q.Exec(ctx, &faveIDs); err != nil { + Where("status_id = ?", statusID). + Exec(ctx); err != nil { return s.db.ProcessError(err) } - defer func() { - // Invalidate all IDs on return. - for _, id := range faveIDs { - s.state.Caches.GTS.StatusFave().Invalidate("ID", id) - } - }() + // Invalidate any cached status faves for this status. + s.state.Caches.GTS.StatusFave().Invalidate("ID", statusID) - // Load all faves into cache, this *really* isn't great - // but it is the only way we can ensure we invalidate all - // related caches correctly (e.g. visibility). - for _, id := range faveIDs { - _, err := s.GetStatusFaveByID(ctx, id) - if err != nil && !errors.Is(err, db.ErrNoEntries) { - return err - } - } + // Invalidate any cached status fave IDs for this status. + s.state.Caches.GTS.StatusFaveIDs().Invalidate(statusID) - // Finally delete all from DB. - _, err := s.db.NewDelete(). - Table("status_faves"). - Where("? IN (?)", bun.Ident("id"), bun.In(faveIDs)). - Exec(ctx) - return s.db.ProcessError(err) + return nil } diff --git a/internal/db/bundb/statusfave_test.go b/internal/db/bundb/statusfave_test.go index 7218390bc..9c99d795b 100644 --- a/internal/db/bundb/statusfave_test.go +++ b/internal/db/bundb/statusfave_test.go @@ -35,7 +35,7 @@ type StatusFaveTestSuite struct { func (suite *StatusFaveTestSuite) TestGetStatusFaves() { testStatus := suite.testStatuses["admin_account_status_1"] - faves, err := suite.db.GetStatusFavesForStatus(context.Background(), testStatus.ID) + faves, err := suite.db.GetStatusFaves(context.Background(), testStatus.ID) if err != nil { suite.FailNow(err.Error()) } @@ -51,7 +51,7 @@ func (suite *StatusFaveTestSuite) TestGetStatusFaves() { func (suite *StatusFaveTestSuite) TestGetStatusFavesNone() { testStatus := suite.testStatuses["admin_account_status_4"] - faves, err := suite.db.GetStatusFavesForStatus(context.Background(), testStatus.ID) + faves, err := suite.db.GetStatusFaves(context.Background(), testStatus.ID) if err != nil { suite.FailNow(err.Error()) } diff --git a/internal/db/media.go b/internal/db/media.go index 66fa258fe..94a365c26 100644 --- a/internal/db/media.go +++ b/internal/db/media.go @@ -41,10 +41,10 @@ type Media interface { // DeleteAttachment deletes the attachment with given ID from the database. DeleteAttachment(ctx context.Context, id string) error - // GetAttachments ... + // GetAttachments fetches media attachments up to a given max ID, and at most limit. GetAttachments(ctx context.Context, maxID string, limit int) ([]*gtsmodel.MediaAttachment, error) - // GetRemoteAttachments ... + // GetRemoteAttachments fetches media attachments with a non-empty domain, up to a given max ID, and at most limit. GetRemoteAttachments(ctx context.Context, maxID string, limit int) ([]*gtsmodel.MediaAttachment, error) // GetCachedAttachmentsOlderThan gets limit n remote attachments (including avatars and headers) older than diff --git a/internal/db/status.go b/internal/db/status.go index 6f9848f57..f4421fa2e 100644 --- a/internal/db/status.go +++ b/internal/db/status.go @@ -34,6 +34,9 @@ type Status interface { // GetStatusByURL returns one status from the database, with no rel fields populated, only their linking ID / URIs GetStatusByURL(ctx context.Context, uri string) (*gtsmodel.Status, error) + // GetStatusBoost fetches the status whose boost_of_id column refers to boostOfID, authored by given account ID. + GetStatusBoost(ctx context.Context, boostOfID string, byAccountID string) (*gtsmodel.Status, error) + // PopulateStatus ensures that all sub-models of a status are populated (e.g. mentions, attachments, etc). PopulateStatus(ctx context.Context, status *gtsmodel.Status) error @@ -46,21 +49,27 @@ type Status interface { // DeleteStatusByID deletes one status from the database. DeleteStatusByID(ctx context.Context, id string) error - // CountStatusReplies returns the amount of replies recorded for a status, or an error if something goes wrong - CountStatusReplies(ctx context.Context, status *gtsmodel.Status) (int, error) - - // CountStatusReblogs returns the amount of reblogs/boosts recorded for a status, or an error if something goes wrong - CountStatusReblogs(ctx context.Context, status *gtsmodel.Status) (int, error) - - // CountStatusFaves returns the amount of faves/likes recorded for a status, or an error if something goes wrong - CountStatusFaves(ctx context.Context, status *gtsmodel.Status) (int, error) - // GetStatuses gets a slice of statuses corresponding to the given status IDs. GetStatusesByIDs(ctx context.Context, ids []string) ([]*gtsmodel.Status, error) // GetStatusesUsingEmoji fetches all status models using emoji with given ID stored in their 'emojis' column. GetStatusesUsingEmoji(ctx context.Context, emojiID string) ([]*gtsmodel.Status, error) + // GetStatusReplies returns the *direct* (i.e. in_reply_to_id column) replies to this status ID. + GetStatusReplies(ctx context.Context, statusID string) ([]*gtsmodel.Status, error) + + // CountStatusReplies returns the number of stored *direct* (i.e. in_reply_to_id column) replies to this status ID. + CountStatusReplies(ctx context.Context, statusID string) (int, error) + + // GetStatusBoosts returns all statuses whose boost_of_id column refer to given status ID. + GetStatusBoosts(ctx context.Context, statusID string) ([]*gtsmodel.Status, error) + + // CountStatusBoosts returns the number of stored boosts for status ID. + CountStatusBoosts(ctx context.Context, statusID string) (int, error) + + // IsStatusBoostedBy checks whether the given status ID is boosted by account ID. + IsStatusBoostedBy(ctx context.Context, statusID string, accountID string) (bool, error) + // GetStatusParents gets the parent statuses of a given status. // // If onlyDirect is true, only the immediate parent will be returned. @@ -71,19 +80,9 @@ type Status interface { // If onlyDirect is true, only the immediate children will be returned. GetStatusChildren(ctx context.Context, status *gtsmodel.Status, onlyDirect bool, minID string) ([]*gtsmodel.Status, error) - // IsStatusFavedBy checks if a given status has been faved by a given account ID - IsStatusFavedBy(ctx context.Context, status *gtsmodel.Status, accountID string) (bool, error) - - // IsStatusRebloggedBy checks if a given status has been reblogged/boosted by a given account ID - IsStatusRebloggedBy(ctx context.Context, status *gtsmodel.Status, accountID string) (bool, error) - // IsStatusMutedBy checks if a given status has been muted by a given account ID IsStatusMutedBy(ctx context.Context, status *gtsmodel.Status, accountID string) (bool, error) // IsStatusBookmarkedBy checks if a given status has been bookmarked by a given account ID IsStatusBookmarkedBy(ctx context.Context, status *gtsmodel.Status, accountID string) (bool, error) - - // GetStatusReblogs returns a slice of statuses that are a boost/reblog of the given status. - // This slice will be unfiltered, not taking account of blocks and whatnot, so filter it before serving it back to a user. - GetStatusReblogs(ctx context.Context, status *gtsmodel.Status) ([]*gtsmodel.Status, error) } diff --git a/internal/db/statusfave.go b/internal/db/statusfave.go index 37769ff79..343a80caa 100644 --- a/internal/db/statusfave.go +++ b/internal/db/statusfave.go @@ -24,16 +24,15 @@ import ( ) type StatusFave interface { - // GetStatusFaveByAccountID gets one status fave created by the given - // accountID, targeting the given statusID. + // GetStatusFaveByAccountID gets one status fave created by the given accountID, targeting the given statusID. GetStatusFave(ctx context.Context, accountID string, statusID string) (*gtsmodel.StatusFave, error) // GetStatusFave returns one status fave with the given id. GetStatusFaveByID(ctx context.Context, id string) (*gtsmodel.StatusFave, error) - // GetStatusFaves returns a slice of faves/likes of the given status. + // GetStatusFaves returns a slice of faves/likes of the status with given ID. // This slice will be unfiltered, not taking account of blocks and whatnot, so filter it before serving it back to a user. - GetStatusFavesForStatus(ctx context.Context, statusID string) ([]*gtsmodel.StatusFave, error) + GetStatusFaves(ctx context.Context, statusID string) ([]*gtsmodel.StatusFave, error) // PopulateStatusFave ensures that all sub-models of a fave are populated (account, status, etc). PopulateStatusFave(ctx context.Context, statusFave *gtsmodel.StatusFave) error @@ -59,8 +58,13 @@ type StatusFave interface { // At least one parameter must not be an empty string. DeleteStatusFaves(ctx context.Context, targetAccountID string, originAccountID string) error - // DeleteStatusFavesForStatus deletes all status faves that target the - // given status ID. This is useful when a status has been deleted, and you need - // to clean up after it. + // DeleteStatusFavesForStatus deletes all status faves that target the given status ID. + // This is useful when a status has been deleted, and you need to clean up after it. DeleteStatusFavesForStatus(ctx context.Context, statusID string) error + + // CountStatusFaves returns the number of status favourites registered for status with ID. + CountStatusFaves(ctx context.Context, statusID string) (int, error) + + // IsStatusFavedBy returns whether the status with ID has been favourited by account with ID. + IsStatusFavedBy(ctx context.Context, statusID string, accountID string) (bool, error) } |