diff options
author | 2022-08-15 12:35:05 +0200 | |
---|---|---|
committer | 2022-08-15 11:35:05 +0100 | |
commit | ac6ed3d939fe9dad81aadbd04541e905c625ca82 (patch) | |
tree | 6116baf25675837dc99f69c49b9fec2ff112ce5c /internal | |
parent | [frontend] Sensitive media spoilers (#752) (diff) | |
download | gotosocial-ac6ed3d939fe9dad81aadbd04541e905c625ca82.tar.xz |
[chore] Update bun / sqlite versions; update gtsmodels (#754)
* upstep bun and sqlite versions
* allow specific columns to be updated in the db
* only update necessary columns for user
* bit tidier
* only update necessary fields of media_attachment
* only update relevant instance fields
* update tests
* update only specific account columns
* use bool pointers on gtsmodels
includes attachment, status, account, user
* update columns more selectively
* test all default fields on new account insert
* updating remaining bools on gtsmodels
* initialize pointer fields when extracting AP emoji
* copy bools properly
* add copyBoolPtr convenience function + test it
* initialize false bool ptrs a bit more neatly
Diffstat (limited to 'internal')
94 files changed, 635 insertions, 359 deletions
diff --git a/internal/ap/extract.go b/internal/ap/extract.go index 9417077f3..e1566059c 100644 --- a/internal/ap/extract.go +++ b/internal/ap/extract.go @@ -529,6 +529,10 @@ func ExtractEmoji(i Emojiable) (*gtsmodel.Emoji, error) { } emoji.ImageRemoteURL = imageURL.String() + // assume false for both to begin + emoji.Disabled = new(bool) + emoji.VisibleInPicker = new(bool) + return emoji, nil } diff --git a/internal/api/client/account/accountverify_test.go b/internal/api/client/account/accountverify_test.go index 6b199d83e..886272865 100644 --- a/internal/api/client/account/accountverify_test.go +++ b/internal/api/client/account/accountverify_test.go @@ -70,8 +70,8 @@ func (suite *AccountVerifyTestSuite) TestAccountVerifyGet() { suite.Equal(testAccount.Username, apimodelAccount.Username) suite.Equal(testAccount.Username, apimodelAccount.Acct) suite.Equal(testAccount.DisplayName, apimodelAccount.DisplayName) - suite.Equal(testAccount.Locked, apimodelAccount.Locked) - suite.Equal(testAccount.Bot, apimodelAccount.Bot) + suite.Equal(*testAccount.Locked, apimodelAccount.Locked) + suite.Equal(*testAccount.Bot, apimodelAccount.Bot) suite.WithinDuration(testAccount.CreatedAt, createdAt, 30*time.Second) // we lose a bit of accuracy serializing so fuzz this a bit suite.Equal(testAccount.URL, apimodelAccount.URL) suite.Equal("http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/avatar/original/01F8MH58A357CV5K7R7TJMSH6S.jpeg", apimodelAccount.Avatar) diff --git a/internal/api/client/admin/accountaction.go b/internal/api/client/admin/accountaction.go index 072a60a9d..a9817f295 100644 --- a/internal/api/client/admin/accountaction.go +++ b/internal/api/client/admin/accountaction.go @@ -87,7 +87,7 @@ func (m *Module) AccountActionPOSTHandler(c *gin.Context) { return } - if !authed.User.Admin { + if !*authed.User.Admin { err := fmt.Errorf("user %s not an admin", authed.User.ID) api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet) return diff --git a/internal/api/client/admin/domainblockcreate.go b/internal/api/client/admin/domainblockcreate.go index cc97f52ca..9ee725bef 100644 --- a/internal/api/client/admin/domainblockcreate.go +++ b/internal/api/client/admin/domainblockcreate.go @@ -123,7 +123,7 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) { return } - if !authed.User.Admin { + if !*authed.User.Admin { err := fmt.Errorf("user %s not an admin", authed.User.ID) api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet) return diff --git a/internal/api/client/admin/domainblockdelete.go b/internal/api/client/admin/domainblockdelete.go index d57018f72..aec1ba6df 100644 --- a/internal/api/client/admin/domainblockdelete.go +++ b/internal/api/client/admin/domainblockdelete.go @@ -75,7 +75,7 @@ func (m *Module) DomainBlockDELETEHandler(c *gin.Context) { return } - if !authed.User.Admin { + if !*authed.User.Admin { err := fmt.Errorf("user %s not an admin", authed.User.ID) api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet) return diff --git a/internal/api/client/admin/domainblockget.go b/internal/api/client/admin/domainblockget.go index e9fdf549b..1aeedce51 100644 --- a/internal/api/client/admin/domainblockget.go +++ b/internal/api/client/admin/domainblockget.go @@ -76,7 +76,7 @@ func (m *Module) DomainBlockGETHandler(c *gin.Context) { return } - if !authed.User.Admin { + if !*authed.User.Admin { err := fmt.Errorf("user %s not an admin", authed.User.ID) api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet) return diff --git a/internal/api/client/admin/domainblocksget.go b/internal/api/client/admin/domainblocksget.go index c3b3ec595..93bd767c4 100644 --- a/internal/api/client/admin/domainblocksget.go +++ b/internal/api/client/admin/domainblocksget.go @@ -81,7 +81,7 @@ func (m *Module) DomainBlocksGETHandler(c *gin.Context) { return } - if !authed.User.Admin { + if !*authed.User.Admin { err := fmt.Errorf("user %s not an admin", authed.User.ID) api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet) return diff --git a/internal/api/client/admin/emojicreate.go b/internal/api/client/admin/emojicreate.go index c5d613312..39ebd5adf 100644 --- a/internal/api/client/admin/emojicreate.go +++ b/internal/api/client/admin/emojicreate.go @@ -90,7 +90,7 @@ func (m *Module) EmojiCreatePOSTHandler(c *gin.Context) { return } - if !authed.User.Admin { + if !*authed.User.Admin { err := fmt.Errorf("user %s not an admin", authed.User.ID) api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet) return diff --git a/internal/api/client/admin/emojicreate_test.go b/internal/api/client/admin/emojicreate_test.go index fa93ce564..ebef858aa 100644 --- a/internal/api/client/admin/emojicreate_test.go +++ b/internal/api/client/admin/emojicreate_test.go @@ -97,9 +97,9 @@ func (suite *EmojiCreateTestSuite) TestEmojiCreate() { suite.Equal("image/png", dbEmoji.ImageStaticContentType) suite.Equal(36702, dbEmoji.ImageFileSize) suite.Equal(10413, dbEmoji.ImageStaticFileSize) - suite.False(dbEmoji.Disabled) + suite.False(*dbEmoji.Disabled) suite.NotEmpty(dbEmoji.URI) - suite.True(dbEmoji.VisibleInPicker) + suite.True(*dbEmoji.VisibleInPicker) suite.Empty(dbEmoji.CategoryID) // emoji should be in storage diff --git a/internal/api/client/admin/mediacleanup.go b/internal/api/client/admin/mediacleanup.go index 6065acd3b..44f0a1b52 100644 --- a/internal/api/client/admin/mediacleanup.go +++ b/internal/api/client/admin/mediacleanup.go @@ -74,7 +74,7 @@ func (m *Module) MediaCleanupPOSTHandler(c *gin.Context) { return } - if !authed.User.Admin { + if !*authed.User.Admin { err := fmt.Errorf("user %s not an admin", authed.User.ID) api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet) return diff --git a/internal/api/client/admin/mediacleanup_test.go b/internal/api/client/admin/mediacleanup_test.go index 50f23b2f0..345402ad5 100644 --- a/internal/api/client/admin/mediacleanup_test.go +++ b/internal/api/client/admin/mediacleanup_test.go @@ -35,7 +35,7 @@ type MediaCleanupTestSuite struct { func (suite *MediaCleanupTestSuite) TestMediaCleanup() { testAttachment := suite.testAttachments["remote_account_1_status_1_attachment_2"] - suite.True(testAttachment.Cached) + suite.True(*testAttachment.Cached) // set up the request recorder := httptest.NewRecorder() @@ -55,12 +55,12 @@ func (suite *MediaCleanupTestSuite) TestMediaCleanup() { suite.NoError(err) // the media should no longer be cached - suite.False(prunedAttachment.Cached) + suite.False(*prunedAttachment.Cached) } func (suite *MediaCleanupTestSuite) TestMediaCleanupNoArg() { testAttachment := suite.testAttachments["remote_account_1_status_1_attachment_2"] - suite.True(testAttachment.Cached) + suite.True(*testAttachment.Cached) println("TIME: ", testAttachment.CreatedAt.String()) // set up the request @@ -81,12 +81,12 @@ func (suite *MediaCleanupTestSuite) TestMediaCleanupNoArg() { suite.NoError(err) // the media should no longer be cached - suite.False(prunedAttachment.Cached) + suite.False(*prunedAttachment.Cached) } func (suite *MediaCleanupTestSuite) TestMediaCleanupNotOldEnough() { testAttachment := suite.testAttachments["remote_account_1_status_1_attachment_2"] - suite.True(testAttachment.Cached) + suite.True(*testAttachment.Cached) // set up the request recorder := httptest.NewRecorder() @@ -106,7 +106,7 @@ func (suite *MediaCleanupTestSuite) TestMediaCleanupNotOldEnough() { suite.NoError(err) // the media should still be cached - suite.True(prunedAttachment.Cached) + suite.True(*prunedAttachment.Cached) } func TestMediaCleanupTestSuite(t *testing.T) { diff --git a/internal/api/client/auth/authorize.go b/internal/api/client/auth/authorize.go index 67325a81d..83cddd9b5 100644 --- a/internal/api/client/auth/authorize.go +++ b/internal/api/client/auth/authorize.go @@ -320,13 +320,13 @@ func ensureUserIsAuthorizedOrRedirect(ctx *gin.Context, user *gtsmodel.User, acc return } - if !user.Approved { + if !*user.Approved { ctx.Redirect(http.StatusSeeOther, WaitForApprovalPath) redirected = true return } - if user.Disabled || !account.SuspendedAt.IsZero() { + if *user.Disabled || !account.SuspendedAt.IsZero() { ctx.Redirect(http.StatusSeeOther, AccountDisabledPath) redirected = true return diff --git a/internal/api/client/auth/authorize_test.go b/internal/api/client/auth/authorize_test.go index 35b995e70..eab893416 100644 --- a/internal/api/client/auth/authorize_test.go +++ b/internal/api/client/auth/authorize_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/api/client/auth" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + "github.com/superseriousbusiness/gotosocial/testrig" ) type AuthAuthorizeTestSuite struct { @@ -19,7 +20,7 @@ type AuthAuthorizeTestSuite struct { type authorizeHandlerTestCase struct { description string - mutateUserAccount func(*gtsmodel.User, *gtsmodel.Account) + mutateUserAccount func(*gtsmodel.User, *gtsmodel.Account) []string expectedStatusCode int expectedLocationHeader string } @@ -28,40 +29,44 @@ func (suite *AuthAuthorizeTestSuite) TestAccountAuthorizeHandler() { tests := []authorizeHandlerTestCase{ { description: "user has their email unconfirmed", - mutateUserAccount: func(user *gtsmodel.User, account *gtsmodel.Account) { + mutateUserAccount: func(user *gtsmodel.User, account *gtsmodel.Account) []string { // nothing to do, weed_lord420 already has their email unconfirmed + return nil }, expectedStatusCode: http.StatusSeeOther, expectedLocationHeader: auth.CheckYourEmailPath, }, { description: "user has their email confirmed but is not approved", - mutateUserAccount: func(user *gtsmodel.User, account *gtsmodel.Account) { + mutateUserAccount: func(user *gtsmodel.User, account *gtsmodel.Account) []string { user.ConfirmedAt = time.Now() user.Email = user.UnconfirmedEmail + return []string{"confirmed_at", "email"} }, expectedStatusCode: http.StatusSeeOther, expectedLocationHeader: auth.WaitForApprovalPath, }, { description: "user has their email confirmed and is approved, but User entity has been disabled", - mutateUserAccount: func(user *gtsmodel.User, account *gtsmodel.Account) { + mutateUserAccount: func(user *gtsmodel.User, account *gtsmodel.Account) []string { user.ConfirmedAt = time.Now() user.Email = user.UnconfirmedEmail - user.Approved = true - user.Disabled = true + user.Approved = testrig.TrueBool() + user.Disabled = testrig.TrueBool() + return []string{"confirmed_at", "email", "approved", "disabled"} }, expectedStatusCode: http.StatusSeeOther, expectedLocationHeader: auth.AccountDisabledPath, }, { description: "user has their email confirmed and is approved, but Account entity has been suspended", - mutateUserAccount: func(user *gtsmodel.User, account *gtsmodel.Account) { + mutateUserAccount: func(user *gtsmodel.User, account *gtsmodel.Account) []string { user.ConfirmedAt = time.Now() user.Email = user.UnconfirmedEmail - user.Approved = true - user.Disabled = false + user.Approved = testrig.TrueBool() + user.Disabled = testrig.FalseBool() account.SuspendedAt = time.Now() + return []string{"confirmed_at", "email", "approved", "disabled"} }, expectedStatusCode: http.StatusSeeOther, expectedLocationHeader: auth.AccountDisabledPath, @@ -81,12 +86,13 @@ func (suite *AuthAuthorizeTestSuite) TestAccountAuthorizeHandler() { panic(fmt.Errorf("failed on case %s: %w", testCase.description, err)) } - testCase.mutateUserAccount(user, account) + updatingColumns := testCase.mutateUserAccount(user, account) - testCase.description = fmt.Sprintf("%s, %t, %s", user.Email, user.Disabled, account.SuspendedAt) + testCase.description = fmt.Sprintf("%s, %t, %s", user.Email, *user.Disabled, account.SuspendedAt) + updatingColumns = append(updatingColumns, "updated_at") user.UpdatedAt = time.Now() - err := suite.db.UpdateByPrimaryKey(context.Background(), user) + err := suite.db.UpdateByPrimaryKey(context.Background(), user, updatingColumns...) suite.NoError(err) _, err = suite.db.UpdateAccount(context.Background(), account) suite.NoError(err) diff --git a/internal/api/client/instance/instancepatch.go b/internal/api/client/instance/instancepatch.go index 6f1b3586b..78d0af046 100644 --- a/internal/api/client/instance/instancepatch.go +++ b/internal/api/client/instance/instancepatch.go @@ -124,7 +124,7 @@ func (m *Module) InstanceUpdatePATCHHandler(c *gin.Context) { return } - if !authed.User.Admin { + if !*authed.User.Admin { err := errors.New("user is not an admin so cannot update instance settings") api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet) return diff --git a/internal/api/client/instance/instancepeersget_test.go b/internal/api/client/instance/instancepeersget_test.go index 48fb42a73..cc1adcca4 100644 --- a/internal/api/client/instance/instancepeersget_test.go +++ b/internal/api/client/instance/instancepeersget_test.go @@ -190,7 +190,7 @@ func (suite *InstancePeersGetTestSuite) TestInstancePeersGetAllWithObfuscated() Domain: "omg.just.the.worst.org.ever", CreatedByAccountID: "01F8MH17FWEB39HZJ76B6VXSKF", PublicComment: "just absolutely the worst, wowza", - Obfuscate: true, + Obfuscate: testrig.TrueBool(), }) suite.NoError(err) diff --git a/internal/api/s2s/user/inboxpost_test.go b/internal/api/s2s/user/inboxpost_test.go index b8fe73909..506fa270c 100644 --- a/internal/api/s2s/user/inboxpost_test.go +++ b/internal/api/s2s/user/inboxpost_test.go @@ -469,8 +469,8 @@ func (suite *InboxPostTestSuite) TestPostDelete() { suite.Empty(dbAccount.HeaderRemoteURL) suite.Empty(dbAccount.Reason) suite.Empty(dbAccount.Fields) - suite.True(dbAccount.HideCollections) - suite.False(dbAccount.Discoverable) + suite.True(*dbAccount.HideCollections) + suite.False(*dbAccount.Discoverable) suite.WithinDuration(time.Now(), dbAccount.SuspendedAt, 30*time.Second) suite.Equal(dbAccount.ID, dbAccount.SuspensionOrigin) } diff --git a/internal/api/security/tokencheck.go b/internal/api/security/tokencheck.go index 0e9e7bde2..3df7ee943 100644 --- a/internal/api/security/tokencheck.go +++ b/internal/api/security/tokencheck.go @@ -67,12 +67,12 @@ func (m *Module) TokenCheck(c *gin.Context) { return } - if !user.Approved { + if !*user.Approved { log.Warnf("authenticated user %s's account was never approved by an admin", userID) return } - if user.Disabled { + if *user.Disabled { log.Warnf("authenticated user %s's account was disabled'", userID) return } diff --git a/internal/cache/account.go b/internal/cache/account.go index a71274d1c..ac67b5d07 100644 --- a/internal/cache/account.go +++ b/internal/cache/account.go @@ -103,16 +103,16 @@ func copyAccount(account *gtsmodel.Account) *gtsmodel.Account { Fields: account.Fields, Note: account.Note, NoteRaw: account.NoteRaw, - Memorial: account.Memorial, + Memorial: copyBoolPtr(account.Memorial), MovedToAccountID: account.MovedToAccountID, + Bot: copyBoolPtr(account.Bot), CreatedAt: account.CreatedAt, UpdatedAt: account.UpdatedAt, - Bot: account.Bot, Reason: account.Reason, - Locked: account.Locked, - Discoverable: account.Discoverable, + Locked: copyBoolPtr(account.Locked), + Discoverable: copyBoolPtr(account.Discoverable), Privacy: account.Privacy, - Sensitive: account.Sensitive, + Sensitive: copyBoolPtr(account.Sensitive), Language: account.Language, StatusFormat: account.StatusFormat, URI: account.URI, @@ -131,7 +131,7 @@ func copyAccount(account *gtsmodel.Account) *gtsmodel.Account { SensitizedAt: account.SensitizedAt, SilencedAt: account.SilencedAt, SuspendedAt: account.SuspendedAt, - HideCollections: account.HideCollections, + HideCollections: copyBoolPtr(account.HideCollections), SuspensionOrigin: account.SuspensionOrigin, } } diff --git a/internal/cache/status.go b/internal/cache/status.go index 7e3d85960..f3cbce779 100644 --- a/internal/cache/status.go +++ b/internal/cache/status.go @@ -102,9 +102,9 @@ func copyStatus(status *gtsmodel.Status) *gtsmodel.Status { Mentions: nil, EmojiIDs: status.EmojiIDs, Emojis: nil, + Local: copyBoolPtr(status.Local), CreatedAt: status.CreatedAt, UpdatedAt: status.UpdatedAt, - Local: status.Local, AccountID: status.AccountID, Account: nil, AccountURI: status.AccountURI, @@ -119,15 +119,15 @@ func copyStatus(status *gtsmodel.Status) *gtsmodel.Status { BoostOfAccount: nil, ContentWarning: status.ContentWarning, Visibility: status.Visibility, - Sensitive: status.Sensitive, + Sensitive: copyBoolPtr(status.Sensitive), Language: status.Language, CreatedWithApplicationID: status.CreatedWithApplicationID, - Federated: status.Federated, - Boostable: status.Boostable, - Replyable: status.Replyable, - Likeable: status.Likeable, ActivityStreamsType: status.ActivityStreamsType, Text: status.Text, - Pinned: status.Pinned, + Pinned: copyBoolPtr(status.Pinned), + Federated: copyBoolPtr(status.Federated), + Boostable: copyBoolPtr(status.Boostable), + Replyable: copyBoolPtr(status.Replyable), + Likeable: copyBoolPtr(status.Likeable), } } diff --git a/internal/cache/status_test.go b/internal/cache/status_test.go index 882e92be5..8b0621182 100644 --- a/internal/cache/status_test.go +++ b/internal/cache/status_test.go @@ -72,6 +72,32 @@ func (suite *StatusCacheTestSuite) TestStatusCache() { } } +func (suite *StatusCacheTestSuite) TestBoolPointerCopying() { + originalStatus := suite.data["local_account_1_status_1"] + + // mark the status as pinned + cache it + pinned := true + originalStatus.Pinned = &pinned + suite.cache.Put(originalStatus) + + // retrieve it + cachedStatus, ok := suite.cache.GetByID(originalStatus.ID) + if !ok { + suite.FailNow("status wasn't retrievable from cache") + } + + // we should be able to change the original status values + cached + // values independently since they use different pointers + suite.True(*cachedStatus.Pinned) + *originalStatus.Pinned = false + suite.False(*originalStatus.Pinned) + suite.True(*cachedStatus.Pinned) + *originalStatus.Pinned = true + *cachedStatus.Pinned = false + suite.True(*originalStatus.Pinned) + suite.False(*cachedStatus.Pinned) +} + func TestStatusCache(t *testing.T) { suite.Run(t, &StatusCacheTestSuite{}) } diff --git a/internal/cache/util.go b/internal/cache/util.go new file mode 100644 index 000000000..48204b259 --- /dev/null +++ b/internal/cache/util.go @@ -0,0 +1,31 @@ +/* + GoToSocial + Copyright (C) 2021-2022 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 cache + +// copyBoolPtr returns a bool pointer with the same value as the pointer passed into it. +// +// Useful when copying things from the cache to a caller. +func copyBoolPtr(in *bool) *bool { + if in == nil { + return nil + } + b := new(bool) + *b = *in + return b +} diff --git a/internal/db/basic.go b/internal/db/basic.go index 07f04eae3..6e5184d31 100644 --- a/internal/db/basic.go +++ b/internal/db/basic.go @@ -62,9 +62,11 @@ type Basic interface { // The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice. Put(ctx context.Context, i interface{}) Error - // UpdateByPrimaryKey updates all values of i based on its primary key. + // UpdateByPrimaryKey updates values of i based on its primary key. + // If any columns are specified, these will be updated exclusively. + // Otherwise, the whole model will be updated. // The given interface i will be set to the result of the query, whatever it is. Use a pointer or a slice. - UpdateByPrimaryKey(ctx context.Context, i interface{}) Error + UpdateByPrimaryKey(ctx context.Context, i interface{}, columns ...string) Error // UpdateWhere updates column key of interface i with the given value, where the given parameters apply. UpdateWhere(ctx context.Context, where []Where, key string, value interface{}, i interface{}) Error diff --git a/internal/db/bundb/account.go b/internal/db/bundb/account.go index aaeb646cf..201de6f02 100644 --- a/internal/db/bundb/account.go +++ b/internal/db/bundb/account.go @@ -164,15 +164,15 @@ func (a *accountDB) GetAccountLastPosted(ctx context.Context, accountID string) } func (a *accountDB) SetAccountHeaderOrAvatar(ctx context.Context, mediaAttachment *gtsmodel.MediaAttachment, accountID string) db.Error { - if mediaAttachment.Avatar && mediaAttachment.Header { + if *mediaAttachment.Avatar && *mediaAttachment.Header { return errors.New("one media attachment cannot be both header and avatar") } var headerOrAVI string switch { - case mediaAttachment.Avatar: + case *mediaAttachment.Avatar: headerOrAVI = "avatar" - case mediaAttachment.Header: + case *mediaAttachment.Header: headerOrAVI = "header" default: return errors.New("given media attachment was neither a header nor an avatar") diff --git a/internal/db/bundb/account_test.go b/internal/db/bundb/account_test.go index 134e38940..59b51386d 100644 --- a/internal/db/bundb/account_test.go +++ b/internal/db/bundb/account_test.go @@ -93,11 +93,11 @@ func (suite *AccountTestSuite) TestInsertAccountWithDefaults() { suite.Equal("en", newAccount.Language) suite.WithinDuration(time.Now(), newAccount.CreatedAt, 30*time.Second) suite.WithinDuration(time.Now(), newAccount.UpdatedAt, 30*time.Second) - suite.False(newAccount.Memorial) - suite.False(newAccount.Bot) - suite.False(newAccount.Discoverable) - suite.False(newAccount.Sensitive) - suite.False(newAccount.HideCollections) + suite.False(*newAccount.Memorial) + suite.False(*newAccount.Bot) + suite.False(*newAccount.Discoverable) + suite.False(*newAccount.Sensitive) + suite.False(*newAccount.HideCollections) } func TestAccountTestSuite(t *testing.T) { diff --git a/internal/db/bundb/admin.go b/internal/db/bundb/admin.go index 1bda82d56..f66ed0294 100644 --- a/internal/db/bundb/admin.go +++ b/internal/db/bundb/admin.go @@ -142,6 +142,8 @@ func (a *adminDB) NewSignup(ctx context.Context, username string, reason string, return nil, err } + // if we don't require moderator approval, just pre-approve the user + approved := !requireApproval u := >smodel.User{ ID: newUserID, AccountID: acct.ID, @@ -151,7 +153,7 @@ func (a *adminDB) NewSignup(ctx context.Context, username string, reason string, Locale: locale, UnconfirmedEmail: email, CreatedByApplicationID: appID, - Approved: !requireApproval, // if we don't require moderator approval, just pre-approve the user + Approved: &approved, } if emailVerified { @@ -161,8 +163,10 @@ func (a *adminDB) NewSignup(ctx context.Context, username string, reason string, } if admin { - u.Admin = true - u.Moderator = true + admin := true + moderator := true + u.Admin = &admin + u.Moderator = &moderator } if _, err = a.conn. diff --git a/internal/db/bundb/basic.go b/internal/db/bundb/basic.go index b31a73dd7..cd80c9330 100644 --- a/internal/db/bundb/basic.go +++ b/internal/db/bundb/basic.go @@ -94,10 +94,11 @@ func (b *basicDB) DeleteWhere(ctx context.Context, where []db.Where, i interface return b.conn.ProcessError(err) } -func (b *basicDB) UpdateByPrimaryKey(ctx context.Context, i interface{}) db.Error { +func (b *basicDB) UpdateByPrimaryKey(ctx context.Context, i interface{}, columns ...string) db.Error { q := b.conn. NewUpdate(). Model(i). + Column(columns...). WherePK() _, err := q.Exec(ctx) diff --git a/internal/db/bundb/basic_test.go b/internal/db/bundb/basic_test.go index 1705ebc0d..e5ba8b36a 100644 --- a/internal/db/bundb/basic_test.go +++ b/internal/db/bundb/basic_test.go @@ -21,6 +21,7 @@ package bundb_test import ( "context" "testing" + "time" "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/db" @@ -39,6 +40,75 @@ func (suite *BasicTestSuite) TestGetAccountByID() { suite.NoError(err) } +func (suite *BasicTestSuite) TestPutAccountWithBunDefaultFields() { + testAccount := >smodel.Account{ + ID: "01GADR1AH9VCKH8YYCM86XSZ00", + Username: "test", + URI: "https://example.org/users/test", + URL: "https://example.org/@test", + InboxURI: "https://example.org/users/test/inbox", + OutboxURI: "https://example.org/users/test/outbox", + ActorType: "Person", + PublicKeyURI: "https://example.org/test#main-key", + } + + if err := suite.db.Put(context.Background(), testAccount); err != nil { + suite.FailNow(err.Error()) + } + + a := >smodel.Account{} + if err := suite.db.GetByID(context.Background(), testAccount.ID, a); err != nil { + suite.FailNow(err.Error()) + } + + // check all fields are set as expected, including database defaults + suite.Equal(testAccount.ID, a.ID) + suite.WithinDuration(time.Now(), a.CreatedAt, 5*time.Second) + suite.WithinDuration(time.Now(), a.UpdatedAt, 5*time.Second) + suite.Equal(testAccount.Username, a.Username) + suite.Empty(a.Domain) + suite.Empty(a.AvatarMediaAttachmentID) + suite.Nil(a.AvatarMediaAttachment) + suite.Empty(a.AvatarRemoteURL) + suite.Empty(a.HeaderMediaAttachmentID) + suite.Nil(a.HeaderMediaAttachment) + suite.Empty(a.HeaderRemoteURL) + suite.Empty(a.DisplayName) + suite.Nil(a.Fields) + suite.Empty(a.Note) + suite.Empty(a.NoteRaw) + suite.False(*a.Memorial) + suite.Empty(a.AlsoKnownAs) + suite.Empty(a.MovedToAccountID) + suite.False(*a.Bot) + suite.Empty(a.Reason) + // Locked is especially important, since it's a bool that defaults + // to true, which is why we use pointers for bools in the first place + suite.True(*a.Locked) + suite.False(*a.Discoverable) + suite.Empty(a.Privacy) + suite.False(*a.Sensitive) + suite.Equal("en", a.Language) + suite.Empty(a.StatusFormat) + suite.Equal(testAccount.URI, a.URI) + suite.Equal(testAccount.URL, a.URL) + suite.Zero(testAccount.LastWebfingeredAt) + suite.Equal(testAccount.InboxURI, a.InboxURI) + suite.Equal(testAccount.OutboxURI, a.OutboxURI) + suite.Empty(a.FollowingURI) + suite.Empty(a.FollowersURI) + suite.Empty(a.FeaturedCollectionURI) + suite.Equal(testAccount.ActorType, a.ActorType) + suite.Nil(a.PrivateKey) + suite.Nil(a.PublicKey) + suite.Equal(testAccount.PublicKeyURI, a.PublicKeyURI) + suite.Zero(a.SensitizedAt) + suite.Zero(a.SilencedAt) + suite.Zero(a.SuspendedAt) + suite.False(*a.HideCollections) + suite.Empty(a.SuspensionOrigin) +} + func (suite *BasicTestSuite) TestGetAllStatuses() { s := []*gtsmodel.Status{} err := suite.db.GetAll(context.Background(), &s) diff --git a/internal/db/bundb/bundb.go b/internal/db/bundb/bundb.go index 784a7d443..191350d06 100644 --- a/internal/db/bundb/bundb.go +++ b/internal/db/bundb/bundb.go @@ -409,15 +409,17 @@ func (ps *bunDBService) TagStringsToTags(ctx context.Context, tags []string, ori tag.FirstSeenFromAccountID = originAccountID tag.CreatedAt = time.Now() tag.UpdatedAt = time.Now() - tag.Useable = true - tag.Listable = true + useable := true + tag.Useable = &useable + listable := true + tag.Listable = &listable } else { return nil, fmt.Errorf("error getting tag with name %s: %s", t, err) } } // bail already if the tag isn't useable - if !tag.Useable { + if !*tag.Useable { continue } tag.LastStatusAt = time.Now() diff --git a/internal/db/bundb/notification_test.go b/internal/db/bundb/notification_test.go index d822c9a28..d79c73ad2 100644 --- a/internal/db/bundb/notification_test.go +++ b/internal/db/bundb/notification_test.go @@ -27,6 +27,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/id" + "github.com/superseriousbusiness/gotosocial/testrig" ) func (suite *NotificationTestSuite) spamNotifs() { @@ -71,7 +72,7 @@ func (suite *NotificationTestSuite) spamNotifs() { TargetAccountID: targetAccountID, OriginAccountID: originAccountID, StatusID: statusID, - Read: false, + Read: testrig.FalseBool(), } if err := suite.db.Put(context.Background(), notif); err != nil { diff --git a/internal/db/bundb/relationship.go b/internal/db/bundb/relationship.go index e2e2c96b2..60dd72663 100644 --- a/internal/db/bundb/relationship.go +++ b/internal/db/bundb/relationship.go @@ -114,8 +114,8 @@ func (r *relationshipDB) GetRelationship(ctx context.Context, requestingAccount } else { // follow exists so we can fill these fields out... rel.Following = true - rel.ShowingReblogs = follow.ShowReblogs - rel.Notifying = follow.Notify + rel.ShowingReblogs = *follow.ShowReblogs + rel.Notifying = *follow.Notify } // check if the target account follows the requesting account diff --git a/internal/db/bundb/status_test.go b/internal/db/bundb/status_test.go index 3245314f8..36e329806 100644 --- a/internal/db/bundb/status_test.go +++ b/internal/db/bundb/status_test.go @@ -43,10 +43,10 @@ func (suite *StatusTestSuite) TestGetStatusByID() { suite.Nil(status.BoostOfAccount) suite.Nil(status.InReplyTo) suite.Nil(status.InReplyToAccount) - suite.True(status.Federated) - suite.True(status.Boostable) - suite.True(status.Replyable) - suite.True(status.Likeable) + suite.True(*status.Federated) + suite.True(*status.Boostable) + suite.True(*status.Replyable) + suite.True(*status.Likeable) } func (suite *StatusTestSuite) TestGetStatusByURI() { @@ -61,10 +61,10 @@ func (suite *StatusTestSuite) TestGetStatusByURI() { suite.Nil(status.BoostOfAccount) suite.Nil(status.InReplyTo) suite.Nil(status.InReplyToAccount) - suite.True(status.Federated) - suite.True(status.Boostable) - suite.False(status.Replyable) - suite.False(status.Likeable) + suite.True(*status.Federated) + suite.True(*status.Boostable) + suite.False(*status.Replyable) + suite.False(*status.Likeable) } func (suite *StatusTestSuite) TestGetStatusWithExtras() { @@ -78,10 +78,10 @@ func (suite *StatusTestSuite) TestGetStatusWithExtras() { suite.NotEmpty(status.Tags) suite.NotEmpty(status.Attachments) suite.NotEmpty(status.Emojis) - suite.True(status.Federated) - suite.True(status.Boostable) - suite.True(status.Replyable) - suite.True(status.Likeable) + suite.True(*status.Federated) + suite.True(*status.Boostable) + suite.True(*status.Replyable) + suite.True(*status.Likeable) } func (suite *StatusTestSuite) TestGetStatusWithMention() { @@ -95,10 +95,10 @@ func (suite *StatusTestSuite) TestGetStatusWithMention() { suite.NotEmpty(status.MentionIDs) suite.NotEmpty(status.InReplyToID) suite.NotEmpty(status.InReplyToAccountID) - suite.True(status.Federated) - suite.True(status.Boostable) - suite.True(status.Replyable) - suite.True(status.Likeable) + suite.True(*status.Federated) + suite.True(*status.Boostable) + suite.True(*status.Replyable) + suite.True(*status.Likeable) } func (suite *StatusTestSuite) TestGetStatusTwice() { diff --git a/internal/federation/dereferencing/status_test.go b/internal/federation/dereferencing/status_test.go index 6f6c26145..964de23ec 100644 --- a/internal/federation/dereferencing/status_test.go +++ b/internal/federation/dereferencing/status_test.go @@ -46,7 +46,7 @@ func (suite *StatusTestSuite) TestDereferenceSimpleStatus() { suite.Equal("https://unknown-instance.com/users/@brand_new_person/01FE4NTHKWW7THT67EF10EB839", status.URL) suite.Equal("Hello world!", status.Content) suite.Equal("https://unknown-instance.com/users/brand_new_person", status.AccountURI) - suite.False(status.Local) + suite.False(*status.Local) suite.Empty(status.ContentWarning) suite.Equal(gtsmodel.VisibilityPublic, status.Visibility) suite.Equal(ap.ObjectNote, status.ActivityStreamsType) @@ -55,16 +55,16 @@ func (suite *StatusTestSuite) TestDereferenceSimpleStatus() { dbStatus, err := suite.db.GetStatusByURI(context.Background(), status.URI) suite.NoError(err) suite.Equal(status.ID, dbStatus.ID) - suite.True(dbStatus.Federated) - suite.True(dbStatus.Boostable) - suite.True(dbStatus.Replyable) - suite.True(dbStatus.Likeable) + suite.True(*dbStatus.Federated) + suite.True(*dbStatus.Boostable) + suite.True(*dbStatus.Replyable) + suite.True(*dbStatus.Likeable) // account should be in the database now too account, err := suite.db.GetAccountByURI(context.Background(), status.AccountURI) suite.NoError(err) suite.NotNil(account) - suite.True(account.Discoverable) + suite.True(*account.Discoverable) suite.Equal("https://unknown-instance.com/users/brand_new_person", account.URI) suite.Equal("hey I'm a new person, your instance hasn't seen me yet uwu", account.Note) suite.Equal("Geoff Brando New Personson", account.DisplayName) @@ -86,7 +86,7 @@ func (suite *StatusTestSuite) TestDereferenceStatusWithMention() { suite.Equal("https://unknown-instance.com/users/@brand_new_person/01FE5Y30E3W4P7TRE0R98KAYQV", status.URL) suite.Equal("Hey @the_mighty_zork@localhost:8080 how's it going?", status.Content) suite.Equal("https://unknown-instance.com/users/brand_new_person", status.AccountURI) - suite.False(status.Local) + suite.False(*status.Local) suite.Empty(status.ContentWarning) suite.Equal(gtsmodel.VisibilityPublic, status.Visibility) suite.Equal(ap.ObjectNote, status.ActivityStreamsType) @@ -95,16 +95,16 @@ func (suite *StatusTestSuite) TestDereferenceStatusWithMention() { dbStatus, err := suite.db.GetStatusByURI(context.Background(), status.URI) suite.NoError(err) suite.Equal(status.ID, dbStatus.ID) - suite.True(dbStatus.Federated) - suite.True(dbStatus.Boostable) - suite.True(dbStatus.Replyable) - suite.True(dbStatus.Likeable) + suite.True(*dbStatus.Federated) + suite.True(*dbStatus.Boostable) + suite.True(*dbStatus.Replyable) + suite.True(*dbStatus.Likeable) // account should be in the database now too account, err := suite.db.GetAccountByURI(context.Background(), status.AccountURI) suite.NoError(err) suite.NotNil(account) - suite.True(account.Discoverable) + suite.True(*account.Discoverable) suite.Equal("https://unknown-instance.com/users/brand_new_person", account.URI) suite.Equal("hey I'm a new person, your instance hasn't seen me yet uwu", account.Note) suite.Equal("Geoff Brando New Personson", account.DisplayName) @@ -121,7 +121,7 @@ func (suite *StatusTestSuite) TestDereferenceStatusWithMention() { suite.Equal(account.ID, m.OriginAccountID) suite.Equal(fetchingAccount.ID, m.TargetAccountID) suite.Equal(account.URI, m.OriginAccountURI) - suite.False(m.Silent) + suite.False(*m.Silent) } func (suite *StatusTestSuite) TestDereferenceStatusWithImageAndNoContent() { @@ -137,7 +137,7 @@ func (suite *StatusTestSuite) TestDereferenceStatusWithImageAndNoContent() { suite.Equal("https://turnip.farm/@turniplover6969/70c53e54-3146-42d5-a630-83c8b6c7c042", status.URL) suite.Equal("", status.Content) suite.Equal("https://turnip.farm/users/turniplover6969", status.AccountURI) - suite.False(status.Local) + suite.False(*status.Local) suite.Empty(status.ContentWarning) suite.Equal(gtsmodel.VisibilityPublic, status.Visibility) suite.Equal(ap.ObjectNote, status.ActivityStreamsType) @@ -146,16 +146,16 @@ func (suite *StatusTestSuite) TestDereferenceStatusWithImageAndNoContent() { dbStatus, err := suite.db.GetStatusByURI(context.Background(), status.URI) suite.NoError(err) suite.Equal(status.ID, dbStatus.ID) - suite.True(dbStatus.Federated) - suite.True(dbStatus.Boostable) - suite.True(dbStatus.Replyable) - suite.True(dbStatus.Likeable) + suite.True(*dbStatus.Federated) + suite.True(*dbStatus.Boostable) + suite.True(*dbStatus.Replyable) + suite.True(*dbStatus.Likeable) // account should be in the database now too account, err := suite.db.GetAccountByURI(context.Background(), status.AccountURI) suite.NoError(err) suite.NotNil(account) - suite.True(account.Discoverable) + suite.True(*account.Discoverable) suite.Equal("https://turnip.farm/users/turniplover6969", account.URI) suite.Equal("I just think they're neat", account.Note) suite.Equal("Turnip Lover 6969", account.DisplayName) diff --git a/internal/federation/federatingactor_test.go b/internal/federation/federatingactor_test.go index 905b6a7b4..59687345a 100644 --- a/internal/federation/federatingactor_test.go +++ b/internal/federation/federatingactor_test.go @@ -82,9 +82,9 @@ func (suite *FederatingActorTestSuite) TestSendRemoteFollower() { UpdatedAt: testrig.TimeMustParse("2022-06-02T12:22:21+02:00"), AccountID: testRemoteAccount.ID, TargetAccountID: testAccount.ID, - ShowReblogs: true, + ShowReblogs: testrig.TrueBool(), URI: "http://fossbros-anonymous.io/users/foss_satan/follows/01G1TRWV4AYCDBX5HRWT2EVBCV", - Notify: false, + Notify: testrig.FalseBool(), }) suite.NoError(err) diff --git a/internal/federation/federatingdb/owns.go b/internal/federation/federatingdb/owns.go index 51d11f018..6146f9c0a 100644 --- a/internal/federation/federatingdb/owns.go +++ b/internal/federation/federatingdb/owns.go @@ -62,7 +62,7 @@ func (f *federatingDB) Owns(ctx context.Context, id *url.URL) (bool, error) { // an actual error happened return false, fmt.Errorf("database error fetching status with id %s: %s", uid, err) } - return status.Local, nil + return *status.Local, nil } if uris.IsUserPath(id) { diff --git a/internal/gtsmodel/account.go b/internal/gtsmodel/account.go index 9dcb7bd5b..49db7dbda 100644 --- a/internal/gtsmodel/account.go +++ b/internal/gtsmodel/account.go @@ -44,15 +44,15 @@ type Account struct { Fields []Field `validate:"-"` // a key/value map of fields that this account has added to their profile Note string `validate:"-" bun:""` // A note that this account has on their profile (ie., the account's bio/description of themselves) NoteRaw string `validate:"-" bun:""` // The raw contents of .Note without conversion to HTML, only available when requester = target - Memorial bool `validate:"-" bun:",default:false"` // Is this a memorial account, ie., has the user passed away? + Memorial *bool `validate:"-" bun:",default:false"` // Is this a memorial account, ie., has the user passed away? AlsoKnownAs string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // This account is associated with x account id (TODO: migrate to be AlsoKnownAsID) MovedToAccountID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // This account has moved this account id in the database - Bot bool `validate:"-" bun:",default:false"` // Does this account identify itself as a bot? + Bot *bool `validate:"-" bun:",default:false"` // Does this account identify itself as a bot? Reason string `validate:"-" bun:""` // What reason was given for signing up when this account was created? - Locked bool `validate:"-" bun:",default:true"` // Does this account need an approval for new followers? - Discoverable bool `validate:"-" bun:",default:false"` // Should this account be shown in the instance's profile directory? + Locked *bool `validate:"-" bun:",default:true"` // Does this account need an approval for new followers? + Discoverable *bool `validate:"-" bun:",default:false"` // Should this account be shown in the instance's profile directory? Privacy Visibility `validate:"required_without=Domain,omitempty,oneof=public unlocked followers_only mutuals_only direct" bun:",nullzero"` // Default post privacy for this account - Sensitive bool `validate:"-" bun:",default:false"` // Set posts from this account to sensitive by default? + Sensitive *bool `validate:"-" bun:",default:false"` // Set posts from this account to sensitive by default? Language string `validate:"omitempty,bcp47_language_tag" bun:",nullzero,notnull,default:'en'"` // What language does this account post in? StatusFormat string `validate:"required_without=Domain,omitempty,oneof=plain markdown" bun:",nullzero"` // What is the default format for statuses posted by this account (only for local accounts). URI string `validate:"required,url" bun:",nullzero,notnull,unique"` // ActivityPub URI for this account. @@ -70,7 +70,7 @@ type Account struct { SensitizedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // When was this account set to have all its media shown as sensitive? SilencedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // When was this account silenced (eg., statuses only visible to followers, not public)? SuspendedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero"` // When was this account suspended (eg., don't allow it to log in/post, don't accept media/posts from this account) - HideCollections bool `validate:"-" bun:",default:false"` // Hide this account's collections + HideCollections *bool `validate:"-" bun:",default:false"` // Hide this account's collections SuspensionOrigin string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // id of the database entry that caused this account to become suspended -- can be an account ID or a domain block ID } diff --git a/internal/gtsmodel/domainblock.go b/internal/gtsmodel/domainblock.go index 5782b851d..000298aa9 100644 --- a/internal/gtsmodel/domainblock.go +++ b/internal/gtsmodel/domainblock.go @@ -30,6 +30,6 @@ type DomainBlock struct { CreatedByAccount *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to createdByAccountID PrivateComment string `validate:"-" bun:""` // Private comment on this block, viewable to admins PublicComment string `validate:"-" bun:""` // Public comment on this block, viewable (optionally) by everyone - Obfuscate bool `validate:"-" bun:",default:false"` // whether the domain name should appear obfuscated when displaying it publicly + Obfuscate *bool `validate:"-" bun:",nullzero,notnull,default:false"` // whether the domain name should appear obfuscated when displaying it publicly SubscriptionID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // if this block was created through a subscription, what's the subscription ID? } diff --git a/internal/gtsmodel/emoji.go b/internal/gtsmodel/emoji.go index 860eb46c8..3460cc63e 100644 --- a/internal/gtsmodel/emoji.go +++ b/internal/gtsmodel/emoji.go @@ -38,8 +38,8 @@ type Emoji struct { ImageFileSize int `validate:"required,min=1" bun:",nullzero,notnull"` // Size of the emoji image file in bytes, for serving purposes. ImageStaticFileSize int `validate:"required,min=1" bun:",nullzero,notnull"` // Size of the static version of the emoji image file in bytes, for serving purposes. ImageUpdatedAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // When was the emoji image last updated? - Disabled bool `validate:"-" bun:",notnull,default:false"` // Has a moderation action disabled this emoji from being shown? + Disabled *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Has a moderation action disabled this emoji from being shown? URI string `validate:"url" bun:",nullzero,notnull,unique"` // ActivityPub uri of this emoji. Something like 'https://example.org/emojis/1234' - VisibleInPicker bool `validate:"-" bun:",notnull,default:true"` // Is this emoji visible in the admin emoji picker? + VisibleInPicker *bool `validate:"-" bun:",nullzero,notnull,default:true"` // Is this emoji visible in the admin emoji picker? CategoryID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // In which emoji category is this emoji visible? } diff --git a/internal/gtsmodel/follow.go b/internal/gtsmodel/follow.go index 15f01cfd2..8f5a01d3b 100644 --- a/internal/gtsmodel/follow.go +++ b/internal/gtsmodel/follow.go @@ -30,6 +30,6 @@ type Follow struct { Account *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to accountID TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:srctarget,notnull,nullzero"` // Who is the target of this follow ? TargetAccount *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to targetAccountID - ShowReblogs bool `validate:"-" bun:",default:true"` // Does this follow also want to see reblogs and not just posts? - Notify bool `validate:"-" bun:",default:false"` // does the following account want to be notified when the followed account posts? + ShowReblogs *bool `validate:"-" bun:",nullzero,notnull,default:true"` // Does this follow also want to see reblogs and not just posts? + Notify *bool `validate:"-" bun:",nullzero,notnull,default:false"` // does the following account want to be notified when the followed account posts? } diff --git a/internal/gtsmodel/followrequest.go b/internal/gtsmodel/followrequest.go index 4f7c8b807..b6c49a6ec 100644 --- a/internal/gtsmodel/followrequest.go +++ b/internal/gtsmodel/followrequest.go @@ -30,6 +30,6 @@ type FollowRequest struct { Account *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to accountID TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),unique:frsrctarget,notnull,nullzero"` // Who is the target of this follow request? TargetAccount *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to targetAccountID - ShowReblogs bool `validate:"-" bun:",default:true"` // Does this follow also want to see reblogs and not just posts? - Notify bool `validate:"-" bun:",default:false"` // does the following account want to be notified when the followed account posts? + ShowReblogs *bool `validate:"-" bun:",nullzero,notnull,default:true"` // Does this follow also want to see reblogs and not just posts? + Notify *bool `validate:"-" bun:",nullzero,notnull,default:false"` // does the following account want to be notified when the followed account posts? } diff --git a/internal/gtsmodel/mediaattachment.go b/internal/gtsmodel/mediaattachment.go index 2cd287eea..8fe8e2ca6 100644 --- a/internal/gtsmodel/mediaattachment.go +++ b/internal/gtsmodel/mediaattachment.go @@ -41,9 +41,9 @@ type MediaAttachment struct { Processing ProcessingStatus `validate:"oneof=0 1 2 666" bun:",notnull,default:2"` // What is the processing status of this attachment File File `validate:"required" bun:",embed:file_,notnull,nullzero"` // metadata for the whole file Thumbnail Thumbnail `validate:"required" bun:",embed:thumbnail_,notnull,nullzero"` // small image thumbnail derived from a larger image, video, or audio file. - Avatar bool `validate:"-" bun:",notnull,default:false"` // Is this attachment being used as an avatar? - Header bool `validate:"-" bun:",notnull,default:false"` // Is this attachment being used as a header? - Cached bool `validate:"-" bun:",notnull"` // Is this attachment currently cached by our instance? + Avatar *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this attachment being used as an avatar? + Header *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this attachment being used as a header? + Cached *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this attachment currently cached by our instance? } // File refers to the metadata for the whole file diff --git a/internal/gtsmodel/mention.go b/internal/gtsmodel/mention.go index fd1d45622..dfc6cc3f4 100644 --- a/internal/gtsmodel/mention.go +++ b/internal/gtsmodel/mention.go @@ -35,7 +35,7 @@ type Mention struct { OriginAccount *Account `validate:"-" bun:"rel:belongs-to"` // account referred to by originAccountID TargetAccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // Mention target/receiver account ID TargetAccount *Account `validate:"-" bun:"rel:belongs-to"` // account referred to by targetAccountID - Silent bool `validate:"-" bun:",notnull,default:false"` // Prevent this mention from generating a notification? + Silent *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Prevent this mention from generating a notification? /* NON-DATABASE CONVENIENCE FIELDS diff --git a/internal/gtsmodel/notification.go b/internal/gtsmodel/notification.go index 6a5ee06ef..d05898615 100644 --- a/internal/gtsmodel/notification.go +++ b/internal/gtsmodel/notification.go @@ -32,7 +32,7 @@ type Notification struct { OriginAccount *Account `validate:"-" bun:"rel:belongs-to"` // Account corresponding to originAccountID StatusID string `validate:"required_if=NotificationType mention,required_if=NotificationType reblog,required_if=NotificationType favourite,required_if=NotificationType status,omitempty,ulid" bun:"type:CHAR(26),nullzero"` // If the notification pertains to a status, what is the database ID of that status? Status *Status `validate:"-" bun:"rel:belongs-to"` // Status corresponding to statusID - Read bool `validate:"-" bun:",notnull,default:false"` // Notification has been seen/read + Read *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Notification has been seen/read } // NotificationType describes the reason/type of this notification. diff --git a/internal/gtsmodel/status.go b/internal/gtsmodel/status.go index e798ea41b..1b4cc7174 100644 --- a/internal/gtsmodel/status.go +++ b/internal/gtsmodel/status.go @@ -38,7 +38,7 @@ type Status struct { Mentions []*Mention `validate:"-" bun:"attached_mentions,rel:has-many"` // Mentions corresponding to mentionIDs EmojiIDs []string `validate:"dive,ulid" bun:"emojis,array"` // Database IDs of any emojis used in this status Emojis []*Emoji `validate:"-" bun:"attached_emojis,m2m:status_to_emojis"` // Emojis corresponding to emojiIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation - Local bool `validate:"-" bun:",notnull,default:false"` // is this status from a local account? + Local *bool `validate:"-" bun:",nullzero,notnull,default:false"` // is this status from a local account? AccountID string `validate:"required,ulid" bun:"type:CHAR(26),nullzero,notnull"` // which account posted this status? Account *Account `validate:"-" bun:"rel:belongs-to"` // account corresponding to accountID AccountURI string `validate:"required,url" bun:",nullzero,notnull"` // activitypub uri of the owner of this status @@ -53,17 +53,17 @@ type Status struct { BoostOfAccount *Account `validate:"-" bun:"rel:belongs-to"` // account that corresponds to boostOfAccountID ContentWarning string `validate:"-" bun:",nullzero"` // cw string for this status Visibility Visibility `validate:"oneof=public unlocked followers_only mutuals_only direct" bun:",nullzero,notnull"` // visibility entry for this status - Sensitive bool `validate:"-" bun:",notnull,default:false"` // mark the status as sensitive? + Sensitive *bool `validate:"-" bun:",nullzero,notnull,default:false"` // mark the status as sensitive? Language string `validate:"-" bun:",nullzero"` // what language is this status written in? CreatedWithApplicationID string `validate:"required_if=Local true,omitempty,ulid" bun:"type:CHAR(26),nullzero"` // Which application was used to create this status? CreatedWithApplication *Application `validate:"-" bun:"rel:belongs-to"` // application corresponding to createdWithApplicationID ActivityStreamsType string `validate:"required" bun:",nullzero,notnull"` // What is the activitystreams type of this status? See: https://www.w3.org/TR/activitystreams-vocabulary/#object-types. Will probably almost always be Note but who knows!. Text string `validate:"-" bun:""` // Original text of the status without formatting - Pinned bool `validate:"-" bun:",notnull,default:false"` // Has this status been pinned by its owner? - Federated bool `validate:"-" bun:",notnull"` // This status will be federated beyond the local timeline(s) - Boostable bool `validate:"-" bun:",notnull"` // This status can be boosted/reblogged - Replyable bool `validate:"-" bun:",notnull"` // This status can be replied to - Likeable bool `validate:"-" bun:",notnull"` // This status can be liked/faved + Pinned *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Has this status been pinned by its owner? + Federated *bool `validate:"-" bun:",notnull"` // This status will be federated beyond the local timeline(s) + Boostable *bool `validate:"-" bun:",notnull"` // This status can be boosted/reblogged + Replyable *bool `validate:"-" bun:",notnull"` // This status can be replied to + Likeable *bool `validate:"-" bun:",notnull"` // This status can be liked/faved } /* diff --git a/internal/gtsmodel/tag.go b/internal/gtsmodel/tag.go index c73555311..09da27f3a 100644 --- a/internal/gtsmodel/tag.go +++ b/internal/gtsmodel/tag.go @@ -28,7 +28,7 @@ type Tag struct { URL string `validate:"required,url" bun:",nullzero,notnull"` // Href/web address of this tag, eg https://example.org/tags/somehashtag Name string `validate:"required" bun:",unique,nullzero,notnull"` // name of this tag -- the tag without the hash part FirstSeenFromAccountID string `validate:"omitempty,ulid" bun:"type:CHAR(26),nullzero"` // Which account ID is the first one we saw using this tag? - Useable bool `validate:"-" bun:",notnull,default:true"` // can our instance users use this tag? - Listable bool `validate:"-" bun:",notnull,default:true"` // can our instance users look up this tag? + Useable *bool `validate:"-" bun:",nullzero,notnull,default:true"` // can our instance users use this tag? + Listable *bool `validate:"-" bun:",nullzero,notnull,default:true"` // can our instance users look up this tag? LastStatusAt time.Time `validate:"-" bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was this tag last used? } diff --git a/internal/gtsmodel/user.go b/internal/gtsmodel/user.go index 929939572..0d108253a 100644 --- a/internal/gtsmodel/user.go +++ b/internal/gtsmodel/user.go @@ -50,10 +50,10 @@ type User struct { ConfirmationSentAt time.Time `validate:"required_with=ConfirmationToken" bun:"type:timestamptz,nullzero"` // When did we send email confirmation to this user? ConfirmedAt time.Time `validate:"required_with=Email" bun:"type:timestamptz,nullzero"` // When did the user confirm their email address UnconfirmedEmail string `validate:"required_without=Email" bun:",nullzero"` // Email address that hasn't yet been confirmed - Moderator bool `validate:"-" bun:",notnull,default:false"` // Is this user a moderator? - Admin bool `validate:"-" bun:",notnull,default:false"` // Is this user an admin? - Disabled bool `validate:"-" bun:",notnull,default:false"` // Is this user disabled from posting? - Approved bool `validate:"-" bun:",notnull,default:false"` // Has this user been approved by a moderator? + Moderator *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this user a moderator? + Admin *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this user an admin? + Disabled *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Is this user disabled from posting? + Approved *bool `validate:"-" bun:",nullzero,notnull,default:false"` // Has this user been approved by a moderator? ResetPasswordToken string `validate:"required_with=ResetPasswordSentAt" bun:",nullzero"` // The generated token that the user can use to reset their password ResetPasswordSentAt time.Time `validate:"required_with=ResetPasswordToken" bun:"type:timestamptz,nullzero"` // When did we email the user their reset-password email? } diff --git a/internal/media/processingemoji.go b/internal/media/processingemoji.go index b28dc7f02..098d0aa19 100644 --- a/internal/media/processingemoji.go +++ b/internal/media/processingemoji.go @@ -230,6 +230,9 @@ func (m *manager) preProcessEmoji(ctx context.Context, data DataFunc, postData P return nil, fmt.Errorf("preProcessEmoji: error fetching this instance account from the db: %s", err) } + disabled := false + visibleInPicker := true + // populate initial fields on the emoji -- some of these will be overwritten as we proceed emoji := >smodel.Emoji{ ID: id, @@ -248,9 +251,9 @@ func (m *manager) preProcessEmoji(ctx context.Context, data DataFunc, postData P ImageFileSize: 0, ImageStaticFileSize: 0, ImageUpdatedAt: time.Now(), - Disabled: false, + Disabled: &disabled, URI: uri, - VisibleInPicker: true, + VisibleInPicker: &visibleInPicker, CategoryID: "", } @@ -274,11 +277,11 @@ func (m *manager) preProcessEmoji(ctx context.Context, data DataFunc, postData P } if ai.Disabled != nil { - emoji.Disabled = *ai.Disabled + emoji.Disabled = ai.Disabled } if ai.VisibleInPicker != nil { - emoji.VisibleInPicker = *ai.VisibleInPicker + emoji.VisibleInPicker = ai.VisibleInPicker } if ai.CategoryID != nil { diff --git a/internal/media/processingmedia.go b/internal/media/processingmedia.go index 885e97417..914d6d276 100644 --- a/internal/media/processingmedia.go +++ b/internal/media/processingmedia.go @@ -346,7 +346,9 @@ func (p *ProcessingMedia) store(ctx context.Context) error { if err := p.storage.PutStream(ctx, p.attachment.File.Path, clean); err != nil { return fmt.Errorf("store: error storing stream: %s", err) } - p.attachment.Cached = true + + cached := true + p.attachment.Cached = &cached p.read = true if p.postData != nil { @@ -376,6 +378,10 @@ func (m *manager) preProcessMedia(ctx context.Context, data DataFunc, postData P UpdatedAt: time.Now(), } + avatar := false + header := false + cached := false + // populate initial fields on the media attachment -- some of these will be overwritten as we proceed attachment := >smodel.MediaAttachment{ ID: id, @@ -393,9 +399,9 @@ func (m *manager) preProcessMedia(ctx context.Context, data DataFunc, postData P Processing: gtsmodel.ProcessingStatusReceived, File: file, Thumbnail: thumbnail, - Avatar: false, - Header: false, - Cached: false, + Avatar: &avatar, + Header: &header, + Cached: &cached, } // check if we have additional info to add to the attachment, @@ -426,11 +432,11 @@ func (m *manager) preProcessMedia(ctx context.Context, data DataFunc, postData P } if ai.Avatar != nil { - attachment.Avatar = *ai.Avatar + attachment.Avatar = ai.Avatar } if ai.Header != nil { - attachment.Header = *ai.Header + attachment.Header = ai.Header } if ai.FocusX != nil { diff --git a/internal/media/prunemeta.go b/internal/media/prunemeta.go index 7b2b14f98..63bdb00b5 100644 --- a/internal/media/prunemeta.go +++ b/internal/media/prunemeta.go @@ -46,8 +46,8 @@ func (m *manager) PruneAllMeta(ctx context.Context) (int, error) { // - is an avatar but isn't the owning account's current avatar for _, attachment := range attachments { if attachment.Account == nil || - (attachment.Header && attachment.ID != attachment.Account.HeaderMediaAttachmentID) || - (attachment.Avatar && attachment.ID != attachment.Account.AvatarMediaAttachmentID) { + (*attachment.Header && attachment.ID != attachment.Account.HeaderMediaAttachmentID) || + (*attachment.Avatar && attachment.ID != attachment.Account.AvatarMediaAttachmentID) { if err := m.pruneOneAvatarOrHeader(ctx, attachment); err != nil { return totalPruned, err } diff --git a/internal/media/prunemeta_test.go b/internal/media/prunemeta_test.go index b02156587..32a3b9a5c 100644 --- a/internal/media/prunemeta_test.go +++ b/internal/media/prunemeta_test.go @@ -40,7 +40,7 @@ func (suite *PruneMetaTestSuite) TestPruneMeta() { zork := suite.testAccounts["local_account_1"] zork.AvatarMediaAttachmentID = "" zork.HeaderMediaAttachmentID = "" - if err := suite.db.UpdateByPrimaryKey(ctx, zork); err != nil { + if err := suite.db.UpdateByPrimaryKey(ctx, zork, "avatar_media_attachment_id", "header_media_attachment_id"); err != nil { panic(err) } @@ -72,7 +72,7 @@ func (suite *PruneMetaTestSuite) TestPruneMetaTwice() { zork := suite.testAccounts["local_account_1"] zork.AvatarMediaAttachmentID = "" zork.HeaderMediaAttachmentID = "" - if err := suite.db.UpdateByPrimaryKey(ctx, zork); err != nil { + if err := suite.db.UpdateByPrimaryKey(ctx, zork, "avatar_media_attachment_id", "header_media_attachment_id"); err != nil { panic(err) } @@ -95,14 +95,14 @@ func (suite *PruneMetaTestSuite) TestPruneMetaMultipleAccounts() { zork := suite.testAccounts["local_account_1"] zork.AvatarMediaAttachmentID = "" zork.HeaderMediaAttachmentID = "" - if err := suite.db.UpdateByPrimaryKey(ctx, zork); err != nil { + if err := suite.db.UpdateByPrimaryKey(ctx, zork, "avatar_media_attachment_id", "header_media_attachment_id"); err != nil { panic(err) } // set zork's unused header as belonging to turtle turtle := suite.testAccounts["local_account_1"] zorkOldHeader.AccountID = turtle.ID - if err := suite.db.UpdateByPrimaryKey(ctx, zorkOldHeader); err != nil { + if err := suite.db.UpdateByPrimaryKey(ctx, zorkOldHeader, "account_id"); err != nil { panic(err) } diff --git a/internal/media/pruneremote.go b/internal/media/pruneremote.go index 5c3335511..43ce53cdc 100644 --- a/internal/media/pruneremote.go +++ b/internal/media/pruneremote.go @@ -64,13 +64,17 @@ func (m *manager) PruneAllRemote(ctx context.Context, olderThanDays int) (int, e } func (m *manager) pruneOneRemote(ctx context.Context, attachment *gtsmodel.MediaAttachment) error { + var changed bool + if attachment.File.Path != "" { // delete the full size attachment from storage log.Tracef("pruneOneRemote: deleting %s", attachment.File.Path) if err := m.storage.Delete(ctx, attachment.File.Path); err != nil && err != storage.ErrNotFound { return err } - attachment.Cached = false + cached := false + attachment.Cached = &cached + changed = true } if attachment.Thumbnail.Path != "" { @@ -79,9 +83,15 @@ func (m *manager) pruneOneRemote(ctx context.Context, attachment *gtsmodel.Media if err := m.storage.Delete(ctx, attachment.Thumbnail.Path); err != nil && err != storage.ErrNotFound { return err } - attachment.Cached = false + cached := false + attachment.Cached = &cached + changed = true } // update the attachment to reflect that we no longer have it cached - return m.db.UpdateByPrimaryKey(ctx, attachment) + if changed { + return m.db.UpdateByPrimaryKey(ctx, attachment, "updated_at", "cached") + } + + return nil } diff --git a/internal/media/pruneremote_test.go b/internal/media/pruneremote_test.go index f5ed8a618..ddf4cb568 100644 --- a/internal/media/pruneremote_test.go +++ b/internal/media/pruneremote_test.go @@ -35,7 +35,7 @@ type PruneRemoteTestSuite struct { func (suite *PruneRemoteTestSuite) TestPruneRemote() { testAttachment := suite.testAttachments["remote_account_1_status_1_attachment_1"] - suite.True(testAttachment.Cached) + suite.True(*testAttachment.Cached) totalPruned, err := suite.manager.PruneAllRemote(context.Background(), 1) suite.NoError(err) @@ -45,7 +45,7 @@ func (suite *PruneRemoteTestSuite) TestPruneRemote() { suite.NoError(err) // the media should no longer be cached - suite.False(prunedAttachment.Cached) + suite.False(*prunedAttachment.Cached) } func (suite *PruneRemoteTestSuite) TestPruneRemoteTwice() { @@ -91,7 +91,7 @@ func (suite *PruneRemoteTestSuite) TestPruneAndRecache() { suite.NotNil(recachedAttachment) // recachedAttachment should be basically the same as the old attachment - suite.True(recachedAttachment.Cached) + suite.True(*recachedAttachment.Cached) suite.Equal(testAttachment.ID, recachedAttachment.ID) suite.Equal(testAttachment.File.Path, recachedAttachment.File.Path) // file should be stored in the same place suite.Equal(testAttachment.Thumbnail.Path, recachedAttachment.Thumbnail.Path) // as should the thumbnail @@ -111,7 +111,7 @@ func (suite *PruneRemoteTestSuite) TestPruneOneNonExistent() { // Delete this attachment cached on disk media, err := suite.db.GetAttachmentByID(ctx, testAttachment.ID) suite.NoError(err) - suite.True(media.Cached) + suite.True(*media.Cached) err = suite.storage.Delete(ctx, media.File.Path) suite.NoError(err) diff --git a/internal/media/pruneunusedlocal_test.go b/internal/media/pruneunusedlocal_test.go index 976a6226c..d1424a4c9 100644 --- a/internal/media/pruneunusedlocal_test.go +++ b/internal/media/pruneunusedlocal_test.go @@ -32,7 +32,7 @@ type PruneUnusedLocalTestSuite struct { func (suite *PruneUnusedLocalTestSuite) TestPruneUnusedLocal() { testAttachment := suite.testAttachments["local_account_1_unattached_1"] - suite.True(testAttachment.Cached) + suite.True(*testAttachment.Cached) totalPruned, err := suite.manager.PruneUnusedLocalAttachments(context.Background()) suite.NoError(err) @@ -60,7 +60,7 @@ func (suite *PruneUnusedLocalTestSuite) TestPruneOneNonExistent() { // Delete this attachment cached on disk media, err := suite.db.GetAttachmentByID(ctx, testAttachment.ID) suite.NoError(err) - suite.True(media.Cached) + suite.True(*media.Cached) err = suite.storage.Delete(ctx, media.File.Path) suite.NoError(err) diff --git a/internal/processing/account/createfollow.go b/internal/processing/account/createfollow.go index 49430b9fb..7e7692135 100644 --- a/internal/processing/account/createfollow.go +++ b/internal/processing/account/createfollow.go @@ -76,19 +76,21 @@ func (p *processor) FollowCreate(ctx context.Context, requestingAccount *gtsmode return nil, gtserror.NewErrorInternalError(err) } + showReblogs := true + notify := false fr := >smodel.FollowRequest{ ID: newFollowID, AccountID: requestingAccount.ID, TargetAccountID: form.ID, - ShowReblogs: true, + ShowReblogs: &showReblogs, URI: uris.GenerateURIForFollow(requestingAccount.Username, newFollowID), - Notify: false, + Notify: ¬ify, } if form.Reblogs != nil { - fr.ShowReblogs = *form.Reblogs + fr.ShowReblogs = form.Reblogs } if form.Notify != nil { - fr.Notify = *form.Notify + fr.Notify = form.Notify } // whack it in the database @@ -97,7 +99,7 @@ func (p *processor) FollowCreate(ctx context.Context, requestingAccount *gtsmode } // if it's a local account that's not locked we can just straight up accept the follow request - if !targetAcct.Locked && targetAcct.Domain == "" { + if !*targetAcct.Locked && targetAcct.Domain == "" { if _, err := p.db.AcceptFollowRequest(ctx, requestingAccount.ID, form.ID); err != nil { return nil, gtserror.NewErrorInternalError(fmt.Errorf("accountfollowcreate: error accepting folow request for local unlocked account: %s", err)) } diff --git a/internal/processing/account/delete.go b/internal/processing/account/delete.go index 7b382f17c..f71f08954 100644 --- a/internal/processing/account/delete.go +++ b/internal/processing/account/delete.go @@ -285,8 +285,10 @@ selectStatusesLoop: account.HeaderRemoteURL = "" account.Reason = "" account.Fields = []gtsmodel.Field{} - account.HideCollections = true - account.Discoverable = false + hideCollections := true + account.HideCollections = &hideCollections + discoverable := false + account.Discoverable = &discoverable account.SuspendedAt = time.Now() account.SuspensionOrigin = origin diff --git a/internal/processing/account/update.go b/internal/processing/account/update.go index 804e7ba7e..3b844a160 100644 --- a/internal/processing/account/update.go +++ b/internal/processing/account/update.go @@ -39,11 +39,11 @@ import ( func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form *apimodel.UpdateCredentialsRequest) (*apimodel.Account, gtserror.WithCode) { if form.Discoverable != nil { - account.Discoverable = *form.Discoverable + account.Discoverable = form.Discoverable } if form.Bot != nil { - account.Bot = *form.Bot + account.Bot = form.Bot } if form.DisplayName != nil { @@ -92,7 +92,7 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form } if form.Locked != nil { - account.Locked = *form.Locked + account.Locked = form.Locked } if form.Source != nil { @@ -104,7 +104,7 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form } if form.Source.Sensitive != nil { - account.Sensitive = *form.Source.Sensitive + account.Sensitive = form.Source.Sensitive } if form.Source.Privacy != nil { diff --git a/internal/processing/account/update_test.go b/internal/processing/account/update_test.go index 7e4ca818e..0483154c6 100644 --- a/internal/processing/account/update_test.go +++ b/internal/processing/account/update_test.go @@ -65,7 +65,7 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateSimple() { // fields should be updated in the database as well dbAccount, err := suite.db.GetAccountByID(context.Background(), testAccount.ID) suite.NoError(err) - suite.True(dbAccount.Locked) + suite.True(*dbAccount.Locked) suite.Equal(displayName, dbAccount.DisplayName) suite.Equal(`<p><a href="http://localhost:8080/tags/hello" class="mention hashtag" rel="tag nofollow noreferrer noopener" target="_blank">#<span>hello</span></a> here i am!</p>`, dbAccount.Note) } @@ -107,7 +107,7 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateWithMention() { // fields should be updated in the database as well dbAccount, err := suite.db.GetAccountByID(context.Background(), testAccount.ID) suite.NoError(err) - suite.True(dbAccount.Locked) + suite.True(*dbAccount.Locked) suite.Equal(displayName, dbAccount.DisplayName) suite.Equal(noteExpected, dbAccount.Note) } diff --git a/internal/processing/admin/createdomainblock.go b/internal/processing/admin/createdomainblock.go index b42445380..969cb52de 100644 --- a/internal/processing/admin/createdomainblock.go +++ b/internal/processing/admin/createdomainblock.go @@ -62,7 +62,7 @@ func (p *processor) DomainBlockCreate(ctx context.Context, account *gtsmodel.Acc CreatedByAccountID: account.ID, PrivateComment: text.SanitizePlaintext(privateComment), PublicComment: text.SanitizePlaintext(publicComment), - Obfuscate: obfuscate, + Obfuscate: &obfuscate, SubscriptionID: subscriptionID, } @@ -101,6 +101,19 @@ func (p *processor) initiateDomainBlockSideEffects(ctx context.Context, account // if we have an instance entry for this domain, update it with the new block ID and clear all fields instance := >smodel.Instance{} if err := p.db.GetWhere(ctx, []db.Where{{Key: "domain", Value: block.Domain}}, instance); err == nil { + updatingColumns := []string{ + "title", + "updated_at", + "suspended_at", + "domain_block_id", + "short_description", + "description", + "terms", + "contact_email", + "contact_account_username", + "contact_account_id", + "version", + } instance.Title = "" instance.UpdatedAt = time.Now() instance.SuspendedAt = time.Now() @@ -112,7 +125,7 @@ func (p *processor) initiateDomainBlockSideEffects(ctx context.Context, account instance.ContactAccountUsername = "" instance.ContactAccountID = "" instance.Version = "" - if err := p.db.UpdateByPrimaryKey(ctx, instance); err != nil { + if err := p.db.UpdateByPrimaryKey(ctx, instance, updatingColumns...); err != nil { l.Errorf("domainBlockProcessSideEffects: db error updating instance: %s", err) } l.Debug("domainBlockProcessSideEffects: instance entry updated") diff --git a/internal/processing/admin/deletedomainblock.go b/internal/processing/admin/deletedomainblock.go index 832b9256e..29e911888 100644 --- a/internal/processing/admin/deletedomainblock.go +++ b/internal/processing/admin/deletedomainblock.go @@ -58,9 +58,11 @@ func (p *processor) DomainBlockDelete(ctx context.Context, account *gtsmodel.Acc {Key: "domain", Value: domainBlock.Domain, CaseInsensitive: true}, {Key: "domain_block_id", Value: id}, }, i); err == nil { + updatingColumns := []string{"suspended_at", "domain_block_id", "updated_at"} i.SuspendedAt = time.Time{} i.DomainBlockID = "" - if err := p.db.UpdateByPrimaryKey(ctx, i); err != nil { + i.UpdatedAt = time.Now() + if err := p.db.UpdateByPrimaryKey(ctx, i, updatingColumns...); err != nil { return nil, gtserror.NewErrorInternalError(fmt.Errorf("couldn't update database entry for instance %s: %s", domainBlock.Domain, err)) } } diff --git a/internal/processing/admin/emoji.go b/internal/processing/admin/emoji.go index f91f972a8..36657a6aa 100644 --- a/internal/processing/admin/emoji.go +++ b/internal/processing/admin/emoji.go @@ -33,7 +33,7 @@ import ( ) func (p *processor) EmojiCreate(ctx context.Context, account *gtsmodel.Account, user *gtsmodel.User, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, gtserror.WithCode) { - if !user.Admin { + if !*user.Admin { return nil, gtserror.NewErrorUnauthorized(fmt.Errorf("user %s not an admin", user.ID), "user is not an admin") } diff --git a/internal/processing/fromclientapi.go b/internal/processing/fromclientapi.go index 0f684f200..2c4f20d81 100644 --- a/internal/processing/fromclientapi.go +++ b/internal/processing/fromclientapi.go @@ -392,7 +392,7 @@ func (p *processor) federateAccountDelete(ctx context.Context, account *gtsmodel func (p *processor) federateStatus(ctx context.Context, status *gtsmodel.Status) error { // do nothing if the status shouldn't be federated - if !status.Federated { + if !*status.Federated { return nil } diff --git a/internal/processing/fromclientapi_test.go b/internal/processing/fromclientapi_test.go index 014266b37..0e620c9e9 100644 --- a/internal/processing/fromclientapi_test.go +++ b/internal/processing/fromclientapi_test.go @@ -66,20 +66,21 @@ func (suite *FromClientAPITestSuite) TestProcessStreamNewStatus() { EmojiIDs: []string{}, CreatedAt: testrig.TimeMustParse("2021-10-20T11:36:45Z"), UpdatedAt: testrig.TimeMustParse("2021-10-20T11:36:45Z"), - Local: true, + Local: testrig.TrueBool(), AccountURI: "http://localhost:8080/users/admin", AccountID: "01F8MH17FWEB39HZJ76B6VXSKF", InReplyToID: "", BoostOfID: "", ContentWarning: "", Visibility: gtsmodel.VisibilityFollowersOnly, - Sensitive: false, + Sensitive: testrig.FalseBool(), Language: "en", CreatedWithApplicationID: "01F8MGXQRHYF5QPMTMXP78QC2F", - Federated: false, // set federated as false for this one, since we're not testing federation stuff now - Boostable: true, - Replyable: true, - Likeable: true, + Pinned: testrig.FalseBool(), + Federated: testrig.FalseBool(), + Boostable: testrig.TrueBool(), + Replyable: testrig.TrueBool(), + Likeable: testrig.TrueBool(), ActivityStreamsType: ap.ObjectNote, } diff --git a/internal/processing/fromfederator.go b/internal/processing/fromfederator.go index 132b33f4c..ad8273869 100644 --- a/internal/processing/fromfederator.go +++ b/internal/processing/fromfederator.go @@ -259,7 +259,7 @@ func (p *processor) processCreateFollowRequestFromFederator(ctx context.Context, followRequest.TargetAccount = a } - if followRequest.TargetAccount.Locked { + if *followRequest.TargetAccount.Locked { // if the account is locked just notify the follow request and nothing else return p.notifyFollowRequest(ctx, followRequest) } diff --git a/internal/processing/fromfederator_test.go b/internal/processing/fromfederator_test.go index 4d339cb18..86b63dade 100644 --- a/internal/processing/fromfederator_test.go +++ b/internal/processing/fromfederator_test.go @@ -84,13 +84,14 @@ func (suite *FromFederatorTestSuite) TestProcessFederationAnnounce() { suite.Equal(boostedStatus.AccountID, notif.TargetAccountID) suite.Equal(announceStatus.AccountID, notif.OriginAccountID) suite.Equal(announceStatus.ID, notif.StatusID) - suite.False(notif.Read) + suite.False(*notif.Read) } func (suite *FromFederatorTestSuite) TestProcessReplyMention() { repliedAccount := suite.testAccounts["local_account_1"] repliedStatus := suite.testStatuses["local_account_1_status_1"] replyingAccount := suite.testAccounts["remote_account_1"] + replyingStatus := >smodel.Status{ CreatedAt: time.Now(), UpdatedAt: time.Now(), @@ -110,10 +111,10 @@ func (suite *FromFederatorTestSuite) TestProcessReplyMention() { InReplyToAccountID: repliedAccount.ID, Visibility: gtsmodel.VisibilityUnlocked, ActivityStreamsType: ap.ObjectNote, - Federated: true, - Boostable: true, - Replyable: true, - Likeable: true, + Federated: testrig.TrueBool(), + Boostable: testrig.TrueBool(), + Replyable: testrig.TrueBool(), + Likeable: testrig.FalseBool(), } wssStream, errWithCode := suite.processor.OpenStreamForAccount(context.Background(), repliedAccount, stream.TimelineHome) @@ -156,10 +157,17 @@ func (suite *FromFederatorTestSuite) TestProcessReplyMention() { suite.Equal(replyingStatus.InReplyToAccountID, notif.TargetAccountID) suite.Equal(replyingStatus.AccountID, notif.OriginAccountID) suite.Equal(replyingStatus.ID, notif.StatusID) - suite.False(notif.Read) + suite.False(*notif.Read) + + // the notification should be streamed + var msg *stream.Message + select { + case msg = <-wssStream.Messages: + // fine + case <-time.After(5 * time.Second): + suite.FailNow("no message from wssStream") + } - // the notification should also be streamed - msg := <-wssStream.Messages suite.Equal(stream.EventTypeNotification, msg.Event) suite.NotEmpty(msg.Payload) suite.EqualValues([]string{stream.TimelineHome}, msg.Stream) @@ -222,10 +230,16 @@ func (suite *FromFederatorTestSuite) TestProcessFave() { suite.Equal(fave.TargetAccountID, notif.TargetAccountID) suite.Equal(fave.AccountID, notif.OriginAccountID) suite.Equal(fave.StatusID, notif.StatusID) - suite.False(notif.Read) + suite.False(*notif.Read) // 2. a notification should be streamed - msg := <-wssStream.Messages + var msg *stream.Message + select { + case msg = <-wssStream.Messages: + // fine + case <-time.After(5 * time.Second): + suite.FailNow("no message from wssStream") + } suite.Equal(stream.EventTypeNotification, msg.Event) suite.NotEmpty(msg.Payload) suite.EqualValues([]string{stream.TimelineNotifications}, msg.Stream) @@ -289,7 +303,7 @@ func (suite *FromFederatorTestSuite) TestProcessFaveWithDifferentReceivingAccoun suite.Equal(fave.TargetAccountID, notif.TargetAccountID) suite.Equal(fave.AccountID, notif.OriginAccountID) suite.Equal(fave.StatusID, notif.StatusID) - suite.False(notif.Read) + suite.False(*notif.Read) // 2. no notification should be streamed to the account that received the fave message, because they weren't the target suite.Empty(wssStream.Messages) @@ -309,9 +323,9 @@ func (suite *FromFederatorTestSuite) TestProcessAccountDelete() { UpdatedAt: time.Now().Add(-1 * time.Hour), AccountID: deletedAccount.ID, TargetAccountID: receivingAccount.ID, - ShowReblogs: true, + ShowReblogs: testrig.TrueBool(), URI: fmt.Sprintf("%s/follows/01FGRY72ASHBSET64353DPHK9T", deletedAccount.URI), - Notify: false, + Notify: testrig.FalseBool(), } err := suite.db.Put(ctx, zorkFollowSatan) suite.NoError(err) @@ -322,9 +336,9 @@ func (suite *FromFederatorTestSuite) TestProcessAccountDelete() { UpdatedAt: time.Now().Add(-1 * time.Hour), AccountID: receivingAccount.ID, TargetAccountID: deletedAccount.ID, - ShowReblogs: true, + ShowReblogs: testrig.TrueBool(), URI: fmt.Sprintf("%s/follows/01FGRYAVAWWPP926J175QGM0WV", receivingAccount.URI), - Notify: false, + Notify: testrig.FalseBool(), } err = suite.db.Put(ctx, satanFollowZork) suite.NoError(err) @@ -369,8 +383,8 @@ func (suite *FromFederatorTestSuite) TestProcessAccountDelete() { suite.Empty(dbAccount.HeaderRemoteURL) suite.Empty(dbAccount.Reason) suite.Empty(dbAccount.Fields) - suite.True(dbAccount.HideCollections) - suite.False(dbAccount.Discoverable) + suite.True(*dbAccount.HideCollections) + suite.False(*dbAccount.Discoverable) suite.WithinDuration(time.Now(), dbAccount.SuspendedAt, 30*time.Second) suite.Equal(dbAccount.ID, dbAccount.SuspensionOrigin) } @@ -395,9 +409,9 @@ func (suite *FromFederatorTestSuite) TestProcessFollowRequestLocked() { Account: originAccount, TargetAccountID: targetAccount.ID, TargetAccount: targetAccount, - ShowReblogs: true, + ShowReblogs: testrig.TrueBool(), URI: fmt.Sprintf("%s/follows/01FGRYAVAWWPP926J175QGM0WV", originAccount.URI), - Notify: false, + Notify: testrig.FalseBool(), } err := suite.db.Put(ctx, satanFollowRequestTurtle) @@ -412,7 +426,13 @@ func (suite *FromFederatorTestSuite) TestProcessFollowRequestLocked() { suite.NoError(err) // a notification should be streamed - msg := <-wssStream.Messages + var msg *stream.Message + select { + case msg = <-wssStream.Messages: + // fine + case <-time.After(5 * time.Second): + suite.FailNow("no message from wssStream") + } suite.Equal(stream.EventTypeNotification, msg.Event) suite.NotEmpty(msg.Payload) suite.EqualValues([]string{stream.TimelineHome}, msg.Stream) @@ -446,9 +466,9 @@ func (suite *FromFederatorTestSuite) TestProcessFollowRequestUnlocked() { Account: originAccount, TargetAccountID: targetAccount.ID, TargetAccount: targetAccount, - ShowReblogs: true, + ShowReblogs: testrig.TrueBool(), URI: fmt.Sprintf("%s/follows/01FGRYAVAWWPP926J175QGM0WV", originAccount.URI), - Notify: false, + Notify: testrig.FalseBool(), } err := suite.db.Put(ctx, satanFollowRequestTurtle) @@ -463,7 +483,13 @@ func (suite *FromFederatorTestSuite) TestProcessFollowRequestUnlocked() { suite.NoError(err) // a notification should be streamed - msg := <-wssStream.Messages + var msg *stream.Message + select { + case msg = <-wssStream.Messages: + // fine + case <-time.After(5 * time.Second): + suite.FailNow("no message from wssStream") + } suite.Equal(stream.EventTypeNotification, msg.Event) suite.NotEmpty(msg.Payload) suite.EqualValues([]string{stream.TimelineHome}, msg.Stream) diff --git a/internal/processing/instance.go b/internal/processing/instance.go index 4d1e8b8fd..0a13917ea 100644 --- a/internal/processing/instance.go +++ b/internal/processing/instance.go @@ -81,7 +81,7 @@ func (p *processor) InstancePeersGet(ctx context.Context, authed *oauth.Auth, in } for _, d := range domainBlocks { - if d.Obfuscate { + if *d.Obfuscate { d.Domain = obfuscate(d.Domain) } @@ -123,11 +123,14 @@ func (p *processor) InstancePatch(ctx context.Context, form *apimodel.InstanceSe return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error fetching instance account %s: %s", host, err)) } + updatingColumns := []string{} + // validate & update site title if it's set on the form if form.Title != nil { if err := validate.SiteTitle(*form.Title); err != nil { return nil, gtserror.NewErrorBadRequest(err, fmt.Sprintf("site title invalid: %s", err)) } + updatingColumns = append(updatingColumns, "title") i.Title = text.SanitizePlaintext(*form.Title) // don't allow html in site title } @@ -153,15 +156,16 @@ func (p *processor) InstancePatch(ctx context.Context, form *apimodel.InstanceSe err := fmt.Errorf("user of selected contact account %s is not confirmed", contactAccount.Username) return nil, gtserror.NewErrorBadRequest(err, err.Error()) } - if !contactUser.Approved { + if !*contactUser.Approved { err := fmt.Errorf("user of selected contact account %s is not approved", contactAccount.Username) return nil, gtserror.NewErrorBadRequest(err, err.Error()) } // contact account user must be admin or moderator otherwise what's the point of contacting them - if !contactUser.Admin && !contactUser.Moderator { + if !*contactUser.Admin && !*contactUser.Moderator { err := fmt.Errorf("user of selected contact account %s is neither admin nor moderator", contactAccount.Username) return nil, gtserror.NewErrorBadRequest(err, err.Error()) } + updatingColumns = append(updatingColumns, "contact_account_id") i.ContactAccountID = contactAccount.ID } @@ -173,6 +177,7 @@ func (p *processor) InstancePatch(ctx context.Context, form *apimodel.InstanceSe return nil, gtserror.NewErrorBadRequest(err, err.Error()) } } + updatingColumns = append(updatingColumns, "contact_email") i.ContactEmail = contactEmail } @@ -181,6 +186,7 @@ func (p *processor) InstancePatch(ctx context.Context, form *apimodel.InstanceSe if err := validate.SiteShortDescription(*form.ShortDescription); err != nil { return nil, gtserror.NewErrorBadRequest(err, err.Error()) } + updatingColumns = append(updatingColumns, "short_description") i.ShortDescription = text.SanitizeHTML(*form.ShortDescription) // html is OK in site description, but we should sanitize it } @@ -189,6 +195,7 @@ func (p *processor) InstancePatch(ctx context.Context, form *apimodel.InstanceSe if err := validate.SiteDescription(*form.Description); err != nil { return nil, gtserror.NewErrorBadRequest(err, err.Error()) } + updatingColumns = append(updatingColumns, "description") i.Description = text.SanitizeHTML(*form.Description) // html is OK in site description, but we should sanitize it } @@ -197,6 +204,7 @@ func (p *processor) InstancePatch(ctx context.Context, form *apimodel.InstanceSe if err := validate.SiteTerms(*form.Terms); err != nil { return nil, gtserror.NewErrorBadRequest(err, err.Error()) } + updatingColumns = append(updatingColumns, "terms") i.Terms = text.SanitizeHTML(*form.Terms) // html is OK in site terms, but we should sanitize it } @@ -216,7 +224,7 @@ func (p *processor) InstancePatch(ctx context.Context, form *apimodel.InstanceSe } } - if err := p.db.UpdateByPrimaryKey(ctx, i); err != nil { + if err := p.db.UpdateByPrimaryKey(ctx, i, updatingColumns...); err != nil { return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error updating instance %s: %s", host, err)) } diff --git a/internal/processing/media/getfile.go b/internal/processing/media/getfile.go index 3227cb8c8..52cdcc052 100644 --- a/internal/processing/media/getfile.go +++ b/internal/processing/media/getfile.go @@ -112,7 +112,7 @@ func (p *processor) getAttachmentContent(ctx context.Context, requestingAccount } // if we have the media cached on our server already, we can now simply return it from storage - if a.Cached { + if *a.Cached { return p.retrieveFromStorage(ctx, storagePath, attachmentContent) } @@ -236,7 +236,7 @@ func (p *processor) getEmojiContent(ctx context.Context, wantedEmojiID string, e return nil, gtserror.NewErrorNotFound(fmt.Errorf("emoji %s could not be taken from the db: %s", wantedEmojiID, err)) } - if e.Disabled { + if *e.Disabled { return nil, gtserror.NewErrorNotFound(fmt.Errorf("emoji %s has been disabled", wantedEmojiID)) } diff --git a/internal/processing/media/getfile_test.go b/internal/processing/media/getfile_test.go index 7c6525abe..6ba06426f 100644 --- a/internal/processing/media/getfile_test.go +++ b/internal/processing/media/getfile_test.go @@ -28,6 +28,7 @@ import ( "github.com/stretchr/testify/suite" apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/media" + "github.com/superseriousbusiness/gotosocial/testrig" ) type GetFileTestSuite struct { @@ -67,8 +68,8 @@ func (suite *GetFileTestSuite) TestGetRemoteFileUncached() { // uncache the file from local testAttachment := suite.testAttachments["remote_account_1_status_1_attachment_1"] - testAttachment.Cached = false - err := suite.db.UpdateByPrimaryKey(ctx, testAttachment) + testAttachment.Cached = testrig.FalseBool() + err := suite.db.UpdateByPrimaryKey(ctx, testAttachment, "cached") suite.NoError(err) err = suite.storage.Delete(ctx, testAttachment.File.Path) suite.NoError(err) @@ -103,7 +104,7 @@ func (suite *GetFileTestSuite) TestGetRemoteFileUncached() { // the attachment should be updated in the database dbAttachment, err := suite.db.GetAttachmentByID(ctx, testAttachment.ID) suite.NoError(err) - suite.True(dbAttachment.Cached) + suite.True(*dbAttachment.Cached) // the file should be back in storage at the same path as before refreshedBytes, err := suite.storage.Get(ctx, testAttachment.File.Path) @@ -116,8 +117,8 @@ func (suite *GetFileTestSuite) TestGetRemoteFileUncachedInterrupted() { // uncache the file from local testAttachment := suite.testAttachments["remote_account_1_status_1_attachment_1"] - testAttachment.Cached = false - err := suite.db.UpdateByPrimaryKey(ctx, testAttachment) + testAttachment.Cached = testrig.FalseBool() + err := suite.db.UpdateByPrimaryKey(ctx, testAttachment, "cached") suite.NoError(err) err = suite.storage.Delete(ctx, testAttachment.File.Path) suite.NoError(err) @@ -153,7 +154,7 @@ func (suite *GetFileTestSuite) TestGetRemoteFileUncachedInterrupted() { // the attachment should still be updated in the database even though the caller hung up dbAttachment, err := suite.db.GetAttachmentByID(ctx, testAttachment.ID) suite.NoError(err) - suite.True(dbAttachment.Cached) + suite.True(*dbAttachment.Cached) // the file should be back in storage at the same path as before refreshedBytes, err := suite.storage.Get(ctx, testAttachment.File.Path) @@ -170,8 +171,8 @@ func (suite *GetFileTestSuite) TestGetRemoteFileThumbnailUncached() { suite.NoError(err) // uncache the file from local - testAttachment.Cached = false - err = suite.db.UpdateByPrimaryKey(ctx, testAttachment) + testAttachment.Cached = testrig.FalseBool() + err = suite.db.UpdateByPrimaryKey(ctx, testAttachment, "cached") suite.NoError(err) err = suite.storage.Delete(ctx, testAttachment.File.Path) suite.NoError(err) diff --git a/internal/processing/media/unattach.go b/internal/processing/media/unattach.go index bb09525fe..5ef8f81f4 100644 --- a/internal/processing/media/unattach.go +++ b/internal/processing/media/unattach.go @@ -43,10 +43,11 @@ func (p *processor) Unattach(ctx context.Context, account *gtsmodel.Account, med return nil, gtserror.NewErrorNotFound(errors.New("attachment not owned by requesting account")) } + updatingColumns := []string{"updated_at", "status_id"} attachment.UpdatedAt = time.Now() attachment.StatusID = "" - if err := p.db.UpdateByPrimaryKey(ctx, attachment); err != nil { + if err := p.db.UpdateByPrimaryKey(ctx, attachment, updatingColumns...); err != nil { return nil, gtserror.NewErrorNotFound(fmt.Errorf("db error updating attachment: %s", err)) } diff --git a/internal/processing/media/unattach_test.go b/internal/processing/media/unattach_test.go index 60efc2688..7572741ac 100644 --- a/internal/processing/media/unattach_test.go +++ b/internal/processing/media/unattach_test.go @@ -30,7 +30,7 @@ type UnattachTestSuite struct { MediaStandardTestSuite } -func (suite *GetFileTestSuite) TestUnattachMedia() { +func (suite *UnattachTestSuite) TestUnattachMedia() { ctx := context.Background() testAttachment := suite.testAttachments["admin_account_status_1_attachment_1"] diff --git a/internal/processing/media/update.go b/internal/processing/media/update.go index 116588a48..b8177eeb4 100644 --- a/internal/processing/media/update.go +++ b/internal/processing/media/update.go @@ -44,11 +44,11 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, media return nil, gtserror.NewErrorNotFound(errors.New("attachment not owned by requesting account")) } + updatingColumns := []string{} + if form.Description != nil { attachment.Description = text.SanitizePlaintext(*form.Description) - if err := p.db.UpdateByPrimaryKey(ctx, attachment); err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("database error updating description: %s", err)) - } + updatingColumns = append(updatingColumns, "description") } if form.Focus != nil { @@ -58,9 +58,11 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, media } attachment.FileMeta.Focus.X = focusx attachment.FileMeta.Focus.Y = focusy - if err := p.db.UpdateByPrimaryKey(ctx, attachment); err != nil { - return nil, gtserror.NewErrorInternalError(fmt.Errorf("database error updating focus: %s", err)) - } + updatingColumns = append(updatingColumns, "focus_x", "focus_y") + } + + if err := p.db.UpdateByPrimaryKey(ctx, attachment, updatingColumns...); err != nil { + return nil, gtserror.NewErrorInternalError(fmt.Errorf("database error updating media: %s", err)) } a, err := p.tc.AttachmentToAPIAttachment(ctx, attachment) diff --git a/internal/processing/status/create.go b/internal/processing/status/create.go index 2c509809a..fef857c7c 100644 --- a/internal/processing/status/create.go +++ b/internal/processing/status/create.go @@ -40,18 +40,21 @@ func (p *processor) Create(ctx context.Context, account *gtsmodel.Account, appli return nil, gtserror.NewErrorInternalError(err) } + local := true + sensitive := form.Sensitive + newStatus := >smodel.Status{ ID: thisStatusID, URI: accountURIs.StatusesURI + "/" + thisStatusID, URL: accountURIs.StatusesURL + "/" + thisStatusID, CreatedAt: time.Now(), UpdatedAt: time.Now(), - Local: true, + Local: &local, AccountID: account.ID, AccountURI: account.URI, ContentWarning: text.SanitizePlaintext(form.SpoilerText), ActivityStreamsType: ap.ObjectNote, - Sensitive: form.Sensitive, + Sensitive: &sensitive, Language: form.Language, CreatedWithApplicationID: application.ID, Text: form.Status, diff --git a/internal/processing/status/fave.go b/internal/processing/status/fave.go index 1b40d9da1..f80d65358 100644 --- a/internal/processing/status/fave.go +++ b/internal/processing/status/fave.go @@ -49,7 +49,7 @@ func (p *processor) Fave(ctx context.Context, requestingAccount *gtsmodel.Accoun if !visible { return nil, gtserror.NewErrorNotFound(errors.New("status is not visible")) } - if !targetStatus.Likeable { + if !*targetStatus.Likeable { return nil, gtserror.NewErrorForbidden(errors.New("status is not faveable")) } diff --git a/internal/processing/status/util.go b/internal/processing/status/util.go index 64b496673..5e961e2ea 100644 --- a/internal/processing/status/util.go +++ b/internal/processing/status/util.go @@ -98,10 +98,10 @@ func (p *processor) ProcessVisibility(ctx context.Context, form *apimodel.Advanc } status.Visibility = vis - status.Federated = federated - status.Boostable = boostable - status.Replyable = replyable - status.Likeable = likeable + status.Federated = &federated + status.Boostable = &boostable + status.Replyable = &replyable + status.Likeable = &likeable return nil } @@ -128,7 +128,7 @@ func (p *processor) ProcessReplyToID(ctx context.Context, form *apimodel.Advance err := fmt.Errorf("db error fetching status with id %s: %s", form.InReplyToID, err) return gtserror.NewErrorInternalError(err) } - if !repliedStatus.Replyable { + if !*repliedStatus.Replyable { err := fmt.Errorf("status with id %s is marked as not replyable", form.InReplyToID) return gtserror.NewErrorForbidden(err, err.Error()) } diff --git a/internal/processing/user/changepassword.go b/internal/processing/user/changepassword.go index 50c7a7517..ddfec6898 100644 --- a/internal/processing/user/changepassword.go +++ b/internal/processing/user/changepassword.go @@ -20,6 +20,7 @@ package user import ( "context" + "time" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" @@ -42,7 +43,9 @@ func (p *processor) ChangePassword(ctx context.Context, user *gtsmodel.User, old } user.EncryptedPassword = string(newPasswordHash) - if err := p.db.UpdateByPrimaryKey(ctx, user); err != nil { + user.UpdatedAt = time.Now() + + if err := p.db.UpdateByPrimaryKey(ctx, user, "encrypted_password", "updated_at"); err != nil { return gtserror.NewErrorInternalError(err, "database error") } diff --git a/internal/processing/user/emailconfirm.go b/internal/processing/user/emailconfirm.go index eccaae5ec..6bffce7d9 100644 --- a/internal/processing/user/emailconfirm.go +++ b/internal/processing/user/emailconfirm.go @@ -71,12 +71,13 @@ func (p *processor) SendConfirmEmail(ctx context.Context, user *gtsmodel.User, u } // email sent, now we need to update the user entry with the token we just sent them + updatingColumns := []string{"confirmation_sent_at", "confirmation_token", "last_emailed_at", "updated_at"} user.ConfirmationSentAt = time.Now() user.ConfirmationToken = confirmationToken user.LastEmailedAt = time.Now() user.UpdatedAt = time.Now() - if err := p.db.UpdateByPrimaryKey(ctx, user); err != nil { + if err := p.db.UpdateByPrimaryKey(ctx, user, updatingColumns...); err != nil { return fmt.Errorf("SendConfirmEmail: error updating user entry after email sent: %s", err) } @@ -118,13 +119,14 @@ func (p *processor) ConfirmEmail(ctx context.Context, token string) (*gtsmodel.U } // mark the user's email address as confirmed + remove the unconfirmed address and the token + updatingColumns := []string{"email", "unconfirmed_email", "confirmed_at", "confirmation_token", "updated_at"} user.Email = user.UnconfirmedEmail user.UnconfirmedEmail = "" user.ConfirmedAt = time.Now() user.ConfirmationToken = "" user.UpdatedAt = time.Now() - if err := p.db.UpdateByPrimaryKey(ctx, user); err != nil { + if err := p.db.UpdateByPrimaryKey(ctx, user, updatingColumns...); err != nil { return nil, gtserror.NewErrorInternalError(err) } diff --git a/internal/processing/user/emailconfirm_test.go b/internal/processing/user/emailconfirm_test.go index 6f22306a1..87aff9756 100644 --- a/internal/processing/user/emailconfirm_test.go +++ b/internal/processing/user/emailconfirm_test.go @@ -67,13 +67,14 @@ func (suite *EmailConfirmTestSuite) TestConfirmEmail() { user := suite.testUsers["local_account_1"] // set a bunch of stuff on the user as though zork hasn't been confirmed yet, but has had an email sent 5 minutes ago + updatingColumns := []string{"unconfirmed_email", "email", "confirmed_at", "confirmation_sent_at", "confirmation_token"} user.UnconfirmedEmail = "some.email@example.org" user.Email = "" user.ConfirmedAt = time.Time{} user.ConfirmationSentAt = time.Now().Add(-5 * time.Minute) user.ConfirmationToken = "1d1aa44b-afa4-49c8-ac4b-eceb61715cc6" - err := suite.db.UpdateByPrimaryKey(ctx, user) + err := suite.db.UpdateByPrimaryKey(ctx, user, updatingColumns...) suite.NoError(err) // confirm with the token set above @@ -94,13 +95,14 @@ func (suite *EmailConfirmTestSuite) TestConfirmEmailOldToken() { user := suite.testUsers["local_account_1"] // set a bunch of stuff on the user as though zork hasn't been confirmed yet, but has had an email sent 8 days ago + updatingColumns := []string{"unconfirmed_email", "email", "confirmed_at", "confirmation_sent_at", "confirmation_token"} user.UnconfirmedEmail = "some.email@example.org" user.Email = "" user.ConfirmedAt = time.Time{} user.ConfirmationSentAt = time.Now().Add(-192 * time.Hour) user.ConfirmationToken = "1d1aa44b-afa4-49c8-ac4b-eceb61715cc6" - err := suite.db.UpdateByPrimaryKey(ctx, user) + err := suite.db.UpdateByPrimaryKey(ctx, user, updatingColumns...) suite.NoError(err) // confirm with the token set above diff --git a/internal/processing/user/user_test.go b/internal/processing/user/user_test.go index 007a5f8ce..56947f69a 100644 --- a/internal/processing/user/user_test.go +++ b/internal/processing/user/user_test.go @@ -40,8 +40,8 @@ type UserStandardTestSuite struct { } func (suite *UserStandardTestSuite) SetupTest() { - testrig.InitTestLog() testrig.InitTestConfig() + testrig.InitTestLog() suite.db = testrig.NewTestDB() suite.sentEmails = make(map[string]string) diff --git a/internal/trans/model/account.go b/internal/trans/model/account.go index 12d54a06f..1687d7676 100644 --- a/internal/trans/model/account.go +++ b/internal/trans/model/account.go @@ -34,7 +34,7 @@ type Account struct { Domain string `json:"domain,omitempty" bun:",nullzero"` HeaderRemoteURL string `json:"headerRemoteURL,omitempty" bun:",nullzero"` AvatarRemoteURL string `json:"avatarRemoteURL,omitempty" bun:",nullzero"` - Locked bool `json:"locked"` + Locked *bool `json:"locked" bun:",nullzero,notnull,default:true"` Language string `json:"language,omitempty" bun:",nullzero"` URI string `json:"uri" bun:",nullzero"` URL string `json:"url" bun:",nullzero"` diff --git a/internal/trans/model/domainblock.go b/internal/trans/model/domainblock.go index 48cebb4aa..5e0006bd7 100644 --- a/internal/trans/model/domainblock.go +++ b/internal/trans/model/domainblock.go @@ -29,6 +29,6 @@ type DomainBlock struct { CreatedByAccountID string `json:"createdByAccountID" bun:",nullzero"` PrivateComment string `json:"privateComment,omitempty" bun:",nullzero"` PublicComment string `json:"publicComment,omitempty" bun:",nullzero"` - Obfuscate bool `json:"obfuscate" bun:",nullzero"` + Obfuscate *bool `json:"obfuscate" bun:",nullzero,notnull,default:false"` SubscriptionID string `json:"subscriptionID,omitempty" bun:",nullzero"` } diff --git a/internal/trans/model/user.go b/internal/trans/model/user.go index 317b4debf..09e7a25a3 100644 --- a/internal/trans/model/user.go +++ b/internal/trans/model/user.go @@ -41,10 +41,10 @@ type User struct { ConfirmationSentAt *time.Time `json:"confirmationTokenSentAt,omitempty" bun:",nullzero"` ConfirmedAt *time.Time `json:"confirmedAt,omitempty" bun:",nullzero"` UnconfirmedEmail string `json:"unconfirmedEmail,omitempty" bun:",nullzero"` - Moderator bool `json:"moderator"` - Admin bool `json:"admin"` - Disabled bool `json:"disabled"` - Approved bool `json:"approved"` + Moderator *bool `json:"moderator" bun:",nullzero,notnull,default:false"` + Admin *bool `json:"admin" bun:",nullzero,notnull,default:false"` + Disabled *bool `json:"disabled" bun:",nullzero,notnull,default:false"` + Approved *bool `json:"approved" bun:",nullzero,notnull,default:false"` ResetPasswordToken string `json:"resetPasswordToken,omitempty" bun:",nullzero"` ResetPasswordSentAt *time.Time `json:"resetPasswordSentAt,omitempty" bun:",nullzero"` } diff --git a/internal/typeutils/astointernal.go b/internal/typeutils/astointernal.go index e30608150..7ec45335d 100644 --- a/internal/typeutils/astointernal.go +++ b/internal/typeutils/astointernal.go @@ -99,29 +99,46 @@ func (c *converter) ASRepresentationToAccount(ctx context.Context, accountable a switch accountable.GetTypeName() { case ap.ActorPerson, ap.ActorGroup, ap.ActorOrganization: // people, groups, and organizations aren't bots - acct.Bot = false + bot := false + acct.Bot = &bot // apps and services are case ap.ActorApplication, ap.ActorService: - acct.Bot = true + bot := true + acct.Bot = &bot default: // we don't know what this is! return nil, fmt.Errorf("type name %s not recognised or not convertible to ap.ActivityStreamsActor", accountable.GetTypeName()) } acct.ActorType = accountable.GetTypeName() + // assume not memorial (todo) + memorial := false + acct.Memorial = &memorial + + // assume not sensitive (todo) + sensitive := false + acct.Sensitive = &sensitive + + // assume not hide collections (todo) + hideCollections := false + acct.HideCollections = &hideCollections + // locked aka manuallyApprovesFollowers - acct.Locked = true // assume locked by default + locked := true + acct.Locked = &locked // assume locked by default maf := accountable.GetActivityStreamsManuallyApprovesFollowers() if maf != nil && maf.IsXMLSchemaBoolean() { - acct.Locked = maf.Get() + locked = maf.Get() + acct.Locked = &locked } // discoverable // default to false -- take custom value if it's set though - acct.Discoverable = false - discoverable, err := ap.ExtractDiscoverable(accountable) + discoverable := false + acct.Discoverable = &discoverable + d, err := ap.ExtractDiscoverable(accountable) if err == nil { - acct.Discoverable = discoverable + acct.Discoverable = &d } // url property @@ -289,13 +306,20 @@ func (c *converter) ASStatusToStatus(ctx context.Context, statusable ap.Statusab // advanced visibility for this status // TODO: a lot of work to be done here -- a new type needs to be created for this in go-fed/activity using ASTOOL // for now we just set everything to true - status.Federated = true - status.Boostable = true - status.Replyable = true - status.Likeable = true - + pinned := false + federated := true + boostable := true + replyable := true + likeable := true + + status.Pinned = &pinned + status.Federated = &federated + status.Boostable = &boostable + status.Replyable = &replyable + status.Likeable = &likeable // sensitive - status.Sensitive = ap.ExtractSensitive(statusable) + sensitive := ap.ExtractSensitive(statusable) + status.Sensitive = &sensitive // language // we might be able to extract this from the contentMap field diff --git a/internal/typeutils/astointernal_test.go b/internal/typeutils/astointernal_test.go index dbd852d3f..7024018d6 100644 --- a/internal/typeutils/astointernal_test.go +++ b/internal/typeutils/astointernal_test.go @@ -51,9 +51,9 @@ func (suite *ASToInternalTestSuite) TestParsePerson() { suite.Equal("Geoff Brando New Personson", acct.DisplayName) suite.Equal("hey I'm a new person, your instance hasn't seen me yet uwu", acct.Note) suite.Equal("https://unknown-instance.com/@brand_new_person", acct.URL) - suite.True(acct.Discoverable) + suite.True(*acct.Discoverable) suite.Equal("https://unknown-instance.com/users/brand_new_person#main-key", acct.PublicKeyURI) - suite.False(acct.Locked) + suite.False(*acct.Locked) } func (suite *ASToInternalTestSuite) TestParsePublicStatus() { @@ -145,10 +145,10 @@ func (suite *ASToInternalTestSuite) TestParseReplyWithMention() { suite.Equal(inReplyToAccount.ID, status.InReplyToAccountID) suite.Equal(inReplyToStatus.ID, status.InReplyToID) suite.Equal(inReplyToStatus.URI, status.InReplyToURI) - suite.True(status.Federated) - suite.True(status.Boostable) - suite.True(status.Replyable) - suite.True(status.Likeable) + suite.True(*status.Federated) + suite.True(*status.Boostable) + suite.True(*status.Replyable) + suite.True(*status.Likeable) suite.Equal(`<p><span class="h-card"><a href="http://localhost:8080/@the_mighty_zork" class="u-url mention">@<span>the_mighty_zork</span></a></span> nice there it is:</p><p><a href="http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/activity" rel="nofollow noopener noreferrer" target="_blank"><span class="invisible">https://</span><span class="ellipsis">social.pixie.town/users/f0x/st</span><span class="invisible">atuses/106221628567855262/activity</span></a></p>`, status.Content) suite.Len(status.Mentions, 1) m1 := status.Mentions[0] @@ -177,9 +177,9 @@ func (suite *ASToInternalTestSuite) TestParseOwncastService() { suite.Equal("https://owncast.example.org/logo/external", acct.HeaderRemoteURL) suite.Equal("Rob's Owncast Server", acct.DisplayName) suite.Equal("linux audio stuff ", acct.Note) - suite.True(acct.Bot) - suite.False(acct.Locked) - suite.True(acct.Discoverable) + suite.True(*acct.Bot) + suite.False(*acct.Locked) + suite.True(*acct.Discoverable) suite.Equal("https://owncast.example.org/federation/user/rgh", acct.URI) suite.Equal("https://owncast.example.org/federation/user/rgh", acct.URL) suite.Equal("https://owncast.example.org/federation/user/rgh/inbox", acct.InboxURI) diff --git a/internal/typeutils/internal.go b/internal/typeutils/internal.go index 0d49ea6b2..ce2c942fd 100644 --- a/internal/typeutils/internal.go +++ b/internal/typeutils/internal.go @@ -11,15 +11,18 @@ import ( ) func (c *converter) FollowRequestToFollow(ctx context.Context, f *gtsmodel.FollowRequest) *gtsmodel.Follow { + showReblogs := *f.ShowReblogs + notify := *f.Notify + return >smodel.Follow{ ID: f.ID, CreatedAt: f.CreatedAt, UpdatedAt: f.UpdatedAt, AccountID: f.AccountID, TargetAccountID: f.TargetAccountID, - ShowReblogs: f.ShowReblogs, + ShowReblogs: &showReblogs, URI: f.URI, - Notify: f.Notify, + Notify: ¬ify, } } @@ -38,6 +41,13 @@ func (c *converter) StatusToBoost(ctx context.Context, s *gtsmodel.Status, boost local = false } + sensitive := *s.Sensitive + pinned := false // can't pin a boost + federated := *s.Federated + boostable := *s.Boostable + replyable := *s.Replyable + likeable := *s.Likeable + boostWrapperStatus := >smodel.Status{ ID: boostWrapperStatusID, URI: boostWrapperStatusURI, @@ -46,7 +56,7 @@ func (c *converter) StatusToBoost(ctx context.Context, s *gtsmodel.Status, boost // the boosted status is not created now, but the boost certainly is CreatedAt: time.Now(), UpdatedAt: time.Now(), - Local: local, + Local: &local, AccountID: boostingAccount.ID, AccountURI: boostingAccount.URI, @@ -64,16 +74,17 @@ func (c *converter) StatusToBoost(ctx context.Context, s *gtsmodel.Status, boost Content: s.Content, ContentWarning: s.ContentWarning, ActivityStreamsType: s.ActivityStreamsType, - Sensitive: s.Sensitive, + Sensitive: &sensitive, Language: s.Language, Text: s.Text, BoostOfID: s.ID, BoostOfAccountID: s.AccountID, Visibility: s.Visibility, - Federated: s.Federated, - Boostable: s.Boostable, - Replyable: s.Replyable, - Likeable: s.Likeable, + Pinned: &pinned, + Federated: &federated, + Boostable: &boostable, + Replyable: &replyable, + Likeable: &likeable, // attach these here for convenience -- the boosted status/account won't go in the DB // but they're needed in the processor and for the frontend. Since we have them, we can diff --git a/internal/typeutils/internaltoas.go b/internal/typeutils/internaltoas.go index bb611dd8f..43036c352 100644 --- a/internal/typeutils/internaltoas.go +++ b/internal/typeutils/internaltoas.go @@ -145,13 +145,13 @@ func (c *converter) AccountToAS(ctx context.Context, a *gtsmodel.Account) (vocab // manuallyApprovesFollowers // Will be shown as a locked account. manuallyApprovesFollowersProp := streams.NewActivityStreamsManuallyApprovesFollowersProperty() - manuallyApprovesFollowersProp.Set(a.Locked) + manuallyApprovesFollowersProp.Set(*a.Locked) person.SetActivityStreamsManuallyApprovesFollowers(manuallyApprovesFollowersProp) // discoverable // Will be shown in the profile directory. discoverableProp := streams.NewTootDiscoverableProperty() - discoverableProp.Set(a.Discoverable) + discoverableProp.Set(*a.Discoverable) person.SetTootDiscoverable(discoverableProp) // devices @@ -539,7 +539,7 @@ func (c *converter) StatusToAS(ctx context.Context, s *gtsmodel.Status) (vocab.A // sensitive sensitiveProp := streams.NewActivityStreamsSensitiveProperty() - sensitiveProp.AppendXMLSchemaBoolean(s.Sensitive) + sensitiveProp.AppendXMLSchemaBoolean(*s.Sensitive) status.SetActivityStreamsSensitive(sensitiveProp) return status, nil diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go index da124ce6c..7da54f979 100644 --- a/internal/typeutils/internaltofrontend.go +++ b/internal/typeutils/internaltofrontend.go @@ -60,7 +60,7 @@ func (c *converter) AccountToAPIAccountSensitive(ctx context.Context, a *gtsmode apiAccount.Source = &model.Source{ Privacy: c.VisToAPIVis(ctx, a.Privacy), - Sensitive: a.Sensitive, + Sensitive: *a.Sensitive, Language: a.Language, StatusFormat: statusFormat, Note: a.NoteRaw, @@ -172,8 +172,8 @@ func (c *converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A Username: a.Username, Acct: acct, DisplayName: a.DisplayName, - Locked: a.Locked, - Bot: a.Bot, + Locked: *a.Locked, + Bot: *a.Bot, CreatedAt: util.FormatISO8601(a.CreatedAt), Note: a.Note, URL: a.URL, @@ -213,7 +213,7 @@ func (c *converter) AccountToAPIAccountBlocked(ctx context.Context, a *gtsmodel. Username: a.Username, Acct: acct, DisplayName: a.DisplayName, - Bot: a.Bot, + Bot: *a.Bot, CreatedAt: util.FormatISO8601(a.CreatedAt), URL: a.URL, Suspended: suspended, @@ -323,7 +323,7 @@ func (c *converter) EmojiToAPIEmoji(ctx context.Context, e *gtsmodel.Emoji) (mod Shortcode: e.Shortcode, URL: e.ImageURL, StaticURL: e.ImageStaticURL, - VisibleInPicker: e.VisibleInPicker, + VisibleInPicker: *e.VisibleInPicker, Category: e.CategoryID, }, nil } @@ -539,7 +539,7 @@ func (c *converter) StatusToAPIStatus(ctx context.Context, s *gtsmodel.Status, r CreatedAt: util.FormatISO8601(s.CreatedAt), InReplyToID: s.InReplyToID, InReplyToAccountID: s.InReplyToAccountID, - Sensitive: s.Sensitive, + Sensitive: *s.Sensitive, SpoilerText: s.ContentWarning, Visibility: c.VisToAPIVis(ctx, s.Visibility), Language: s.Language, @@ -552,7 +552,7 @@ func (c *converter) StatusToAPIStatus(ctx context.Context, s *gtsmodel.Status, r Bookmarked: statusInteractions.Bookmarked, Muted: statusInteractions.Muted, Reblogged: statusInteractions.Reblogged, - Pinned: s.Pinned, + Pinned: *s.Pinned, Content: s.Content, Application: apiApplication, Account: apiAuthorAccount, @@ -762,7 +762,7 @@ func (c *converter) DomainBlockToAPIDomainBlock(ctx context.Context, b *gtsmodel // if we're exporting a domain block, return it with minimal information attached if !export { domainBlock.ID = b.ID - domainBlock.Obfuscate = b.Obfuscate + domainBlock.Obfuscate = *b.Obfuscate domainBlock.PrivateComment = b.PrivateComment domainBlock.SubscriptionID = b.SubscriptionID domainBlock.CreatedBy = b.CreatedByAccountID diff --git a/internal/validate/account_test.go b/internal/validate/account_test.go index e0dda8e16..1c318b4ef 100644 --- a/internal/validate/account_test.go +++ b/internal/validate/account_test.go @@ -28,6 +28,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/validate" + "github.com/superseriousbusiness/gotosocial/testrig" ) func happyAccount() *gtsmodel.Account { @@ -52,15 +53,15 @@ func happyAccount() *gtsmodel.Account { DisplayName: "original zork (he/they)", Fields: []gtsmodel.Field{}, Note: "hey yo this is my profile!", - Memorial: false, + Memorial: testrig.FalseBool(), AlsoKnownAs: "", MovedToAccountID: "", - Bot: false, + Bot: testrig.FalseBool(), Reason: "I wanna be on this damned webbed site so bad! Please! Wow", - Locked: false, - Discoverable: true, + Locked: testrig.FalseBool(), + Discoverable: testrig.TrueBool(), Privacy: gtsmodel.VisibilityPublic, - Sensitive: false, + Sensitive: testrig.FalseBool(), Language: "en", StatusFormat: "plain", URI: "http://localhost:8080/users/the_mighty_zork", @@ -78,7 +79,7 @@ func happyAccount() *gtsmodel.Account { SensitizedAt: time.Time{}, SilencedAt: time.Time{}, SuspendedAt: time.Time{}, - HideCollections: false, + HideCollections: testrig.FalseBool(), SuspensionOrigin: "", } } diff --git a/internal/validate/domainblock_test.go b/internal/validate/domainblock_test.go index cb43a52a5..1406e539c 100644 --- a/internal/validate/domainblock_test.go +++ b/internal/validate/domainblock_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/validate" + "github.com/superseriousbusiness/gotosocial/testrig" ) func happyDomainBlock() *gtsmodel.DomainBlock { @@ -36,7 +37,7 @@ func happyDomainBlock() *gtsmodel.DomainBlock { CreatedByAccountID: "01FEED79PRMVWPRMFHFQM8MJQN", PrivateComment: "we don't like em", PublicComment: "poo poo dudes", - Obfuscate: false, + Obfuscate: testrig.FalseBool(), SubscriptionID: "", } } diff --git a/internal/validate/emoji_test.go b/internal/validate/emoji_test.go index 4ea90f384..8bf5d65e0 100644 --- a/internal/validate/emoji_test.go +++ b/internal/validate/emoji_test.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/validate" + "github.com/superseriousbusiness/gotosocial/testrig" ) func happyEmoji() *gtsmodel.Emoji { @@ -73,9 +74,9 @@ func happyEmoji() *gtsmodel.Emoji { ImageFileSize: 1024, ImageStaticFileSize: 256, ImageUpdatedAt: time.Now(), - Disabled: false, + Disabled: testrig.FalseBool(), URI: "https://example.org/emojis/blob_test", - VisibleInPicker: true, + VisibleInPicker: testrig.TrueBool(), CategoryID: "01FEE47ZH70PWDSEAVBRFNX325", } } diff --git a/internal/validate/mediaattachment_test.go b/internal/validate/mediaattachment_test.go index 14a835014..491e974f9 100644 --- a/internal/validate/mediaattachment_test.go +++ b/internal/validate/mediaattachment_test.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/validate" + "github.com/superseriousbusiness/gotosocial/testrig" ) func happyMediaAttachment() *gtsmodel.MediaAttachment { @@ -97,8 +98,8 @@ func happyMediaAttachment() *gtsmodel.MediaAttachment { URL: "http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/small/01F8MH6NEM8D7527KZAECTCR76.jpeg", RemoteURL: "", }, - Avatar: false, - Header: false, + Avatar: testrig.FalseBool(), + Header: testrig.FalseBool(), } } diff --git a/internal/validate/status_test.go b/internal/validate/status_test.go index cd3bc0f04..bebd3affd 100644 --- a/internal/validate/status_test.go +++ b/internal/validate/status_test.go @@ -26,6 +26,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/validate" + "github.com/superseriousbusiness/gotosocial/testrig" ) func happyStatus() *gtsmodel.Status { @@ -44,7 +45,7 @@ func happyStatus() *gtsmodel.Status { Mentions: nil, EmojiIDs: nil, Emojis: nil, - Local: true, + Local: testrig.TrueBool(), AccountID: "01FEBBQ4KEP3824WW61MF52638", Account: nil, AccountURI: "https://example.org/users/test_user", @@ -59,17 +60,17 @@ func happyStatus() *gtsmodel.Status { BoostOfAccount: nil, ContentWarning: "hello world test post", Visibility: gtsmodel.VisibilityPublic, - Sensitive: false, + Sensitive: testrig.FalseBool(), Language: "en", CreatedWithApplicationID: "01FEBBZHF4GFVRXSJVXD0JTZZ2", CreatedWithApplication: nil, - Federated: true, - Boostable: true, - Replyable: true, - Likeable: true, + Federated: testrig.TrueBool(), + Boostable: testrig.TrueBool(), + Replyable: testrig.TrueBool(), + Likeable: testrig.TrueBool(), ActivityStreamsType: ap.ObjectNote, Text: "Test status! #hello", - Pinned: false, + Pinned: testrig.FalseBool(), } } @@ -127,7 +128,7 @@ func (suite *StatusValidateTestSuite) TestStatusApplicationID() { err := validate.Struct(s) suite.EqualError(err, "Key: 'Status.CreatedWithApplicationID' Error:Field validation for 'CreatedWithApplicationID' failed on the 'required_if' tag") - s.Local = false + s.Local = testrig.FalseBool() err = validate.Struct(s) suite.NoError(err) } diff --git a/internal/validate/tag_test.go b/internal/validate/tag_test.go index 451863570..506563a7c 100644 --- a/internal/validate/tag_test.go +++ b/internal/validate/tag_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/validate" + "github.com/superseriousbusiness/gotosocial/testrig" ) func happyTag() *gtsmodel.Tag { @@ -35,8 +36,8 @@ func happyTag() *gtsmodel.Tag { URL: "https://example.org/tags/some_tag", Name: "some_tag", FirstSeenFromAccountID: "01FE91SR5P2GW06K3AJ98P72MT", - Useable: true, - Listable: true, + Useable: testrig.TrueBool(), + Listable: testrig.TrueBool(), LastStatusAt: time.Now(), } } diff --git a/internal/validate/user_test.go b/internal/validate/user_test.go index f755ac42a..06c8bb0ac 100644 --- a/internal/validate/user_test.go +++ b/internal/validate/user_test.go @@ -26,6 +26,7 @@ import ( "github.com/stretchr/testify/suite" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/validate" + "github.com/superseriousbusiness/gotosocial/testrig" ) func happyUser() *gtsmodel.User { @@ -54,10 +55,10 @@ func happyUser() *gtsmodel.User { ConfirmedAt: time.Now(), ConfirmationSentAt: time.Time{}, UnconfirmedEmail: "", - Moderator: false, - Admin: false, - Disabled: false, - Approved: true, + Moderator: testrig.FalseBool(), + Admin: testrig.FalseBool(), + Disabled: testrig.FalseBool(), + Approved: testrig.TrueBool(), } } diff --git a/internal/visibility/statusboostable.go b/internal/visibility/statusboostable.go index 60699875d..c502019ef 100644 --- a/internal/visibility/statusboostable.go +++ b/internal/visibility/statusboostable.go @@ -57,5 +57,5 @@ func (f *filter) StatusBoostable(ctx context.Context, targetStatus *gtsmodel.Sta // otherwise, status is as boostable as it says it is log.Trace("defaulting to status.boostable value") - return targetStatus.Boostable, nil + return *targetStatus.Boostable, nil } diff --git a/internal/visibility/statushometimelineable_test.go b/internal/visibility/statushometimelineable_test.go index 6161c52c0..ca80664dc 100644 --- a/internal/visibility/statushometimelineable_test.go +++ b/internal/visibility/statushometimelineable_test.go @@ -91,7 +91,7 @@ func (suite *StatusStatusHometimelineableTestSuite) TestChainReplyFollowersOnly( Content: "didn't expect dog", CreatedAt: testrig.TimeMustParse("2021-09-20T12:40:37+02:00"), UpdatedAt: testrig.TimeMustParse("2021-09-20T12:40:37+02:00"), - Local: false, + Local: testrig.FalseBool(), AccountURI: "http://fossbros-anonymous.io/users/foss_satan", AccountID: originalStatusParent.ID, InReplyToID: "", @@ -100,13 +100,13 @@ func (suite *StatusStatusHometimelineableTestSuite) TestChainReplyFollowersOnly( BoostOfID: "", ContentWarning: "", Visibility: gtsmodel.VisibilityFollowersOnly, - Sensitive: false, + Sensitive: testrig.FalseBool(), Language: "en", CreatedWithApplicationID: "", - Federated: true, - Boostable: true, - Replyable: true, - Likeable: true, + Federated: testrig.TrueBool(), + Boostable: testrig.TrueBool(), + Replyable: testrig.TrueBool(), + Likeable: testrig.TrueBool(), ActivityStreamsType: ap.ObjectNote, } if err := suite.db.PutStatus(ctx, originalStatus); err != nil { @@ -125,7 +125,7 @@ func (suite *StatusStatusHometimelineableTestSuite) TestChainReplyFollowersOnly( Content: "nbnbdy expects dog", CreatedAt: testrig.TimeMustParse("2021-09-20T12:41:37+02:00"), UpdatedAt: testrig.TimeMustParse("2021-09-20T12:41:37+02:00"), - Local: false, + Local: testrig.FalseBool(), AccountURI: "http://localhost:8080/users/the_mighty_zork", AccountID: replyingAccount.ID, InReplyToID: originalStatus.ID, @@ -134,13 +134,13 @@ func (suite *StatusStatusHometimelineableTestSuite) TestChainReplyFollowersOnly( BoostOfID: "", ContentWarning: "", Visibility: gtsmodel.VisibilityFollowersOnly, - Sensitive: false, + Sensitive: testrig.FalseBool(), Language: "en", CreatedWithApplicationID: "", - Federated: true, - Boostable: true, - Replyable: true, - Likeable: true, + Federated: testrig.TrueBool(), + Boostable: testrig.TrueBool(), + Replyable: testrig.TrueBool(), + Likeable: testrig.TrueBool(), ActivityStreamsType: ap.ObjectNote, } if err := suite.db.PutStatus(ctx, firstReplyStatus); err != nil { @@ -159,7 +159,7 @@ func (suite *StatusStatusHometimelineableTestSuite) TestChainReplyFollowersOnly( Content: "*nobody", CreatedAt: testrig.TimeMustParse("2021-09-20T12:42:37+02:00"), UpdatedAt: testrig.TimeMustParse("2021-09-20T12:42:37+02:00"), - Local: false, + Local: testrig.FalseBool(), AccountURI: "http://localhost:8080/users/the_mighty_zork", AccountID: replyingAccount.ID, InReplyToID: firstReplyStatus.ID, @@ -168,13 +168,13 @@ func (suite *StatusStatusHometimelineableTestSuite) TestChainReplyFollowersOnly( BoostOfID: "", ContentWarning: "", Visibility: gtsmodel.VisibilityFollowersOnly, - Sensitive: false, + Sensitive: testrig.FalseBool(), Language: "en", CreatedWithApplicationID: "", - Federated: true, - Boostable: true, - Replyable: true, - Likeable: true, + Federated: testrig.TrueBool(), + Boostable: testrig.TrueBool(), + Replyable: testrig.TrueBool(), + Likeable: testrig.TrueBool(), ActivityStreamsType: ap.ObjectNote, } if err := suite.db.PutStatus(ctx, secondReplyStatus); err != nil { @@ -204,7 +204,7 @@ func (suite *StatusStatusHometimelineableTestSuite) TestChainReplyPublicAndUnloc Content: "didn't expect dog", CreatedAt: testrig.TimeMustParse("2021-09-20T12:40:37+02:00"), UpdatedAt: testrig.TimeMustParse("2021-09-20T12:40:37+02:00"), - Local: false, + Local: testrig.FalseBool(), AccountURI: "http://fossbros-anonymous.io/users/foss_satan", AccountID: originalStatusParent.ID, InReplyToID: "", @@ -213,13 +213,13 @@ func (suite *StatusStatusHometimelineableTestSuite) TestChainReplyPublicAndUnloc BoostOfID: "", ContentWarning: "", Visibility: gtsmodel.VisibilityUnlocked, - Sensitive: false, + Sensitive: testrig.FalseBool(), Language: "en", CreatedWithApplicationID: "", - Federated: true, - Boostable: true, - Replyable: true, - Likeable: true, + Federated: testrig.TrueBool(), + Boostable: testrig.TrueBool(), + Replyable: testrig.TrueBool(), + Likeable: testrig.TrueBool(), ActivityStreamsType: ap.ObjectNote, } if err := suite.db.PutStatus(ctx, originalStatus); err != nil { @@ -238,7 +238,7 @@ func (suite *StatusStatusHometimelineableTestSuite) TestChainReplyPublicAndUnloc Content: "nbnbdy expects dog", CreatedAt: testrig.TimeMustParse("2021-09-20T12:41:37+02:00"), UpdatedAt: testrig.TimeMustParse("2021-09-20T12:41:37+02:00"), - Local: false, + Local: testrig.FalseBool(), AccountURI: "http://localhost:8080/users/the_mighty_zork", AccountID: replyingAccount.ID, InReplyToID: originalStatus.ID, @@ -247,13 +247,13 @@ func (suite *StatusStatusHometimelineableTestSuite) TestChainReplyPublicAndUnloc BoostOfID: "", ContentWarning: "", Visibility: gtsmodel.VisibilityPublic, - Sensitive: false, + Sensitive: testrig.FalseBool(), Language: "en", CreatedWithApplicationID: "", - Federated: true, - Boostable: true, - Replyable: true, - Likeable: true, + Federated: testrig.TrueBool(), + Boostable: testrig.TrueBool(), + Replyable: testrig.TrueBool(), + Likeable: testrig.TrueBool(), ActivityStreamsType: ap.ObjectNote, } if err := suite.db.PutStatus(ctx, firstReplyStatus); err != nil { @@ -272,7 +272,7 @@ func (suite *StatusStatusHometimelineableTestSuite) TestChainReplyPublicAndUnloc Content: "*nobody", CreatedAt: testrig.TimeMustParse("2021-09-20T12:42:37+02:00"), UpdatedAt: testrig.TimeMustParse("2021-09-20T12:42:37+02:00"), - Local: false, + Local: testrig.FalseBool(), AccountURI: "http://localhost:8080/users/the_mighty_zork", AccountID: replyingAccount.ID, InReplyToID: firstReplyStatus.ID, @@ -281,13 +281,13 @@ func (suite *StatusStatusHometimelineableTestSuite) TestChainReplyPublicAndUnloc BoostOfID: "", ContentWarning: "", Visibility: gtsmodel.VisibilityUnlocked, - Sensitive: false, + Sensitive: testrig.FalseBool(), Language: "en", CreatedWithApplicationID: "", - Federated: true, - Boostable: true, - Replyable: true, - Likeable: true, + Federated: testrig.TrueBool(), + Boostable: testrig.TrueBool(), + Replyable: testrig.TrueBool(), + Likeable: testrig.TrueBool(), ActivityStreamsType: ap.ObjectNote, } if err := suite.db.PutStatus(ctx, secondReplyStatus); err != nil { diff --git a/internal/visibility/statusvisible.go b/internal/visibility/statusvisible.go index fc8bfeaaa..15d8544ad 100644 --- a/internal/visibility/statusvisible.go +++ b/internal/visibility/statusvisible.go @@ -79,7 +79,7 @@ func (f *filter) StatusVisible(ctx context.Context, targetStatus *gtsmodel.Statu // if target user is disabled, not yet approved, or not confirmed then don't show the status // (although in the latter two cases it's unlikely they posted a status yet anyway, but you never know!) - if targetUser.Disabled || !targetUser.Approved || targetUser.ConfirmedAt.IsZero() { + if *targetUser.Disabled || !*targetUser.Approved || targetUser.ConfirmedAt.IsZero() { l.Trace("target user is disabled, not approved, or not confirmed") return false, nil } @@ -108,7 +108,7 @@ func (f *filter) StatusVisible(ctx context.Context, targetStatus *gtsmodel.Statu return false, fmt.Errorf("StatusVisible: db error selecting user for local requesting account %s: %s", requestingAccount.ID, err) } // okay, user exists, so make sure it has full privileges/is confirmed/approved - if requestingUser.Disabled || !requestingUser.Approved || requestingUser.ConfirmedAt.IsZero() { + if *requestingUser.Disabled || !*requestingUser.Approved || requestingUser.ConfirmedAt.IsZero() { l.Trace("requesting account is local but corresponding user is either disabled, not approved, or not confirmed") return false, nil } |