Add notification to db routing

This commit is contained in:
kolaente 2021-02-07 20:59:18 +01:00
parent c39413bd77
commit 402fe2b7df
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
8 changed files with 233 additions and 14 deletions

View File

@ -95,6 +95,11 @@ func sendMail(opts *Opts) *gomail.Message {
// SendMail puts a mail in the queue
func SendMail(opts *Opts) {
if isUnderTest {
sentMails = append(sentMails, opts)
return
}
m := sendMail(opts)
Queue <- m
}

48
pkg/mail/testing.go Normal file
View File

@ -0,0 +1,48 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public Licensee 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 Licensee for more details.
//
// You should have received a copy of the GNU Affero General Public Licensee
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package mail
import (
"reflect"
"testing"
"github.com/stretchr/testify/assert"
)
var (
isUnderTest bool
sentMails []*Opts
)
// Fake stops any mails from being sent and instead allows for recording and querying them.
func Fake() {
isUnderTest = true
sentMails = nil
}
// AssertSent asserts if a mail has been sent
func AssertSent(t *testing.T, opts *Opts) {
var found bool
for _, testMail := range sentMails {
if reflect.DeepEqual(testMail, opts) {
found = true
break
}
}
assert.True(t, found, "Failed to assert mail '%v' has been sent.", opts)
}

View File

@ -17,8 +17,9 @@
package migration
import (
"src.techknowlogick.com/xormigrate"
"time"
"src.techknowlogick.com/xormigrate"
"xorm.io/xorm"
)

View File

@ -17,10 +17,11 @@
package models
import (
"strconv"
"code.vikunja.io/api/pkg/config"
"code.vikunja.io/api/pkg/notifications"
"code.vikunja.io/api/pkg/user"
"strconv"
)
// ReminderDueNotification represents a ReminderDueNotification notification

View File

@ -17,9 +17,10 @@
package models
import (
"code.vikunja.io/api/pkg/notifications"
"time"
"code.vikunja.io/api/pkg/notifications"
"code.vikunja.io/api/pkg/db"
"xorm.io/xorm"

View File

@ -0,0 +1,55 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public Licensee 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 Licensee for more details.
//
// You should have received a copy of the GNU Affero General Public Licensee
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package notifications
import (
"os"
"testing"
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/mail"
"code.vikunja.io/api/pkg/config"
)
// SetupTests initializes all db tests
func SetupTests() {
var err error
x, err := db.CreateTestEngine()
if err != nil {
log.Fatal(err)
}
err = x.Sync2(&DatabaseNotification{})
if err != nil {
log.Fatal(err)
}
}
// TestMain is the main test function used to bootstrap the test env
func TestMain(m *testing.M) {
// Set default config
config.InitDefaultConfig()
// We need to set the root path even if we're not using the config, otherwise fixtures are not loaded correctly
config.ServiceRootpath.Set(os.Getenv("VIKUNJA_SERVICE_ROOTPATH"))
SetupTests()
mail.Fake()
os.Exit(m.Run())
}

View File

@ -17,8 +17,10 @@
package notifications
import (
"code.vikunja.io/api/pkg/db"
"encoding/json"
"time"
"code.vikunja.io/api/pkg/db"
)
// Notification is a notification which can be sent via mail or db.
@ -29,7 +31,7 @@ type Notification interface {
// Notifiable is an entity which can be notified. Usually a user.
type Notifiable interface {
// Should return the email adress this notifiable has.
// Should return the email address this notifiable has.
RouteForMail() string
// Should return the id of the notifiable entity
RouteForDB() int64
@ -55,27 +57,47 @@ func (d *DatabaseNotification) TableName() string {
}
// Notify notifies a notifiable of a notification
func Notify(notifiable Notifiable, notification Notification) error {
mail := notification.ToMail()
if mail != nil {
err := SendMail(mail)
if err != nil {
return err
}
func Notify(notifiable Notifiable, notification Notification) (err error) {
err = notifyMail(notifiable, notification)
if err != nil {
return
}
return notifyDB(notifiable, notification)
}
func notifyMail(notifiable Notifiable, notification Notification) error {
mail := notification.ToMail()
if mail == nil {
return nil
}
mail.To(notifiable.RouteForMail())
return SendMail(mail)
}
func notifyDB(notifiable Notifiable, notification Notification) (err error) {
dbContent := notification.ToDB()
if dbContent == nil {
return nil
}
content, err := json.Marshal(dbContent)
if err != nil {
return err
}
s := db.NewSession()
dbNotification := &DatabaseNotification{
NotifiableID: notifiable.RouteForDB(),
Notification: dbContent,
Notification: content,
}
if _, err := s.Insert(dbNotification); err != nil {
_, err = s.Insert(dbNotification)
if err != nil {
_ = s.Rollback()
return err
}

View File

@ -0,0 +1,86 @@
// Vikunja is a to-do list application to facilitate your life.
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public Licensee 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 Licensee for more details.
//
// You should have received a copy of the GNU Affero General Public Licensee
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package notifications
import (
"testing"
"code.vikunja.io/api/pkg/db"
"github.com/stretchr/testify/assert"
"xorm.io/xorm/schemas"
)
type testNotification struct {
Test string
OtherValue int64
}
// ToMail returns the mail notification for testNotification
func (n *testNotification) ToMail() *Mail {
return NewMail().
Subject("Test Notification").
Line(n.Test)
}
// ToDB returns the testNotification notification in a format which can be saved in the db
func (n *testNotification) ToDB() interface{} {
data := make(map[string]interface{}, 2)
data["test"] = n.Test
data["other_value"] = n.OtherValue
return data
}
type testNotifiable struct {
}
// RouteForMail routes a test notification for mail
func (t *testNotifiable) RouteForMail() string {
return "some@email.com"
}
// RouteForDB routes a test notification for db
func (t *testNotifiable) RouteForDB() int64 {
return 42
}
func TestNotify(t *testing.T) {
tn := &testNotification{
Test: "somethingsomething",
OtherValue: 42,
}
tnf := &testNotifiable{}
err := Notify(tnf, tn)
assert.NoError(t, err)
vals := map[string]interface{}{
"notifiable_id": 42,
"notification": "'{\"other_value\":42,\"test\":\"somethingsomething\"}'",
}
if db.Type() == schemas.POSTGRES {
vals["notification::jsonb"] = vals["notification"].(string) + "::jsonb"
delete(vals, "notification")
}
if db.Type() == schemas.SQLITE {
vals["CAST(notification AS BLOB)"] = "CAST(" + vals["notification"].(string) + " AS BLOB)"
delete(vals, "notification")
}
db.AssertExists(t, "notifications", vals, true)
}