Tracking Push Notifications in a structured logger (notifications.log) (#10823)

* Remove NotificationRegistry table and use structured logging

* Fix ackId for notification sent

* Notification logger at server level

* Remove unused i18n strings
Этот коммит содержится в:
Elias Nahum
2019-05-13 10:53:46 -04:00
коммит произвёл Christopher Speller
родитель 30061df036
Коммит 5b252e8736
23 изменённых файлов: 206 добавлений и 578 удалений

Просмотреть файл

@@ -334,13 +334,6 @@ func pushNotificationAck(c *Context, w http.ResponseWriter, r *http.Request) {
return return
} }
mlog.Debug("Push notification ack received",
mlog.String("ackId", ack.Id),
mlog.String("platform", ack.ClientPlatform),
mlog.String("type", ack.NotificationType),
mlog.Int64("at", ack.ClientReceivedAt),
)
ReturnStatusOK(w) ReturnStatusOK(w)
return return
} }

Просмотреть файл

@@ -24,6 +24,7 @@ type App struct {
Srv *Server Srv *Server
Log *mlog.Logger Log *mlog.Logger
NotificationsLog *mlog.Logger
T goi18n.TranslateFunc T goi18n.TranslateFunc
Session model.Session Session model.Session

Просмотреть файл

@@ -308,16 +308,13 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
) )
} else { } else {
// register that a notification was not sent // register that a notification was not sent
notificationRegistry := model.NotificationRegistry{ a.NotificationsLog.Warn("Notification not sent",
UserId: id, mlog.String("ackId", ""),
PostId: post.Id, mlog.String("type", model.PUSH_TYPE_MESSAGE),
SendStatus: model.PUSH_NOT_SENT, mlog.String("userId", id),
Type: model.PUSH_TYPE_MESSAGE, mlog.String("postId", post.Id),
} mlog.String("status", model.PUSH_NOT_SENT),
_, appErr := a.Srv.Store.NotificationRegistry().Save(&notificationRegistry) )
if appErr != nil {
mlog.Debug(appErr.Error())
}
} }
} }
@@ -343,16 +340,13 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
) )
} else { } else {
// register that a notification was not sent // register that a notification was not sent
notificationRegistry := model.NotificationRegistry{ a.NotificationsLog.Warn("Notification not sent",
UserId: id, mlog.String("ackId", ""),
PostId: post.Id, mlog.String("type", model.PUSH_TYPE_MESSAGE),
SendStatus: model.PUSH_NOT_SENT, mlog.String("userId", id),
Type: model.PUSH_TYPE_MESSAGE, mlog.String("postId", post.Id),
} mlog.String("status", model.PUSH_NOT_SENT),
_, appErr := a.Srv.Store.NotificationRegistry().Save(&notificationRegistry) )
if appErr != nil {
mlog.Debug(appErr.Error())
}
} }
} }
} }

Просмотреть файл

