Ignore ack and notification counts if notifications are blocked by the device (#27570)
* Ignore performance counts if notifications are blocked by the device * Change the endpoint to allow more information * Add tests and API description * Remove wrong test * Address feedback * Only update the cache when there is no error * Follow same casing as other props * use one single endpoint * Fix tests * Fix i18n --------- Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
b9debc75a0
Коммит
af503d9d45
@@ -674,7 +674,19 @@ func pushNotificationAck(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if ack.NotificationType == model.PushTypeMessage {
|
||||
c.App.CountNotificationAck(model.NotificationTypePush, ack.ClientPlatform)
|
||||
session := c.AppContext.Session()
|
||||
ignoreNotificationACK := session.Props[model.SessionPropDeviceNotificationDisabled] == "true"
|
||||
if ignoreNotificationACK && ack.ClientPlatform == "ios" {
|
||||
// iOS doesn't send ack when the notificications are disabled
|
||||
// so we restore the value the moment we receive an ack
|
||||
c.App.SetExtraSessionProps(session, map[string]string{
|
||||
model.SessionPropDeviceNotificationDisabled: "false",
|
||||
})
|
||||
c.App.ClearSessionCacheForUser(session.UserId)
|
||||
}
|
||||
if !ignoreNotificationACK {
|
||||
c.App.CountNotificationAck(model.NotificationTypePush, ack.ClientPlatform)
|
||||
}
|
||||
}
|
||||
|
||||
err := c.App.SendAckToPushProxy(&ack)
|
||||
|
||||
@@ -885,6 +885,64 @@ func TestPushNotificationAck(t *testing.T) {
|
||||
assert.Equal(t, http.StatusForbidden, resp.Code)
|
||||
assert.NotNil(t, resp.Body)
|
||||
})
|
||||
|
||||
ttcc := []struct {
|
||||
name string
|
||||
propValue string
|
||||
platform string
|
||||
expectedValue string
|
||||
}{
|
||||
{
|
||||
name: "should set session prop device notification disabled to false if an ack is sent from iOS",
|
||||
propValue: "true",
|
||||
platform: "ios",
|
||||
expectedValue: "false",
|
||||
},
|
||||
{
|
||||
name: "no change if empty",
|
||||
propValue: "",
|
||||
platform: "ios",
|
||||
expectedValue: "",
|
||||
},
|
||||
{
|
||||
name: "no change if false",
|
||||
propValue: "false",
|
||||
platform: "ios",
|
||||
expectedValue: "false",
|
||||
},
|
||||
{
|
||||
name: "no change on Android",
|
||||
propValue: "true",
|
||||
platform: "android",
|
||||
expectedValue: "true",
|
||||
},
|
||||
}
|
||||
for _, tc := range ttcc {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defer func() {
|
||||
session.AddProp(model.SessionPropDeviceNotificationDisabled, "")
|
||||
th.Server.Store().Session().UpdateProps(session)
|
||||
th.App.ClearSessionCacheForUser(session.UserId)
|
||||
}()
|
||||
|
||||
session.AddProp(model.SessionPropDeviceNotificationDisabled, tc.propValue)
|
||||
err := th.Server.Store().Session().UpdateProps(session)
|
||||
th.App.ClearSessionCacheForUser(session.UserId)
|
||||
assert.NoError(t, err)
|
||||
|
||||
handler := api.APIHandler(pushNotificationAck)
|
||||
resp := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("POST", "/api/v4/notifications/ack", nil)
|
||||
req.Header.Set(model.HeaderAuth, "Bearer "+session.Token)
|
||||
req.Body = io.NopCloser(bytes.NewBufferString(fmt.Sprintf(`{"id":"123", "is_id_loaded":true, "platform": "%s", "post_id":"%s", "type": "%s"}`, tc.platform, th.BasicPost.Id, model.PushTypeMessage)))
|
||||
|
||||
handler.ServeHTTP(resp, req)
|
||||
updatedSession, _ := th.App.GetSession(th.Client.AuthToken)
|
||||
assert.Equal(t, tc.expectedValue, updatedSession.Props[model.SessionPropDeviceNotificationDisabled])
|
||||
storeSession, _ := th.Server.Store().Session().Get(th.Context, session.Id)
|
||||
assert.Equal(t, tc.expectedValue, storeSession.Props[model.SessionPropDeviceNotificationDisabled])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompleteOnboarding(t *testing.T) {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver/v4"
|
||||
"github.com/mattermost/mattermost/server/public/model"
|
||||
"github.com/mattermost/mattermost/server/public/shared/mlog"
|
||||
|
||||
@@ -74,7 +75,7 @@ func (api *API) InitUser() {
|
||||
api.BaseRoutes.User.Handle("/sessions/revoke", api.APISessionRequired(revokeSession)).Methods(http.MethodPost)
|
||||
api.BaseRoutes.User.Handle("/sessions/revoke/all", api.APISessionRequired(revokeAllSessionsForUser)).Methods(http.MethodPost)
|
||||
api.BaseRoutes.Users.Handle("/sessions/revoke/all", api.APISessionRequired(revokeAllSessionsAllUsers)).Methods(http.MethodPost)
|
||||
api.BaseRoutes.Users.Handle("/sessions/device", api.APISessionRequired(attachDeviceId)).Methods(http.MethodPut)
|
||||
api.BaseRoutes.Users.Handle("/sessions/device", api.APISessionRequired(handleDeviceProps)).Methods(http.MethodPut)
|
||||
api.BaseRoutes.User.Handle("/audits", api.APISessionRequired(getUserAudits)).Methods(http.MethodGet)
|
||||
|
||||
api.BaseRoutes.User.Handle("/tokens", api.APISessionRequired(createUserAccessToken)).Methods(http.MethodPost)
|
||||
@@ -2210,15 +2211,49 @@ func revokeAllSessionsAllUsers(c *Context, w http.ResponseWriter, r *http.Reques
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func attachDeviceId(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
props := model.MapFromJSON(r.Body)
|
||||
func handleDeviceProps(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
receivedProps := model.MapFromJSON(r.Body)
|
||||
deviceId := receivedProps["device_id"]
|
||||
|
||||
deviceId := props["device_id"]
|
||||
if deviceId == "" {
|
||||
c.SetInvalidParam("device_id")
|
||||
newProps := map[string]string{}
|
||||
|
||||
deviceNotificationsDisabled := receivedProps[model.SessionPropDeviceNotificationDisabled]
|
||||
if deviceNotificationsDisabled != "" {
|
||||
if deviceNotificationsDisabled != "false" && deviceNotificationsDisabled != "true" {
|
||||
c.SetInvalidParam(model.SessionPropDeviceNotificationDisabled)
|
||||
return
|
||||
}
|
||||
|
||||
newProps[model.SessionPropDeviceNotificationDisabled] = deviceNotificationsDisabled
|
||||
}
|
||||
|
||||
mobileVersion := receivedProps[model.SessionPropMobileVersion]
|
||||
if mobileVersion != "" {
|
||||
if _, err := semver.Parse(mobileVersion); err != nil {
|
||||
c.SetInvalidParam(model.SessionPropMobileVersion)
|
||||
return
|
||||
}
|
||||
newProps[model.SessionPropMobileVersion] = mobileVersion
|
||||
}
|
||||
|
||||
if deviceId != "" {
|
||||
attachDeviceId(c, w, r, deviceId)
|
||||
}
|
||||
|
||||
if c.Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := c.App.SetExtraSessionProps(c.AppContext.Session(), newProps); err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
c.App.ClearSessionCacheForUser(c.AppContext.Session().UserId)
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func attachDeviceId(c *Context, w http.ResponseWriter, r *http.Request, deviceId string) {
|
||||
auditRec := c.MakeAuditRecord("attachDeviceId", audit.Fail)
|
||||
defer c.LogAuditRec(auditRec)
|
||||
audit.AddEventParameter(auditRec, "device_id", deviceId)
|
||||
@@ -2266,8 +2301,6 @@ func attachDeviceId(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
auditRec.Success()
|
||||
c.LogAudit("")
|
||||
|
||||
ReturnStatusOK(w)
|
||||
}
|
||||
|
||||
func getUserAudits(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -3691,7 +3691,7 @@ func TestAttachDeviceId(t *testing.T) {
|
||||
*cfg.ServiceSettings.SiteURL = tc.SiteURL
|
||||
})
|
||||
|
||||
resp, err := th.Client.AttachDeviceId(context.Background(), deviceId)
|
||||
resp, err := th.Client.AttachDeviceProps(context.Background(), map[string]string{"device_id": deviceId})
|
||||
require.NoError(t, err)
|
||||
|
||||
cookies := resp.Header.Get("Set-Cookie")
|
||||
@@ -3704,19 +3704,88 @@ func TestAttachDeviceId(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid device id", func(t *testing.T) {
|
||||
resp, err := th.Client.AttachDeviceId(context.Background(), "")
|
||||
require.Error(t, err)
|
||||
CheckBadRequestStatus(t, resp)
|
||||
})
|
||||
|
||||
t.Run("not logged in", func(t *testing.T) {
|
||||
th.Client.Logout(context.Background())
|
||||
|
||||
resp, err := th.Client.AttachDeviceId(context.Background(), "")
|
||||
resp, err := th.Client.AttachDeviceProps(context.Background(), map[string]string{})
|
||||
require.Error(t, err)
|
||||
CheckUnauthorizedStatus(t, resp)
|
||||
})
|
||||
|
||||
// Props related tests
|
||||
|
||||
client := th.CreateClient()
|
||||
th.LoginBasicWithClient(client)
|
||||
|
||||
resetSession := func(session *model.Session) {
|
||||
session.AddProp(model.SessionPropDeviceNotificationDisabled, "")
|
||||
session.AddProp(model.SessionPropMobileVersion, "")
|
||||
th.Server.Store().Session().UpdateProps(session)
|
||||
th.App.ClearSessionCacheForUser(session.UserId)
|
||||
}
|
||||
|
||||
t.Run("No props will return ok and no changes in the session", func(t *testing.T) {
|
||||
session, _ := th.App.GetSession(client.AuthToken)
|
||||
defer resetSession(session)
|
||||
res, err := client.AttachDeviceProps(context.Background(), map[string]string{})
|
||||
assert.NoError(t, err)
|
||||
|
||||
updatedSession, _ := th.App.GetSession(client.AuthToken)
|
||||
storeSession, _ := th.Server.Store().Session().Get(th.Context, session.Id)
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||
assert.Equal(t, session.Props, updatedSession.Props)
|
||||
assert.Equal(t, session.Props, storeSession.Props)
|
||||
})
|
||||
t.Run("Unknown props will be ignored, returning ok and no changes in the session", func(t *testing.T) {
|
||||
session, _ := th.App.GetSession(client.AuthToken)
|
||||
defer resetSession(session)
|
||||
res, err := client.AttachDeviceProps(context.Background(), map[string]string{"unknownProp": "foo"})
|
||||
assert.NoError(t, err)
|
||||
|
||||
updatedSession, _ := th.App.GetSession(client.AuthToken)
|
||||
storeSession, _ := th.Server.Store().Session().Get(th.Context, session.Id)
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||
assert.Equal(t, session.Props, updatedSession.Props)
|
||||
assert.Equal(t, session.Props, storeSession.Props)
|
||||
})
|
||||
t.Run("Invalid disabled notification prop will return an error and no changes in the session", func(t *testing.T) {
|
||||
session, _ := th.App.GetSession(client.AuthToken)
|
||||
defer resetSession(session)
|
||||
res, err := client.AttachDeviceProps(context.Background(), map[string]string{model.SessionPropDeviceNotificationDisabled: "foo"})
|
||||
assert.Error(t, err)
|
||||
|
||||
updatedSession, _ := th.App.GetSession(client.AuthToken)
|
||||
storeSession, _ := th.Server.Store().Session().Get(th.Context, session.Id)
|
||||
assert.Equal(t, http.StatusBadRequest, res.StatusCode)
|
||||
assert.Equal(t, session.Props, updatedSession.Props)
|
||||
assert.Equal(t, session.Props, storeSession.Props)
|
||||
})
|
||||
t.Run("Invalid version will return an error and no changes in the session", func(t *testing.T) {
|
||||
session, _ := th.App.GetSession(client.AuthToken)
|
||||
defer resetSession(session)
|
||||
res, err := client.AttachDeviceProps(context.Background(), map[string]string{model.SessionPropMobileVersion: "foo"})
|
||||
assert.Error(t, err)
|
||||
|
||||
updatedSession, _ := th.App.GetSession(client.AuthToken)
|
||||
storeSession, _ := th.Server.Store().Session().Get(th.Context, session.Id)
|
||||
assert.Equal(t, http.StatusBadRequest, res.StatusCode)
|
||||
assert.Equal(t, session.Props, updatedSession.Props)
|
||||
assert.Equal(t, session.Props, storeSession.Props)
|
||||
})
|
||||
t.Run("Will update props", func(t *testing.T) {
|
||||
session, _ := th.App.GetSession(client.AuthToken)
|
||||
defer resetSession(session)
|
||||
res, err := client.AttachDeviceProps(context.Background(), map[string]string{model.SessionPropDeviceNotificationDisabled: "true", model.SessionPropMobileVersion: "2.19.0"})
|
||||
assert.NoError(t, err)
|
||||
|
||||
updatedSession, _ := th.App.GetSession(client.AuthToken)
|
||||
storeSession, _ := th.Server.Store().Session().Get(th.Context, session.Id)
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||
assert.Equal(t, "true", updatedSession.Props[model.SessionPropDeviceNotificationDisabled])
|
||||
assert.Equal(t, "true", storeSession.Props[model.SessionPropDeviceNotificationDisabled])
|
||||
assert.Equal(t, "2.19.0", updatedSession.Props[model.SessionPropMobileVersion])
|
||||
assert.Equal(t, "2.19.0", storeSession.Props[model.SessionPropMobileVersion])
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetUserAudits(t *testing.T) {
|
||||
|
||||
Ссылка в новой задаче
Block a user