fake-smtp/internal/query/get_inbox.go

240 lines
4.1 KiB
Go
Raw Normal View History

2020-04-17 17:53:01 +02:00
package query
import (
"context"
2020-11-03 11:49:31 +01:00
"strings"
"time"
2020-04-17 17:53:01 +02:00
"forge.cadoles.com/wpetit/fake-smtp/internal/model"
"forge.cadoles.com/wpetit/fake-smtp/internal/storm"
2020-11-03 11:49:31 +01:00
stormdb "github.com/asdine/storm/v3"
"github.com/asdine/storm/v3/q"
2020-04-17 17:53:01 +02:00
"github.com/pkg/errors"
"gitlab.com/wpetit/goweb/cqrs"
"gitlab.com/wpetit/goweb/middleware/container"
)
2020-11-03 11:49:31 +01:00
type InboxSearch struct {
To string
From string
Body string
Subject string
2022-02-18 10:14:16 +01:00
Headers map[string]string
2020-11-03 11:49:31 +01:00
After time.Time
Before time.Time
}
2020-04-17 17:53:01 +02:00
type GetInboxRequest struct {
OrderBy string
Limit int
Skip int
Reverse bool
2020-11-03 11:49:31 +01:00
Search *InboxSearch
2020-04-17 17:53:01 +02:00
}
type InboxData struct {
Emails []*model.Email
}
func HandleGetInbox(ctx context.Context, qry cqrs.Query) (interface{}, error) {
req, ok := qry.Request().(*GetInboxRequest)
if !ok {
return nil, cqrs.ErrUnexpectedRequest
}
ctn, err := container.From(ctx)
if err != nil {
return nil, errors.Wrap(err, "could not retrieve service container")
}
db, err := storm.From(ctn)
if err != nil {
return nil, errors.Wrap(err, "could not retrieve storm service")
}
emails := make([]*model.Email, 0)
2020-11-03 11:49:31 +01:00
var query stormdb.Query
if req.Search != nil {
matchers := make([]q.Matcher, 0)
if req.Search.Body != "" {
matchers = append(matchers, q.Or(
q.Re("HTML", req.Search.Body),
q.Re("Text", req.Search.Body),
))
}
if req.Search.Subject != "" {
matchers = append(matchers, q.Re("Subject", req.Search.Subject))
}
query = db.Select(matchers...)
} else {
query = db.Select()
}
2020-04-17 17:53:01 +02:00
if req.OrderBy != "" {
query = query.OrderBy(req.OrderBy)
} else {
query = query.OrderBy("SentAt").Reverse()
}
if req.Reverse {
query = query.Reverse()
}
2020-11-03 11:49:31 +01:00
if req.Limit != 0 {
query = query.Limit(req.Limit)
}
if req.Skip != 0 {
query = query.Limit(req.Skip)
}
2020-04-17 17:53:01 +02:00
if err := query.Find(&emails); err != nil {
if err == storm.ErrNotFound {
return &InboxData{emails}, nil
}
return nil, errors.Wrap(err, "could not retrieve emails")
}
2020-11-03 11:49:31 +01:00
if req.Search == nil {
return &InboxData{emails}, nil
}
2022-02-18 15:16:42 +01:00
filtered := filterEmails(emails, req.Search)
2020-11-03 11:49:31 +01:00
2022-02-18 15:16:42 +01:00
return &InboxData{filtered}, nil
}
2020-11-03 11:49:31 +01:00
2022-02-18 15:16:42 +01:00
var matchers = []emailMatcherFunc{
matchTo,
matchFrom,
matchBefore,
matchAfter,
matchHeaders,
}
2020-11-03 11:49:31 +01:00
2022-02-18 15:16:42 +01:00
type emailMatcherFunc func(*model.Email, *InboxSearch) bool
2020-11-03 11:49:31 +01:00
2022-02-18 15:16:42 +01:00
func matchTo(eml *model.Email, search *InboxSearch) bool {
if search.To == "" {
return true
}
2020-11-03 11:49:31 +01:00
2022-02-18 15:16:42 +01:00
found := false
for _, addr := range eml.To {
if strings.Contains(addr.Name, search.To) || strings.Contains(addr.Address, search.To) {
found = true
break
2020-11-03 11:49:31 +01:00
}
2022-02-18 15:16:42 +01:00
}
2020-11-03 11:49:31 +01:00
2022-02-18 15:16:42 +01:00
return found
}
2020-11-03 11:49:31 +01:00
2022-02-18 15:16:42 +01:00
func matchFrom(eml *model.Email, search *InboxSearch) bool {
if search.From == "" {
return true
}
2020-11-03 11:49:31 +01:00
2022-02-18 15:16:42 +01:00
found := false
2020-11-03 11:49:31 +01:00
2022-02-18 15:16:42 +01:00
for _, addr := range eml.From {
if strings.Contains(addr.Name, search.From) || strings.Contains(addr.Address, search.From) {
found = true
2020-11-03 11:49:31 +01:00
2022-02-18 15:16:42 +01:00
break
2020-11-03 11:49:31 +01:00
}
2022-02-18 15:16:42 +01:00
}
2020-11-03 11:49:31 +01:00
2022-02-18 15:16:42 +01:00
return found
}
func matchAfter(eml *model.Email, search *InboxSearch) bool {
if search.After.IsZero() {
return true
}
return eml.SentAt.After(search.After)
}
2020-11-03 11:49:31 +01:00
2022-02-18 15:16:42 +01:00
func matchBefore(eml *model.Email, search *InboxSearch) bool {
if search.Before.IsZero() {
return true
}
2022-02-18 10:14:16 +01:00
2022-02-18 15:16:42 +01:00
return eml.SentAt.Before(search.Before)
}
2022-02-18 10:14:16 +01:00
2022-02-18 15:16:42 +01:00
func matchHeaders(eml *model.Email, search *InboxSearch) bool {
if eml.Headers == nil {
return true
}
2022-02-18 10:14:16 +01:00
2022-02-18 15:16:42 +01:00
matches := true
2022-02-18 10:14:16 +01:00
2022-02-18 15:16:42 +01:00
for searchKey, searchValue := range search.Headers {
for headerKey, headerValues := range eml.Headers {
if searchKey != headerKey {
continue
}
matchesHeader := true
for _, hv := range headerValues {
if !strings.Contains(hv, searchValue) {
matchesHeader = false
2022-02-18 10:14:16 +01:00
break
}
}
2022-02-18 15:16:42 +01:00
if !matchesHeader {
matches = false
break
2022-02-18 10:14:16 +01:00
}
}
2022-02-18 15:16:42 +01:00
if !matches {
break
2020-11-03 11:49:31 +01:00
}
}
2022-02-18 15:16:42 +01:00
return matches
}
func and(matchers ...emailMatcherFunc) emailMatcherFunc {
return func(eml *model.Email, search *InboxSearch) bool {
for _, match := range matchers {
if !match(eml, search) {
return false
}
}
return true
}
}
func filterEmails(emails []*model.Email, search *InboxSearch) []*model.Email {
filtered := make([]*model.Email, 0)
match := and(matchers...)
for _, eml := range emails {
if !match(eml, search) {
continue
}
filtered = append(filtered, eml)
}
return filtered
2020-04-17 17:53:01 +02:00
}