[feature] Implemented notification clear (#720)

* Implemented notification clear

* Added the cache clear mechanism

* added multi user check test
This commit is contained in:
Artémis 2022-08-01 11:13:49 +02:00 committed by GitHub
parent 8fdc9ed552
commit 4fdbef04b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 109 additions and 1 deletions

View file

@ -33,7 +33,8 @@ const (
BasePath = "/api/v1/notifications" BasePath = "/api/v1/notifications"
// BasePathWithID is just the base path with the ID key in it. // BasePathWithID is just the base path with the ID key in it.
// Use this anywhere you need to know the ID of the notification being queried. // Use this anywhere you need to know the ID of the notification being queried.
BasePathWithID = BasePath + "/:" + IDKey BasePathWithID = BasePath + "/:" + IDKey
BasePathWithClear = BasePath + "/clear"
// MaxIDKey is the url query for setting a max notification ID to return // MaxIDKey is the url query for setting a max notification ID to return
MaxIDKey = "max_id" MaxIDKey = "max_id"
@ -58,5 +59,6 @@ func New(processor processing.Processor) api.ClientModule {
// Route attaches all routes from this module to the given router // Route attaches all routes from this module to the given router
func (m *Module) Route(r router.Router) error { func (m *Module) Route(r router.Router) error {
r.AttachHandler(http.MethodGet, BasePath, m.NotificationsGETHandler) r.AttachHandler(http.MethodGet, BasePath, m.NotificationsGETHandler)
r.AttachHandler(http.MethodPost, BasePathWithClear, m.NotificationsClearPOSTHandler)
return nil return nil
} }

View file

@ -0,0 +1,50 @@
/*
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 notification
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
// NotificationsClearPOSTHandler clears all the notifications
func (m *Module) NotificationsClearPOSTHandler(c *gin.Context) {
authed, err := oauth.Authed(c, true, true, true, true)
if err != nil {
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
return
}
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
return
}
errWithCode := m.processor.NotificationsClear(c.Request.Context(), authed)
if errWithCode != nil {
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
return
}
c.JSON(http.StatusOK, struct{}{})
}

View file

@ -108,3 +108,17 @@ func (n *notificationDB) GetNotifications(ctx context.Context, accountID string,
return notifs, nil return notifs, nil
} }
func (n *notificationDB) ClearNotifications(ctx context.Context, accountID string) db.Error {
if _, err := n.conn.
NewDelete().
Table("notifications").
Where("target_account_id = ?", accountID).
Exec(ctx); err != nil {
return n.conn.ProcessError(err)
}
n.cache.Clear()
return nil
}

View file

@ -118,6 +118,35 @@ func (suite *NotificationTestSuite) TestGetNotificationsWithoutSpam() {
} }
} }
func (suite *NotificationTestSuite) TestClearNotificationsWithSpam() {
suite.spamNotifs()
testAccount := suite.testAccounts["local_account_1"]
err := suite.db.ClearNotifications(context.Background(), testAccount.ID)
suite.NoError(err)
notifications, err := suite.db.GetNotifications(context.Background(), testAccount.ID, 20, "ZZZZZZZZZZZZZZZZZZZZZZZZZZ", "00000000000000000000000000")
suite.NoError(err)
suite.NotNil(notifications)
suite.Empty(notifications)
}
func (suite *NotificationTestSuite) TestClearNotificationsWithTwoAccounts() {
suite.spamNotifs()
testAccount := suite.testAccounts["local_account_1"]
err := suite.db.ClearNotifications(context.Background(), testAccount.ID)
suite.NoError(err)
notifications, err := suite.db.GetNotifications(context.Background(), testAccount.ID, 20, "ZZZZZZZZZZZZZZZZZZZZZZZZZZ", "00000000000000000000000000")
suite.NoError(err)
suite.NotNil(notifications)
suite.Empty(notifications)
notif := []*gtsmodel.Notification{}
err = suite.db.GetAll(context.Background(), &notif)
suite.NoError(err)
suite.NotEmpty(notif)
}
func TestNotificationTestSuite(t *testing.T) { func TestNotificationTestSuite(t *testing.T) {
suite.Run(t, new(NotificationTestSuite)) suite.Run(t, new(NotificationTestSuite))
} }

View file

@ -32,4 +32,6 @@ type Notification interface {
GetNotifications(ctx context.Context, accountID string, limit int, maxID string, sinceID string) ([]*gtsmodel.Notification, Error) GetNotifications(ctx context.Context, accountID string, limit int, maxID string, sinceID string) ([]*gtsmodel.Notification, Error)
// GetNotification returns one notification according to its id. // GetNotification returns one notification according to its id.
GetNotification(ctx context.Context, id string) (*gtsmodel.Notification, Error) GetNotification(ctx context.Context, id string) (*gtsmodel.Notification, Error)
// ClearNotifications deletes every notification that pertain to the given accountID.
ClearNotifications(ctx context.Context, accountID string) Error
} }

View file

@ -58,3 +58,12 @@ func (p *processor) NotificationsGet(ctx context.Context, authed *oauth.Auth, li
Limit: limit, Limit: limit,
}) })
} }
func (p *processor) NotificationsClear(ctx context.Context, authed *oauth.Auth) gtserror.WithCode {
err := p.db.ClearNotifications(ctx, authed.Account.ID)
if err != nil {
return gtserror.NewErrorInternalError(err)
}
return nil
}

View file

@ -155,6 +155,8 @@ type Processor interface {
// NotificationsGet // NotificationsGet
NotificationsGet(ctx context.Context, authed *oauth.Auth, limit int, maxID string, sinceID string) (*apimodel.TimelineResponse, gtserror.WithCode) NotificationsGet(ctx context.Context, authed *oauth.Auth, limit int, maxID string, sinceID string) (*apimodel.TimelineResponse, gtserror.WithCode)
// NotificationsClear
NotificationsClear(ctx context.Context, authed *oauth.Auth) gtserror.WithCode
OAuthHandleTokenRequest(r *http.Request) (map[string]interface{}, gtserror.WithCode) OAuthHandleTokenRequest(r *http.Request) (map[string]interface{}, gtserror.WithCode)
OAuthHandleAuthorizeRequest(w http.ResponseWriter, r *http.Request) error OAuthHandleAuthorizeRequest(w http.ResponseWriter, r *http.Request) error