mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-02-04 20:05:05 +00:00
[feature] Implement Web Push notification policy (#3721)
* Web Push: add policy column to subscriptions * Web Push: add policy to API * Web Push: test notification policy * go-fmt unrelated file (how did this get thru?)
This commit is contained in:
parent
8b74cad422
commit
27844b7da2
|
@ -9922,6 +9922,16 @@ paths:
|
||||||
in: formData
|
in: formData
|
||||||
name: data[alerts][pending.reblog]
|
name: data[alerts][pending.reblog]
|
||||||
type: boolean
|
type: boolean
|
||||||
|
- default: all
|
||||||
|
description: Which accounts to receive push notifications from.
|
||||||
|
enum:
|
||||||
|
- all
|
||||||
|
- followed
|
||||||
|
- follower
|
||||||
|
- none
|
||||||
|
in: formData
|
||||||
|
name: data[policy]
|
||||||
|
type: string
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
|
@ -10019,6 +10029,16 @@ paths:
|
||||||
in: formData
|
in: formData
|
||||||
name: data[alerts][pending.reblog]
|
name: data[alerts][pending.reblog]
|
||||||
type: boolean
|
type: boolean
|
||||||
|
- default: all
|
||||||
|
description: Which accounts to receive push notifications from.
|
||||||
|
enum:
|
||||||
|
- all
|
||||||
|
- followed
|
||||||
|
- follower
|
||||||
|
- none
|
||||||
|
in: formData
|
||||||
|
name: data[policy]
|
||||||
|
type: string
|
||||||
produces:
|
produces:
|
||||||
- application/json
|
- application/json
|
||||||
responses:
|
responses:
|
||||||
|
|
|
@ -147,6 +147,17 @@
|
||||||
// type: boolean
|
// type: boolean
|
||||||
// default: false
|
// default: false
|
||||||
// description: Receive a push notification when a boost is pending?
|
// description: Receive a push notification when a boost is pending?
|
||||||
|
// -
|
||||||
|
// name: data[policy]
|
||||||
|
// in: formData
|
||||||
|
// type: string
|
||||||
|
// enum:
|
||||||
|
// - all
|
||||||
|
// - followed
|
||||||
|
// - follower
|
||||||
|
// - none
|
||||||
|
// default: all
|
||||||
|
// description: Which accounts to receive push notifications from.
|
||||||
//
|
//
|
||||||
// security:
|
// security:
|
||||||
// - OAuth2 Bearer:
|
// - OAuth2 Bearer:
|
||||||
|
|
|
@ -44,6 +44,7 @@ func (suite *PushTestSuite) postSubscription(
|
||||||
p256dh *string,
|
p256dh *string,
|
||||||
alertsMention *bool,
|
alertsMention *bool,
|
||||||
alertsStatus *bool,
|
alertsStatus *bool,
|
||||||
|
policy *string,
|
||||||
requestJson *string,
|
requestJson *string,
|
||||||
expectedHTTPStatus int,
|
expectedHTTPStatus int,
|
||||||
) (*apimodel.WebPushSubscription, error) {
|
) (*apimodel.WebPushSubscription, error) {
|
||||||
|
@ -80,6 +81,9 @@ func (suite *PushTestSuite) postSubscription(
|
||||||
if alertsStatus != nil {
|
if alertsStatus != nil {
|
||||||
ctx.Request.Form["data[alerts][status]"] = []string{strconv.FormatBool(*alertsStatus)}
|
ctx.Request.Form["data[alerts][status]"] = []string{strconv.FormatBool(*alertsStatus)}
|
||||||
}
|
}
|
||||||
|
if policy != nil {
|
||||||
|
ctx.Request.Form["data[policy]"] = []string{*policy}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// trigger the handler
|
// trigger the handler
|
||||||
|
@ -119,6 +123,7 @@ func (suite *PushTestSuite) TestPostSubscription() {
|
||||||
p256dh := "BMYVItYVOX+AHBdtA62Q0i6c+F7MV2Gia3aoDr8mvHkuPBNIOuTLDfmFcnBqoZcQk6BtLcIONbxhHpy2R+mYIUY="
|
p256dh := "BMYVItYVOX+AHBdtA62Q0i6c+F7MV2Gia3aoDr8mvHkuPBNIOuTLDfmFcnBqoZcQk6BtLcIONbxhHpy2R+mYIUY="
|
||||||
alertsMention := true
|
alertsMention := true
|
||||||
alertsStatus := false
|
alertsStatus := false
|
||||||
|
policy := "followed"
|
||||||
subscription, err := suite.postSubscription(
|
subscription, err := suite.postSubscription(
|
||||||
accountFixtureName,
|
accountFixtureName,
|
||||||
tokenFixtureName,
|
tokenFixtureName,
|
||||||
|
@ -127,6 +132,7 @@ func (suite *PushTestSuite) TestPostSubscription() {
|
||||||
&p256dh,
|
&p256dh,
|
||||||
&alertsMention,
|
&alertsMention,
|
||||||
&alertsStatus,
|
&alertsStatus,
|
||||||
|
&policy,
|
||||||
nil,
|
nil,
|
||||||
200,
|
200,
|
||||||
)
|
)
|
||||||
|
@ -138,6 +144,7 @@ func (suite *PushTestSuite) TestPostSubscription() {
|
||||||
suite.False(subscription.Alerts.Status)
|
suite.False(subscription.Alerts.Status)
|
||||||
// Omitted event types should default to off.
|
// Omitted event types should default to off.
|
||||||
suite.False(subscription.Alerts.Favourite)
|
suite.False(subscription.Alerts.Favourite)
|
||||||
|
suite.Equal(apimodel.WebPushNotificationPolicyFollowed, subscription.Policy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,6 +166,7 @@ func (suite *PushTestSuite) TestPostSubscriptionMinimal() {
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
200,
|
200,
|
||||||
)
|
)
|
||||||
if suite.NoError(err) {
|
if suite.NoError(err) {
|
||||||
|
@ -169,6 +177,8 @@ func (suite *PushTestSuite) TestPostSubscriptionMinimal() {
|
||||||
suite.False(subscription.Alerts.Mention)
|
suite.False(subscription.Alerts.Mention)
|
||||||
suite.False(subscription.Alerts.Status)
|
suite.False(subscription.Alerts.Status)
|
||||||
suite.False(subscription.Alerts.Favourite)
|
suite.False(subscription.Alerts.Favourite)
|
||||||
|
// Policy should default to all.
|
||||||
|
suite.Equal(apimodel.WebPushNotificationPolicyAll, subscription.Policy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,6 +202,7 @@ func (suite *PushTestSuite) TestPostInvalidSubscription() {
|
||||||
&alertsMention,
|
&alertsMention,
|
||||||
&alertsStatus,
|
&alertsStatus,
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
422,
|
422,
|
||||||
)
|
)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
@ -215,7 +226,8 @@ func (suite *PushTestSuite) TestPostSubscriptionJSON() {
|
||||||
"alerts": {
|
"alerts": {
|
||||||
"mention": true,
|
"mention": true,
|
||||||
"status": false
|
"status": false
|
||||||
}
|
},
|
||||||
|
"policy": "followed"
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
subscription, err := suite.postSubscription(
|
subscription, err := suite.postSubscription(
|
||||||
|
@ -226,6 +238,7 @@ func (suite *PushTestSuite) TestPostSubscriptionJSON() {
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
&requestJson,
|
&requestJson,
|
||||||
200,
|
200,
|
||||||
)
|
)
|
||||||
|
@ -237,6 +250,7 @@ func (suite *PushTestSuite) TestPostSubscriptionJSON() {
|
||||||
suite.False(subscription.Alerts.Status)
|
suite.False(subscription.Alerts.Status)
|
||||||
// Omitted event types should default to off.
|
// Omitted event types should default to off.
|
||||||
suite.False(subscription.Alerts.Favourite)
|
suite.False(subscription.Alerts.Favourite)
|
||||||
|
suite.Equal(apimodel.WebPushNotificationPolicyFollowed, subscription.Policy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,6 +277,7 @@ func (suite *PushTestSuite) TestPostSubscriptionJSONMinimal() {
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
&requestJson,
|
&requestJson,
|
||||||
200,
|
200,
|
||||||
)
|
)
|
||||||
|
@ -274,6 +289,8 @@ func (suite *PushTestSuite) TestPostSubscriptionJSONMinimal() {
|
||||||
suite.False(subscription.Alerts.Mention)
|
suite.False(subscription.Alerts.Mention)
|
||||||
suite.False(subscription.Alerts.Status)
|
suite.False(subscription.Alerts.Status)
|
||||||
suite.False(subscription.Alerts.Favourite)
|
suite.False(subscription.Alerts.Favourite)
|
||||||
|
// Policy should default to all.
|
||||||
|
suite.Equal(apimodel.WebPushNotificationPolicyAll, subscription.Policy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,6 +323,7 @@ func (suite *PushTestSuite) TestPostInvalidSubscriptionJSON() {
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
&requestJson,
|
&requestJson,
|
||||||
422,
|
422,
|
||||||
)
|
)
|
||||||
|
@ -323,6 +341,7 @@ func (suite *PushTestSuite) TestPostExistingSubscription() {
|
||||||
p256dh := "BMYVItYVOX+AHBdtA62Q0i6c+F7MV2Gia3aoDr8mvHkuPBNIOuTLDfmFcnBqoZcQk6BtLcIONbxhHpy2R+mYIUY="
|
p256dh := "BMYVItYVOX+AHBdtA62Q0i6c+F7MV2Gia3aoDr8mvHkuPBNIOuTLDfmFcnBqoZcQk6BtLcIONbxhHpy2R+mYIUY="
|
||||||
alertsMention := true
|
alertsMention := true
|
||||||
alertsStatus := false
|
alertsStatus := false
|
||||||
|
policy := "followed"
|
||||||
subscription, err := suite.postSubscription(
|
subscription, err := suite.postSubscription(
|
||||||
accountFixtureName,
|
accountFixtureName,
|
||||||
tokenFixtureName,
|
tokenFixtureName,
|
||||||
|
@ -331,6 +350,7 @@ func (suite *PushTestSuite) TestPostExistingSubscription() {
|
||||||
&p256dh,
|
&p256dh,
|
||||||
&alertsMention,
|
&alertsMention,
|
||||||
&alertsStatus,
|
&alertsStatus,
|
||||||
|
&policy,
|
||||||
nil,
|
nil,
|
||||||
200,
|
200,
|
||||||
)
|
)
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PushSubscriptionPUTHandler swagger:operation PUT /api/v1/push/subscription pushSubscriptionPut
|
// PushSubscriptionPUTHandler swagger:operation PUT /api/v1/push/subscription pushSubscriptionPut
|
||||||
|
@ -122,6 +123,17 @@
|
||||||
// type: boolean
|
// type: boolean
|
||||||
// default: false
|
// default: false
|
||||||
// description: Receive a push notification when a boost is pending?
|
// description: Receive a push notification when a boost is pending?
|
||||||
|
// -
|
||||||
|
// name: data[policy]
|
||||||
|
// in: formData
|
||||||
|
// type: string
|
||||||
|
// enum:
|
||||||
|
// - all
|
||||||
|
// - followed
|
||||||
|
// - follower
|
||||||
|
// - none
|
||||||
|
// default: all
|
||||||
|
// description: Which accounts to receive push notifications from.
|
||||||
//
|
//
|
||||||
// security:
|
// security:
|
||||||
// - OAuth2 Bearer:
|
// - OAuth2 Bearer:
|
||||||
|
@ -181,7 +193,8 @@ func (m *Module) PushSubscriptionPUTHandler(c *gin.Context) {
|
||||||
apiutil.JSON(c, http.StatusOK, apiSubscription)
|
apiutil.JSON(c, http.StatusOK, apiSubscription)
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateNormalizeUpdate copies form fields to their canonical JSON equivalents.
|
// validateNormalizeUpdate copies form fields to their canonical JSON equivalents
|
||||||
|
// and sets defaults for fields that have them.
|
||||||
func validateNormalizeUpdate(request *apimodel.WebPushSubscriptionUpdateRequest) error {
|
func validateNormalizeUpdate(request *apimodel.WebPushSubscriptionUpdateRequest) error {
|
||||||
if request.Data == nil {
|
if request.Data == nil {
|
||||||
request.Data = &apimodel.WebPushSubscriptionRequestData{}
|
request.Data = &apimodel.WebPushSubscriptionRequestData{}
|
||||||
|
@ -228,5 +241,12 @@ func validateNormalizeUpdate(request *apimodel.WebPushSubscriptionUpdateRequest)
|
||||||
request.Data.Alerts.Reblog = *request.DataAlertsPendingReblog
|
request.Data.Alerts.Reblog = *request.DataAlertsPendingReblog
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if request.DataPolicy != nil {
|
||||||
|
request.Data.Policy = request.DataPolicy
|
||||||
|
}
|
||||||
|
if request.Data.Policy == nil {
|
||||||
|
request.Data.Policy = util.Ptr(apimodel.WebPushNotificationPolicyAll)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ func (suite *PushTestSuite) putSubscription(
|
||||||
tokenFixtureName string,
|
tokenFixtureName string,
|
||||||
alertsMention *bool,
|
alertsMention *bool,
|
||||||
alertsStatus *bool,
|
alertsStatus *bool,
|
||||||
|
policy *string,
|
||||||
requestJson *string,
|
requestJson *string,
|
||||||
expectedHTTPStatus int,
|
expectedHTTPStatus int,
|
||||||
) (*apimodel.WebPushSubscription, error) {
|
) (*apimodel.WebPushSubscription, error) {
|
||||||
|
@ -68,6 +69,9 @@ func (suite *PushTestSuite) putSubscription(
|
||||||
if alertsStatus != nil {
|
if alertsStatus != nil {
|
||||||
ctx.Request.Form["data[alerts][status]"] = []string{strconv.FormatBool(*alertsStatus)}
|
ctx.Request.Form["data[alerts][status]"] = []string{strconv.FormatBool(*alertsStatus)}
|
||||||
}
|
}
|
||||||
|
if policy != nil {
|
||||||
|
ctx.Request.Form["data[policy]"] = []string{*policy}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// trigger the handler
|
// trigger the handler
|
||||||
|
@ -104,11 +108,13 @@ func (suite *PushTestSuite) TestPutSubscription() {
|
||||||
|
|
||||||
alertsMention := true
|
alertsMention := true
|
||||||
alertsStatus := false
|
alertsStatus := false
|
||||||
|
policy := "followed"
|
||||||
subscription, err := suite.putSubscription(
|
subscription, err := suite.putSubscription(
|
||||||
accountFixtureName,
|
accountFixtureName,
|
||||||
tokenFixtureName,
|
tokenFixtureName,
|
||||||
&alertsMention,
|
&alertsMention,
|
||||||
&alertsStatus,
|
&alertsStatus,
|
||||||
|
&policy,
|
||||||
nil,
|
nil,
|
||||||
200,
|
200,
|
||||||
)
|
)
|
||||||
|
@ -120,6 +126,7 @@ func (suite *PushTestSuite) TestPutSubscription() {
|
||||||
suite.False(subscription.Alerts.Status)
|
suite.False(subscription.Alerts.Status)
|
||||||
// Omitted event types should default to off.
|
// Omitted event types should default to off.
|
||||||
suite.False(subscription.Alerts.Favourite)
|
suite.False(subscription.Alerts.Favourite)
|
||||||
|
suite.Equal(apimodel.WebPushNotificationPolicyFollowed, subscription.Policy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +141,8 @@ func (suite *PushTestSuite) TestPutSubscriptionJSON() {
|
||||||
"alerts": {
|
"alerts": {
|
||||||
"mention": true,
|
"mention": true,
|
||||||
"status": false
|
"status": false
|
||||||
}
|
},
|
||||||
|
"policy": "followed"
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
subscription, err := suite.putSubscription(
|
subscription, err := suite.putSubscription(
|
||||||
|
@ -142,6 +150,7 @@ func (suite *PushTestSuite) TestPutSubscriptionJSON() {
|
||||||
tokenFixtureName,
|
tokenFixtureName,
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
&requestJson,
|
&requestJson,
|
||||||
200,
|
200,
|
||||||
)
|
)
|
||||||
|
@ -153,6 +162,7 @@ func (suite *PushTestSuite) TestPutSubscriptionJSON() {
|
||||||
suite.False(subscription.Alerts.Status)
|
suite.False(subscription.Alerts.Status)
|
||||||
// Omitted event types should default to off.
|
// Omitted event types should default to off.
|
||||||
suite.False(subscription.Alerts.Favourite)
|
suite.False(subscription.Alerts.Favourite)
|
||||||
|
suite.Equal(apimodel.WebPushNotificationPolicyFollowed, subscription.Policy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,6 +180,7 @@ func (suite *PushTestSuite) TestPutMissingSubscription() {
|
||||||
&alertsMention,
|
&alertsMention,
|
||||||
&alertsStatus,
|
&alertsStatus,
|
||||||
nil,
|
nil,
|
||||||
|
nil,
|
||||||
404,
|
404,
|
||||||
)
|
)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
|
@ -138,6 +138,8 @@ type WebPushSubscriptionUpdateRequest struct {
|
||||||
DataAlertsPendingFavourite *bool `form:"data[alerts][pending.favourite]" json:"-"`
|
DataAlertsPendingFavourite *bool `form:"data[alerts][pending.favourite]" json:"-"`
|
||||||
DataAlertsPendingReply *bool `form:"data[alerts][pending.reply]" json:"-"`
|
DataAlertsPendingReply *bool `form:"data[alerts][pending.reply]" json:"-"`
|
||||||
DataAlertsPendingReblog *bool `form:"data[alerts][pending.reblog]" json:"-"`
|
DataAlertsPendingReblog *bool `form:"data[alerts][pending.reblog]" json:"-"`
|
||||||
|
|
||||||
|
DataPolicy *WebPushNotificationPolicy `form:"data[policy]" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebPushSubscriptionRequestData is the part of a Web Push subscription that can be changed after creation.
|
// WebPushSubscriptionRequestData is the part of a Web Push subscription that can be changed after creation.
|
||||||
|
@ -146,6 +148,9 @@ type WebPushSubscriptionUpdateRequest struct {
|
||||||
type WebPushSubscriptionRequestData struct {
|
type WebPushSubscriptionRequestData struct {
|
||||||
// Alerts selects the specific events that this Web Push subscription will receive.
|
// Alerts selects the specific events that this Web Push subscription will receive.
|
||||||
Alerts *WebPushSubscriptionAlerts `form:"-" json:"alerts"`
|
Alerts *WebPushSubscriptionAlerts `form:"-" json:"alerts"`
|
||||||
|
|
||||||
|
// Policy selects which accounts will trigger Web Push notifications.
|
||||||
|
Policy *WebPushNotificationPolicy `form:"-" json:"policy"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebPushNotificationPolicy names sets of accounts that can generate notifications.
|
// WebPushNotificationPolicy names sets of accounts that can generate notifications.
|
||||||
|
@ -154,4 +159,10 @@ type WebPushSubscriptionRequestData struct {
|
||||||
const (
|
const (
|
||||||
// WebPushNotificationPolicyAll allows all accounts to send notifications to the subscribing user.
|
// WebPushNotificationPolicyAll allows all accounts to send notifications to the subscribing user.
|
||||||
WebPushNotificationPolicyAll WebPushNotificationPolicy = "all"
|
WebPushNotificationPolicyAll WebPushNotificationPolicy = "all"
|
||||||
|
// WebPushNotificationPolicyFollowed allows accounts followed by the subscribing user to send notifications.
|
||||||
|
WebPushNotificationPolicyFollowed WebPushNotificationPolicy = "followed"
|
||||||
|
// WebPushNotificationPolicyFollower allows accounts following the subscribing user to send notifications.
|
||||||
|
WebPushNotificationPolicyFollower WebPushNotificationPolicy = "follower"
|
||||||
|
// WebPushNotificationPolicyNone doesn't allow any acounts to send notifications to the subscribing user.
|
||||||
|
WebPushNotificationPolicyNone WebPushNotificationPolicy = "none"
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
// GoToSocial
|
||||||
|
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
"github.com/uptrace/bun/dialect"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
up := func(ctx context.Context, db *bun.DB) error {
|
||||||
|
return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||||
|
model := >smodel.WebPushSubscription{}
|
||||||
|
|
||||||
|
// Get the column definition for the new policy column.
|
||||||
|
modelType := reflect.TypeOf(model)
|
||||||
|
columnDef, err := getBunColumnDef(tx, modelType, "Policy")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the policy column.
|
||||||
|
switch tx.Dialect().Name() {
|
||||||
|
case dialect.SQLite:
|
||||||
|
// Doesn't support Bun feature AlterColumnExists.
|
||||||
|
if _, err = tx.
|
||||||
|
NewAddColumn().
|
||||||
|
Model(model).
|
||||||
|
ColumnExpr(columnDef).
|
||||||
|
Exec(ctx); // nocollapse
|
||||||
|
err != nil && !strings.Contains(err.Error(), "duplicate column name") {
|
||||||
|
// Return errors that aren't about this column already existing.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
case dialect.PG:
|
||||||
|
// Supports Bun feature AlterColumnExists.
|
||||||
|
if _, err = tx.
|
||||||
|
NewAddColumn().
|
||||||
|
Model(model).
|
||||||
|
ColumnExpr(columnDef).
|
||||||
|
IfNotExists().
|
||||||
|
Exec(ctx); // nocollapse
|
||||||
|
err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic("unsupported db type")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
down := func(ctx context.Context, db *bun.DB) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := Migrations.Register(up, down); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,12 +39,15 @@ type WebPushSubscription struct {
|
||||||
// P256dh is a Base64-encoded Diffie-Hellman public key on the P-256 elliptic curve.
|
// P256dh is a Base64-encoded Diffie-Hellman public key on the P-256 elliptic curve.
|
||||||
P256dh string `bun:",nullzero,notnull"`
|
P256dh string `bun:",nullzero,notnull"`
|
||||||
|
|
||||||
// NotificationFlags controls which notifications are delivered to a given subscription.
|
// NotificationFlags controls which notifications are delivered to this subscription.
|
||||||
// Corresponds to model.PushSubscriptionAlerts.
|
|
||||||
NotificationFlags WebPushSubscriptionNotificationFlags `bun:",notnull"`
|
NotificationFlags WebPushSubscriptionNotificationFlags `bun:",notnull"`
|
||||||
|
|
||||||
|
// Policy controls which accounts are allowed to trigger notifications for this subscription.
|
||||||
|
Policy WebPushNotificationPolicy `bun:",nullzero,notnull,default:1"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebPushSubscriptionNotificationFlags is a bitfield representation of a set of NotificationType.
|
// WebPushSubscriptionNotificationFlags is a bitfield representation of a set of NotificationType.
|
||||||
|
// Corresponds to apimodel.WebPushSubscriptionAlerts.
|
||||||
type WebPushSubscriptionNotificationFlags int64
|
type WebPushSubscriptionNotificationFlags int64
|
||||||
|
|
||||||
// WebPushSubscriptionNotificationFlagsFromSlice packs a slice of NotificationType into a WebPushSubscriptionNotificationFlags.
|
// WebPushSubscriptionNotificationFlagsFromSlice packs a slice of NotificationType into a WebPushSubscriptionNotificationFlags.
|
||||||
|
@ -80,3 +83,18 @@ func (n *WebPushSubscriptionNotificationFlags) Set(notificationType Notification
|
||||||
*n &= ^(1 << notificationType)
|
*n &= ^(1 << notificationType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WebPushNotificationPolicy represents the notification policy of a Web Push subscription.
|
||||||
|
// Corresponds to apimodel.WebPushNotificationPolicy.
|
||||||
|
type WebPushNotificationPolicy enumType
|
||||||
|
|
||||||
|
const (
|
||||||
|
// WebPushNotificationPolicyAll allows all accounts to send notifications to the subscribing user.
|
||||||
|
WebPushNotificationPolicyAll WebPushNotificationPolicy = 1
|
||||||
|
// WebPushNotificationPolicyFollowed allows accounts followed by the subscribing user to send notifications.
|
||||||
|
WebPushNotificationPolicyFollowed WebPushNotificationPolicy = 2
|
||||||
|
// WebPushNotificationPolicyFollower allows accounts following the subscribing user to send notifications.
|
||||||
|
WebPushNotificationPolicyFollower WebPushNotificationPolicy = 3
|
||||||
|
// WebPushNotificationPolicyNone doesn't allow any accounts to send notifications to the subscribing user.
|
||||||
|
WebPushNotificationPolicyNone WebPushNotificationPolicy = 4
|
||||||
|
)
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateOrReplace creates a Web Push subscription for the given access token,
|
// CreateOrReplace creates a Web Push subscription for the given access token,
|
||||||
|
@ -54,6 +55,7 @@ func (p *Processor) CreateOrReplace(
|
||||||
Auth: request.Subscription.Keys.Auth,
|
Auth: request.Subscription.Keys.Auth,
|
||||||
P256dh: request.Subscription.Keys.P256dh,
|
P256dh: request.Subscription.Keys.P256dh,
|
||||||
NotificationFlags: alertsToNotificationFlags(request.Data.Alerts),
|
NotificationFlags: alertsToNotificationFlags(request.Data.Alerts),
|
||||||
|
Policy: typeutils.APIWebPushNotificationPolicyToWebPushNotificationPolicy(*request.Data.Policy),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := p.state.DB.PutWebPushSubscription(ctx, subscription); err != nil {
|
if err := p.state.DB.PutWebPushSubscription(ctx, subscription); err != nil {
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Update updates the Web Push subscription for the given access token.
|
// Update updates the Web Push subscription for the given access token.
|
||||||
|
@ -50,10 +51,13 @@ func (p *Processor) Update(
|
||||||
|
|
||||||
// Update it.
|
// Update it.
|
||||||
subscription.NotificationFlags = alertsToNotificationFlags(request.Data.Alerts)
|
subscription.NotificationFlags = alertsToNotificationFlags(request.Data.Alerts)
|
||||||
|
subscription.Policy = typeutils.APIWebPushNotificationPolicyToWebPushNotificationPolicy(*request.Data.Policy)
|
||||||
|
|
||||||
if err = p.state.DB.UpdateWebPushSubscription(
|
if err = p.state.DB.UpdateWebPushSubscription(
|
||||||
ctx,
|
ctx,
|
||||||
subscription,
|
subscription,
|
||||||
"notification_flags",
|
"notification_flags",
|
||||||
|
"policy",
|
||||||
); err != nil {
|
); err != nil {
|
||||||
err := gtserror.Newf("couldn't update Web Push subscription for token ID %s: %w", tokenID, err)
|
err := gtserror.Newf("couldn't update Web Push subscription for token ID %s: %w", tokenID, err)
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
|
|
@ -231,3 +231,17 @@ func(uri gtsmodel.PolicyValue) bool {
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func APIWebPushNotificationPolicyToWebPushNotificationPolicy(policy apimodel.WebPushNotificationPolicy) gtsmodel.WebPushNotificationPolicy {
|
||||||
|
switch policy {
|
||||||
|
case apimodel.WebPushNotificationPolicyAll:
|
||||||
|
return gtsmodel.WebPushNotificationPolicyAll
|
||||||
|
case apimodel.WebPushNotificationPolicyFollowed:
|
||||||
|
return gtsmodel.WebPushNotificationPolicyFollowed
|
||||||
|
case apimodel.WebPushNotificationPolicyFollower:
|
||||||
|
return gtsmodel.WebPushNotificationPolicyFollower
|
||||||
|
case apimodel.WebPushNotificationPolicyNone:
|
||||||
|
return gtsmodel.WebPushNotificationPolicyNone
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
|
@ -3019,6 +3019,20 @@ func (c *Converter) InteractionReqToAPIInteractionReq(
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func webPushNotificationPolicyToAPIWebPushNotificationPolicy(policy gtsmodel.WebPushNotificationPolicy) apimodel.WebPushNotificationPolicy {
|
||||||
|
switch policy {
|
||||||
|
case gtsmodel.WebPushNotificationPolicyAll:
|
||||||
|
return apimodel.WebPushNotificationPolicyAll
|
||||||
|
case gtsmodel.WebPushNotificationPolicyFollowed:
|
||||||
|
return apimodel.WebPushNotificationPolicyFollowed
|
||||||
|
case gtsmodel.WebPushNotificationPolicyFollower:
|
||||||
|
return apimodel.WebPushNotificationPolicyFollower
|
||||||
|
case gtsmodel.WebPushNotificationPolicyNone:
|
||||||
|
return apimodel.WebPushNotificationPolicyNone
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Converter) WebPushSubscriptionToAPIWebPushSubscription(
|
func (c *Converter) WebPushSubscriptionToAPIWebPushSubscription(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
subscription *gtsmodel.WebPushSubscription,
|
subscription *gtsmodel.WebPushSubscription,
|
||||||
|
@ -3047,7 +3061,7 @@ func (c *Converter) WebPushSubscriptionToAPIWebPushSubscription(
|
||||||
PendingReply: subscription.NotificationFlags.Get(gtsmodel.NotificationPendingReply),
|
PendingReply: subscription.NotificationFlags.Get(gtsmodel.NotificationPendingReply),
|
||||||
PendingReblog: subscription.NotificationFlags.Get(gtsmodel.NotificationPendingReblog),
|
PendingReblog: subscription.NotificationFlags.Get(gtsmodel.NotificationPendingReblog),
|
||||||
},
|
},
|
||||||
Policy: apimodel.WebPushNotificationPolicyAll,
|
Policy: webPushNotificationPolicyToAPIWebPushNotificationPolicy(subscription.Policy),
|
||||||
Standard: true,
|
Standard: true,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,8 +67,7 @@ func (r *realSender) Send(
|
||||||
relevantSubscriptions := slices.DeleteFunc(
|
relevantSubscriptions := slices.DeleteFunc(
|
||||||
subscriptions,
|
subscriptions,
|
||||||
func(subscription *gtsmodel.WebPushSubscription) bool {
|
func(subscription *gtsmodel.WebPushSubscription) bool {
|
||||||
// Remove subscriptions that don't want this type of notification.
|
return r.shouldSkipSubscription(ctx, notification, subscription)
|
||||||
return !subscription.NotificationFlags.Get(notification.NotificationType)
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if len(relevantSubscriptions) == 0 {
|
if len(relevantSubscriptions) == 0 {
|
||||||
|
@ -117,6 +116,68 @@ func(subscription *gtsmodel.WebPushSubscription) bool {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// shouldSkipSubscription returns true if this subscription is not relevant to this notification.
|
||||||
|
func (r *realSender) shouldSkipSubscription(
|
||||||
|
ctx context.Context,
|
||||||
|
notification *gtsmodel.Notification,
|
||||||
|
subscription *gtsmodel.WebPushSubscription,
|
||||||
|
) bool {
|
||||||
|
// Remove subscriptions that don't want this type of notification.
|
||||||
|
if !subscription.NotificationFlags.Get(notification.NotificationType) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check against subscription's notification policy.
|
||||||
|
switch subscription.Policy {
|
||||||
|
case gtsmodel.WebPushNotificationPolicyAll:
|
||||||
|
// Allow notifications from any account.
|
||||||
|
return false
|
||||||
|
|
||||||
|
case gtsmodel.WebPushNotificationPolicyFollowed:
|
||||||
|
// Allow if the subscription account follows the notifying account.
|
||||||
|
isFollowing, err := r.state.DB.IsFollowing(ctx, subscription.AccountID, notification.OriginAccountID)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(
|
||||||
|
ctx,
|
||||||
|
"error checking whether account %s follows account %s: %v",
|
||||||
|
subscription.AccountID,
|
||||||
|
notification.OriginAccountID,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return !isFollowing
|
||||||
|
|
||||||
|
case gtsmodel.WebPushNotificationPolicyFollower:
|
||||||
|
// Allow if the notifying account follows the subscription account.
|
||||||
|
isFollowing, err := r.state.DB.IsFollowing(ctx, notification.OriginAccountID, subscription.AccountID)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf(
|
||||||
|
ctx,
|
||||||
|
"error checking whether account %s follows account %s: %v",
|
||||||
|
notification.OriginAccountID,
|
||||||
|
subscription.AccountID,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return !isFollowing
|
||||||
|
|
||||||
|
case gtsmodel.WebPushNotificationPolicyNone:
|
||||||
|
// This subscription doesn't want any push notifications.
|
||||||
|
return true
|
||||||
|
|
||||||
|
default:
|
||||||
|
log.Errorf(
|
||||||
|
ctx,
|
||||||
|
"unknown Web Push notification policy for subscription with token ID %s: %d",
|
||||||
|
subscription.TokenID,
|
||||||
|
subscription.Policy,
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// sendToSubscription sends a notification to a single Web Push subscription.
|
// sendToSubscription sends a notification to a single Web Push subscription.
|
||||||
func (r *realSender) sendToSubscription(
|
func (r *realSender) sendToSubscription(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
// for go:linkname
|
// for go:linkname
|
||||||
_ "unsafe"
|
_ "unsafe"
|
||||||
|
|
||||||
|
@ -43,6 +42,7 @@
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/subscriptions"
|
"github.com/superseriousbusiness/gotosocial/internal/subscriptions"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/webpush"
|
"github.com/superseriousbusiness/gotosocial/internal/webpush"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
@ -62,16 +62,7 @@ type RealSenderStandardTestSuite struct {
|
||||||
webPushSender webpush.Sender
|
webPushSender webpush.Sender
|
||||||
|
|
||||||
// standard suite models
|
// standard suite models
|
||||||
testTokens map[string]*gtsmodel.Token
|
|
||||||
testClients map[string]*gtsmodel.Client
|
|
||||||
testApplications map[string]*gtsmodel.Application
|
|
||||||
testUsers map[string]*gtsmodel.User
|
|
||||||
testAccounts map[string]*gtsmodel.Account
|
testAccounts map[string]*gtsmodel.Account
|
||||||
testAttachments map[string]*gtsmodel.MediaAttachment
|
|
||||||
testStatuses map[string]*gtsmodel.Status
|
|
||||||
testTags map[string]*gtsmodel.Tag
|
|
||||||
testMentions map[string]*gtsmodel.Mention
|
|
||||||
testEmojis map[string]*gtsmodel.Emoji
|
|
||||||
testNotifications map[string]*gtsmodel.Notification
|
testNotifications map[string]*gtsmodel.Notification
|
||||||
testWebPushSubscriptions map[string]*gtsmodel.WebPushSubscription
|
testWebPushSubscriptions map[string]*gtsmodel.WebPushSubscription
|
||||||
|
|
||||||
|
@ -81,16 +72,7 @@ type RealSenderStandardTestSuite struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *RealSenderStandardTestSuite) SetupSuite() {
|
func (suite *RealSenderStandardTestSuite) SetupSuite() {
|
||||||
suite.testTokens = testrig.NewTestTokens()
|
|
||||||
suite.testClients = testrig.NewTestClients()
|
|
||||||
suite.testApplications = testrig.NewTestApplications()
|
|
||||||
suite.testUsers = testrig.NewTestUsers()
|
|
||||||
suite.testAccounts = testrig.NewTestAccounts()
|
suite.testAccounts = testrig.NewTestAccounts()
|
||||||
suite.testAttachments = testrig.NewTestAttachments()
|
|
||||||
suite.testStatuses = testrig.NewTestStatuses()
|
|
||||||
suite.testTags = testrig.NewTestTags()
|
|
||||||
suite.testMentions = testrig.NewTestMentions()
|
|
||||||
suite.testEmojis = testrig.NewTestEmojis()
|
|
||||||
suite.testNotifications = testrig.NewTestNotifications()
|
suite.testNotifications = testrig.NewTestNotifications()
|
||||||
suite.testWebPushSubscriptions = testrig.NewTestWebPushSubscriptions()
|
suite.testWebPushSubscriptions = testrig.NewTestWebPushSubscriptions()
|
||||||
}
|
}
|
||||||
|
@ -184,14 +166,16 @@ func (rc *notifyingReadCloser) Close() error {
|
||||||
|
|
||||||
// Simulate sending a push notification with the suite's fake web client.
|
// Simulate sending a push notification with the suite's fake web client.
|
||||||
func (suite *RealSenderStandardTestSuite) simulatePushNotification(
|
func (suite *RealSenderStandardTestSuite) simulatePushNotification(
|
||||||
|
notificationID string,
|
||||||
statusCode int,
|
statusCode int,
|
||||||
|
expectSend bool,
|
||||||
expectDeletedSubscription bool,
|
expectDeletedSubscription bool,
|
||||||
) error {
|
) error {
|
||||||
// Don't let the test run forever if the push notification was not sent for some reason.
|
// Don't let the test run forever if the push notification was not sent for some reason.
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
notification, err := suite.state.DB.GetNotificationByID(ctx, suite.testNotifications["local_account_1_like"].ID)
|
notification, err := suite.state.DB.GetNotificationByID(ctx, notificationID)
|
||||||
if !suite.NoError(err) {
|
if !suite.NoError(err) {
|
||||||
suite.FailNow("Couldn't fetch notification to send")
|
suite.FailNow("Couldn't fetch notification to send")
|
||||||
}
|
}
|
||||||
|
@ -221,6 +205,14 @@ func (suite *RealSenderStandardTestSuite) simulatePushNotification(
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
contextExpired = true
|
contextExpired = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In some cases we expect the notification *not* to be sent.
|
||||||
|
if !expectSend {
|
||||||
|
suite.False(bodyClosed)
|
||||||
|
suite.True(contextExpired)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
suite.True(bodyClosed)
|
suite.True(bodyClosed)
|
||||||
suite.False(contextExpired)
|
suite.False(contextExpired)
|
||||||
|
|
||||||
|
@ -240,25 +232,48 @@ func (suite *RealSenderStandardTestSuite) simulatePushNotification(
|
||||||
|
|
||||||
// Test a successful response to sending a push notification.
|
// Test a successful response to sending a push notification.
|
||||||
func (suite *RealSenderStandardTestSuite) TestSendSuccess() {
|
func (suite *RealSenderStandardTestSuite) TestSendSuccess() {
|
||||||
suite.NoError(suite.simulatePushNotification(http.StatusOK, false))
|
notificationID := suite.testNotifications["local_account_1_like"].ID
|
||||||
|
suite.NoError(suite.simulatePushNotification(notificationID, http.StatusOK, true, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test a rate-limiting response to sending a push notification.
|
// Test a rate-limiting response to sending a push notification.
|
||||||
// This should not delete the subscription.
|
// This should not delete the subscription.
|
||||||
func (suite *RealSenderStandardTestSuite) TestRateLimited() {
|
func (suite *RealSenderStandardTestSuite) TestRateLimited() {
|
||||||
suite.NoError(suite.simulatePushNotification(http.StatusTooManyRequests, false))
|
notificationID := suite.testNotifications["local_account_1_like"].ID
|
||||||
|
suite.NoError(suite.simulatePushNotification(notificationID, http.StatusTooManyRequests, true, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test a non-special-cased client error response to sending a push notification.
|
// Test a non-special-cased client error response to sending a push notification.
|
||||||
// This should delete the subscription.
|
// This should delete the subscription.
|
||||||
func (suite *RealSenderStandardTestSuite) TestClientError() {
|
func (suite *RealSenderStandardTestSuite) TestClientError() {
|
||||||
suite.NoError(suite.simulatePushNotification(http.StatusBadRequest, true))
|
notificationID := suite.testNotifications["local_account_1_like"].ID
|
||||||
|
suite.NoError(suite.simulatePushNotification(notificationID, http.StatusBadRequest, true, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test a server error response to sending a push notification.
|
// Test a server error response to sending a push notification.
|
||||||
// This should not delete the subscription.
|
// This should not delete the subscription.
|
||||||
func (suite *RealSenderStandardTestSuite) TestServerError() {
|
func (suite *RealSenderStandardTestSuite) TestServerError() {
|
||||||
suite.NoError(suite.simulatePushNotification(http.StatusInternalServerError, false))
|
notificationID := suite.testNotifications["local_account_1_like"].ID
|
||||||
|
suite.NoError(suite.simulatePushNotification(notificationID, http.StatusInternalServerError, true, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't send a push notification if it doesn't match policy.
|
||||||
|
func (suite *RealSenderStandardTestSuite) TestSendPolicyMismatch() {
|
||||||
|
// Setup: create a new notification from an account that the subscribed account doesn't follow.
|
||||||
|
notification := >smodel.Notification{
|
||||||
|
ID: "01JJZ2Y9Z8E1XKT90EHZ5KZBDW",
|
||||||
|
NotificationType: gtsmodel.NotificationFavourite,
|
||||||
|
TargetAccountID: suite.testAccounts["local_account_1"].ID,
|
||||||
|
OriginAccountID: suite.testAccounts["remote_account_1"].ID,
|
||||||
|
StatusID: "01F8MHAMCHF6Y650WCRSCP4WMY",
|
||||||
|
Read: util.Ptr(false),
|
||||||
|
}
|
||||||
|
if err := suite.db.PutNotification(context.Background(), notification); !suite.NoError(err) {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.NoError(suite.simulatePushNotification(notification.ID, 0, false, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRealSenderStandardTestSuite(t *testing.T) {
|
func TestRealSenderStandardTestSuite(t *testing.T) {
|
||||||
|
|
|
@ -3610,6 +3610,7 @@ func NewTestWebPushSubscriptions() map[string]*gtsmodel.WebPushSubscription {
|
||||||
gtsmodel.NotificationPendingReply,
|
gtsmodel.NotificationPendingReply,
|
||||||
gtsmodel.NotificationPendingReblog,
|
gtsmodel.NotificationPendingReblog,
|
||||||
}),
|
}),
|
||||||
|
Policy: gtsmodel.WebPushNotificationPolicyFollowed,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue