diff options
Diffstat (limited to 'internal/db/bundb')
| -rw-r--r-- | internal/db/bundb/account.go | 33 | ||||
| -rw-r--r-- | internal/db/bundb/account_test.go | 41 | ||||
| -rw-r--r-- | internal/db/bundb/migrations/20230221150957_status_pin_client_api.go | 65 | ||||
| -rw-r--r-- | internal/db/bundb/timeline_test.go | 1 | 
4 files changed, 130 insertions, 10 deletions
| diff --git a/internal/db/bundb/account.go b/internal/db/bundb/account.go index 937f4ba23..26959001f 100644 --- a/internal/db/bundb/account.go +++ b/internal/db/bundb/account.go @@ -350,7 +350,16 @@ func (a *accountDB) CountAccountStatuses(ctx context.Context, accountID string)  		Count(ctx)  } -func (a *accountDB) GetAccountStatuses(ctx context.Context, accountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, pinnedOnly bool, mediaOnly bool, publicOnly bool) ([]*gtsmodel.Status, db.Error) { +func (a *accountDB) CountAccountPinned(ctx context.Context, accountID string) (int, db.Error) { +	return a.conn. +		NewSelect(). +		TableExpr("? AS ?", bun.Ident("statuses"), bun.Ident("status")). +		Where("? = ?", bun.Ident("status.account_id"), accountID). +		Where("? IS NOT NULL", bun.Ident("status.pinned_at")). +		Count(ctx) +} + +func (a *accountDB) GetAccountStatuses(ctx context.Context, accountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, mediaOnly bool, publicOnly bool) ([]*gtsmodel.Status, db.Error) {  	statusIDs := []string{}  	q := a.conn. @@ -390,10 +399,6 @@ func (a *accountDB) GetAccountStatuses(ctx context.Context, accountID string, li  		q = q.Where("? > ?", bun.Ident("status.id"), minID)  	} -	if pinnedOnly { -		q = q.Where("? = ?", bun.Ident("status.pinned"), true) -	} -  	if mediaOnly {  		// attachments are stored as a json object;  		// this implementation differs between sqlite and postgres, @@ -429,6 +434,24 @@ func (a *accountDB) GetAccountStatuses(ctx context.Context, accountID string, li  	return a.statusesFromIDs(ctx, statusIDs)  } +func (a *accountDB) GetAccountPinnedStatuses(ctx context.Context, accountID string) ([]*gtsmodel.Status, db.Error) { +	statusIDs := []string{} + +	q := a.conn. +		NewSelect(). +		TableExpr("? AS ?", bun.Ident("statuses"), bun.Ident("status")). +		Column("status.id"). +		Where("? = ?", bun.Ident("status.account_id"), accountID). +		Where("? IS NOT NULL", bun.Ident("status.pinned_at")). +		Order("status.pinned_at DESC") + +	if err := q.Scan(ctx, &statusIDs); err != nil { +		return nil, a.conn.ProcessError(err) +	} + +	return a.statusesFromIDs(ctx, statusIDs) +} +  func (a *accountDB) GetAccountWebStatuses(ctx context.Context, accountID string, limit int, maxID string) ([]*gtsmodel.Status, db.Error) {  	statusIDs := []string{} diff --git a/internal/db/bundb/account_test.go b/internal/db/bundb/account_test.go index 13bdba20d..a510e2152 100644 --- a/internal/db/bundb/account_test.go +++ b/internal/db/bundb/account_test.go @@ -28,6 +28,7 @@ import (  	"github.com/stretchr/testify/suite"  	"github.com/superseriousbusiness/gotosocial/internal/ap" +	"github.com/superseriousbusiness/gotosocial/internal/db"  	"github.com/superseriousbusiness/gotosocial/internal/db/bundb"  	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"  	"github.com/uptrace/bun" @@ -38,25 +39,25 @@ type AccountTestSuite struct {  }  func (suite *AccountTestSuite) TestGetAccountStatuses() { -	statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, false, false, "", "", false, false, false) +	statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, false, false, "", "", false, false)  	suite.NoError(err)  	suite.Len(statuses, 5)  }  func (suite *AccountTestSuite) TestGetAccountStatusesExcludeRepliesAndReblogs() { -	statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, true, true, "", "", false, false, false) +	statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, true, true, "", "", false, false)  	suite.NoError(err)  	suite.Len(statuses, 5)  }  func (suite *AccountTestSuite) TestGetAccountStatusesExcludeRepliesAndReblogsPublicOnly() { -	statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, true, true, "", "", false, false, true) +	statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, true, true, "", "", false, true)  	suite.NoError(err)  	suite.Len(statuses, 1)  }  func (suite *AccountTestSuite) TestGetAccountStatusesMediaOnly() { -	statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, false, false, "", "", false, true, false) +	statuses, err := suite.db.GetAccountStatuses(context.Background(), suite.testAccounts["local_account_1"].ID, 20, false, false, "", "", true, false)  	suite.NoError(err)  	suite.Len(statuses, 1)  } @@ -214,6 +215,38 @@ func (suite *AccountTestSuite) TestGettingBookmarksWithNoAccount() {  	suite.Nil(statuses)  } +func (suite *AccountTestSuite) TestGetAccountPinnedStatusesSomeResults() { +	testAccount := suite.testAccounts["admin_account"] + +	statuses, err := suite.db.GetAccountPinnedStatuses(context.Background(), testAccount.ID) +	suite.NoError(err) +	suite.Len(statuses, 2) // This account has 2 statuses pinned. +} + +func (suite *AccountTestSuite) TestGetAccountPinnedStatusesNothingPinned() { +	testAccount := suite.testAccounts["local_account_1"] + +	statuses, err := suite.db.GetAccountPinnedStatuses(context.Background(), testAccount.ID) +	suite.ErrorIs(err, db.ErrNoEntries) +	suite.Empty(statuses) // This account has nothing pinned. +} + +func (suite *AccountTestSuite) TestCountAccountPinnedSomeResults() { +	testAccount := suite.testAccounts["admin_account"] + +	pinned, err := suite.db.CountAccountPinned(context.Background(), testAccount.ID) +	suite.NoError(err) +	suite.Equal(pinned, 2) // This account has 2 statuses pinned. +} + +func (suite *AccountTestSuite) TestCountAccountPinnedNothingPinned() { +	testAccount := suite.testAccounts["local_account_1"] + +	pinned, err := suite.db.CountAccountPinned(context.Background(), testAccount.ID) +	suite.NoError(err) +	suite.Equal(pinned, 0) // This account has nothing pinned. +} +  func TestAccountTestSuite(t *testing.T) {  	suite.Run(t, new(AccountTestSuite))  } diff --git a/internal/db/bundb/migrations/20230221150957_status_pin_client_api.go b/internal/db/bundb/migrations/20230221150957_status_pin_client_api.go new file mode 100644 index 000000000..b2464fe30 --- /dev/null +++ b/internal/db/bundb/migrations/20230221150957_status_pin_client_api.go @@ -0,0 +1,65 @@ +/* +   GoToSocial +   Copyright (C) 2021-2023 GoToSocial Authors admin@gotosocial.org + +   This program is free software: you can redistribute it and/or modify +   it under the terms of the GNU Affero General Public License as published by +   the Free Software Foundation, either version 3 of the License, or +   (at your option) any later version. + +   This program is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +   GNU Affero General Public License for more details. + +   You should have received a copy of the GNU Affero General Public License +   along with this program.  If not, see <http://www.gnu.org/licenses/>. +*/ + +package migrations + +import ( +	"context" +	"strings" + +	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" +	"github.com/uptrace/bun" +) + +func init() { +	up := func(ctx context.Context, db *bun.DB) error { +		return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { +			// Drop the now unused 'pinned' column in statuses. +			if _, err := tx.ExecContext(ctx, "ALTER TABLE ? DROP COLUMN ?", bun.Ident("statuses"), bun.Ident("pinned")); err != nil && +				!(strings.Contains(err.Error(), "no such column") || strings.Contains(err.Error(), "does not exist") || strings.Contains(err.Error(), "SQLSTATE 42703")) { +				return err +			} + +			// Create new (more useful) pinned_at column. +			if _, err := tx.NewAddColumn().Model(>smodel.Status{}).ColumnExpr("? TIMESTAMPTZ", bun.Ident("pinned_at")).Exec(ctx); err != nil && +				!(strings.Contains(err.Error(), "already exists") || strings.Contains(err.Error(), "duplicate column name") || strings.Contains(err.Error(), "SQLSTATE 42701")) { +				return err +			} + +			// Index new column appropriately. +			if _, err := tx. +				NewCreateIndex(). +				Model(>smodel.Status{}). +				Index("statuses_account_id_pinned_at_idx"). +				Column("account_id", "pinned_at"). +				Exec(ctx); err != nil { +				return err +			} + +			return nil +		}) +	} + +	down := func(ctx context.Context, db *bun.DB) error { +		return nil +	} + +	if err := Migrations.Register(up, down); err != nil { +		panic(err) +	} +} diff --git a/internal/db/bundb/timeline_test.go b/internal/db/bundb/timeline_test.go index 91a4fef15..3b98de45d 100644 --- a/internal/db/bundb/timeline_test.go +++ b/internal/db/bundb/timeline_test.go @@ -113,7 +113,6 @@ func getFutureStatus() *gtsmodel.Status {  		Sensitive:                testrig.FalseBool(),  		Language:                 "en",  		CreatedWithApplicationID: "01F8MGXQRHYF5QPMTMXP78QC2F", -		Pinned:                   testrig.FalseBool(),  		Federated:                testrig.TrueBool(),  		Boostable:                testrig.TrueBool(),  		Replyable:                testrig.TrueBool(), | 