@@ -112,34 +112,30 @@ func (a *App) sendPushNotificationSync(post *model.Post, user *model.User, chann
tmpMessage.SetDeviceIdAndPlatform(session.DeviceId) tmpMessage.SetDeviceIdAndPlatform(session.DeviceId)
tmpMessage.AckId = model.NewId() tmpMessage.AckId = model.NewId()
mlog.Debug(
"Sending push notification",
mlog.String("ackId", tmpMessage.AckId),
mlog.String("deviceId", tmpMessage.DeviceId),
mlog.String("userId", user.Id),
)
err := a.sendToPushProxy(*tmpMessage, session) err := a.sendToPushProxy(*tmpMessage, session)
if err != nil { if err != nil {
mlog.Error( a.NotificationsLog.Error("Notification error",
"Failed to send Push Notification:", mlog.String("ackId", tmpMessage.AckId),
mlog.String("error", err.Error()), mlog.String("type", tmpMessage.Type),
mlog.String("userId", session.UserId), mlog.String("userId", session.UserId),
mlog.String("sessionId", session.Id), mlog.String("postId", tmpMessage.PostId),
mlog.String("deviceId", msg.DeviceId), mlog.String("channelId", tmpMessage.ChannelId),
mlog.String("ackId", msg.AckId), mlog.String("deviceId", tmpMessage.DeviceId),
mlog.String("status", err.Error()),
) )
appErr := a.Srv.Store.NotificationRegistry().UpdateSendStatus(tmpMessage.AckId, model.PUSH_SEND_ERROR+": "+err.Error())
if appErr != nil {
mlog.Debug(appErr.Error())
}
continue continue
} }
appErr := a.Srv.Store.NotificationRegistry().UpdateSendStatus(tmpMessage.AckId, model.PUSH_SEND_SUCCESS) a.NotificationsLog.Info("Notification sent",
if appErr != nil { mlog.String("ackId", tmpMessage.AckId),
mlog.Debug(appErr.Error()) mlog.String("type", tmpMessage.Type),
} mlog.String("userId", session.UserId),
mlog.String("postId", tmpMessage.PostId),
mlog.String("channelId", tmpMessage.ChannelId),
mlog.String("deviceId", tmpMessage.DeviceId),
mlog.String("status", model.PUSH_SEND_SUCCESS),
)
if a.Metrics != nil { if a.Metrics != nil {
a.Metrics.IncrementPostSentPush() a.Metrics.IncrementPostSentPush()
@@ -248,35 +244,30 @@ func (a *App) ClearPushNotificationSync(currentSessionId, userId, channelId stri
tmpMessage.SetDeviceIdAndPlatform(session.DeviceId) tmpMessage.SetDeviceIdAndPlatform(session.DeviceId)
tmpMessage.AckId = model.NewId() tmpMessage.AckId = model.NewId()
mlog.Debug(
"Sending clear push notification",
mlog.String("ackId", tmpMessage.AckId),
mlog.String("deviceId", tmpMessage.DeviceId),
mlog.String("userId", session.UserId),
mlog.String("channelId", channelId), //should we remove the message from the logs?
)
err := a.sendToPushProxy(*tmpMessage, session) err := a.sendToPushProxy(*tmpMessage, session)
if err != nil { if err != nil {
mlog.Error( a.NotificationsLog.Error("Notification error",
"Failed to send Push Notification:", mlog.String("ackId", tmpMessage.AckId),
mlog.String("error", err.Error()), mlog.String("type", tmpMessage.Type),
mlog.String("userId", session.UserId), mlog.String("userId", session.UserId),
mlog.String("sessionId", session.Id), mlog.String("postId", tmpMessage.PostId),
mlog.String("deviceId", msg.DeviceId), mlog.String("channelId", tmpMessage.ChannelId),
mlog.String("ackId", msg.AckId), mlog.String("deviceId", tmpMessage.DeviceId),
mlog.String("status", err.Error()),
) )
appErr := a.Srv.Store.NotificationRegistry().UpdateSendStatus(tmpMessage.AckId, model.PUSH_SEND_ERROR+": "+err.Error())
if appErr != nil {
mlog.Debug(appErr.Error())
}
continue continue
} }
appErr := a.Srv.Store.NotificationRegistry().UpdateSendStatus(tmpMessage.AckId, model.PUSH_SEND_SUCCESS) a.NotificationsLog.Info("Notification sent",
if appErr != nil { mlog.String("ackId", tmpMessage.AckId),
mlog.Debug(appErr.Error()) mlog.String("type", tmpMessage.Type),
} mlog.String("userId", session.UserId),
mlog.String("postId", tmpMessage.PostId),
mlog.String("channelId", tmpMessage.ChannelId),
mlog.String("deviceId", tmpMessage.DeviceId),
mlog.String("status", model.PUSH_SEND_SUCCESS),
)
if a.Metrics != nil { if a.Metrics != nil {
a.Metrics.IncrementPostSentPush() a.Metrics.IncrementPostSentPush()
@@ -343,18 +334,13 @@ func (a *App) StopPushNotificationsHubWorkers() {
func (a *App) sendToPushProxy(msg model.PushNotification, session *model.Session) error { func (a *App) sendToPushProxy(msg model.PushNotification, session *model.Session) error {
msg.ServerId = a.DiagnosticId() msg.ServerId = a.DiagnosticId()
notificationRegistry := model.NotificationRegistry{ a.NotificationsLog.Info("Notification will be sent",
AckId: msg.AckId, mlog.String("ackId", msg.AckId),
DeviceId: msg.DeviceId, mlog.String("type", msg.Type),
UserId: session.UserId, mlog.String("userId", session.UserId),
PostId: msg.PostId, mlog.String("postId", msg.PostId),
Type: msg.Type, mlog.String("status", model.PUSH_SEND_PREPARE),
} )
_, appErr := a.Srv.Store.NotificationRegistry().Save(&notificationRegistry)
if appErr != nil {
return appErr
}
request, err := http.NewRequest("POST", strings.TrimRight(*a.Config().EmailSettings.PushNotificationServer, "/")+model.API_URL_SUFFIX_V1+"/send_push", strings.NewReader(msg.ToJson())) request, err := http.NewRequest("POST", strings.TrimRight(*a.Config().EmailSettings.PushNotificationServer, "/")+model.API_URL_SUFFIX_V1+"/send_push", strings.NewReader(msg.ToJson()))
if err != nil { if err != nil {
@@ -388,10 +374,13 @@ func (a *App) SendAckToPushProxy(ack *model.PushNotificationAck) error {
return nil return nil
} }
appErr := a.Srv.Store.NotificationRegistry().MarkAsReceived(ack.Id, ack.ClientReceivedAt) a.NotificationsLog.Info("Notification received",
if appErr != nil { mlog.String("ackId", ack.Id),
return appErr mlog.String("type", ack.NotificationType),
} mlog.String("deviceType", ack.ClientPlatform),
mlog.Int64("receivedAt", ack.ClientReceivedAt),
mlog.String("status", model.PUSH_RECEIVED),
)
request, err := http.NewRequest( request, err := http.NewRequest(
"POST", "POST",

Просмотреть файл

@@ -99,6 +99,7 @@ func ServerConnector(s *Server) AppOption {
a.Srv = s a.Srv = s
a.Log = s.Log a.Log = s.Log
a.NotificationsLog = s.NotificationsLog
a.AccountMigration = s.AccountMigration a.AccountMigration = s.AccountMigration
a.Cluster = s.Cluster a.Cluster = s.Cluster

Просмотреть файл

@@ -109,6 +109,7 @@ type Server struct {
ImageProxy *imageproxy.ImageProxy ImageProxy *imageproxy.ImageProxy
Log *mlog.Logger Log *mlog.Logger
NotificationsLog *mlog.Logger
joinCluster bool joinCluster bool
startMetrics bool startMetrics bool
@@ -152,7 +153,13 @@ func NewServer(options ...Option) (*Server, error) {
} }
if s.Log == nil { if s.Log == nil {
s.Log = mlog.NewLogger(utils.MloggerConfigFromLoggerConfig(&s.Config().LogSettings)) s.Log = mlog.NewLogger(utils.MloggerConfigFromLoggerConfig(&s.Config().LogSettings, utils.GetLogFileLocation))
}
if s.NotificationsLog == nil {
notificationLogSettings := utils.GetLogSettingsFromNotificationsLogSettings(&s.Config().NotificationLogSettings)
s.NotificationsLog = mlog.NewLogger(utils.MloggerConfigFromLoggerConfig(notificationLogSettings, utils.GetNotificationsLogFileLocation)).
WithCallerSkip(1).With(mlog.String("logSource", "notifications"))
} }
// Redirect default golang logger to this logger // Redirect default golang logger to this logger
@@ -162,7 +169,10 @@ func NewServer(options ...Option) (*Server, error) {
mlog.InitGlobalLogger(s.Log) mlog.InitGlobalLogger(s.Log)
s.logListenerId = s.AddConfigListener(func(_, after *model.Config) { s.logListenerId = s.AddConfigListener(func(_, after *model.Config) {
s.Log.ChangeLevels(utils.MloggerConfigFromLoggerConfig(&after.LogSettings)) s.Log.ChangeLevels(utils.MloggerConfigFromLoggerConfig(&after.LogSettings, utils.GetLogFileLocation))
notificationLogSettings := utils.GetLogSettingsFromNotificationsLogSettings(&after.NotificationLogSettings)
s.NotificationsLog.ChangeLevels(utils.MloggerConfigFromLoggerConfig(notificationLogSettings, utils.GetNotificationsLogFileLocation))
}) })
s.HTTPService = httpservice.MakeHTTPService(s.FakeApp()) s.HTTPService = httpservice.MakeHTTPService(s.FakeApp())

Просмотреть файл

@@ -147,6 +147,15 @@
"EnableWebhookDebugging": true, "EnableWebhookDebugging": true,
"EnableDiagnostics": true "EnableDiagnostics": true
}, },
"NotificationLogSettings": {
"EnableConsole": true,
"ConsoleLevel": "DEBUG",
"ConsoleJson": true,
"EnableFile": true,
"FileLevel": "INFO",
"FileJson": true,
"FileLocation": ""
},
"PasswordSettings": { "PasswordSettings": {
"MinimumLength": 5, "MinimumLength": 5,
"Lowercase": false, "Lowercase": false,

Просмотреть файл

@@ -4790,34 +4790,6 @@
"id": "model.link_metadata.is_valid.url.app_error", "id": "model.link_metadata.is_valid.url.app_error",
"translation": "Link metadata URL must be set" "translation": "Link metadata URL must be set"
}, },
{
"id": "model.notification_registry.ack_id.app_error",
"translation": "Invalid Acknowledge Id"
},
{
"id": "model.notification_registry.create_at.app_error",
"translation": "Create at must be a valid time"
},
{
"id": "model.notification_registry.device_id.app_error",
"translation": "Invalid Device Id"
},
{
"id": "model.notification_registry.post_id.app_error",
"translation": "Invalid Post Id"
},
{
"id": "model.notification_registry.status.app_error",
"translation": "Invalid Send Status"
},
{
"id": "model.notification_registry.type.app_error",
"translation": "Invalid push type, must be \"clear\" or \"message\""
},
{
"id": "model.notification_registry.user_id.app_error",
"translation": "Invalid User Id"
},
{ {
"id": "model.oauth.is_valid.app_id.app_error", "id": "model.oauth.is_valid.app_id.app_error",
"translation": "Invalid app id" "translation": "Invalid app id"
@@ -5890,18 +5862,6 @@
"id": "store.sql_link_metadata.save.app_error", "id": "store.sql_link_metadata.save.app_error",
"translation": "Unable to save the link metadata" "translation": "Unable to save the link metadata"
}, },
{
"id": "store.sql_notification.mark_as_received.app_error",
"translation": "An error occurred marking the notification as received"
},
{
"id": "store.sql_notification.save.app_error",
"translation": "An error occurred while saving the notification registry"
},
{
"id": "store.sql_notification.update_status.app_error",
"translation": "An error occurred while updating the notification status"
},
{ {
"id": "store.sql_oauth.delete.commit_transaction.app_error", "id": "store.sql_oauth.delete.commit_transaction.app_error",
"translation": "Unable to commit transaction" "translation": "Unable to commit transaction"

Просмотреть файл

@@ -912,6 +912,46 @@ func (s *LogSettings) SetDefaults() {
} }
} }
type NotificationLogSettings struct {
EnableConsole *bool `restricted:"true"`
ConsoleLevel *string `restricted:"true"`
ConsoleJson *bool `restricted:"true"`
EnableFile *bool `restricted:"true"`
FileLevel *string `restricted:"true"`
FileJson *bool `restricted:"true"`
FileLocation *string `restricted:"true"`
}
func (s *NotificationLogSettings) SetDefaults() {
if s.EnableConsole == nil {
s.EnableConsole = NewBool(true)
}
if s.ConsoleLevel == nil {
s.ConsoleLevel = NewString("DEBUG")
}
if s.EnableFile == nil {
s.EnableFile = NewBool(true)
}
if s.FileLevel == nil {
s.FileLevel = NewString("INFO")
}
if s.FileLocation == nil {
s.FileLocation = NewString("")
}
if s.ConsoleJson == nil {
s.ConsoleJson = NewBool(true)
}
if s.FileJson == nil {
s.FileJson = NewBool(true)
}
}
type PasswordSettings struct { type PasswordSettings struct {
MinimumLength *int MinimumLength *int
Lowercase *bool Lowercase *bool
@@ -2246,6 +2286,7 @@ type Config struct {
ClientRequirements ClientRequirements ClientRequirements ClientRequirements
SqlSettings SqlSettings SqlSettings SqlSettings
LogSettings LogSettings LogSettings LogSettings
NotificationLogSettings NotificationLogSettings
PasswordSettings PasswordSettings PasswordSettings PasswordSettings
FileSettings FileSettings FileSettings FileSettings
EmailSettings EmailSettings EmailSettings EmailSettings
@@ -2344,6 +2385,7 @@ func (o *Config) SetDefaults() {
o.DataRetentionSettings.SetDefaults() o.DataRetentionSettings.SetDefaults()
o.RateLimitSettings.SetDefaults() o.RateLimitSettings.SetDefaults()
o.LogSettings.SetDefaults() o.LogSettings.SetDefaults()
o.NotificationLogSettings.SetDefaults()
o.JobSettings.SetDefaults() o.JobSettings.SetDefaults()
o.MessageExportSettings.SetDefaults() o.MessageExportSettings.SetDefaults()
o.DisplaySettings.SetDefaults() o.DisplaySettings.SetDefaults()

Просмотреть файл

@@ -6,7 +6,6 @@ package model
import ( import (
"encoding/json" "encoding/json"
"io" "io"
"net/http"
"strings" "strings"
) )
@@ -26,22 +25,12 @@ const (
MHPNS = "https://push.mattermost.com" MHPNS = "https://push.mattermost.com"
PUSH_SEND_PREPARE = "Prepared to send"
PUSH_SEND_SUCCESS = "Successful" PUSH_SEND_SUCCESS = "Successful"
PUSH_SEND_ERROR = "Error"
PUSH_NOT_SENT = "Not Sent due to preferences" PUSH_NOT_SENT = "Not Sent due to preferences"
PUSH_RECEIVED = "Received by device"
) )
type NotificationRegistry struct {
AckId string
CreateAt int64
UserId string
DeviceId string
PostId string
SendStatus string
Type string
ReceiveAt int64
}
type PushNotificationAck struct { type PushNotificationAck struct {
Id string `json:"id"` Id string `json:"id"`
ClientReceivedAt int64 `json:"received_at"` ClientReceivedAt int64 `json:"received_at"`
@@ -104,44 +93,3 @@ func (ack *PushNotificationAck) ToJson() string {
b, _ := json.Marshal(ack) b, _ := json.Marshal(ack)
return string(b) return string(b)
} }
func (o *NotificationRegistry) IsValid() *AppError {
if len(o.AckId) != 26 {
return NewAppError("NotificationRegistry.IsValid", "model.notification_registry.ack_id.app_error", nil, "", http.StatusBadRequest)
}
if o.CreateAt == 0 {
return NewAppError("NotificationRegistry.IsValid", "model.notification_registry.create_at.app_error", nil, "AckId="+o.AckId, http.StatusBadRequest)
}
if len(o.UserId) != 26 {
return NewAppError("NotificationRegistry.IsValid", "model.notification_registry.user_id.app_error", nil, "", http.StatusBadRequest)
}
if len(o.PostId) > 0 && len(o.PostId) != 26 {
return NewAppError("NotificationRegistry.IsValid", "model.notification_registry.post_id.app_error", nil, "", http.StatusBadRequest)
}
if len(o.DeviceId) > 512 {
return NewAppError("NotificationRegistry.IsValid", "model.notification_registry.device_id.app_error", nil, "", http.StatusBadRequest)
}
if len(o.SendStatus) > 4096 {
return NewAppError("NotificationRegistry.IsValid", "model.notification_registry.status.app_error", nil, "", http.StatusBadRequest)
}
if o.Type != PUSH_TYPE_CLEAR && o.Type != PUSH_TYPE_MESSAGE {
return NewAppError("NotificationRegistry.IsValid", "model.notification_registry.type.app_error", nil, "", http.StatusBadRequest)
}
return nil
}
func (o *NotificationRegistry) PreSave() {
if o.AckId == "" {
o.AckId = NewId()
}
o.CreateAt = GetMillis()
}

Просмотреть файл

@@ -195,10 +195,6 @@ func (s *LayeredStore) LinkMetadata() LinkMetadataStore {
return s.DatabaseLayer.LinkMetadata() return s.DatabaseLayer.LinkMetadata()
} }
func (s *LayeredStore) NotificationRegistry() NotificationRegistryStore {
return s.DatabaseLayer.NotificationRegistry()
}
func (s *LayeredStore) MarkSystemRanUnitTests() { func (s *LayeredStore) MarkSystemRanUnitTests() {
s.DatabaseLayer.MarkSystemRanUnitTests() s.DatabaseLayer.MarkSystemRanUnitTests()
} }

Просмотреть файл

@@ -1,81 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package sqlstore
import (
"net/http"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
)
type SqlNotificationRegistryStore struct {
SqlStore
}
func NewSqlNotificationRegistryStore(sqlStore SqlStore) store.NotificationRegistryStore {
s := &SqlNotificationRegistryStore{sqlStore}
for _, db := range sqlStore.GetAllConns() {
table := db.AddTableWithName(model.NotificationRegistry{}, "NotificationRegistry").SetKeys(false, "AckId")
table.ColMap("AckId").SetMaxSize(26)
table.ColMap("UserId").SetMaxSize(26)
table.ColMap("PostId").SetMaxSize(26)
table.ColMap("DeviceId").SetMaxSize(512)
table.ColMap("Type").SetMaxSize(26)
table.ColMap("SendStatus").SetMaxSize(4096)
}
return s
}
func (s *SqlNotificationRegistryStore) CreateIndexesIfNotExists() {
s.CreateIndexIfNotExists("idx_notification_ack_id", "NotificationRegistry", "AckId")
s.CreateIndexIfNotExists("idx_notification_create_at", "NotificationRegistry", "CreateAt")
s.CreateIndexIfNotExists("idx_notification_receive_at", "NotificationRegistry", "ReceiveAt")
s.CreateIndexIfNotExists("idx_notification_post_id", "NotificationRegistry", "PostId")
s.CreateIndexIfNotExists("idx_notification_user_id", "NotificationRegistry", "UserId")
s.CreateIndexIfNotExists("idx_notification_type", "NotificationRegistry", "Type")
}
func (s *SqlNotificationRegistryStore) Save(notification *model.NotificationRegistry) (*model.NotificationRegistry, *model.AppError) {
notification.PreSave()
appErr := notification.IsValid()
if appErr != nil {
return nil, appErr
}
err := s.GetMaster().Insert(notification)
if err != nil {
appErr = model.NewAppError("SqlNotificationRegistryStore.Save", "store.sql_notification.save.app_error", nil, "id="+notification.AckId+", "+err.Error(), http.StatusInternalServerError)
return nil, appErr
}
return notification, nil
}
func (s *SqlNotificationRegistryStore) MarkAsReceived(ackId string, time int64) *model.AppError {
result, err := s.GetMaster().Exec("UPDATE NotificationRegistry SET ReceiveAt = :ReceiveAt WHERE AckId = :AckId AND ReceiveAt = 0", map[string]interface{}{"AckId": ackId, "ReceiveAt": time})
if err != nil {
return model.NewAppError("SqlNotificationRegistryStore.Save", "store.sql_notification.mark_as_received.app_error", nil, "id="+ackId+", "+err.Error(), http.StatusInternalServerError)
}
affected, err := result.RowsAffected()
if err != nil {
return model.NewAppError("SqlNotificationRegistryStore.Save", "store.sql_notification.mark_as_received.app_error", nil, "id="+ackId+", "+err.Error(), http.StatusInternalServerError)
} else if affected != 1 {
return model.NewAppError("SqlNotificationRegistryStore.Save", "store.sql_notification.mark_as_received.app_error", nil, "id="+ackId+", Message already received", http.StatusInternalServerError)
}
return nil
}
func (s *SqlNotificationRegistryStore) UpdateSendStatus(ackId, status string) *model.AppError {
_, err := s.GetMaster().Exec("UPDATE NotificationRegistry SET SendStatus = :Status WHERE AckId = :AckId", map[string]interface{}{"AckId": ackId, "Status": status})
if err != nil {
return model.NewAppError("SqlNotificationRegistryStore.Save", "store.sql_notification.update_status.app_error", nil, "id="+ackId+", "+err.Error(), http.StatusInternalServerError)
}
return nil
}

Просмотреть файл

@@ -1,14 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package sqlstore
import (
"testing"
"github.com/mattermost/mattermost-server/store/storetest"
)
func TestNotificationRegistryStore(t *testing.T) {
StoreTest(t, storetest.TestNotificationRegistryStore)
}

Просмотреть файл

@@ -99,5 +99,4 @@ type SqlStore interface {
UserTermsOfService() store.UserTermsOfServiceStore UserTermsOfService() store.UserTermsOfServiceStore
LinkMetadata() store.LinkMetadataStore LinkMetadata() store.LinkMetadataStore
getQueryBuilder() sq.StatementBuilderType getQueryBuilder() sq.StatementBuilderType
NotificationRegistry() store.NotificationRegistryStore
} }

Просмотреть файл

@@ -99,7 +99,6 @@ type SqlSupplierOldStores struct {
group store.GroupStore group store.GroupStore
UserTermsOfService store.UserTermsOfServiceStore UserTermsOfService store.UserTermsOfServiceStore
linkMetadata store.LinkMetadataStore linkMetadata store.LinkMetadataStore
notificationRegistry store.NotificationRegistryStore
} }
type SqlSupplier struct { type SqlSupplier struct {
@@ -152,7 +151,6 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter
supplier.oldStores.TermsOfService = NewSqlTermsOfServiceStore(supplier, metrics) supplier.oldStores.TermsOfService = NewSqlTermsOfServiceStore(supplier, metrics)
supplier.oldStores.UserTermsOfService = NewSqlUserTermsOfServiceStore(supplier) supplier.oldStores.UserTermsOfService = NewSqlUserTermsOfServiceStore(supplier)
supplier.oldStores.linkMetadata = NewSqlLinkMetadataStore(supplier) supplier.oldStores.linkMetadata = NewSqlLinkMetadataStore(supplier)
supplier.oldStores.notificationRegistry = NewSqlNotificationRegistryStore(supplier)
initSqlSupplierReactions(supplier) initSqlSupplierReactions(supplier)
initSqlSupplierRoles(supplier) initSqlSupplierRoles(supplier)
@@ -1055,10 +1053,6 @@ func (ss *SqlSupplier) LinkMetadata() store.LinkMetadataStore {
return ss.oldStores.linkMetadata return ss.oldStores.linkMetadata
} }
func (ss *SqlSupplier) NotificationRegistry() store.NotificationRegistryStore {
return ss.oldStores.notificationRegistry
}
func (ss *SqlSupplier) DropAllTables() { func (ss *SqlSupplier) DropAllTables() {
ss.master.TruncateTables() ss.master.TruncateTables()
} }

Просмотреть файл

@@ -78,7 +78,6 @@ type Store interface {
TotalMasterDbConnections() int TotalMasterDbConnections() int
TotalReadDbConnections() int TotalReadDbConnections() int
TotalSearchDbConnections() int TotalSearchDbConnections() int
NotificationRegistry() NotificationRegistryStore
} }
type TeamStore interface { type TeamStore interface {
@@ -605,9 +604,3 @@ type LinkMetadataStore interface {
Save(linkMetadata *model.LinkMetadata) StoreChannel Save(linkMetadata *model.LinkMetadata) StoreChannel
Get(url string, timestamp int64) StoreChannel Get(url string, timestamp int64) StoreChannel
} }
type NotificationRegistryStore interface {
Save(notification *model.NotificationRegistry) (*model.NotificationRegistry, *model.AppError)
MarkAsReceived(ackId string, time int64) *model.AppError
UpdateSendStatus(ackId, status string) *model.AppError
}

Просмотреть файл

@@ -780,22 +780,6 @@ func (_m *LayeredStoreDatabaseLayer) Next() store.LayeredStoreSupplier {
return r0 return r0
} }
// NotificationRegistry provides a mock function with given fields:
func (_m *LayeredStoreDatabaseLayer) NotificationRegistry() store.NotificationRegistryStore {
ret := _m.Called()
var r0 store.NotificationRegistryStore
if rf, ok := ret.Get(0).(func() store.NotificationRegistryStore); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.NotificationRegistryStore)
}
}
return r0
}
// OAuth provides a mock function with given fields: // OAuth provides a mock function with given fields:
func (_m *LayeredStoreDatabaseLayer) OAuth() store.OAuthStore { func (_m *LayeredStoreDatabaseLayer) OAuth() store.OAuthStore {
ret := _m.Called() ret := _m.Called()

Просмотреть файл

@@ -1,70 +0,0 @@
// Code generated by mockery v1.0.0. DO NOT EDIT.
// Regenerate this file using `make store-mocks`.
package mocks
import mock "github.com/stretchr/testify/mock"
import model "github.com/mattermost/mattermost-server/model"
// NotificationRegistryStore is an autogenerated mock type for the NotificationRegistryStore type
type NotificationRegistryStore struct {
mock.Mock
}
// MarkAsReceived provides a mock function with given fields: ackId, time
func (_m *NotificationRegistryStore) MarkAsReceived(ackId string, time int64) *model.AppError {
ret := _m.Called(ackId, time)
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(string, int64) *model.AppError); ok {
r0 = rf(ackId, time)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}
// Save provides a mock function with given fields: notification
func (_m *NotificationRegistryStore) Save(notification *model.NotificationRegistry) (*model.NotificationRegistry, *model.AppError) {
ret := _m.Called(notification)
var r0 *model.NotificationRegistry
if rf, ok := ret.Get(0).(func(*model.NotificationRegistry) *model.NotificationRegistry); ok {
r0 = rf(notification)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.NotificationRegistry)
}
}
var r1 *model.AppError
if rf, ok := ret.Get(1).(func(*model.NotificationRegistry) *model.AppError); ok {
r1 = rf(notification)
} else {
if ret.Get(1) != nil {
r1 = ret.Get(1).(*model.AppError)
}
}
return r0, r1
}
// UpdateSendStatus provides a mock function with given fields: ackId, status
func (_m *NotificationRegistryStore) UpdateSendStatus(ackId string, status string) *model.AppError {
ret := _m.Called(ackId, status)
var r0 *model.AppError
if rf, ok := ret.Get(0).(func(string, string) *model.AppError); ok {
r0 = rf(ackId, status)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.AppError)
}
}
return r0
}

Просмотреть файл

@@ -482,22 +482,6 @@ func (_m *SqlStore) MarkSystemRanUnitTests() {
_m.Called() _m.Called()
} }
// NotificationRegistry provides a mock function with given fields:
func (_m *SqlStore) NotificationRegistry() store.NotificationRegistryStore {
ret := _m.Called()
var r0 store.NotificationRegistryStore
if rf, ok := ret.Get(0).(func() store.NotificationRegistryStore); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.NotificationRegistryStore)
}
}
return r0
}
// OAuth provides a mock function with given fields: // OAuth provides a mock function with given fields:
func (_m *SqlStore) OAuth() store.OAuthStore { func (_m *SqlStore) OAuth() store.OAuthStore {
ret := _m.Called() ret := _m.Called()

Просмотреть файл

@@ -256,22 +256,6 @@ func (_m *Store) MarkSystemRanUnitTests() {
_m.Called() _m.Called()
} }
// NotificationRegistry provides a mock function with given fields:
func (_m *Store) NotificationRegistry() store.NotificationRegistryStore {
ret := _m.Called()
var r0 store.NotificationRegistryStore
if rf, ok := ret.Get(0).(func() store.NotificationRegistryStore); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.NotificationRegistryStore)
}
}
return r0
}
// OAuth provides a mock function with given fields: // OAuth provides a mock function with given fields:
func (_m *Store) OAuth() store.OAuthStore { func (_m *Store) OAuth() store.OAuthStore {
ret := _m.Called() ret := _m.Called()

Просмотреть файл

@@ -1,106 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package storetest
import (
"testing"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNotificationRegistryStore(t *testing.T, ss store.Store) {
t.Run("Save", func(t *testing.T) { testNotificationRegistryStoreSave(t, ss) })
t.Run("MarkAsReceived", func(t *testing.T) { testNotificationRegistryStoreMarkAsReceived(t, ss) })
t.Run("UpdateSendStatus", func(t *testing.T) { testNotificationRegistryStoreUpdateSendStatus(t, ss) })
}
func testNotificationRegistryStoreSave(t *testing.T, ss store.Store) {
t.Run("should save notification registry", func(t *testing.T) {
notificationRegistry := &model.NotificationRegistry{
AckId: model.NewId(),
DeviceId: "apple_or_android_device_identifier",
PostId: model.NewId(),
UserId: model.NewId(),
Type: model.PUSH_TYPE_MESSAGE,
}
result, err := ss.NotificationRegistry().Save(notificationRegistry)
require.Nil(t, err)
require.IsType(t, notificationRegistry, result)
assert.NotZero(t, result.CreateAt)
})
t.Run("should save notification registry without AckId", func(t *testing.T) {
notificationRegistry := &model.NotificationRegistry{
DeviceId: "apple_or_android_device_identifier",
PostId: model.NewId(),
UserId: model.NewId(),
Type: model.PUSH_TYPE_MESSAGE,
}
result, err := ss.NotificationRegistry().Save(notificationRegistry)
require.Nil(t, err)
require.IsType(t, notificationRegistry, result)
assert.NotEmpty(t, result.AckId)
assert.NotZero(t, result.CreateAt)
})
t.Run("should fail to save invalid notification registry", func(t *testing.T) {
notificationRegistry := &model.NotificationRegistry{
DeviceId: "apple_or_android_device_identifier",
PostId: model.NewId(),
UserId: model.NewId(),
Type: "garbage",
}
_, err := ss.NotificationRegistry().Save(notificationRegistry)
assert.NotNil(t, err)
})
}
func testNotificationRegistryStoreMarkAsReceived(t *testing.T, ss store.Store) {
notificationRegistry := &model.NotificationRegistry{
DeviceId: "apple_or_android_device_identifier",
PostId: model.NewId(),
UserId: model.NewId(),
Type: model.PUSH_TYPE_MESSAGE,
}
t.Run("should update when the notification was received", func(t *testing.T) {
result, err := ss.NotificationRegistry().Save(notificationRegistry)
if err != nil {
t.Fatal("Notification should have been saved")
}
err = ss.NotificationRegistry().MarkAsReceived(result.AckId, model.GetMillis())
assert.Nil(t, err)
})
}
func testNotificationRegistryStoreUpdateSendStatus(t *testing.T, ss store.Store) {
notificationRegistry := &model.NotificationRegistry{
DeviceId: "apple_or_android_device_identifier",
UserId: model.NewId(),
Type: model.PUSH_TYPE_CLEAR,
}
t.Run("should update when the notification send status", func(t *testing.T) {
result, err := ss.NotificationRegistry().Save(notificationRegistry)
if err != nil {
t.Fatal("Notification should have been saved")
}
err = ss.NotificationRegistry().UpdateSendStatus(result.AckId, "Some Status")
assert.Nil(t, err)
})
}

Просмотреть файл

@@ -50,7 +50,6 @@ type Store struct {
GroupStore mocks.GroupStore GroupStore mocks.GroupStore
UserTermsOfServiceStore mocks.UserTermsOfServiceStore UserTermsOfServiceStore mocks.UserTermsOfServiceStore
LinkMetadataStore mocks.LinkMetadataStore LinkMetadataStore mocks.LinkMetadataStore
NotificationRegistryStore mocks.NotificationRegistryStore
} }
func (s *Store) Team() store.TeamStore { return &s.TeamStore } func (s *Store) Team() store.TeamStore { return &s.TeamStore }
@@ -69,9 +68,6 @@ func (s *Store) Command() store.CommandStore { return &s.Comma
func (s *Store) CommandWebhook() store.CommandWebhookStore { return &s.CommandWebhookStore } func (s *Store) CommandWebhook() store.CommandWebhookStore { return &s.CommandWebhookStore }
func (s *Store) Preference() store.PreferenceStore { return &s.PreferenceStore } func (s *Store) Preference() store.PreferenceStore { return &s.PreferenceStore }
func (s *Store) License() store.LicenseStore { return &s.LicenseStore } func (s *Store) License() store.LicenseStore { return &s.LicenseStore }
func (s *Store) NotificationRegistry() store.NotificationRegistryStore {
return &s.NotificationRegistryStore
}
func (s *Store) Token() store.TokenStore { return &s.TokenStore } func (s *Store) Token() store.TokenStore { return &s.TokenStore }
func (s *Store) Emoji() store.EmojiStore { return &s.EmojiStore } func (s *Store) Emoji() store.EmojiStore { return &s.EmojiStore }
func (s *Store) Status() store.StatusStore { return &s.StatusStore } func (s *Store) Status() store.StatusStore { return &s.StatusStore }
@@ -116,7 +112,6 @@ func (s *Store) AssertExpectations(t mock.TestingT) bool {
&s.CommandWebhookStore, &s.CommandWebhookStore,
&s.PreferenceStore, &s.PreferenceStore,
&s.LicenseStore, &s.LicenseStore,
&s.NotificationRegistryStore,
&s.TokenStore, &s.TokenStore,
&s.EmojiStore, &s.EmojiStore,
&s.StatusStore, &s.StatusStore,

Просмотреть файл

@@ -15,9 +15,12 @@ import (
const ( const (
LOG_ROTATE_SIZE = 10000 LOG_ROTATE_SIZE = 10000
LOG_FILENAME = "mattermost.log" LOG_FILENAME = "mattermost.log"
LOG_NOTIFICATION_FILENAME = "notifications.log"
) )
func MloggerConfigFromLoggerConfig(s *model.LogSettings) *mlog.LoggerConfiguration { type fileLocationFunc func(string) string
func MloggerConfigFromLoggerConfig(s *model.LogSettings, getFileFunc fileLocationFunc) *mlog.LoggerConfiguration {
return &mlog.LoggerConfiguration{ return &mlog.LoggerConfiguration{
EnableConsole: *s.EnableConsole, EnableConsole: *s.EnableConsole,
ConsoleJson: *s.ConsoleJson, ConsoleJson: *s.ConsoleJson,
@@ -25,7 +28,7 @@ func MloggerConfigFromLoggerConfig(s *model.LogSettings) *mlog.LoggerConfigurati
EnableFile: *s.EnableFile, EnableFile: *s.EnableFile,
FileJson: *s.FileJson, FileJson: *s.FileJson,
FileLevel: strings.ToLower(*s.FileLevel), FileLevel: strings.ToLower(*s.FileLevel),
FileLocation: GetLogFileLocation(*s.FileLocation), FileLocation: getFileFunc(*s.FileLocation),
} }
} }
@@ -37,6 +40,26 @@ func GetLogFileLocation(fileLocation string) string {
return filepath.Join(fileLocation, LOG_FILENAME) return filepath.Join(fileLocation, LOG_FILENAME)
} }
func GetNotificationsLogFileLocation(fileLocation string) string {
if fileLocation == "" {
fileLocation, _ = fileutils.FindDir("logs")
}
return filepath.Join(fileLocation, LOG_NOTIFICATION_FILENAME)
}
func GetLogSettingsFromNotificationsLogSettings(notificationLogSettings *model.NotificationLogSettings) *model.LogSettings {
return &model.LogSettings{
ConsoleJson: notificationLogSettings.ConsoleJson,
ConsoleLevel: notificationLogSettings.ConsoleLevel,
EnableConsole: notificationLogSettings.EnableConsole,
EnableFile: notificationLogSettings.EnableFile,
FileJson: notificationLogSettings.FileJson,
FileLevel: notificationLogSettings.FileLevel,
FileLocation: notificationLogSettings.FileLocation,
}
}
// DON'T USE THIS Modify the level on the app logger // DON'T USE THIS Modify the level on the app logger
func DisableDebugLogForTest() { func DisableDebugLogForTest() {
mlog.GloballyDisableDebugLogForTest() mlog.GloballyDisableDebugLogForTest()