Refactor bounces package to remove db/queries dependency.

Instead of passing a DB/SQL statement references, instead pass a
callback that inserts a bounce into the DB via the `core` package.
This commit is contained in:
Kailash Nadh 2022-05-01 14:53:01 +05:30
parent b5cd9498b1
commit aa19771307
5 changed files with 37 additions and 25 deletions

View file

@ -603,12 +603,12 @@ func initNotifTemplates(path string, fs stuffbin.FileSystem, i *i18n.I18n, cs *c
// for incoming bounce events.
func initBounceManager(app *App) *bounce.Manager {
opt := bounce.Opt{
BounceCount: ko.MustInt("bounce.count"),
BounceAction: ko.MustString("bounce.action"),
WebhooksEnabled: ko.Bool("bounce.webhooks_enabled"),
SESEnabled: ko.Bool("bounce.ses_enabled"),
SendgridEnabled: ko.Bool("bounce.sendgrid_enabled"),
SendgridKey: ko.String("bounce.sendgrid_key"),
RecordBounceCB: app.core.RecordBounce,
}
// For now, only one mailbox is supported.

View file

@ -174,6 +174,8 @@ func main() {
app.core = core.New(&core.Opt{
Constants: core.Constants{
SendOptinConfirmation: app.constants.SendOptinConfirmation,
MaxBounceCount: ko.MustInt("bounce.count"),
BounceAction: ko.MustString("bounce.action"),
},
Queries: queries,
DB: db,

View file

@ -9,7 +9,6 @@ import (
"github.com/knadh/listmonk/internal/bounce/mailbox"
"github.com/knadh/listmonk/internal/bounce/webhooks"
"github.com/knadh/listmonk/models"
"github.com/lib/pq"
)
const (
@ -27,9 +26,6 @@ type Mailbox interface {
// Opt represents bounce processing options.
type Opt struct {
BounceCount int `json:"count"`
BounceAction string `json:"action"`
MailboxEnabled bool `json:"mailbox_enabled"`
MailboxType string `json:"mailbox_type"`
Mailbox mailbox.Opt `json:"mailbox"`
@ -37,6 +33,8 @@ type Opt struct {
SESEnabled bool `json:"ses_enabled"`
SendgridEnabled bool `json:"sendgrid_enabled"`
SendgridKey string `json:"sendgrid_key"`
RecordBounceCB func(models.Bounce) error
}
// Manager handles e-mail bounces.
@ -106,27 +104,12 @@ func (m *Manager) Run() {
return
}
date := b.CreatedAt
if date.IsZero() {
date = time.Now()
if b.CreatedAt.IsZero() {
b.CreatedAt = time.Now()
}
_, err := m.queries.RecordQuery.Exec(b.SubscriberUUID,
b.Email,
b.CampaignUUID,
b.Type,
b.Source,
b.Meta,
date,
m.opt.BounceCount,
m.opt.BounceAction)
if err != nil {
// Ignore the error if it complained of no subscriber.
if pqErr, ok := err.(*pq.Error); ok && pqErr.Column == "subscriber_id" {
m.log.Printf("bounced subscriber (%s / %s) not found", b.SubscriberUUID, b.Email)
continue
}
m.log.Printf("error recording bounce: %v", err)
if err := m.opt.RecordBounceCB(b); err != nil {
continue
}
}
}

View file

@ -50,6 +50,31 @@ func (c *Core) GetBounce(id int) (models.Bounce, error) {
return out[0], nil
}
// RecordBounce records a new bounce.
func (c *Core) RecordBounce(b models.Bounce) error {
_, err := c.q.RecordBounce.Exec(b.SubscriberUUID,
b.Email,
b.CampaignUUID,
b.Type,
b.Source,
b.Meta,
b.CreatedAt,
c.constants.MaxBounceCount,
c.constants.BounceAction)
if err != nil {
// Ignore the error if it complained of no subscriber.
if pqErr, ok := err.(*pq.Error); ok && pqErr.Column == "subscriber_id" {
c.log.Printf("bounced subscriber (%s / %s) not found", b.SubscriberUUID, b.Email)
return nil
}
c.log.Printf("error recording bounce: %v", err)
}
return err
}
// DeleteBounce deletes a list.
func (c *Core) DeleteBounce(id int) error {
return c.DeleteBounces([]int{id})

View file

@ -36,6 +36,8 @@ type Core struct {
// Constants represents constant config.
type Constants struct {
SendOptinConfirmation bool
MaxBounceCount int
BounceAction string
}
// Hooks contains external function hooks that are required by the core package.