summaryrefslogtreecommitdiff
path: root/internal/processing
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2022-10-10 15:52:49 +0200
committerLibravatar GitHub <noreply@github.com>2022-10-10 15:52:49 +0200
commit832befd727a257581d774bb6bbdeb0f81cd658e2 (patch)
treeb1fb78172b18f262ded37fd110bc8be318af233d /internal/processing
parent[feature] Add opt-in RSS feed for account's latest Public posts (#897) (diff)
downloadgotosocial-832befd727a257581d774bb6bbdeb0f81cd658e2.tar.xz
[chore] Make paging logic more generic (#901)
* make paging logic more generic not just for timelines! * linty linterson
Diffstat (limited to 'internal/processing')
-rw-r--r--internal/processing/account.go4
-rw-r--r--internal/processing/account/account.go4
-rw-r--r--internal/processing/account/getstatuses.go73
-rw-r--r--internal/processing/notification.go36
-rw-r--r--internal/processing/processor.go12
-rw-r--r--internal/processing/statustimeline.go82
6 files changed, 134 insertions, 77 deletions
diff --git a/internal/processing/account.go b/internal/processing/account.go
index 6cba8b9c4..94f0dac89 100644
--- a/internal/processing/account.go
+++ b/internal/processing/account.go
@@ -55,11 +55,11 @@ func (p *processor) AccountUpdate(ctx context.Context, authed *oauth.Auth, form
return p.accountProcessor.Update(ctx, authed.Account, form)
}
-func (p *processor) AccountStatusesGet(ctx context.Context, authed *oauth.Auth, targetAccountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, pinnedOnly bool, mediaOnly bool, publicOnly bool) (*apimodel.TimelineResponse, gtserror.WithCode) {
+func (p *processor) AccountStatusesGet(ctx context.Context, authed *oauth.Auth, targetAccountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, pinnedOnly bool, mediaOnly bool, publicOnly bool) (*apimodel.PageableResponse, gtserror.WithCode) {
return p.accountProcessor.StatusesGet(ctx, authed.Account, targetAccountID, limit, excludeReplies, excludeReblogs, maxID, minID, pinnedOnly, mediaOnly, publicOnly)
}
-func (p *processor) AccountWebStatusesGet(ctx context.Context, targetAccountID string, maxID string) (*apimodel.TimelineResponse, gtserror.WithCode) {
+func (p *processor) AccountWebStatusesGet(ctx context.Context, targetAccountID string, maxID string) (*apimodel.PageableResponse, gtserror.WithCode) {
return p.accountProcessor.WebStatusesGet(ctx, targetAccountID, maxID)
}
diff --git a/internal/processing/account/account.go b/internal/processing/account/account.go
index b18e705ca..d2c3cddbe 100644
--- a/internal/processing/account/account.go
+++ b/internal/processing/account/account.go
@@ -60,10 +60,10 @@ type Processor interface {
Update(ctx context.Context, account *gtsmodel.Account, form *apimodel.UpdateCredentialsRequest) (*apimodel.Account, gtserror.WithCode)
// StatusesGet fetches a number of statuses (in time descending order) from the given account, filtered by visibility for
// the account given in authed.
- StatusesGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, pinned bool, mediaOnly bool, publicOnly bool) (*apimodel.TimelineResponse, gtserror.WithCode)
+ StatusesGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, pinned bool, mediaOnly bool, publicOnly bool) (*apimodel.PageableResponse, gtserror.WithCode)
// WebStatusesGet fetches a number of statuses (in descending order) from the given account. It selects only
// statuses which are suitable for showing on the public web profile of an account.
- WebStatusesGet(ctx context.Context, targetAccountID string, maxID string) (*apimodel.TimelineResponse, gtserror.WithCode)
+ WebStatusesGet(ctx context.Context, targetAccountID string, maxID string) (*apimodel.PageableResponse, gtserror.WithCode)
// FollowersGet fetches a list of the target account's followers.
FollowersGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string) ([]apimodel.Account, gtserror.WithCode)
// FollowingGet fetches a list of the accounts that target account is following.
diff --git a/internal/processing/account/getstatuses.go b/internal/processing/account/getstatuses.go
index 7dbe68462..9c78917c3 100644
--- a/internal/processing/account/getstatuses.go
+++ b/internal/processing/account/getstatuses.go
@@ -26,11 +26,10 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/timeline"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
-func (p *processor) StatusesGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, pinnedOnly bool, mediaOnly bool, publicOnly bool) (*apimodel.TimelineResponse, gtserror.WithCode) {
+func (p *processor) StatusesGet(ctx context.Context, requestingAccount *gtsmodel.Account, targetAccountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, pinnedOnly bool, mediaOnly bool, publicOnly bool) (*apimodel.PageableResponse, gtserror.WithCode) {
if requestingAccount != nil {
if blocked, err := p.db.IsBlocked(ctx, requestingAccount.ID, targetAccountID, true); err != nil {
return nil, gtserror.NewErrorInternalError(err)
@@ -42,7 +41,7 @@ func (p *processor) StatusesGet(ctx context.Context, requestingAccount *gtsmodel
statuses, err := p.db.GetAccountStatuses(ctx, targetAccountID, limit, excludeReplies, excludeReblogs, maxID, minID, pinnedOnly, mediaOnly, publicOnly)
if err != nil {
if err == db.ErrNoEntries {
- return util.EmptyTimelineResponse(), nil
+ return util.EmptyPageableResponse(), nil
}
return nil, gtserror.NewErrorInternalError(err)
}
@@ -55,25 +54,37 @@ func (p *processor) StatusesGet(ctx context.Context, requestingAccount *gtsmodel
}
}
- if len(filtered) == 0 {
- return util.EmptyTimelineResponse(), nil
+ count := len(filtered)
+
+ if count == 0 {
+ return util.EmptyPageableResponse(), nil
}
- timelineables := []timeline.Timelineable{}
- for _, i := range filtered {
- apiStatus, err := p.tc.StatusToAPIStatus(ctx, i, requestingAccount)
+ items := []interface{}{}
+ nextMaxIDValue := ""
+ prevMinIDValue := ""
+ for i, s := range filtered {
+ item, err := p.tc.StatusToAPIStatus(ctx, s, requestingAccount)
if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status to api: %s", err))
}
- timelineables = append(timelineables, apiStatus)
+ if i == count-1 {
+ nextMaxIDValue = item.GetID()
+ }
+
+ if i == 0 {
+ prevMinIDValue = item.GetID()
+ }
+
+ items = append(items, item)
}
- return util.PackageTimelineableResponse(util.TimelineableResponseParams{
- Items: timelineables,
+ return util.PackagePageableResponse(util.PageableResponseParams{
+ Items: items,
Path: fmt.Sprintf("/api/v1/accounts/%s/statuses", targetAccountID),
- NextMaxIDValue: timelineables[len(timelineables)-1].GetID(),
- PrevMinIDValue: timelineables[0].GetID(),
+ NextMaxIDValue: nextMaxIDValue,
+ PrevMinIDValue: prevMinIDValue,
Limit: limit,
ExtraQueryParams: []string{
fmt.Sprintf("exclude_replies=%t", excludeReplies),
@@ -85,7 +96,7 @@ func (p *processor) StatusesGet(ctx context.Context, requestingAccount *gtsmodel
})
}
-func (p *processor) WebStatusesGet(ctx context.Context, targetAccountID string, maxID string) (*apimodel.TimelineResponse, gtserror.WithCode) {
+func (p *processor) WebStatusesGet(ctx context.Context, targetAccountID string, maxID string) (*apimodel.PageableResponse, gtserror.WithCode) {
acct, err := p.db.GetAccountByID(ctx, targetAccountID)
if err != nil {
if err == db.ErrNoEntries {
@@ -103,26 +114,42 @@ func (p *processor) WebStatusesGet(ctx context.Context, targetAccountID string,
statuses, err := p.db.GetAccountWebStatuses(ctx, targetAccountID, 10, maxID)
if err != nil {
if err == db.ErrNoEntries {
- return util.EmptyTimelineResponse(), nil
+ return util.EmptyPageableResponse(), nil
}
return nil, gtserror.NewErrorInternalError(err)
}
- timelineables := []timeline.Timelineable{}
- for _, i := range statuses {
- apiStatus, err := p.tc.StatusToAPIStatus(ctx, i, nil)
+ count := len(statuses)
+
+ if count == 0 {
+ return util.EmptyPageableResponse(), nil
+ }
+
+ items := []interface{}{}
+ nextMaxIDValue := ""
+ prevMinIDValue := ""
+ for i, s := range statuses {
+ item, err := p.tc.StatusToAPIStatus(ctx, s, nil)
if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status to api: %s", err))
}
- timelineables = append(timelineables, apiStatus)
+ if i == count-1 {
+ nextMaxIDValue = item.GetID()
+ }
+
+ if i == 0 {
+ prevMinIDValue = item.GetID()
+ }
+
+ items = append(items, item)
}
- return util.PackageTimelineableResponse(util.TimelineableResponseParams{
- Items: timelineables,
+ return util.PackagePageableResponse(util.PageableResponseParams{
+ Items: items,
Path: "/@" + acct.Username,
- NextMaxIDValue: timelineables[len(timelineables)-1].GetID(),
- PrevMinIDValue: timelineables[0].GetID(),
+ NextMaxIDValue: nextMaxIDValue,
+ PrevMinIDValue: prevMinIDValue,
ExtraQueryParams: []string{},
})
}
diff --git a/internal/processing/notification.go b/internal/processing/notification.go
index fd2651d4a..7e748bdc3 100644
--- a/internal/processing/notification.go
+++ b/internal/processing/notification.go
@@ -25,36 +25,48 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
- "github.com/superseriousbusiness/gotosocial/internal/timeline"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
-func (p *processor) NotificationsGet(ctx context.Context, authed *oauth.Auth, excludeTypes []string, limit int, maxID string, sinceID string) (*apimodel.TimelineResponse, gtserror.WithCode) {
+func (p *processor) NotificationsGet(ctx context.Context, authed *oauth.Auth, excludeTypes []string, limit int, maxID string, sinceID string) (*apimodel.PageableResponse, gtserror.WithCode) {
notifs, err := p.db.GetNotifications(ctx, authed.Account.ID, excludeTypes, limit, maxID, sinceID)
if err != nil {
return nil, gtserror.NewErrorInternalError(err)
}
- if len(notifs) == 0 {
- return util.EmptyTimelineResponse(), nil
+ count := len(notifs)
+
+ if count == 0 {
+ return util.EmptyPageableResponse(), nil
}
- timelineables := []timeline.Timelineable{}
- for _, n := range notifs {
- apiNotif, err := p.tc.NotificationToAPINotification(ctx, n)
+ items := []interface{}{}
+ nextMaxIDValue := ""
+ prevMinIDValue := ""
+ for i, n := range notifs {
+ item, err := p.tc.NotificationToAPINotification(ctx, n)
if err != nil {
log.Debugf("got an error converting a notification to api, will skip it: %s", err)
continue
}
- timelineables = append(timelineables, apiNotif)
+
+ if i == count-1 {
+ nextMaxIDValue = item.GetID()
+ }
+
+ if i == 0 {
+ prevMinIDValue = item.GetID()
+ }
+
+ items = append(items, item)
}
- return util.PackageTimelineableResponse(util.TimelineableResponseParams{
- Items: timelineables,
+ return util.PackagePageableResponse(util.PageableResponseParams{
+ Items: items,
Path: "api/v1/notifications",
- NextMaxIDValue: timelineables[len(timelineables)-1].GetID(),
+ NextMaxIDValue: nextMaxIDValue,
PrevMinIDKey: "since_id",
- PrevMinIDValue: timelineables[0].GetID(),
+ PrevMinIDValue: prevMinIDValue,
Limit: limit,
})
}
diff --git a/internal/processing/processor.go b/internal/processing/processor.go
index 09bb579ba..c76b1623b 100644
--- a/internal/processing/processor.go
+++ b/internal/processing/processor.go
@@ -89,10 +89,10 @@ type Processor interface {
AccountUpdate(ctx context.Context, authed *oauth.Auth, form *apimodel.UpdateCredentialsRequest) (*apimodel.Account, gtserror.WithCode)
// AccountStatusesGet fetches a number of statuses (in time descending order) from the given account, filtered by visibility for
// the account given in authed.
- AccountStatusesGet(ctx context.Context, authed *oauth.Auth, targetAccountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, pinned bool, mediaOnly bool, publicOnly bool) (*apimodel.TimelineResponse, gtserror.WithCode)
+ AccountStatusesGet(ctx context.Context, authed *oauth.Auth, targetAccountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, pinned bool, mediaOnly bool, publicOnly bool) (*apimodel.PageableResponse, gtserror.WithCode)
// AccountWebStatusesGet fetches a number of statuses (in descending order) from the given account. It selects only
// statuses which are suitable for showing on the public web profile of an account.
- AccountWebStatusesGet(ctx context.Context, targetAccountID string, maxID string) (*apimodel.TimelineResponse, gtserror.WithCode)
+ AccountWebStatusesGet(ctx context.Context, targetAccountID string, maxID string) (*apimodel.PageableResponse, gtserror.WithCode)
// AccountFollowersGet fetches a list of the target account's followers.
AccountFollowersGet(ctx context.Context, authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, gtserror.WithCode)
// AccountFollowingGet fetches a list of the accounts that target account is following.
@@ -160,7 +160,7 @@ type Processor interface {
MediaUpdate(ctx context.Context, authed *oauth.Auth, attachmentID string, form *apimodel.AttachmentUpdateRequest) (*apimodel.Attachment, gtserror.WithCode)
// NotificationsGet
- NotificationsGet(ctx context.Context, authed *oauth.Auth, excludeTypes []string, limit int, maxID string, sinceID string) (*apimodel.TimelineResponse, gtserror.WithCode)
+ NotificationsGet(ctx context.Context, authed *oauth.Auth, excludeTypes []string, limit int, maxID string, sinceID string) (*apimodel.PageableResponse, gtserror.WithCode)
// NotificationsClear
NotificationsClear(ctx context.Context, authed *oauth.Auth) gtserror.WithCode
@@ -192,11 +192,11 @@ type Processor interface {
StatusGetContext(ctx context.Context, authed *oauth.Auth, targetStatusID string) (*apimodel.Context, gtserror.WithCode)
// HomeTimelineGet returns statuses from the home timeline, with the given filters/parameters.
- HomeTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) (*apimodel.TimelineResponse, gtserror.WithCode)
+ HomeTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) (*apimodel.PageableResponse, gtserror.WithCode)
// PublicTimelineGet returns statuses from the public/local timeline, with the given filters/parameters.
- PublicTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) (*apimodel.TimelineResponse, gtserror.WithCode)
+ PublicTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) (*apimodel.PageableResponse, gtserror.WithCode)
// FavedTimelineGet returns faved statuses, with the given filters/parameters.
- FavedTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, minID string, limit int) (*apimodel.TimelineResponse, gtserror.WithCode)
+ FavedTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, minID string, limit int) (*apimodel.PageableResponse, gtserror.WithCode)
// AuthorizeStreamingRequest returns a gotosocial account in exchange for an access token, or an error if the given token is not valid.
AuthorizeStreamingRequest(ctx context.Context, accessToken string) (*gtsmodel.Account, gtserror.WithCode)
diff --git a/internal/processing/statustimeline.go b/internal/processing/statustimeline.go
index b7313818c..4491308f3 100644
--- a/internal/processing/statustimeline.go
+++ b/internal/processing/statustimeline.go
@@ -137,40 +137,47 @@ func StatusSkipInsertFunction() timeline.SkipInsertFunction {
}
}
-func (p *processor) HomeTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) (*apimodel.TimelineResponse, gtserror.WithCode) {
+func (p *processor) HomeTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) (*apimodel.PageableResponse, gtserror.WithCode) {
preparedItems, err := p.statusTimelines.GetTimeline(ctx, authed.Account.ID, maxID, sinceID, minID, limit, local)
if err != nil {
return nil, gtserror.NewErrorInternalError(err)
}
- if len(preparedItems) == 0 {
- return util.EmptyTimelineResponse(), nil
+ count := len(preparedItems)
+
+ if count == 0 {
+ return util.EmptyPageableResponse(), nil
}
- timelineables := []timeline.Timelineable{}
- for _, i := range preparedItems {
- status, ok := i.(*apimodel.Status)
- if !ok {
- return nil, gtserror.NewErrorInternalError(errors.New("error converting prepared timeline entry to api status"))
+ items := []interface{}{}
+ nextMaxIDValue := ""
+ prevMinIDValue := ""
+ for i, item := range preparedItems {
+ if i == count-1 {
+ nextMaxIDValue = item.GetID()
}
- timelineables = append(timelineables, status)
+
+ if i == 0 {
+ prevMinIDValue = item.GetID()
+ }
+ items = append(items, item)
}
- return util.PackageTimelineableResponse(util.TimelineableResponseParams{
- Items: timelineables,
+ return util.PackagePageableResponse(util.PageableResponseParams{
+ Items: items,
Path: "api/v1/timelines/home",
- NextMaxIDValue: timelineables[len(timelineables)-1].GetID(),
- PrevMinIDValue: timelineables[0].GetID(),
+ NextMaxIDValue: nextMaxIDValue,
+ PrevMinIDValue: prevMinIDValue,
Limit: limit,
})
}
-func (p *processor) PublicTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) (*apimodel.TimelineResponse, gtserror.WithCode) {
+func (p *processor) PublicTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) (*apimodel.PageableResponse, gtserror.WithCode) {
statuses, err := p.db.GetPublicTimeline(ctx, authed.Account.ID, maxID, sinceID, minID, limit, local)
if err != nil {
if err == db.ErrNoEntries {
// there are just no entries left
- return util.EmptyTimelineResponse(), nil
+ return util.EmptyPageableResponse(), nil
}
// there's an actual error
return nil, gtserror.NewErrorInternalError(err)
@@ -181,30 +188,41 @@ func (p *processor) PublicTimelineGet(ctx context.Context, authed *oauth.Auth, m
return nil, gtserror.NewErrorInternalError(err)
}
- if len(filtered) == 0 {
- return util.EmptyTimelineResponse(), nil
+ count := len(filtered)
+
+ if count == 0 {
+ return util.EmptyPageableResponse(), nil
}
- timelineables := []timeline.Timelineable{}
- for _, i := range filtered {
- timelineables = append(timelineables, i)
+ items := []interface{}{}
+ nextMaxIDValue := ""
+ prevMinIDValue := ""
+ for i, item := range filtered {
+ if i == count-1 {
+ nextMaxIDValue = item.GetID()
+ }
+
+ if i == 0 {
+ prevMinIDValue = item.GetID()
+ }
+ items = append(items, item)
}
- return util.PackageTimelineableResponse(util.TimelineableResponseParams{
- Items: timelineables,
+ return util.PackagePageableResponse(util.PageableResponseParams{
+ Items: items,
Path: "api/v1/timelines/public",
- NextMaxIDValue: timelineables[len(timelineables)-1].GetID(),
- PrevMinIDValue: timelineables[0].GetID(),
+ NextMaxIDValue: nextMaxIDValue,
+ PrevMinIDValue: prevMinIDValue,
Limit: limit,
})
}
-func (p *processor) FavedTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, minID string, limit int) (*apimodel.TimelineResponse, gtserror.WithCode) {
+func (p *processor) FavedTimelineGet(ctx context.Context, authed *oauth.Auth, maxID string, minID string, limit int) (*apimodel.PageableResponse, gtserror.WithCode) {
statuses, nextMaxID, prevMinID, err := p.db.GetFavedTimeline(ctx, authed.Account.ID, maxID, minID, limit)
if err != nil {
if err == db.ErrNoEntries {
// there are just no entries left
- return util.EmptyTimelineResponse(), nil
+ return util.EmptyPageableResponse(), nil
}
// there's an actual error
return nil, gtserror.NewErrorInternalError(err)
@@ -216,16 +234,16 @@ func (p *processor) FavedTimelineGet(ctx context.Context, authed *oauth.Auth, ma
}
if len(filtered) == 0 {
- return util.EmptyTimelineResponse(), nil
+ return util.EmptyPageableResponse(), nil
}
- timelineables := []timeline.Timelineable{}
- for _, i := range filtered {
- timelineables = append(timelineables, i)
+ items := []interface{}{}
+ for _, item := range filtered {
+ items = append(items, item)
}
- return util.PackageTimelineableResponse(util.TimelineableResponseParams{
- Items: timelineables,
+ return util.PackagePageableResponse(util.PageableResponseParams{
+ Items: items,
Path: "api/v1/favourites",
NextMaxIDValue: nextMaxID,
PrevMinIDValue: prevMinID,