From c0e53662cf3d82cf07c69795b494325f1e9fc88c Mon Sep 17 00:00:00 2001 From: Vyr Cossont Date: Fri, 31 Jan 2025 12:34:20 -0800 Subject: [PATCH] Web Push: add policy to API --- docs/api/swagger.yaml | 20 +++++++++++++++++ .../api/client/push/pushsubscriptionpost.go | 11 ++++++++++ .../client/push/pushsubscriptionpost_test.go | 22 ++++++++++++++++++- .../api/client/push/pushsubscriptionput.go | 22 ++++++++++++++++++- .../client/push/pushsubscriptionput_test.go | 13 ++++++++++- internal/processing/push/create.go | 2 ++ internal/processing/push/update.go | 4 ++++ internal/typeutils/frontendtointernal.go | 14 ++++++++++++ internal/typeutils/internaltofrontend.go | 16 +++++++++++++- testrig/testmodels.go | 1 + 10 files changed, 121 insertions(+), 4 deletions(-) diff --git a/docs/api/swagger.yaml b/docs/api/swagger.yaml index d95825b6e..73c37cc98 100644 --- a/docs/api/swagger.yaml +++ b/docs/api/swagger.yaml @@ -9922,6 +9922,16 @@ paths: in: formData name: data[alerts][pending.reblog] 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: - application/json responses: @@ -10019,6 +10029,16 @@ paths: in: formData name: data[alerts][pending.reblog] 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: - application/json responses: diff --git a/internal/api/client/push/pushsubscriptionpost.go b/internal/api/client/push/pushsubscriptionpost.go index a7e299894..cc1be185f 100644 --- a/internal/api/client/push/pushsubscriptionpost.go +++ b/internal/api/client/push/pushsubscriptionpost.go @@ -147,6 +147,17 @@ import ( // type: boolean // default: false // 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: // - OAuth2 Bearer: diff --git a/internal/api/client/push/pushsubscriptionpost_test.go b/internal/api/client/push/pushsubscriptionpost_test.go index bdd22d729..e7e8582df 100644 --- a/internal/api/client/push/pushsubscriptionpost_test.go +++ b/internal/api/client/push/pushsubscriptionpost_test.go @@ -44,6 +44,7 @@ func (suite *PushTestSuite) postSubscription( p256dh *string, alertsMention *bool, alertsStatus *bool, + policy *string, requestJson *string, expectedHTTPStatus int, ) (*apimodel.WebPushSubscription, error) { @@ -80,6 +81,9 @@ func (suite *PushTestSuite) postSubscription( if alertsStatus != nil { ctx.Request.Form["data[alerts][status]"] = []string{strconv.FormatBool(*alertsStatus)} } + if policy != nil { + ctx.Request.Form["data[policy]"] = []string{*policy} + } } // trigger the handler @@ -119,6 +123,7 @@ func (suite *PushTestSuite) TestPostSubscription() { p256dh := "BMYVItYVOX+AHBdtA62Q0i6c+F7MV2Gia3aoDr8mvHkuPBNIOuTLDfmFcnBqoZcQk6BtLcIONbxhHpy2R+mYIUY=" alertsMention := true alertsStatus := false + policy := "followed" subscription, err := suite.postSubscription( accountFixtureName, tokenFixtureName, @@ -127,6 +132,7 @@ func (suite *PushTestSuite) TestPostSubscription() { &p256dh, &alertsMention, &alertsStatus, + &policy, nil, 200, ) @@ -138,6 +144,7 @@ func (suite *PushTestSuite) TestPostSubscription() { suite.False(subscription.Alerts.Status) // Omitted event types should default to off. suite.False(subscription.Alerts.Favourite) + suite.Equal(apimodel.WebPushNotificationPolicyFollowed, subscription.Policy) } } @@ -159,6 +166,7 @@ func (suite *PushTestSuite) TestPostSubscriptionMinimal() { nil, nil, nil, + nil, 200, ) if suite.NoError(err) { @@ -169,6 +177,8 @@ func (suite *PushTestSuite) TestPostSubscriptionMinimal() { suite.False(subscription.Alerts.Mention) suite.False(subscription.Alerts.Status) 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, &alertsStatus, nil, + nil, 422, ) suite.NoError(err) @@ -215,7 +226,8 @@ func (suite *PushTestSuite) TestPostSubscriptionJSON() { "alerts": { "mention": true, "status": false - } + }, + "policy": "followed" } }` subscription, err := suite.postSubscription( @@ -226,6 +238,7 @@ func (suite *PushTestSuite) TestPostSubscriptionJSON() { nil, nil, nil, + nil, &requestJson, 200, ) @@ -237,6 +250,7 @@ func (suite *PushTestSuite) TestPostSubscriptionJSON() { suite.False(subscription.Alerts.Status) // Omitted event types should default to off. suite.False(subscription.Alerts.Favourite) + suite.Equal(apimodel.WebPushNotificationPolicyFollowed, subscription.Policy) } } @@ -263,6 +277,7 @@ func (suite *PushTestSuite) TestPostSubscriptionJSONMinimal() { nil, nil, nil, + nil, &requestJson, 200, ) @@ -274,6 +289,8 @@ func (suite *PushTestSuite) TestPostSubscriptionJSONMinimal() { suite.False(subscription.Alerts.Mention) suite.False(subscription.Alerts.Status) 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, &requestJson, 422, ) @@ -323,6 +341,7 @@ func (suite *PushTestSuite) TestPostExistingSubscription() { p256dh := "BMYVItYVOX+AHBdtA62Q0i6c+F7MV2Gia3aoDr8mvHkuPBNIOuTLDfmFcnBqoZcQk6BtLcIONbxhHpy2R+mYIUY=" alertsMention := true alertsStatus := false + policy := "followed" subscription, err := suite.postSubscription( accountFixtureName, tokenFixtureName, @@ -331,6 +350,7 @@ func (suite *PushTestSuite) TestPostExistingSubscription() { &p256dh, &alertsMention, &alertsStatus, + &policy, nil, 200, ) diff --git a/internal/api/client/push/pushsubscriptionput.go b/internal/api/client/push/pushsubscriptionput.go index 06575f4ee..4d1c5765e 100644 --- a/internal/api/client/push/pushsubscriptionput.go +++ b/internal/api/client/push/pushsubscriptionput.go @@ -25,6 +25,7 @@ import ( apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/oauth" + "github.com/superseriousbusiness/gotosocial/internal/util" ) // PushSubscriptionPUTHandler swagger:operation PUT /api/v1/push/subscription pushSubscriptionPut @@ -122,6 +123,17 @@ import ( // type: boolean // default: false // 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: // - OAuth2 Bearer: @@ -181,7 +193,8 @@ func (m *Module) PushSubscriptionPUTHandler(c *gin.Context) { 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 { if request.Data == nil { request.Data = &apimodel.WebPushSubscriptionRequestData{} @@ -228,5 +241,12 @@ func validateNormalizeUpdate(request *apimodel.WebPushSubscriptionUpdateRequest) 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 } diff --git a/internal/api/client/push/pushsubscriptionput_test.go b/internal/api/client/push/pushsubscriptionput_test.go index 924e3d475..d9f0e395e 100644 --- a/internal/api/client/push/pushsubscriptionput_test.go +++ b/internal/api/client/push/pushsubscriptionput_test.go @@ -41,6 +41,7 @@ func (suite *PushTestSuite) putSubscription( tokenFixtureName string, alertsMention *bool, alertsStatus *bool, + policy *string, requestJson *string, expectedHTTPStatus int, ) (*apimodel.WebPushSubscription, error) { @@ -68,6 +69,9 @@ func (suite *PushTestSuite) putSubscription( if alertsStatus != nil { ctx.Request.Form["data[alerts][status]"] = []string{strconv.FormatBool(*alertsStatus)} } + if policy != nil { + ctx.Request.Form["data[policy]"] = []string{*policy} + } } // trigger the handler @@ -104,11 +108,13 @@ func (suite *PushTestSuite) TestPutSubscription() { alertsMention := true alertsStatus := false + policy := "followed" subscription, err := suite.putSubscription( accountFixtureName, tokenFixtureName, &alertsMention, &alertsStatus, + &policy, nil, 200, ) @@ -120,6 +126,7 @@ func (suite *PushTestSuite) TestPutSubscription() { suite.False(subscription.Alerts.Status) // Omitted event types should default to off. suite.False(subscription.Alerts.Favourite) + suite.Equal(apimodel.WebPushNotificationPolicyFollowed, subscription.Policy) } } @@ -134,7 +141,8 @@ func (suite *PushTestSuite) TestPutSubscriptionJSON() { "alerts": { "mention": true, "status": false - } + }, + "policy": "followed" } }` subscription, err := suite.putSubscription( @@ -142,6 +150,7 @@ func (suite *PushTestSuite) TestPutSubscriptionJSON() { tokenFixtureName, nil, nil, + nil, &requestJson, 200, ) @@ -153,6 +162,7 @@ func (suite *PushTestSuite) TestPutSubscriptionJSON() { suite.False(subscription.Alerts.Status) // Omitted event types should default to off. suite.False(subscription.Alerts.Favourite) + suite.Equal(apimodel.WebPushNotificationPolicyFollowed, subscription.Policy) } } @@ -170,6 +180,7 @@ func (suite *PushTestSuite) TestPutMissingSubscription() { &alertsMention, &alertsStatus, nil, + nil, 404, ) suite.NoError(err) diff --git a/internal/processing/push/create.go b/internal/processing/push/create.go index 42a67dc19..dc15ccf12 100644 --- a/internal/processing/push/create.go +++ b/internal/processing/push/create.go @@ -24,6 +24,7 @@ import ( "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/id" + "github.com/superseriousbusiness/gotosocial/internal/typeutils" ) // CreateOrReplace creates a Web Push subscription for the given access token, @@ -54,6 +55,7 @@ func (p *Processor) CreateOrReplace( Auth: request.Subscription.Keys.Auth, P256dh: request.Subscription.Keys.P256dh, NotificationFlags: alertsToNotificationFlags(request.Data.Alerts), + Policy: typeutils.APIWebPushNotificationPolicyToWebPushNotificationPolicy(*request.Data.Policy), } if err := p.state.DB.PutWebPushSubscription(ctx, subscription); err != nil { diff --git a/internal/processing/push/update.go b/internal/processing/push/update.go index 370536f9b..94529455a 100644 --- a/internal/processing/push/update.go +++ b/internal/processing/push/update.go @@ -24,6 +24,7 @@ import ( apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" "github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/gtserror" + "github.com/superseriousbusiness/gotosocial/internal/typeutils" ) // Update updates the Web Push subscription for the given access token. @@ -50,10 +51,13 @@ func (p *Processor) Update( // Update it. subscription.NotificationFlags = alertsToNotificationFlags(request.Data.Alerts) + subscription.Policy = typeutils.APIWebPushNotificationPolicyToWebPushNotificationPolicy(*request.Data.Policy) + if err = p.state.DB.UpdateWebPushSubscription( ctx, subscription, "notification_flags", + "policy", ); err != nil { err := gtserror.Newf("couldn't update Web Push subscription for token ID %s: %w", tokenID, err) return nil, gtserror.NewErrorInternalError(err) diff --git a/internal/typeutils/frontendtointernal.go b/internal/typeutils/frontendtointernal.go index 82957ee05..b341aa6ae 100644 --- a/internal/typeutils/frontendtointernal.go +++ b/internal/typeutils/frontendtointernal.go @@ -231,3 +231,17 @@ func APIInteractionPolicyToInteractionPolicy( }, }, 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 +} diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go index 487e8434e..d966c054c 100644 --- a/internal/typeutils/internaltofrontend.go +++ b/internal/typeutils/internaltofrontend.go @@ -3019,6 +3019,20 @@ func (c *Converter) InteractionReqToAPIInteractionReq( }, 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( ctx context.Context, subscription *gtsmodel.WebPushSubscription, @@ -3047,7 +3061,7 @@ func (c *Converter) WebPushSubscriptionToAPIWebPushSubscription( PendingReply: subscription.NotificationFlags.Get(gtsmodel.NotificationPendingReply), PendingReblog: subscription.NotificationFlags.Get(gtsmodel.NotificationPendingReblog), }, - Policy: apimodel.WebPushNotificationPolicyAll, + Policy: webPushNotificationPolicyToAPIWebPushNotificationPolicy(subscription.Policy), Standard: true, }, nil } diff --git a/testrig/testmodels.go b/testrig/testmodels.go index 9a54aba70..1e7dceb56 100644 --- a/testrig/testmodels.go +++ b/testrig/testmodels.go @@ -3610,6 +3610,7 @@ func NewTestWebPushSubscriptions() map[string]*gtsmodel.WebPushSubscription { gtsmodel.NotificationPendingReply, gtsmodel.NotificationPendingReblog, }), + Policy: gtsmodel.WebPushNotificationPolicyAll, }, } }