diff options
author | 2021-05-15 11:58:11 +0200 | |
---|---|---|
committer | 2021-05-15 11:58:11 +0200 | |
commit | cc48294c31a76e94fa879ad0d8d5dbd7e94c651b (patch) | |
tree | 7c26d33b41bab33bbdfbba540958444f4c296602 /internal/db | |
parent | Mediahandler (#21) (diff) | |
download | gotosocial-cc48294c31a76e94fa879ad0d8d5dbd7e94c651b.tar.xz |
Inbox post (#22)
Inbox POST from federated servers now working for statuses and follow requests.
Follow request client API added.
Start work on federating outgoing messages.
Other fixes and changes/tidying up.
Diffstat (limited to 'internal/db')
-rw-r--r-- | internal/db/db.go | 14 | ||||
-rw-r--r-- | internal/db/federating_db.go | 359 | ||||
-rw-r--r-- | internal/db/federating_db_test.go | 21 | ||||
-rw-r--r-- | internal/db/pg/pg.go (renamed from internal/db/pg.go) | 94 | ||||
-rw-r--r-- | internal/db/pg_test.go | 21 |
5 files changed, 79 insertions, 430 deletions
diff --git a/internal/db/db.go b/internal/db/db.go index b281dd8d7..a354ddee8 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -26,7 +26,10 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" ) -const DBTypePostgres string = "POSTGRES" +const ( + // DBTypePostgres represents an underlying POSTGRES database type. + DBTypePostgres string = "POSTGRES" +) // ErrNoEntries is to be returned from the DB interface when no entries are found for a given query. type ErrNoEntries struct{} @@ -112,6 +115,10 @@ type DB interface { HANDY SHORTCUTS */ + // AcceptFollowRequest moves a follow request in the database from the follow_requests table to the follows table. + // In other words, it should create the follow, and delete the existing follow request. + AcceptFollowRequest(originAccountID string, targetAccountID string) error + // CreateInstanceAccount creates an account in the database with the same username as the instance host value. // Ie., if the instance is hosted at 'example.org' the instance user will have a username of 'example.org'. // This is needed for things like serving files that belong to the instance and not an individual user/account. @@ -148,6 +155,11 @@ type DB interface { // In case of no entries, a 'no entries' error will be returned GetFollowersByAccountID(accountID string, followers *[]gtsmodel.Follow) error + // GetFavesByAccountID is a shortcut for the common action of fetching a list of faves made by the given accountID. + // The given slice 'faves' will be set to the result of the query, whatever it is. + // In case of no entries, a 'no entries' error will be returned + GetFavesByAccountID(accountID string, faves *[]gtsmodel.StatusFave) error + // GetStatusesByAccountID is a shortcut for the common action of fetching a list of statuses produced by accountID. // The given slice 'statuses' will be set to the result of the query, whatever it is. // In case of no entries, a 'no entries' error will be returned diff --git a/internal/db/federating_db.go b/internal/db/federating_db.go deleted file mode 100644 index ab66b19de..000000000 --- a/internal/db/federating_db.go +++ /dev/null @@ -1,359 +0,0 @@ -/* - GoToSocial - Copyright (C) 2021 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 db - -import ( - "context" - "errors" - "fmt" - "net/url" - "sync" - - "github.com/go-fed/activity/pub" - "github.com/go-fed/activity/streams/vocab" - "github.com/sirupsen/logrus" - "github.com/superseriousbusiness/gotosocial/internal/config" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" - "github.com/superseriousbusiness/gotosocial/internal/util" -) - -// FederatingDB uses the underlying DB interface to implement the go-fed pub.Database interface. -// It doesn't care what the underlying implementation of the DB interface is, as long as it works. -type federatingDB struct { - locks *sync.Map - db DB - config *config.Config - log *logrus.Logger -} - -func NewFederatingDB(db DB, config *config.Config, log *logrus.Logger) pub.Database { - return &federatingDB{ - locks: new(sync.Map), - db: db, - config: config, - log: log, - } -} - -/* - GO-FED DB INTERFACE-IMPLEMENTING FUNCTIONS -*/ - -// Lock takes a lock for the object at the specified id. If an error -// is returned, the lock must not have been taken. -// -// The lock must be able to succeed for an id that does not exist in -// the database. This means acquiring the lock does not guarantee the -// entry exists in the database. -// -// Locks are encouraged to be lightweight and in the Go layer, as some -// processes require tight loops acquiring and releasing locks. -// -// Used to ensure race conditions in multiple requests do not occur. -func (f *federatingDB) Lock(c context.Context, id *url.URL) error { - // Before any other Database methods are called, the relevant `id` - // entries are locked to allow for fine-grained concurrency. - - // Strategy: create a new lock, if stored, continue. Otherwise, lock the - // existing mutex. - mu := &sync.Mutex{} - mu.Lock() // Optimistically lock if we do store it. - i, loaded := f.locks.LoadOrStore(id.String(), mu) - if loaded { - mu = i.(*sync.Mutex) - mu.Lock() - } - return nil -} - -// Unlock makes the lock for the object at the specified id available. -// If an error is returned, the lock must have still been freed. -// -// Used to ensure race conditions in multiple requests do not occur. -func (f *federatingDB) Unlock(c context.Context, id *url.URL) error { - // Once Go-Fed is done calling Database methods, the relevant `id` - // entries are unlocked. - - i, ok := f.locks.Load(id.String()) - if !ok { - return errors.New("missing an id in unlock") - } - mu := i.(*sync.Mutex) - mu.Unlock() - return nil -} - -// InboxContains returns true if the OrderedCollection at 'inbox' -// contains the specified 'id'. -// -// The library makes this call only after acquiring a lock first. -func (f *federatingDB) InboxContains(c context.Context, inbox, id *url.URL) (contains bool, err error) { - - if !util.IsInboxPath(inbox) { - return false, fmt.Errorf("%s is not an inbox URI", inbox.String()) - } - - if !util.IsStatusesPath(id) { - return false, fmt.Errorf("%s is not a status URI", id.String()) - } - _, statusID, err := util.ParseStatusesPath(inbox) - if err != nil { - return false, fmt.Errorf("status URI %s was not parseable: %s", id.String(), err) - } - - if err := f.db.GetByID(statusID, >smodel.Status{}); err != nil { - if _, ok := err.(ErrNoEntries); ok { - // we don't have it - return false, nil - } - // actual error - return false, fmt.Errorf("error getting status from db: %s", err) - } - - // we must have it - return true, nil -} - -// GetInbox returns the first ordered collection page of the outbox at -// the specified IRI, for prepending new items. -// -// The library makes this call only after acquiring a lock first. -func (f *federatingDB) GetInbox(c context.Context, inboxIRI *url.URL) (inbox vocab.ActivityStreamsOrderedCollectionPage, err error) { - return nil, nil -} - -// SetInbox saves the inbox value given from GetInbox, with new items -// prepended. Note that the new items must not be added as independent -// database entries. Separate calls to Create will do that. -// -// The library makes this call only after acquiring a lock first. -func (f *federatingDB) SetInbox(c context.Context, inbox vocab.ActivityStreamsOrderedCollectionPage) error { - return nil -} - -// Owns returns true if the IRI belongs to this instance, and if -// the database has an entry for the IRI. -// The library makes this call only after acquiring a lock first. -func (f *federatingDB) Owns(c context.Context, id *url.URL) (bool, error) { - // if the id host isn't this instance host, we don't own this IRI - if id.Host != f.config.Host { - return false, nil - } - - // apparently we own it, so what *is* it? - - // check if it's a status, eg /users/example_username/statuses/SOME_UUID_OF_A_STATUS - if util.IsStatusesPath(id) { - _, uid, err := util.ParseStatusesPath(id) - if err != nil { - return false, fmt.Errorf("error parsing statuses path for url %s: %s", id.String(), err) - } - if err := f.db.GetWhere("uri", uid, >smodel.Status{}); err != nil { - if _, ok := err.(ErrNoEntries); ok { - // there are no entries for this status - return false, nil - } - // an actual error happened - return false, fmt.Errorf("database error fetching status with id %s: %s", uid, err) - } - return true, nil - } - - // check if it's a user, eg /users/example_username - if util.IsUserPath(id) { - username, err := util.ParseUserPath(id) - if err != nil { - return false, fmt.Errorf("error parsing statuses path for url %s: %s", id.String(), err) - } - if err := f.db.GetLocalAccountByUsername(username, >smodel.Account{}); err != nil { - if _, ok := err.(ErrNoEntries); ok { - // there are no entries for this username - return false, nil - } - // an actual error happened - return false, fmt.Errorf("database error fetching account with username %s: %s", username, err) - } - return true, nil - } - - return false, fmt.Errorf("could not match activityID: %s", id.String()) -} - -// ActorForOutbox fetches the actor's IRI for the given outbox IRI. -// -// The library makes this call only after acquiring a lock first. -func (f *federatingDB) ActorForOutbox(c context.Context, outboxIRI *url.URL) (actorIRI *url.URL, err error) { - if !util.IsOutboxPath(outboxIRI) { - return nil, fmt.Errorf("%s is not an outbox URI", outboxIRI.String()) - } - acct := >smodel.Account{} - if err := f.db.GetWhere("outbox_uri", outboxIRI.String(), acct); err != nil { - if _, ok := err.(ErrNoEntries); ok { - return nil, fmt.Errorf("no actor found that corresponds to outbox %s", outboxIRI.String()) - } - return nil, fmt.Errorf("db error searching for actor with outbox %s", outboxIRI.String()) - } - return url.Parse(acct.URI) -} - -// ActorForInbox fetches the actor's IRI for the given outbox IRI. -// -// The library makes this call only after acquiring a lock first. -func (f *federatingDB) ActorForInbox(c context.Context, inboxIRI *url.URL) (actorIRI *url.URL, err error) { - if !util.IsInboxPath(inboxIRI) { - return nil, fmt.Errorf("%s is not an inbox URI", inboxIRI.String()) - } - acct := >smodel.Account{} - if err := f.db.GetWhere("inbox_uri", inboxIRI.String(), acct); err != nil { - if _, ok := err.(ErrNoEntries); ok { - return nil, fmt.Errorf("no actor found that corresponds to inbox %s", inboxIRI.String()) - } - return nil, fmt.Errorf("db error searching for actor with inbox %s", inboxIRI.String()) - } - return url.Parse(acct.URI) -} - -// OutboxForInbox fetches the corresponding actor's outbox IRI for the -// actor's inbox IRI. -// -// The library makes this call only after acquiring a lock first. -func (f *federatingDB) OutboxForInbox(c context.Context, inboxIRI *url.URL) (outboxIRI *url.URL, err error) { - if !util.IsInboxPath(inboxIRI) { - return nil, fmt.Errorf("%s is not an inbox URI", inboxIRI.String()) - } - acct := >smodel.Account{} - if err := f.db.GetWhere("inbox_uri", inboxIRI.String(), acct); err != nil { - if _, ok := err.(ErrNoEntries); ok { - return nil, fmt.Errorf("no actor found that corresponds to inbox %s", inboxIRI.String()) - } - return nil, fmt.Errorf("db error searching for actor with inbox %s", inboxIRI.String()) - } - return url.Parse(acct.OutboxURI) -} - -// Exists returns true if the database has an entry for the specified -// id. It may not be owned by this application instance. -// -// The library makes this call only after acquiring a lock first. -func (f *federatingDB) Exists(c context.Context, id *url.URL) (exists bool, err error) { - return false, nil -} - -// Get returns the database entry for the specified id. -// -// The library makes this call only after acquiring a lock first. -func (f *federatingDB) Get(c context.Context, id *url.URL) (value vocab.Type, err error) { - return nil, nil -} - -// Create adds a new entry to the database which must be able to be -// keyed by its id. -// -// Note that Activity values received from federated peers may also be -// created in the database this way if the Federating Protocol is -// enabled. The client may freely decide to store only the id instead of -// the entire value. -// -// The library makes this call only after acquiring a lock first. -// -// Under certain conditions and network activities, Create may be called -// multiple times for the same ActivityStreams object. -func (f *federatingDB) Create(c context.Context, asType vocab.Type) error { - return nil -} - -// Update sets an existing entry to the database based on the value's -// id. -// -// Note that Activity values received from federated peers may also be -// updated in the database this way if the Federating Protocol is -// enabled. The client may freely decide to store only the id instead of -// the entire value. -// -// The library makes this call only after acquiring a lock first. -func (f *federatingDB) Update(c context.Context, asType vocab.Type) error { - return nil -} - -// Delete removes the entry with the given id. -// -// Delete is only called for federated objects. Deletes from the Social -// Protocol instead call Update to create a Tombstone. -// -// The library makes this call only after acquiring a lock first. -func (f *federatingDB) Delete(c context.Context, id *url.URL) error { - return nil -} - -// GetOutbox returns the first ordered collection page of the outbox -// at the specified IRI, for prepending new items. -// -// The library makes this call only after acquiring a lock first. -func (f *federatingDB) GetOutbox(c context.Context, outboxIRI *url.URL) (inbox vocab.ActivityStreamsOrderedCollectionPage, err error) { - return nil, nil -} - -// SetOutbox saves the outbox value given from GetOutbox, with new items -// prepended. Note that the new items must not be added as independent -// database entries. Separate calls to Create will do that. -// -// The library makes this call only after acquiring a lock first. -func (f *federatingDB) SetOutbox(c context.Context, outbox vocab.ActivityStreamsOrderedCollectionPage) error { - return nil -} - -// NewID creates a new IRI id for the provided activity or object. The -// implementation does not need to set the 'id' property and simply -// needs to determine the value. -// -// The go-fed library will handle setting the 'id' property on the -// activity or object provided with the value returned. -func (f *federatingDB) NewID(c context.Context, t vocab.Type) (id *url.URL, err error) { - return nil, nil -} - -// Followers obtains the Followers Collection for an actor with the -// given id. -// -// If modified, the library will then call Update. -// -// The library makes this call only after acquiring a lock first. -func (f *federatingDB) Followers(c context.Context, actorIRI *url.URL) (followers vocab.ActivityStreamsCollection, err error) { - return nil, nil -} - -// Following obtains the Following Collection for an actor with the -// given id. -// -// If modified, the library will then call Update. -// -// The library makes this call only after acquiring a lock first. -func (f *federatingDB) Following(c context.Context, actorIRI *url.URL) (followers vocab.ActivityStreamsCollection, err error) { - return nil, nil -} - -// Liked obtains the Liked Collection for an actor with the -// given id. -// -// If modified, the library will then call Update. -// -// The library makes this call only after acquiring a lock first. -func (f *federatingDB) Liked(c context.Context, actorIRI *url.URL) (followers vocab.ActivityStreamsCollection, err error) { - return nil, nil -} diff --git a/internal/db/federating_db_test.go b/internal/db/federating_db_test.go deleted file mode 100644 index 529d2efd0..000000000 --- a/internal/db/federating_db_test.go +++ /dev/null @@ -1,21 +0,0 @@ -/* - GoToSocial - Copyright (C) 2021 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 db - -// TODO: write tests for pgfed diff --git a/internal/db/pg.go b/internal/db/pg/pg.go index c0fbcc9e0..f8c2fdbe8 100644 --- a/internal/db/pg.go +++ b/internal/db/pg/pg.go @@ -16,7 +16,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ -package db +package pg import ( "context" @@ -37,6 +37,8 @@ import ( "github.com/google/uuid" "github.com/sirupsen/logrus" "github.com/superseriousbusiness/gotosocial/internal/config" + "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/federation" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/util" "golang.org/x/crypto/bcrypt" @@ -53,7 +55,7 @@ type postgresService struct { // NewPostgresService returns a postgresService derived from the provided config, which implements the go-fed DB interface. // Under the hood, it uses https://github.com/go-pg/pg to create and maintain a database connection. -func NewPostgresService(ctx context.Context, c *config.Config, log *logrus.Logger) (DB, error) { +func NewPostgresService(ctx context.Context, c *config.Config, log *logrus.Logger) (db.DB, error) { opts, err := derivePGOptions(c) if err != nil { return nil, fmt.Errorf("could not create postgres service: %s", err) @@ -95,7 +97,7 @@ func NewPostgresService(ctx context.Context, c *config.Config, log *logrus.Logge cancel: cancel, } - federatingDB := NewFederatingDB(ps, c, log) + federatingDB := federation.NewFederatingDB(ps, c, log) ps.federationDB = federatingDB // we can confidently return this useable postgres service now @@ -109,8 +111,8 @@ func NewPostgresService(ctx context.Context, c *config.Config, log *logrus.Logge // derivePGOptions takes an application config and returns either a ready-to-use *pg.Options // with sensible defaults, or an error if it's not satisfied by the provided config. func derivePGOptions(c *config.Config) (*pg.Options, error) { - if strings.ToUpper(c.DBConfig.Type) != DBTypePostgres { - return nil, fmt.Errorf("expected db type of %s but got %s", DBTypePostgres, c.DBConfig.Type) + if strings.ToUpper(c.DBConfig.Type) != db.DBTypePostgres { + return nil, fmt.Errorf("expected db type of %s but got %s", db.DBTypePostgres, c.DBConfig.Type) } // validate port @@ -219,7 +221,7 @@ func (ps *postgresService) CreateSchema(ctx context.Context) error { func (ps *postgresService) GetByID(id string, i interface{}) error { if err := ps.conn.Model(i).Where("id = ?", id).Select(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return db.ErrNoEntries{} } return err @@ -230,7 +232,7 @@ func (ps *postgresService) GetByID(id string, i interface{}) error { func (ps *postgresService) GetWhere(key string, value interface{}, i interface{}) error { if err := ps.conn.Model(i).Where("? = ?", pg.Safe(key), value).Select(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return db.ErrNoEntries{} } return err } @@ -244,7 +246,7 @@ func (ps *postgresService) GetWhere(key string, value interface{}, i interface{} func (ps *postgresService) GetAll(i interface{}) error { if err := ps.conn.Model(i).Select(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return db.ErrNoEntries{} } return err } @@ -259,7 +261,7 @@ func (ps *postgresService) Put(i interface{}) error { func (ps *postgresService) Upsert(i interface{}, conflictColumn string) error { if _, err := ps.conn.Model(i).OnConflict(fmt.Sprintf("(%s) DO UPDATE", conflictColumn)).Insert(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return db.ErrNoEntries{} } return err } @@ -269,7 +271,7 @@ func (ps *postgresService) Upsert(i interface{}, conflictColumn string) error { func (ps *postgresService) UpdateByID(id string, i interface{}) error { if _, err := ps.conn.Model(i).Where("id = ?", id).OnConflict("(id) DO UPDATE").Insert(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return db.ErrNoEntries{} } return err } @@ -284,7 +286,7 @@ func (ps *postgresService) UpdateOneByID(id string, key string, value interface{ func (ps *postgresService) DeleteByID(id string, i interface{}) error { if _, err := ps.conn.Model(i).Where("id = ?", id).Delete(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return db.ErrNoEntries{} } return err } @@ -294,7 +296,7 @@ func (ps *postgresService) DeleteByID(id string, i interface{}) error { func (ps *postgresService) DeleteWhere(key string, value interface{}, i interface{}) error { if _, err := ps.conn.Model(i).Where("? = ?", pg.Safe(key), value).Delete(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return db.ErrNoEntries{} } return err } @@ -305,6 +307,32 @@ func (ps *postgresService) DeleteWhere(key string, value interface{}, i interfac HANDY SHORTCUTS */ +func (ps *postgresService) AcceptFollowRequest(originAccountID string, targetAccountID string) error { + fr := >smodel.FollowRequest{} + if err := ps.conn.Model(fr).Where("account_id = ?", originAccountID).Where("target_account_id = ?", targetAccountID).Select(); err != nil { + if err == pg.ErrMultiRows { + return db.ErrNoEntries{} + } + return err + } + + follow := >smodel.Follow{ + AccountID: originAccountID, + TargetAccountID: targetAccountID, + URI: fr.URI, + } + + if _, err := ps.conn.Model(follow).Insert(); err != nil { + return err + } + + if _, err := ps.conn.Model(>smodel.FollowRequest{}).Where("account_id = ?", originAccountID).Where("target_account_id = ?", targetAccountID).Delete(); err != nil { + return err + } + + return nil +} + func (ps *postgresService) CreateInstanceAccount() error { username := ps.config.Host key, err := rsa.GenerateKey(rand.Reader, 2048) @@ -365,13 +393,13 @@ func (ps *postgresService) GetAccountByUserID(userID string, account *gtsmodel.A } if err := ps.conn.Model(user).Where("id = ?", userID).Select(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return db.ErrNoEntries{} } return err } if err := ps.conn.Model(account).Where("id = ?", user.AccountID).Select(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return db.ErrNoEntries{} } return err } @@ -381,7 +409,7 @@ func (ps *postgresService) GetAccountByUserID(userID string, account *gtsmodel.A func (ps *postgresService) GetLocalAccountByUsername(username string, account *gtsmodel.Account) error { if err := ps.conn.Model(account).Where("username = ?", username).Where("? IS NULL", pg.Ident("domain")).Select(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return db.ErrNoEntries{} } return err } @@ -391,7 +419,7 @@ func (ps *postgresService) GetLocalAccountByUsername(username string, account *g func (ps *postgresService) GetFollowRequestsForAccountID(accountID string, followRequests *[]gtsmodel.FollowRequest) error { if err := ps.conn.Model(followRequests).Where("target_account_id = ?", accountID).Select(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return nil } return err } @@ -401,7 +429,7 @@ func (ps *postgresService) GetFollowRequestsForAccountID(accountID string, follo func (ps *postgresService) GetFollowingByAccountID(accountID string, following *[]gtsmodel.Follow) error { if err := ps.conn.Model(following).Where("account_id = ?", accountID).Select(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return nil } return err } @@ -411,7 +439,17 @@ func (ps *postgresService) GetFollowingByAccountID(accountID string, following * func (ps *postgresService) GetFollowersByAccountID(accountID string, followers *[]gtsmodel.Follow) error { if err := ps.conn.Model(followers).Where("target_account_id = ?", accountID).Select(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return nil + } + return err + } + return nil +} + +func (ps *postgresService) GetFavesByAccountID(accountID string, faves *[]gtsmodel.StatusFave) error { + if err := ps.conn.Model(faves).Where("account_id = ?", accountID).Select(); err != nil { + if err == pg.ErrNoRows { + return nil } return err } @@ -421,7 +459,7 @@ func (ps *postgresService) GetFollowersByAccountID(accountID string, followers * func (ps *postgresService) GetStatusesByAccountID(accountID string, statuses *[]gtsmodel.Status) error { if err := ps.conn.Model(statuses).Where("account_id = ?", accountID).Select(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return db.ErrNoEntries{} } return err } @@ -438,7 +476,7 @@ func (ps *postgresService) GetStatusesByTimeDescending(accountID string, statuse } if err := q.Select(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return db.ErrNoEntries{} } return err } @@ -448,7 +486,7 @@ func (ps *postgresService) GetStatusesByTimeDescending(accountID string, statuse func (ps *postgresService) GetLastStatusForAccountID(accountID string, status *gtsmodel.Status) error { if err := ps.conn.Model(status).Order("created_at DESC").Limit(1).Where("account_id = ?", accountID).Select(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return db.ErrNoEntries{} } return err } @@ -574,18 +612,18 @@ func (ps *postgresService) GetHeaderForAccountID(header *gtsmodel.MediaAttachmen acct := >smodel.Account{} if err := ps.conn.Model(acct).Where("id = ?", accountID).Select(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return db.ErrNoEntries{} } return err } if acct.HeaderMediaAttachmentID == "" { - return ErrNoEntries{} + return db.ErrNoEntries{} } if err := ps.conn.Model(header).Where("id = ?", acct.HeaderMediaAttachmentID).Select(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return db.ErrNoEntries{} } return err } @@ -596,18 +634,18 @@ func (ps *postgresService) GetAvatarForAccountID(avatar *gtsmodel.MediaAttachmen acct := >smodel.Account{} if err := ps.conn.Model(acct).Where("id = ?", accountID).Select(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return db.ErrNoEntries{} } return err } if acct.AvatarMediaAttachmentID == "" { - return ErrNoEntries{} + return db.ErrNoEntries{} } if err := ps.conn.Model(avatar).Where("id = ?", acct.AvatarMediaAttachmentID).Select(); err != nil { if err == pg.ErrNoRows { - return ErrNoEntries{} + return db.ErrNoEntries{} } return err } @@ -645,7 +683,7 @@ func (ps *postgresService) StatusVisible(targetStatus *gtsmodel.Status, targetAc if err := ps.conn.Model(targetUser).Where("account_id = ?", targetAccount.ID).Select(); err != nil { l.Debug("target user could not be selected") if err == pg.ErrNoRows { - return false, ErrNoEntries{} + return false, db.ErrNoEntries{} } return false, err } diff --git a/internal/db/pg_test.go b/internal/db/pg_test.go deleted file mode 100644 index a54784022..000000000 --- a/internal/db/pg_test.go +++ /dev/null @@ -1,21 +0,0 @@ -/* - GoToSocial - Copyright (C) 2021 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 db_test - -// TODO: write tests for postgres |